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 ripple {
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_)
74 : name(name_), feature(feature_)
75 {
76 }
77
78 // These structs are used by the `features` multi_index_container to
79 // provide access to the features collection by size_t index, string
80 // name, and uint256 feature identifier
81 struct byIndex
82 {
83 };
84 struct byName
85 {
86 };
87 struct byFeature
88 {
89 };
90 };
91
92 // Intermediate types to help with readability
93 template <class tag, typename Type, Type Feature::*PtrToMember>
94 using feature_hashed_unique = boost::multi_index::hashed_unique<
95 boost::multi_index::tag<tag>,
96 boost::multi_index::member<Feature, Type, PtrToMember>>;
97
98 // Intermediate types to help with readability
99 using feature_indexing = boost::multi_index::indexed_by<
100 boost::multi_index::random_access<
101 boost::multi_index::tag<Feature::byIndex>>,
102 feature_hashed_unique<Feature::byFeature, uint256, &Feature::feature>,
103 feature_hashed_unique<Feature::byName, std::string, &Feature::name>>;
104
105 // This multi_index_container provides access to the features collection by
106 // name, index, and uint256 feature identifier
107 boost::multi_index::multi_index_container<Feature, feature_indexing>
108 features;
111 std::size_t upVotes = 0;
112 std::size_t downVotes = 0;
113 mutable std::atomic<bool> readOnly = false;
114
115 // These helper functions provide access to the features collection by name,
116 // index, and uint256 feature identifier, so the details of
117 // multi_index_container can be hidden
118 Feature const&
119 getByIndex(size_t i) const
120 {
121 if (i >= features.size())
122 LogicError("Invalid FeatureBitset index");
123 auto const& sequence = features.get<Feature::byIndex>();
124 return sequence[i];
125 }
126 size_t
127 getIndex(Feature const& feature) const
128 {
129 auto const& sequence = features.get<Feature::byIndex>();
130 auto const it_to = sequence.iterator_to(feature);
131 return it_to - sequence.begin();
132 }
133 Feature const*
134 getByFeature(uint256 const& feature) const
135 {
136 auto const& feature_index = features.get<Feature::byFeature>();
137 auto const feature_it = feature_index.find(feature);
138 return feature_it == feature_index.end() ? nullptr : &*feature_it;
139 }
140 Feature const*
141 getByName(std::string const& name) const
142 {
143 auto const& name_index = features.get<Feature::byName>();
144 auto const name_it = name_index.find(name);
145 return name_it == name_index.end() ? nullptr : &*name_it;
146 }
147
148public:
149 FeatureCollections();
150
152 getRegisteredFeature(std::string const& name) const;
153
154 uint256
156 std::string const& name,
157 Supported support,
158 VoteBehavior vote);
159
161 bool
163
165 featureToBitsetIndex(uint256 const& f) const;
166
167 uint256 const&
168 bitsetIndexToFeature(size_t i) const;
169
171 featureToName(uint256 const& f) const;
172
175 allAmendments() const
176 {
177 return all;
178 }
179
184 supportedAmendments() const
185 {
186 return supported;
187 }
188
192 {
193 return downVotes;
194 }
195
199 {
200 return upVotes;
201 }
202};
203
204//------------------------------------------------------------------------------
205
206FeatureCollections::FeatureCollections()
207{
208 features.reserve(ripple::detail::numFeatures);
209}
210
212FeatureCollections::getRegisteredFeature(std::string const& name) const
213{
214 XRPL_ASSERT(
215 readOnly.load(),
216 "ripple::FeatureCollections::getRegisteredFeature : startup completed");
217 Feature const* feature = getByName(name);
218 if (feature)
219 return feature->feature;
220 return std::nullopt;
221}
222
223void
224check(bool condition, char const* logicErrorMessage)
225{
226 if (!condition)
227 LogicError(logicErrorMessage);
228}
229
231FeatureCollections::registerFeature(
232 std::string const& name,
233 Supported support,
234 VoteBehavior vote)
235{
236 check(!readOnly, "Attempting to register a feature after startup.");
237 check(
238 support == Supported::yes || vote == VoteBehavior::DefaultNo,
239 "Invalid feature parameters. Must be supported to be up-voted.");
240 Feature const* i = getByName(name);
241 if (!i)
242 {
243 check(
244 features.size() < detail::numFeatures,
245 "More features defined than allocated.");
246
247 auto const f = sha512Half(Slice(name.data(), name.size()));
248
249 features.emplace_back(name, f);
250
251 auto const getAmendmentSupport = [=]() {
252 if (vote == VoteBehavior::Obsolete)
254 return support == Supported::yes ? AmendmentSupport::Supported
256 };
257 all.emplace(name, getAmendmentSupport());
258
259 if (support == Supported::yes)
260 {
261 supported.emplace(name, vote);
262
263 if (vote == VoteBehavior::DefaultYes)
264 ++upVotes;
265 else
266 ++downVotes;
267 }
268 check(
269 upVotes + downVotes == supported.size(),
270 "Feature counting logic broke");
271 check(
272 supported.size() <= features.size(),
273 "More supported features than defined features");
274 check(
275 features.size() == all.size(),
276 "The 'all' features list is populated incorrectly");
277 return f;
278 }
279 else
280 // Each feature should only be registered once
281 LogicError("Duplicate feature registration");
282}
283
285bool
286FeatureCollections::registrationIsDone()
287{
288 readOnly = true;
289 return true;
290}
291
292size_t
293FeatureCollections::featureToBitsetIndex(uint256 const& f) const
294{
295 XRPL_ASSERT(
296 readOnly.load(),
297 "ripple::FeatureCollections::featureToBitsetIndex : startup completed");
298
299 Feature const* feature = getByFeature(f);
300 if (!feature)
301 LogicError("Invalid Feature ID");
302
303 return getIndex(*feature);
304}
305
306uint256 const&
307FeatureCollections::bitsetIndexToFeature(size_t i) const
308{
309 XRPL_ASSERT(
310 readOnly.load(),
311 "ripple::FeatureCollections::bitsetIndexToFeature : startup completed");
312 Feature const& feature = getByIndex(i);
313 return feature.feature;
314}
315
317FeatureCollections::featureToName(uint256 const& f) const
318{
319 XRPL_ASSERT(
320 readOnly.load(),
321 "ripple::FeatureCollections::featureToName : startup completed");
322 Feature const* feature = getByFeature(f);
323 return feature ? feature->name : to_string(f);
324}
325
326static FeatureCollections featureCollections;
327
328} // namespace
329
333{
334 return featureCollections.allAmendments();
335}
336
342{
343 return featureCollections.supportedAmendments();
344}
345
349{
350 return featureCollections.numDownVotedAmendments();
351}
352
356{
357 return featureCollections.numUpVotedAmendments();
358}
359
360//------------------------------------------------------------------------------
361
364{
365 return featureCollections.getRegisteredFeature(name);
366}
367
370{
371 return featureCollections.registerFeature(name, support, vote);
372}
373
374// Retired features are in the ledger and have no code controlled by the
375// feature. They need to be supported, but do not need to be voted on.
378{
379 return registerFeature(name, Supported::yes, VoteBehavior::Obsolete);
380}
381
383bool
385{
386 return featureCollections.registrationIsDone();
387}
388
389size_t
391{
392 return featureCollections.featureToBitsetIndex(f);
393}
394
397{
398 return featureCollections.bitsetIndexToFeature(i);
399}
400
403{
404 return featureCollections.featureToName(f);
405}
406
407// All known amendments must be registered either here or below with the
408// "retired" amendments
409
410#pragma push_macro("XRPL_FEATURE")
411#undef XRPL_FEATURE
412#pragma push_macro("XRPL_FIX")
413#undef XRPL_FIX
414#pragma push_macro("XRPL_RETIRE_FEATURE")
415#undef XRPL_RETIRE_FEATURE
416#pragma push_macro("XRPL_RETIRE_FIX")
417#undef XRPL_RETIRE_FIX
418
419#define XRPL_FEATURE(name, supported, vote) \
420 uint256 const feature##name = registerFeature(#name, supported, vote);
421#define XRPL_FIX(name, supported, vote) \
422 uint256 const fix##name = registerFeature("fix" #name, supported, vote);
423
424// clang-format off
425#define XRPL_RETIRE_FEATURE(name) \
426 [[deprecated("The referenced feature amendment has been retired")]] \
427 [[maybe_unused]] \
428 uint256 const retiredFeature##name = retireFeature(#name);
429
430#define XRPL_RETIRE_FIX(name) \
431 [[deprecated("The referenced fix amendment has been retired")]] \
432 [[maybe_unused]] \
433 uint256 const retiredFix##name = retireFeature("fix" #name);
434// clang-format on
435
436#include <xrpl/protocol/detail/features.macro>
437
438#undef XRPL_RETIRE_FEATURE
439#pragma pop_macro("XRPL_RETIRE_FEATURE")
440#undef XRPL_RETIRE_FIX
441#pragma pop_macro("XRPL_RETIRE_FIX")
442#undef XRPL_FIX
443#pragma pop_macro("XRPL_FIX")
444#undef XRPL_FEATURE
445#pragma pop_macro("XRPL_FEATURE")
446
447// All of the features should now be registered, since variables in a cpp file
448// are initialized from top to bottom.
449//
450// Use initialization of one final static variable to set
451// featureCollections::readOnly.
452[[maybe_unused]] static bool const readOnlySet =
453 featureCollections.registrationIsDone();
454
455} // namespace ripple
T data(T... args)
T is_same_v
T load(T... args)
void check(bool condition, std::string const &message)
static constexpr std::size_t numFeatures
Definition Feature.h:95
std::size_t numUpVotedAmendments()
Amendments that this server will vote for by default.
Definition Feature.cpp:355
std::size_t numDownVotedAmendments()
Amendments that this server won't vote for by default.
Definition Feature.cpp:348
std::map< std::string, VoteBehavior > const & supportedAmendments()
Amendments that this server supports and the default voting behavior.
Definition Feature.cpp:341
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
uint256 bitsetIndexToFeature(size_t i)
Definition Feature.cpp:396
base_uint< 256 > uint256
Definition base_uint.h:539
uint256 registerFeature(std::string const &name, Supported support, VoteBehavior vote)
Definition Feature.cpp:369
size_t featureToBitsetIndex(uint256 const &f)
Definition Feature.cpp:390
@ no
Definition Steps.h:26
@ yes
Definition Steps.h:26
std::map< std::string, AmendmentSupport > const & allAmendments()
All amendments libxrpl knows about.
Definition Feature.cpp:332
std::size_t hash_value(ripple::uint256 const &feature)
Definition Feature.cpp:25
uint256 retireFeature(std::string const &name)
Definition Feature.cpp:377
std::string featureToName(uint256 const &f)
Definition Feature.cpp:402
std::optional< uint256 > getRegisteredFeature(std::string const &name)
Definition Feature.cpp:363
VoteBehavior
Definition Feature.h:68
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:611
bool registrationIsDone()
Tell FeatureCollections when registration is complete.
Definition Feature.cpp:384
sha512_half_hasher::result_type sha512Half(Args const &... args)
Returns the SHA512-Half of a series of objects.
Definition digest.h:205
static bool const readOnlySet
Definition Feature.cpp:452
void LogicError(std::string const &how) noexcept
Called when faulty logic causes a broken invariant.
T size(T... args)