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 const auto& sequence = features.get<Feature::byIndex>();
143 return sequence[i];
144 }
145 size_t
146 getIndex(Feature const& feature) const
147 {
148 const auto& 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 const auto& 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 const auto& 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, const char* 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 // If this check fails, and you just added a feature, increase the
263 // numFeatures value in Feature.h
264 check(
265 features.size() < detail::numFeatures,
266 "More features defined than allocated. Adjust numFeatures in "
267 "Feature.h.");
268
269 auto const f = sha512Half(Slice(name.data(), name.size()));
270
271 features.emplace_back(name, f);
272
273 auto const getAmendmentSupport = [=]() {
274 if (vote == VoteBehavior::Obsolete)
276 return support == Supported::yes ? AmendmentSupport::Supported
278 };
279 all.emplace(name, getAmendmentSupport());
280
281 if (support == Supported::yes)
282 {
283 supported.emplace(name, vote);
284
285 if (vote == VoteBehavior::DefaultYes)
286 ++upVotes;
287 else
288 ++downVotes;
289 }
290 check(
291 upVotes + downVotes == supported.size(),
292 "Feature counting logic broke");
293 check(
294 supported.size() <= features.size(),
295 "More supported features than defined features");
296 check(
297 features.size() == all.size(),
298 "The 'all' features list is populated incorrectly");
299 return f;
300 }
301 else
302 // Each feature should only be registered once
303 LogicError("Duplicate feature registration");
304}
305
307bool
308FeatureCollections::registrationIsDone()
309{
310 readOnly = true;
311 return true;
312}
313
314size_t
315FeatureCollections::featureToBitsetIndex(uint256 const& f) const
316{
317 XRPL_ASSERT(
318 readOnly.load(),
319 "ripple::FeatureCollections::featureToBitsetIndex : startup completed");
320
321 Feature const* feature = getByFeature(f);
322 if (!feature)
323 LogicError("Invalid Feature ID");
324
325 return getIndex(*feature);
326}
327
328uint256 const&
329FeatureCollections::bitsetIndexToFeature(size_t i) const
330{
331 XRPL_ASSERT(
332 readOnly.load(),
333 "ripple::FeatureCollections::bitsetIndexToFeature : startup completed");
334 Feature const& feature = getByIndex(i);
335 return feature.feature;
336}
337
339FeatureCollections::featureToName(uint256 const& f) const
340{
341 XRPL_ASSERT(
342 readOnly.load(),
343 "ripple::FeatureCollections::featureToName : startup completed");
344 Feature const* feature = getByFeature(f);
345 return feature ? feature->name : to_string(f);
346}
347
348static FeatureCollections featureCollections;
349
350} // namespace
351
355{
356 return featureCollections.allAmendments();
357}
358
364{
365 return featureCollections.supportedAmendments();
366}
367
371{
372 return featureCollections.numDownVotedAmendments();
373}
374
378{
379 return featureCollections.numUpVotedAmendments();
380}
381
382//------------------------------------------------------------------------------
383
386{
387 return featureCollections.getRegisteredFeature(name);
388}
389
392{
393 return featureCollections.registerFeature(name, support, vote);
394}
395
396// Retired features are in the ledger and have no code controlled by the
397// feature. They need to be supported, but do not need to be voted on.
400{
401 return registerFeature(name, Supported::yes, VoteBehavior::Obsolete);
402}
403
405bool
407{
408 return featureCollections.registrationIsDone();
409}
410
411size_t
413{
414 return featureCollections.featureToBitsetIndex(f);
415}
416
419{
420 return featureCollections.bitsetIndexToFeature(i);
421}
422
425{
426 return featureCollections.featureToName(f);
427}
428
429// All known amendments must be registered either here or below with the
430// "retired" amendments
431
432#pragma push_macro("XRPL_FEATURE")
433#undef XRPL_FEATURE
434#pragma push_macro("XRPL_FIX")
435#undef XRPL_FIX
436
437#define XRPL_FEATURE(name, supported, vote) \
438 uint256 const feature##name = registerFeature(#name, supported, vote);
439#define XRPL_FIX(name, supported, vote) \
440 uint256 const fix##name = registerFeature("fix" #name, supported, vote);
441
442#include <xrpl/protocol/detail/features.macro>
443
444#undef XRPL_FIX
445#pragma pop_macro("XRPL_FIX")
446#undef XRPL_FEATURE
447#pragma pop_macro("XRPL_FEATURE")
448
449// clang-format off
450
451// The following amendments have been active for at least two years. Their
452// pre-amendment code has been removed and the identifiers are deprecated.
453// All known amendments and amendments that may appear in a validated
454// ledger must be registered either here or above with the "active" amendments
455[[deprecated("The referenced amendment has been retired"), maybe_unused]]
456uint256 const
472
473// clang-format on
474
475// All of the features should now be registered, since variables in a cpp file
476// are initialized from top to bottom.
477//
478// Use initialization of one final static variable to set
479// featureCollections::readOnly.
480[[maybe_unused]] static const bool readOnlySet =
481 featureCollections.registrationIsDone();
482
483} // namespace ripple
T data(T... args)
T load(T... args)
void check(bool condition, std::string const &message)
Definition: json/Writer.h:253
static constexpr std::size_t numFeatures
Definition: Feature.h:85
std::size_t numUpVotedAmendments()
Amendments that this server will vote for by default.
Definition: Feature.cpp:377
std::size_t numDownVotedAmendments()
Amendments that this server won't vote for by default.
Definition: Feature.cpp:370
std::map< std::string, VoteBehavior > const & supportedAmendments()
Amendments that this server supports and the default voting behavior.
Definition: Feature.cpp:363
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
uint256 const retiredFeeEscalation
Definition: Feature.cpp:459
uint256 const retiredFix1528
Definition: Feature.cpp:471
uint256 const retiredFix1368
Definition: Feature.cpp:463
uint256 bitsetIndexToFeature(size_t i)
Definition: Feature.cpp:418
uint256 const retiredFix1512
Definition: Feature.cpp:469
static const bool readOnlySet
Definition: Feature.cpp:480
base_uint< 256 > uint256
Definition: base_uint.h:558
uint256 const retiredTrustSetAuth
Definition: Feature.cpp:458
uint256 registerFeature(std::string const &name, Supported support, VoteBehavior vote)
Definition: Feature.cpp:391
uint256 const retiredCryptoConditions
Definition: Feature.cpp:461
uint256 const retiredSortedDirectories
Definition: Feature.cpp:467
uint256 const retiredFix1523
Definition: Feature.cpp:470
size_t featureToBitsetIndex(uint256 const &f)
Definition: Feature.cpp:412
@ no
Definition: Steps.h:43
@ yes
Definition: Steps.h:43
std::map< std::string, AmendmentSupport > const & allAmendments()
All amendments libxrpl knows about.
Definition: Feature.cpp:354
std::size_t hash_value(ripple::uint256 const &feature)
Definition: Feature.cpp:44
uint256 const retiredFix1373
Definition: Feature.cpp:465
uint256 retireFeature(std::string const &name)
Definition: Feature.cpp:399
uint256 const retiredEscrow
Definition: Feature.cpp:464
std::string featureToName(uint256 const &f)
Definition: Feature.cpp:424
std::optional< uint256 > getRegisteredFeature(std::string const &name)
Definition: Feature.cpp:385
VoteBehavior
Definition: Feature.h:72
uint256 const retiredFix1201
Definition: Feature.cpp:468
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:406
uint256 const retiredMultiSign
Definition: Feature.cpp:457
uint256 const retiredTickSize
Definition: Feature.cpp:462
uint256 const retiredPayChan
Definition: Feature.cpp:460
sha512_half_hasher::result_type sha512Half(Args const &... args)
Returns the SHA512-Half of a series of objects.
Definition: digest.h:225
uint256 const retiredEnforceInvariants
Definition: Feature.cpp:466
void LogicError(std::string const &how) noexcept
Called when faulty logic causes a broken invariant.
Definition: contract.cpp:50
T size(T... args)