rippled
Loading...
Searching...
No Matches
Feature.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012, 2013 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <xrpl/basics/Slice.h>
21#include <xrpl/basics/base_uint.h>
22#include <xrpl/basics/contract.h>
23#include <xrpl/beast/utility/instrumentation.h>
24#include <xrpl/protocol/Feature.h>
25#include <xrpl/protocol/digest.h>
26
27#include <boost/container_hash/hash.hpp>
28#include <boost/multi_index/hashed_index.hpp>
29#include <boost/multi_index/indexed_by.hpp>
30#include <boost/multi_index/member.hpp>
31#include <boost/multi_index/random_access_index.hpp>
32#include <boost/multi_index/tag.hpp>
33#include <boost/multi_index_container.hpp>
34
35#include <atomic>
36#include <cstddef>
37#include <map>
38#include <optional>
39#include <string>
40
41namespace ripple {
42
43inline std::size_t
45{
46 std::size_t seed = 0;
47 using namespace boost;
48 for (auto const& n : feature)
49 hash_combine(seed, n);
50 return seed;
51}
52
53namespace {
54
55enum class Supported : bool { no = false, yes };
56
57// *NOTE*
58//
59// Features, or Amendments as they are called elsewhere, are enabled on the
60// network at some specific time based on Validator voting. Features are
61// enabled using run-time conditionals based on the state of the amendment.
62// There is value in retaining that conditional code for some time after
63// the amendment is enabled to make it simple to replay old transactions.
64// However, once an amendment has been enabled for, say, more than two years
65// then retaining that conditional code has less value since it is
66// uncommon to replay such old transactions.
67//
68// Starting in January of 2020 Amendment conditionals from before January
69// 2018 are being removed. So replaying any ledger from before January
70// 2018 needs to happen on an older version of the server code. There's
71// a log message in Application.cpp that warns about replaying old ledgers.
72//
73// At some point in the future someone may wish to remove amendment
74// conditional code for amendments that were enabled after January 2018.
75// When that happens then the log message in Application.cpp should be
76// updated.
77//
78// Generally, amendments which introduce new features should be set as
79// "VoteBehavior::DefaultNo" whereas in rare cases, amendments that fix
80// critical bugs should be set as "VoteBehavior::DefaultYes", if off-chain
81// consensus is reached amongst reviewers, validator operators, and other
82// participants.
83
84class FeatureCollections
85{
86 struct Feature
87 {
88 std::string name;
89 uint256 feature;
90
91 Feature() = delete;
92 explicit Feature(std::string const& name_, uint256 const& feature_)
93 : name(name_), feature(feature_)
94 {
95 }
96
97 // These structs are used by the `features` multi_index_container to
98 // provide access to the features collection by size_t index, string
99 // name, and uint256 feature identifier
100 struct byIndex
101 {
102 };
103 struct byName
104 {
105 };
106 struct byFeature
107 {
108 };
109 };
110
111 // Intermediate types to help with readability
112 template <class tag, typename Type, Type Feature::*PtrToMember>
113 using feature_hashed_unique = boost::multi_index::hashed_unique<
114 boost::multi_index::tag<tag>,
115 boost::multi_index::member<Feature, Type, PtrToMember>>;
116
117 // Intermediate types to help with readability
118 using feature_indexing = boost::multi_index::indexed_by<
119 boost::multi_index::random_access<
120 boost::multi_index::tag<Feature::byIndex>>,
121 feature_hashed_unique<Feature::byFeature, uint256, &Feature::feature>,
122 feature_hashed_unique<Feature::byName, std::string, &Feature::name>>;
123
124 // This multi_index_container provides access to the features collection by
125 // name, index, and uint256 feature identifier
126 boost::multi_index::multi_index_container<Feature, feature_indexing>
127 features;
130 std::size_t upVotes = 0;
131 std::size_t downVotes = 0;
132 mutable std::atomic<bool> readOnly = false;
133
134 // These helper functions provide access to the features collection by name,
135 // index, and uint256 feature identifier, so the details of
136 // multi_index_container can be hidden
137 Feature const&
138 getByIndex(size_t i) const
139 {
140 if (i >= features.size())
141 LogicError("Invalid FeatureBitset index");
142 auto const& sequence = features.get<Feature::byIndex>();
143 return sequence[i];
144 }
145 size_t
146 getIndex(Feature const& feature) const
147 {
148 auto const& sequence = features.get<Feature::byIndex>();
149 auto const it_to = sequence.iterator_to(feature);
150 return it_to - sequence.begin();
151 }
152 Feature const*
153 getByFeature(uint256 const& feature) const
154 {
155 auto const& feature_index = features.get<Feature::byFeature>();
156 auto const feature_it = feature_index.find(feature);
157 return feature_it == feature_index.end() ? nullptr : &*feature_it;
158 }
159 Feature const*
160 getByName(std::string const& name) const
161 {
162 auto const& name_index = features.get<Feature::byName>();
163 auto const name_it = name_index.find(name);
164 return name_it == name_index.end() ? nullptr : &*name_it;
165 }
166
167public:
168 FeatureCollections();
169
171 getRegisteredFeature(std::string const& name) const;
172
173 uint256
175 std::string const& name,
176 Supported support,
177 VoteBehavior vote);
178
180 bool
182
184 featureToBitsetIndex(uint256 const& f) const;
185
186 uint256 const&
187 bitsetIndexToFeature(size_t i) const;
188
190 featureToName(uint256 const& f) const;
191
194 allAmendments() const
195 {
196 return all;
197 }
198
203 supportedAmendments() const
204 {
205 return supported;
206 }
207
211 {
212 return downVotes;
213 }
214
218 {
219 return upVotes;
220 }
221};
222
223//------------------------------------------------------------------------------
224
225FeatureCollections::FeatureCollections()
226{
227 features.reserve(ripple::detail::numFeatures);
228}
229
231FeatureCollections::getRegisteredFeature(std::string const& name) const
232{
233 XRPL_ASSERT(
234 readOnly.load(),
235 "ripple::FeatureCollections::getRegisteredFeature : startup completed");
236 Feature const* feature = getByName(name);
237 if (feature)
238 return feature->feature;
239 return std::nullopt;
240}
241
242void
243check(bool condition, char const* logicErrorMessage)
244{
245 if (!condition)
246 LogicError(logicErrorMessage);
247}
248
250FeatureCollections::registerFeature(
251 std::string const& name,
252 Supported support,
253 VoteBehavior vote)
254{
255 check(!readOnly, "Attempting to register a feature after startup.");
256 check(
257 support == Supported::yes || vote == VoteBehavior::DefaultNo,
258 "Invalid feature parameters. Must be supported to be up-voted.");
259 Feature const* i = getByName(name);
260 if (!i)
261 {
262 check(
263 features.size() < detail::numFeatures,
264 "More features defined than allocated.");
265
266 auto const f = sha512Half(Slice(name.data(), name.size()));
267
268 features.emplace_back(name, f);
269
270 auto const getAmendmentSupport = [=]() {
271 if (vote == VoteBehavior::Obsolete)
273 return support == Supported::yes ? AmendmentSupport::Supported
275 };
276 all.emplace(name, getAmendmentSupport());
277
278 if (support == Supported::yes)
279 {
280 supported.emplace(name, vote);
281
282 if (vote == VoteBehavior::DefaultYes)
283 ++upVotes;
284 else
285 ++downVotes;
286 }
287 check(
288 upVotes + downVotes == supported.size(),
289 "Feature counting logic broke");
290 check(
291 supported.size() <= features.size(),
292 "More supported features than defined features");
293 check(
294 features.size() == all.size(),
295 "The 'all' features list is populated incorrectly");
296 return f;
297 }
298 else
299 // Each feature should only be registered once
300 LogicError("Duplicate feature registration");
301}
302
304bool
305FeatureCollections::registrationIsDone()
306{
307 readOnly = true;
308 return true;
309}
310
311size_t
312FeatureCollections::featureToBitsetIndex(uint256 const& f) const
313{
314 XRPL_ASSERT(
315 readOnly.load(),
316 "ripple::FeatureCollections::featureToBitsetIndex : startup completed");
317
318 Feature const* feature = getByFeature(f);
319 if (!feature)
320 LogicError("Invalid Feature ID");
321
322 return getIndex(*feature);
323}
324
325uint256 const&
326FeatureCollections::bitsetIndexToFeature(size_t i) const
327{
328 XRPL_ASSERT(
329 readOnly.load(),
330 "ripple::FeatureCollections::bitsetIndexToFeature : startup completed");
331 Feature const& feature = getByIndex(i);
332 return feature.feature;
333}
334
336FeatureCollections::featureToName(uint256 const& f) const
337{
338 XRPL_ASSERT(
339 readOnly.load(),
340 "ripple::FeatureCollections::featureToName : startup completed");
341 Feature const* feature = getByFeature(f);
342 return feature ? feature->name : to_string(f);
343}
344
345static FeatureCollections featureCollections;
346
347} // namespace
348
352{
353 return featureCollections.allAmendments();
354}
355
361{
362 return featureCollections.supportedAmendments();
363}
364
368{
369 return featureCollections.numDownVotedAmendments();
370}
371
375{
376 return featureCollections.numUpVotedAmendments();
377}
378
379//------------------------------------------------------------------------------
380
383{
384 return featureCollections.getRegisteredFeature(name);
385}
386
389{
390 return featureCollections.registerFeature(name, support, vote);
391}
392
393// Retired features are in the ledger and have no code controlled by the
394// feature. They need to be supported, but do not need to be voted on.
397{
398 return registerFeature(name, Supported::yes, VoteBehavior::Obsolete);
399}
400
402bool
404{
405 return featureCollections.registrationIsDone();
406}
407
408size_t
410{
411 return featureCollections.featureToBitsetIndex(f);
412}
413
416{
417 return featureCollections.bitsetIndexToFeature(i);
418}
419
422{
423 return featureCollections.featureToName(f);
424}
425
426// All known amendments must be registered either here or below with the
427// "retired" amendments
428
429#pragma push_macro("XRPL_FEATURE")
430#undef XRPL_FEATURE
431#pragma push_macro("XRPL_FIX")
432#undef XRPL_FIX
433#pragma push_macro("XRPL_RETIRE")
434#undef XRPL_RETIRE
435
436#define XRPL_FEATURE(name, supported, vote) \
437 uint256 const feature##name = registerFeature(#name, supported, vote);
438#define XRPL_FIX(name, supported, vote) \
439 uint256 const fix##name = registerFeature("fix" #name, supported, vote);
440
441// clang-format off
442#define XRPL_RETIRE(name) \
443 [[deprecated("The referenced amendment has been retired")]] \
444 [[maybe_unused]] \
445 uint256 const retired##name = retireFeature(#name);
446// clang-format on
447
448#include <xrpl/protocol/detail/features.macro>
449
450#undef XRPL_RETIRE
451#pragma pop_macro("XRPL_RETIRE")
452#undef XRPL_FIX
453#pragma pop_macro("XRPL_FIX")
454#undef XRPL_FEATURE
455#pragma pop_macro("XRPL_FEATURE")
456
457// All of the features should now be registered, since variables in a cpp file
458// are initialized from top to bottom.
459//
460// Use initialization of one final static variable to set
461// featureCollections::readOnly.
462[[maybe_unused]] static bool const readOnlySet =
463 featureCollections.registrationIsDone();
464
465} // 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:111
std::size_t numUpVotedAmendments()
Amendments that this server will vote for by default.
Definition Feature.cpp:374
std::size_t numDownVotedAmendments()
Amendments that this server won't vote for by default.
Definition Feature.cpp:367
std::map< std::string, VoteBehavior > const & supportedAmendments()
Amendments that this server supports and the default voting behavior.
Definition Feature.cpp:360
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
uint256 bitsetIndexToFeature(size_t i)
Definition Feature.cpp:415
base_uint< 256 > uint256
Definition base_uint.h:558
uint256 registerFeature(std::string const &name, Supported support, VoteBehavior vote)
Definition Feature.cpp:388
size_t featureToBitsetIndex(uint256 const &f)
Definition Feature.cpp:409
@ no
Definition Steps.h:45
@ yes
Definition Steps.h:45
std::map< std::string, AmendmentSupport > const & allAmendments()
All amendments libxrpl knows about.
Definition Feature.cpp:351
std::size_t hash_value(ripple::uint256 const &feature)
Definition Feature.cpp:44
uint256 retireFeature(std::string const &name)
Definition Feature.cpp:396
std::string featureToName(uint256 const &f)
Definition Feature.cpp:421
std::optional< uint256 > getRegisteredFeature(std::string const &name)
Definition Feature.cpp:382
VoteBehavior
Definition Feature.h:87
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:630
bool registrationIsDone()
Tell FeatureCollections when registration is complete.
Definition Feature.cpp:403
sha512_half_hasher::result_type sha512Half(Args const &... args)
Returns the SHA512-Half of a series of objects.
Definition digest.h:224
static bool const readOnlySet
Definition Feature.cpp:462
void LogicError(std::string const &how) noexcept
Called when faulty logic causes a broken invariant.
T size(T... args)