rippled
Loading...
Searching...
No Matches
Feature.cpp
1#include <xrpl/basics/Slice.h>
2#include <xrpl/basics/base_uint.h>
3#include <xrpl/basics/contract.h>
4#include <xrpl/beast/utility/instrumentation.h>
5#include <xrpl/protocol/Feature.h>
6#include <xrpl/protocol/digest.h>
7
8#include <boost/container_hash/hash.hpp>
9#include <boost/multi_index/hashed_index.hpp>
10#include <boost/multi_index/indexed_by.hpp>
11#include <boost/multi_index/member.hpp>
12#include <boost/multi_index/random_access_index.hpp>
13#include <boost/multi_index/tag.hpp>
14#include <boost/multi_index_container.hpp>
15
16#include <atomic>
17#include <cstddef>
18#include <map>
19#include <optional>
20#include <string>
21
22namespace xrpl {
23
24inline std::size_t
26{
27 std::size_t seed = 0;
28 using namespace boost;
29 for (auto const& n : feature)
30 hash_combine(seed, n);
31 return seed;
32}
33
34namespace {
35
36enum class Supported : bool { no = false, yes };
37
38// *NOTE*
39//
40// Features, or Amendments as they are called elsewhere, are enabled on the
41// network at some specific time based on Validator voting. Features are
42// enabled using run-time conditionals based on the state of the amendment.
43// There is value in retaining that conditional code for some time after
44// the amendment is enabled to make it simple to replay old transactions.
45// However, once an amendment has been enabled for, say, more than two years
46// then retaining that conditional code has less value since it is
47// uncommon to replay such old transactions.
48//
49// Starting in January of 2020 Amendment conditionals from before January
50// 2018 are being removed. So replaying any ledger from before January
51// 2018 needs to happen on an older version of the server code. There's
52// a log message in Application.cpp that warns about replaying old ledgers.
53//
54// At some point in the future someone may wish to remove amendment
55// conditional code for amendments that were enabled after January 2018.
56// When that happens then the log message in Application.cpp should be
57// updated.
58//
59// Generally, amendments which introduce new features should be set as
60// "VoteBehavior::DefaultNo" whereas in rare cases, amendments that fix
61// critical bugs should be set as "VoteBehavior::DefaultYes", if off-chain
62// consensus is reached amongst reviewers, validator operators, and other
63// participants.
64
65class FeatureCollections
66{
67 struct Feature
68 {
69 std::string name;
70 uint256 feature;
71
72 Feature() = delete;
73 explicit Feature(std::string const& name_, uint256 const& feature_) : name(name_), feature(feature_)
74 {
75 }
76
77 // These structs are used by the `features` multi_index_container to
78 // provide access to the features collection by size_t index, string
79 // name, and uint256 feature identifier
80 struct byIndex
81 {
82 };
83 struct byName
84 {
85 };
86 struct byFeature
87 {
88 };
89 };
90
91 // Intermediate types to help with readability
92 template <class tag, typename Type, Type Feature::*PtrToMember>
93 using feature_hashed_unique = boost::multi_index::
94 hashed_unique<boost::multi_index::tag<tag>, boost::multi_index::member<Feature, Type, PtrToMember>>;
95
96 // Intermediate types to help with readability
97 using feature_indexing = boost::multi_index::indexed_by<
98 boost::multi_index::random_access<boost::multi_index::tag<Feature::byIndex>>,
99 feature_hashed_unique<Feature::byFeature, uint256, &Feature::feature>,
100 feature_hashed_unique<Feature::byName, std::string, &Feature::name>>;
101
102 // This multi_index_container provides access to the features collection by
103 // name, index, and uint256 feature identifier
104 boost::multi_index::multi_index_container<Feature, feature_indexing> features;
107 std::size_t upVotes = 0;
108 std::size_t downVotes = 0;
109 mutable std::atomic<bool> readOnly = false;
110
111 // These helper functions provide access to the features collection by name,
112 // index, and uint256 feature identifier, so the details of
113 // multi_index_container can be hidden
114 Feature const&
115 getByIndex(size_t i) const
116 {
117 if (i >= features.size())
118 LogicError("Invalid FeatureBitset index");
119 auto const& sequence = features.get<Feature::byIndex>();
120 return sequence[i];
121 }
122 size_t
123 getIndex(Feature const& feature) const
124 {
125 auto const& sequence = features.get<Feature::byIndex>();
126 auto const it_to = sequence.iterator_to(feature);
127 return it_to - sequence.begin();
128 }
129 Feature const*
130 getByFeature(uint256 const& feature) const
131 {
132 auto const& feature_index = features.get<Feature::byFeature>();
133 auto const feature_it = feature_index.find(feature);
134 return feature_it == feature_index.end() ? nullptr : &*feature_it;
135 }
136 Feature const*
137 getByName(std::string const& name) const
138 {
139 auto const& name_index = features.get<Feature::byName>();
140 auto const name_it = name_index.find(name);
141 return name_it == name_index.end() ? nullptr : &*name_it;
142 }
143
144public:
145 FeatureCollections();
146
148 getRegisteredFeature(std::string const& name) const;
149
150 uint256
151 registerFeature(std::string const& name, Supported support, VoteBehavior vote);
152
154 bool
156
158 featureToBitsetIndex(uint256 const& f) const;
159
160 uint256 const&
161 bitsetIndexToFeature(size_t i) const;
162
164 featureToName(uint256 const& f) const;
165
168 allAmendments() const
169 {
170 return all;
171 }
172
177 supportedAmendments() const
178 {
179 return supported;
180 }
181
185 {
186 return downVotes;
187 }
188
192 {
193 return upVotes;
194 }
195};
196
197//------------------------------------------------------------------------------
198
199FeatureCollections::FeatureCollections()
200{
201 features.reserve(xrpl::detail::numFeatures);
202}
203
205FeatureCollections::getRegisteredFeature(std::string const& name) const
206{
207 XRPL_ASSERT(readOnly.load(), "xrpl::FeatureCollections::getRegisteredFeature : startup completed");
208 Feature const* feature = getByName(name);
209 if (feature)
210 return feature->feature;
211 return std::nullopt;
212}
213
214void
215check(bool condition, char const* logicErrorMessage)
216{
217 if (!condition)
218 LogicError(logicErrorMessage);
219}
220
222FeatureCollections::registerFeature(std::string const& name, Supported support, VoteBehavior vote)
223{
224 check(!readOnly, "Attempting to register a feature after startup.");
225 check(
226 support == Supported::yes || vote == VoteBehavior::DefaultNo,
227 "Invalid feature parameters. Must be supported to be up-voted.");
228 Feature const* i = getByName(name);
229 if (!i)
230 {
231 check(features.size() < detail::numFeatures, "More features defined than allocated.");
232
233 auto const f = sha512Half(Slice(name.data(), name.size()));
234
235 features.emplace_back(name, f);
236
237 auto const getAmendmentSupport = [=]() {
238 if (vote == VoteBehavior::Obsolete)
240 return support == Supported::yes ? AmendmentSupport::Supported : AmendmentSupport::Unsupported;
241 };
242 all.emplace(name, getAmendmentSupport());
243
244 if (support == Supported::yes)
245 {
246 supported.emplace(name, vote);
247
248 if (vote == VoteBehavior::DefaultYes)
249 ++upVotes;
250 else
251 ++downVotes;
252 }
253 check(upVotes + downVotes == supported.size(), "Feature counting logic broke");
254 check(supported.size() <= features.size(), "More supported features than defined features");
255 check(features.size() == all.size(), "The 'all' features list is populated incorrectly");
256 return f;
257 }
258 else
259 // Each feature should only be registered once
260 LogicError("Duplicate feature registration");
261}
262
264bool
265FeatureCollections::registrationIsDone()
266{
267 readOnly = true;
268 return true;
269}
270
271size_t
272FeatureCollections::featureToBitsetIndex(uint256 const& f) const
273{
274 XRPL_ASSERT(readOnly.load(), "xrpl::FeatureCollections::featureToBitsetIndex : startup completed");
275
276 Feature const* feature = getByFeature(f);
277 if (!feature)
278 LogicError("Invalid Feature ID");
279
280 return getIndex(*feature);
281}
282
283uint256 const&
284FeatureCollections::bitsetIndexToFeature(size_t i) const
285{
286 XRPL_ASSERT(readOnly.load(), "xrpl::FeatureCollections::bitsetIndexToFeature : startup completed");
287 Feature const& feature = getByIndex(i);
288 return feature.feature;
289}
290
292FeatureCollections::featureToName(uint256 const& f) const
293{
294 XRPL_ASSERT(readOnly.load(), "xrpl::FeatureCollections::featureToName : startup completed");
295 Feature const* feature = getByFeature(f);
296 return feature ? feature->name : to_string(f);
297}
298
299static FeatureCollections featureCollections;
300
301} // namespace
302
306{
307 return featureCollections.allAmendments();
308}
309
315{
316 return featureCollections.supportedAmendments();
317}
318
322{
323 return featureCollections.numDownVotedAmendments();
324}
325
329{
330 return featureCollections.numUpVotedAmendments();
331}
332
333//------------------------------------------------------------------------------
334
337{
338 return featureCollections.getRegisteredFeature(name);
339}
340
343{
344 return featureCollections.registerFeature(name, support, vote);
345}
346
347// Retired features are in the ledger and have no code controlled by the
348// feature. They need to be supported, but do not need to be voted on.
351{
352 return registerFeature(name, Supported::yes, VoteBehavior::Obsolete);
353}
354
356bool
358{
359 return featureCollections.registrationIsDone();
360}
361
362size_t
364{
365 return featureCollections.featureToBitsetIndex(f);
366}
367
370{
371 return featureCollections.bitsetIndexToFeature(i);
372}
373
376{
377 return featureCollections.featureToName(f);
378}
379
380// All known amendments must be registered either here or below with the
381// "retired" amendments
382
383#pragma push_macro("XRPL_FEATURE")
384#undef XRPL_FEATURE
385#pragma push_macro("XRPL_FIX")
386#undef XRPL_FIX
387#pragma push_macro("XRPL_RETIRE_FEATURE")
388#undef XRPL_RETIRE_FEATURE
389#pragma push_macro("XRPL_RETIRE_FIX")
390#undef XRPL_RETIRE_FIX
391
392#define XRPL_FEATURE(name, supported, vote) uint256 const feature##name = registerFeature(#name, supported, vote);
393#define XRPL_FIX(name, supported, vote) uint256 const fix##name = registerFeature("fix" #name, supported, vote);
394
395// clang-format off
396#define XRPL_RETIRE_FEATURE(name) \
397 [[deprecated("The referenced feature amendment has been retired")]] \
398 [[maybe_unused]] \
399 uint256 const retiredFeature##name = retireFeature(#name);
400
401#define XRPL_RETIRE_FIX(name) \
402 [[deprecated("The referenced fix amendment has been retired")]] \
403 [[maybe_unused]] \
404 uint256 const retiredFix##name = retireFeature("fix" #name);
405// clang-format on
406
407#include <xrpl/protocol/detail/features.macro>
408
409#undef XRPL_RETIRE_FEATURE
410#pragma pop_macro("XRPL_RETIRE_FEATURE")
411#undef XRPL_RETIRE_FIX
412#pragma pop_macro("XRPL_RETIRE_FIX")
413#undef XRPL_FIX
414#pragma pop_macro("XRPL_FIX")
415#undef XRPL_FEATURE
416#pragma pop_macro("XRPL_FEATURE")
417
418// All of the features should now be registered, since variables in a cpp file
419// are initialized from top to bottom.
420//
421// Use initialization of one final static variable to set
422// featureCollections::readOnly.
423[[maybe_unused]] static bool const readOnlySet = featureCollections.registrationIsDone();
424
425} // namespace xrpl
T data(T... args)
T is_same_v
T load(T... args)
void check(bool condition, std::string const &message)
std::size_t numDownVotedAmendments()
Amendments that this server won't vote for by default.
Definition Feature.cpp:321
std::map< std::string, VoteBehavior > const & supportedAmendments()
Amendments that this server supports and the default voting behavior.
Definition Feature.cpp:314
std::size_t numUpVotedAmendments()
Amendments that this server will vote for by default.
Definition Feature.cpp:328
static constexpr std::size_t numFeatures
Definition Feature.h:95
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
uint256 registerFeature(std::string const &name, Supported support, VoteBehavior vote)
Definition Feature.cpp:342
void LogicError(std::string const &how) noexcept
Called when faulty logic causes a broken invariant.
sha512_half_hasher::result_type sha512Half(Args const &... args)
Returns the SHA512-Half of a series of objects.
Definition digest.h:205
uint256 retireFeature(std::string const &name)
Definition Feature.cpp:350
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:598
VoteBehavior
Definition Feature.h:68
static bool const readOnlySet
Definition Feature.cpp:423
size_t featureToBitsetIndex(uint256 const &f)
Definition Feature.cpp:363
bool registrationIsDone()
Tell FeatureCollections when registration is complete.
Definition Feature.cpp:357
base_uint< 256 > uint256
Definition base_uint.h:527
std::map< std::string, AmendmentSupport > const & allAmendments()
All amendments libxrpl knows about.
Definition Feature.cpp:305
std::string featureToName(uint256 const &f)
Definition Feature.cpp:375
uint256 bitsetIndexToFeature(size_t i)
Definition Feature.cpp:369
@ no
Definition Steps.h:26
@ yes
Definition Steps.h:26
std::optional< uint256 > getRegisteredFeature(std::string const &name)
Definition Feature.cpp:336
std::size_t hash_value(xrpl::uint256 const &feature)
Definition Feature.cpp:25
T size(T... args)