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/protocol/Feature.h>
21
22#include <xrpl/basics/Slice.h>
23#include <xrpl/basics/contract.h>
24#include <xrpl/protocol/digest.h>
25#include <boost/container_hash/hash.hpp>
26#include <boost/multi_index/hashed_index.hpp>
27#include <boost/multi_index/key_extractors.hpp>
28#include <boost/multi_index/random_access_index.hpp>
29#include <boost/multi_index_container.hpp>
30#include <cstring>
31
32namespace ripple {
33
34inline std::size_t
36{
37 std::size_t seed = 0;
38 using namespace boost;
39 for (auto const& n : feature)
40 hash_combine(seed, n);
41 return seed;
42}
43
44namespace {
45
46enum class Supported : bool { no = false, yes };
47
48// *NOTE*
49//
50// Features, or Amendments as they are called elsewhere, are enabled on the
51// network at some specific time based on Validator voting. Features are
52// enabled using run-time conditionals based on the state of the amendment.
53// There is value in retaining that conditional code for some time after
54// the amendment is enabled to make it simple to replay old transactions.
55// However, once an amendment has been enabled for, say, more than two years
56// then retaining that conditional code has less value since it is
57// uncommon to replay such old transactions.
58//
59// Starting in January of 2020 Amendment conditionals from before January
60// 2018 are being removed. So replaying any ledger from before January
61// 2018 needs to happen on an older version of the server code. There's
62// a log message in Application.cpp that warns about replaying old ledgers.
63//
64// At some point in the future someone may wish to remove amendment
65// conditional code for amendments that were enabled after January 2018.
66// When that happens then the log message in Application.cpp should be
67// updated.
68//
69// Generally, amendments which introduce new features should be set as
70// "VoteBehavior::DefaultNo" whereas in rare cases, amendments that fix
71// critical bugs should be set as "VoteBehavior::DefaultYes", if off-chain
72// consensus is reached amongst reviewers, validator operators, and other
73// participants.
74
75class FeatureCollections
76{
77 struct Feature
78 {
79 std::string name;
80 uint256 feature;
81
82 Feature() = delete;
83 explicit Feature(std::string const& name_, uint256 const& feature_)
84 : name(name_), feature(feature_)
85 {
86 }
87
88 // These structs are used by the `features` multi_index_container to
89 // provide access to the features collection by size_t index, string
90 // name, and uint256 feature identifier
91 struct byIndex
92 {
93 };
94 struct byName
95 {
96 };
97 struct byFeature
98 {
99 };
100 };
101
102 // Intermediate types to help with readability
103 template <class tag, typename Type, Type Feature::*PtrToMember>
104 using feature_hashed_unique = boost::multi_index::hashed_unique<
105 boost::multi_index::tag<tag>,
106 boost::multi_index::member<Feature, Type, PtrToMember>>;
107
108 // Intermediate types to help with readability
109 using feature_indexing = boost::multi_index::indexed_by<
110 boost::multi_index::random_access<
111 boost::multi_index::tag<Feature::byIndex>>,
112 feature_hashed_unique<Feature::byFeature, uint256, &Feature::feature>,
113 feature_hashed_unique<Feature::byName, std::string, &Feature::name>>;
114
115 // This multi_index_container provides access to the features collection by
116 // name, index, and uint256 feature identifier
117 boost::multi_index::multi_index_container<Feature, feature_indexing>
118 features;
121 std::size_t upVotes = 0;
122 std::size_t downVotes = 0;
123 mutable std::atomic<bool> readOnly = false;
124
125 // These helper functions provide access to the features collection by name,
126 // index, and uint256 feature identifier, so the details of
127 // multi_index_container can be hidden
128 Feature const&
129 getByIndex(size_t i) const
130 {
131 if (i >= features.size())
132 LogicError("Invalid FeatureBitset index");
133 const auto& sequence = features.get<Feature::byIndex>();
134 return sequence[i];
135 }
136 size_t
137 getIndex(Feature const& feature) const
138 {
139 const auto& sequence = features.get<Feature::byIndex>();
140 auto const it_to = sequence.iterator_to(feature);
141 return it_to - sequence.begin();
142 }
143 Feature const*
144 getByFeature(uint256 const& feature) const
145 {
146 const auto& feature_index = features.get<Feature::byFeature>();
147 auto const feature_it = feature_index.find(feature);
148 return feature_it == feature_index.end() ? nullptr : &*feature_it;
149 }
150 Feature const*
151 getByName(std::string const& name) const
152 {
153 const auto& name_index = features.get<Feature::byName>();
154 auto const name_it = name_index.find(name);
155 return name_it == name_index.end() ? nullptr : &*name_it;
156 }
157
158public:
159 FeatureCollections();
160
162 getRegisteredFeature(std::string const& name) const;
163
164 uint256
166 std::string const& name,
167 Supported support,
168 VoteBehavior vote);
169
171 bool
173
175 featureToBitsetIndex(uint256 const& f) const;
176
177 uint256 const&
178 bitsetIndexToFeature(size_t i) const;
179
181 featureToName(uint256 const& f) const;
182
185 allAmendments() const
186 {
187 return all;
188 }
189
194 supportedAmendments() const
195 {
196 return supported;
197 }
198
202 {
203 return downVotes;
204 }
205
209 {
210 return upVotes;
211 }
212};
213
214//------------------------------------------------------------------------------
215
216FeatureCollections::FeatureCollections()
217{
218 features.reserve(ripple::detail::numFeatures);
219}
220
222FeatureCollections::getRegisteredFeature(std::string const& name) const
223{
224 XRPL_ASSERT(
225 readOnly.load(),
226 "ripple::FeatureCollections::getRegisteredFeature : startup completed");
227 Feature const* feature = getByName(name);
228 if (feature)
229 return feature->feature;
230 return std::nullopt;
231}
232
233void
234check(bool condition, const char* logicErrorMessage)
235{
236 if (!condition)
237 LogicError(logicErrorMessage);
238}
239
241FeatureCollections::registerFeature(
242 std::string const& name,
243 Supported support,
244 VoteBehavior vote)
245{
246 check(!readOnly, "Attempting to register a feature after startup.");
247 check(
248 support == Supported::yes || vote == VoteBehavior::DefaultNo,
249 "Invalid feature parameters. Must be supported to be up-voted.");
250 Feature const* i = getByName(name);
251 if (!i)
252 {
253 // If this check fails, and you just added a feature, increase the
254 // numFeatures value in Feature.h
255 check(
256 features.size() < detail::numFeatures,
257 "More features defined than allocated. Adjust numFeatures in "
258 "Feature.h.");
259
260 auto const f = sha512Half(Slice(name.data(), name.size()));
261
262 features.emplace_back(name, f);
263
264 auto const getAmendmentSupport = [=]() {
265 if (vote == VoteBehavior::Obsolete)
267 return support == Supported::yes ? AmendmentSupport::Supported
269 };
270 all.emplace(name, getAmendmentSupport());
271
272 if (support == Supported::yes)
273 {
274 supported.emplace(name, vote);
275
276 if (vote == VoteBehavior::DefaultYes)
277 ++upVotes;
278 else
279 ++downVotes;
280 }
281 check(
282 upVotes + downVotes == supported.size(),
283 "Feature counting logic broke");
284 check(
285 supported.size() <= features.size(),
286 "More supported features than defined features");
287 check(
288 features.size() == all.size(),
289 "The 'all' features list is populated incorrectly");
290 return f;
291 }
292 else
293 // Each feature should only be registered once
294 LogicError("Duplicate feature registration");
295}
296
298bool
299FeatureCollections::registrationIsDone()
300{
301 readOnly = true;
302 return true;
303}
304
305size_t
306FeatureCollections::featureToBitsetIndex(uint256 const& f) const
307{
308 XRPL_ASSERT(
309 readOnly.load(),
310 "ripple::FeatureCollections::featureToBitsetIndex : startup completed");
311
312 Feature const* feature = getByFeature(f);
313 if (!feature)
314 LogicError("Invalid Feature ID");
315
316 return getIndex(*feature);
317}
318
319uint256 const&
320FeatureCollections::bitsetIndexToFeature(size_t i) const
321{
322 XRPL_ASSERT(
323 readOnly.load(),
324 "ripple::FeatureCollections::bitsetIndexToFeature : startup completed");
325 Feature const& feature = getByIndex(i);
326 return feature.feature;
327}
328
330FeatureCollections::featureToName(uint256 const& f) const
331{
332 XRPL_ASSERT(
333 readOnly.load(),
334 "ripple::FeatureCollections::featureToName : startup completed");
335 Feature const* feature = getByFeature(f);
336 return feature ? feature->name : to_string(f);
337}
338
339static FeatureCollections featureCollections;
340
341} // namespace
342
346{
347 return featureCollections.allAmendments();
348}
349
355{
356 return featureCollections.supportedAmendments();
357}
358
362{
363 return featureCollections.numDownVotedAmendments();
364}
365
369{
370 return featureCollections.numUpVotedAmendments();
371}
372
373//------------------------------------------------------------------------------
374
377{
378 return featureCollections.getRegisteredFeature(name);
379}
380
383{
384 return featureCollections.registerFeature(name, support, vote);
385}
386
387// Retired features are in the ledger and have no code controlled by the
388// feature. They need to be supported, but do not need to be voted on.
391{
392 return registerFeature(name, Supported::yes, VoteBehavior::Obsolete);
393}
394
396bool
398{
399 return featureCollections.registrationIsDone();
400}
401
402size_t
404{
405 return featureCollections.featureToBitsetIndex(f);
406}
407
410{
411 return featureCollections.bitsetIndexToFeature(i);
412}
413
416{
417 return featureCollections.featureToName(f);
418}
419
420// All known amendments must be registered either here or below with the
421// "retired" amendments
422
423#pragma push_macro("XRPL_FEATURE")
424#undef XRPL_FEATURE
425#pragma push_macro("XRPL_FIX")
426#undef XRPL_FIX
427
428#define XRPL_FEATURE(name, supported, vote) \
429 uint256 const feature##name = registerFeature(#name, supported, vote);
430#define XRPL_FIX(name, supported, vote) \
431 uint256 const fix##name = registerFeature("fix" #name, supported, vote);
432
433#include <xrpl/protocol/detail/features.macro>
434
435#undef XRPL_FIX
436#pragma pop_macro("XRPL_FIX")
437#undef XRPL_FEATURE
438#pragma pop_macro("XRPL_FEATURE")
439
440// clang-format off
441
442// The following amendments have been active for at least two years. Their
443// pre-amendment code has been removed and the identifiers are deprecated.
444// All known amendments and amendments that may appear in a validated
445// ledger must be registered either here or above with the "active" amendments
446[[deprecated("The referenced amendment has been retired"), maybe_unused]]
447uint256 const
463
464// clang-format on
465
466// All of the features should now be registered, since variables in a cpp file
467// are initialized from top to bottom.
468//
469// Use initialization of one final static variable to set
470// featureCollections::readOnly.
471[[maybe_unused]] static const bool readOnlySet =
472 featureCollections.registrationIsDone();
473
474} // namespace ripple
T data(T... args)
T load(T... args)
void check(bool condition, std::string const &message)
Definition: json/Writer.h:252
static constexpr std::size_t numFeatures
Definition: Feature.h:83
std::size_t numUpVotedAmendments()
Amendments that this server will vote for by default.
Definition: Feature.cpp:368
std::size_t numDownVotedAmendments()
Amendments that this server won't vote for by default.
Definition: Feature.cpp:361
std::map< std::string, VoteBehavior > const & supportedAmendments()
Amendments that this server supports and the default voting behavior.
Definition: Feature.cpp:354
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
uint256 const retiredFeeEscalation
Definition: Feature.cpp:450
uint256 const retiredFix1528
Definition: Feature.cpp:462
uint256 const retiredFix1368
Definition: Feature.cpp:454
uint256 bitsetIndexToFeature(size_t i)
Definition: Feature.cpp:409
uint256 const retiredFix1512
Definition: Feature.cpp:460
static const bool readOnlySet
Definition: Feature.cpp:471
base_uint< 256 > uint256
Definition: base_uint.h:557
uint256 const retiredTrustSetAuth
Definition: Feature.cpp:449
uint256 registerFeature(std::string const &name, Supported support, VoteBehavior vote)
Definition: Feature.cpp:382
uint256 const retiredCryptoConditions
Definition: Feature.cpp:452
uint256 const retiredSortedDirectories
Definition: Feature.cpp:458
uint256 const retiredFix1523
Definition: Feature.cpp:461
size_t featureToBitsetIndex(uint256 const &f)
Definition: Feature.cpp:403
@ no
Definition: Steps.h:42
@ yes
Definition: Steps.h:42
std::map< std::string, AmendmentSupport > const & allAmendments()
All amendments libxrpl knows about.
Definition: Feature.cpp:345
std::size_t hash_value(ripple::uint256 const &feature)
Definition: Feature.cpp:35
uint256 const retiredFix1373
Definition: Feature.cpp:456
uint256 retireFeature(std::string const &name)
Definition: Feature.cpp:390
uint256 const retiredEscrow
Definition: Feature.cpp:455
std::string featureToName(uint256 const &f)
Definition: Feature.cpp:415
std::optional< uint256 > getRegisteredFeature(std::string const &name)
Definition: Feature.cpp:376
VoteBehavior
Definition: Feature.h:70
uint256 const retiredFix1201
Definition: Feature.cpp:459
std::string to_string(base_uint< Bits, Tag > const &a)
Definition: base_uint.h:629
bool registrationIsDone()
Tell FeatureCollections when registration is complete.
Definition: Feature.cpp:397
uint256 const retiredMultiSign
Definition: Feature.cpp:448
uint256 const retiredTickSize
Definition: Feature.cpp:453
uint256 const retiredPayChan
Definition: Feature.cpp:451
sha512_half_hasher::result_type sha512Half(Args const &... args)
Returns the SHA512-Half of a series of objects.
Definition: digest.h:223
uint256 const retiredEnforceInvariants
Definition: Feature.cpp:457
void LogicError(std::string const &how) noexcept
Called when faulty logic causes a broken invariant.
Definition: contract.cpp:48
T size(T... args)