rippled
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 <ripple/protocol/Feature.h>
21 
22 #include <ripple/basics/Slice.h>
23 #include <ripple/basics/contract.h>
24 #include <ripple/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 
32 namespace ripple {
33 
34 inline std::size_t
35 hash_value(ripple::uint256 const& feature)
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 
44 namespace {
45 
46 enum 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 // "DefaultVote::no" whereas in rare cases, amendments that fix critical
71 // bugs should be set as "DefaultVote::yes", if off-chain consensus is
72 // reached amongst reviewers, validator operators, and other participants.
73 
74 class FeatureCollections
75 {
76  struct Feature
77  {
78  std::string name;
79  uint256 feature;
80 
81  Feature() = delete;
82  explicit Feature(std::string const& name_, uint256 const& feature_)
83  : name(name_), feature(feature_)
84  {
85  }
86 
87  // These structs are used by the `features` multi_index_container to
88  // provide access to the features collection by size_t index, string
89  // name, and uint256 feature identifier
90  struct byIndex
91  {
92  };
93  struct byName
94  {
95  };
96  struct byFeature
97  {
98  };
99  };
100 
101  // Intermediate types to help with readability
102  template <class tag, typename Type, Type Feature::*PtrToMember>
103  using feature_hashed_unique = boost::multi_index::hashed_unique<
104  boost::multi_index::tag<tag>,
105  boost::multi_index::member<Feature, Type, PtrToMember>>;
106 
107  // Intermediate types to help with readability
108  using feature_indexing = boost::multi_index::indexed_by<
109  boost::multi_index::random_access<
110  boost::multi_index::tag<Feature::byIndex>>,
111  feature_hashed_unique<Feature::byFeature, uint256, &Feature::feature>,
112  feature_hashed_unique<Feature::byName, std::string, &Feature::name>>;
113 
114  // This multi_index_container provides access to the features collection by
115  // name, index, and uint256 feature identifier
116  boost::multi_index::multi_index_container<Feature, feature_indexing>
117  features;
119  std::size_t upVotes = 0;
120  std::size_t downVotes = 0;
121  mutable std::atomic<bool> readOnly = false;
122 
123  // These helper functions provide access to the features collection by name,
124  // index, and uint256 feature identifier, so the details of
125  // multi_index_container can be hidden
126  Feature const&
127  getByIndex(size_t i) const
128  {
129  if (i >= features.size())
130  LogicError("Invalid FeatureBitset index");
131  const auto& sequence = features.get<Feature::byIndex>();
132  return sequence[i];
133  }
134  size_t
135  getIndex(Feature const& feature) const
136  {
137  const auto& sequence = features.get<Feature::byIndex>();
138  auto const it_to = sequence.iterator_to(feature);
139  return it_to - sequence.begin();
140  }
141  Feature const*
142  getByFeature(uint256 const& feature) const
143  {
144  const auto& feature_index = features.get<Feature::byFeature>();
145  auto const feature_it = feature_index.find(feature);
146  return feature_it == feature_index.end() ? nullptr : &*feature_it;
147  }
148  Feature const*
149  getByName(std::string const& name) const
150  {
151  const auto& name_index = features.get<Feature::byName>();
152  auto const name_it = name_index.find(name);
153  return name_it == name_index.end() ? nullptr : &*name_it;
154  }
155 
156 public:
157  FeatureCollections();
158 
160  getRegisteredFeature(std::string const& name) const;
161 
162  uint256
164  std::string const& name,
165  Supported support,
166  DefaultVote vote);
167 
169  bool
171 
173  featureToBitsetIndex(uint256 const& f) const;
174 
175  uint256 const&
176  bitsetIndexToFeature(size_t i) const;
177 
179  featureToName(uint256 const& f) const;
180 
185  supportedAmendments() const
186  {
187  return supported;
188  }
189 
192  numDownVotedAmendments() const
193  {
194  return downVotes;
195  }
196 
199  numUpVotedAmendments() const
200  {
201  return upVotes;
202  }
203 };
204 
205 //------------------------------------------------------------------------------
206 
207 FeatureCollections::FeatureCollections()
208 {
209  features.reserve(ripple::detail::numFeatures);
210 }
211 
213 FeatureCollections::getRegisteredFeature(std::string const& name) const
214 {
215  assert(readOnly);
216  Feature const* feature = getByName(name);
217  if (feature)
218  return feature->feature;
219  return std::nullopt;
220 }
221 
222 void
223 check(bool condition, const char* logicErrorMessage)
224 {
225  if (!condition)
226  LogicError(logicErrorMessage);
227 }
228 
229 uint256
230 FeatureCollections::registerFeature(
231  std::string const& name,
232  Supported support,
233  DefaultVote vote)
234 {
235  check(!readOnly, "Attempting to register a feature after startup.");
236  check(
237  support == Supported::yes || vote == DefaultVote::no,
238  "Invalid feature parameters. Must be supported to be up-voted.");
239  Feature const* i = getByName(name);
240  if (!i)
241  {
242  // If this check fails, and you just added a feature, increase the
243  // numFeatures value in Feature.h
244  check(
245  features.size() < detail::numFeatures,
246  "More features defined than allocated. Adjust numFeatures in "
247  "Feature.h.");
248 
249  auto const f = sha512Half(Slice(name.data(), name.size()));
250 
251  features.emplace_back(name, f);
252 
253  if (support == Supported::yes)
254  {
255  supported.emplace(name, vote);
256 
257  if (vote == DefaultVote::yes)
258  ++upVotes;
259  else
260  ++downVotes;
261  }
262  check(
263  upVotes + downVotes == supported.size(),
264  "Feature counting logic broke");
265  check(
266  supported.size() <= features.size(),
267  "More supported features than defined features");
268  return f;
269  }
270  else
271  // Each feature should only be registered once
272  LogicError("Duplicate feature registration");
273 }
274 
276 bool
277 FeatureCollections::registrationIsDone()
278 {
279  readOnly = true;
280  return true;
281 }
282 
283 size_t
284 FeatureCollections::featureToBitsetIndex(uint256 const& f) const
285 {
286  assert(readOnly);
287 
288  Feature const* feature = getByFeature(f);
289  if (!feature)
290  LogicError("Invalid Feature ID");
291 
292  return getIndex(*feature);
293 }
294 
295 uint256 const&
296 FeatureCollections::bitsetIndexToFeature(size_t i) const
297 {
298  assert(readOnly);
299  Feature const& feature = getByIndex(i);
300  return feature.feature;
301 }
302 
304 FeatureCollections::featureToName(uint256 const& f) const
305 {
306  assert(readOnly);
307  Feature const* feature = getByFeature(f);
308  return feature ? feature->name : to_string(f);
309 }
310 
311 static FeatureCollections featureCollections;
312 
313 } // namespace
314 
320 {
321  return featureCollections.supportedAmendments();
322 }
323 
327 {
328  return featureCollections.numDownVotedAmendments();
329 }
330 
334 {
335  return featureCollections.numUpVotedAmendments();
336 }
337 
338 //------------------------------------------------------------------------------
339 
342 {
343  return featureCollections.getRegisteredFeature(name);
344 }
345 
346 uint256
347 registerFeature(std::string const& name, Supported support, DefaultVote vote)
348 {
349  return featureCollections.registerFeature(name, support, vote);
350 }
351 
352 // Retired features are in the ledger and have no code controlled by the
353 // feature. They need to be supported, but do not need to be voted on.
354 uint256
356 {
357  return registerFeature(name, Supported::yes, DefaultVote::no);
358 }
359 
361 bool
363 {
364  return featureCollections.registrationIsDone();
365 }
366 
367 size_t
369 {
370  return featureCollections.featureToBitsetIndex(f);
371 }
372 
373 uint256
375 {
376  return featureCollections.bitsetIndexToFeature(i);
377 }
378 
381 {
382  return featureCollections.featureToName(f);
383 }
384 
385 #pragma push_macro("REGISTER_FEATURE")
386 #undef REGISTER_FEATURE
387 
393 #define REGISTER_FEATURE(fName, supported, defaultvote) \
394  uint256 const feature##fName = \
395  registerFeature(#fName, supported, defaultvote)
396 
397 #pragma push_macro("REGISTER_FIX")
398 #undef REGISTER_FIX
399 
405 #define REGISTER_FIX(fName, supported, defaultvote) \
406  uint256 const fName = registerFeature(#fName, supported, defaultvote)
407 
408 // clang-format off
409 
410 // All known amendments must be registered either here or below with the
411 // "retired" amendments
412 REGISTER_FEATURE(OwnerPaysFee, Supported::no, DefaultVote::no);
413 REGISTER_FEATURE(Flow, Supported::yes, DefaultVote::yes);
414 REGISTER_FEATURE(FlowCross, Supported::yes, DefaultVote::yes);
415 REGISTER_FEATURE(CryptoConditionsSuite, Supported::yes, DefaultVote::no);
416 REGISTER_FIX (fix1513, Supported::yes, DefaultVote::yes);
417 REGISTER_FEATURE(DepositAuth, Supported::yes, DefaultVote::yes);
418 REGISTER_FEATURE(Checks, Supported::yes, DefaultVote::yes);
419 REGISTER_FIX (fix1571, Supported::yes, DefaultVote::yes);
420 REGISTER_FIX (fix1543, Supported::yes, DefaultVote::yes);
421 REGISTER_FIX (fix1623, Supported::yes, DefaultVote::yes);
422 REGISTER_FEATURE(DepositPreauth, Supported::yes, DefaultVote::yes);
423 // Use liquidity from strands that consume max offers, but mark as dry
424 REGISTER_FIX (fix1515, Supported::yes, DefaultVote::yes);
425 REGISTER_FIX (fix1578, Supported::yes, DefaultVote::yes);
426 REGISTER_FEATURE(MultiSignReserve, Supported::yes, DefaultVote::yes);
431 REGISTER_FEATURE(DeletableAccounts, Supported::yes, DefaultVote::yes);
432 // fixQualityUpperBound should be activated before FlowCross
434 REGISTER_FEATURE(RequireFullyCanonicalSig, Supported::yes, DefaultVote::yes);
435 // fix1781: XRPEndpointSteps should be included in the circular payment check
436 REGISTER_FIX (fix1781, Supported::yes, DefaultVote::yes);
437 REGISTER_FEATURE(HardenedValidations, Supported::yes, DefaultVote::yes);
439 REGISTER_FEATURE(NegativeUNL, Supported::yes, DefaultVote::yes);
440 REGISTER_FEATURE(TicketBatch, Supported::yes, DefaultVote::yes);
441 REGISTER_FEATURE(FlowSortStrands, Supported::yes, DefaultVote::yes);
444 REGISTER_FEATURE(CheckCashMakesTrustLine, Supported::yes, DefaultVote::no);
445 REGISTER_FEATURE(NonFungibleTokensV1, Supported::yes, DefaultVote::no);
446 REGISTER_FEATURE(ExpandedSignerList, Supported::yes, DefaultVote::no);
447 REGISTER_FIX (fixNFTokenDirV1, Supported::yes, DefaultVote::no);
449 REGISTER_FEATURE(NonFungibleTokensV1_1, Supported::yes, DefaultVote::no);
452 REGISTER_FEATURE(ImmediateOfferKilled, Supported::yes, DefaultVote::no);
453 REGISTER_FEATURE(DisallowIncoming, Supported::yes, DefaultVote::no);
454 
455 // The following amendments have been active for at least two years. Their
456 // pre-amendment code has been removed and the identifiers are deprecated.
457 // All known amendments and amendments that may appear in a validated
458 // ledger must be registered either here or above with the "active" amendments
459 [[deprecated("The referenced amendment has been retired"), maybe_unused]]
460 uint256 const
465  retiredCryptoConditions = retireFeature("CryptoConditions"),
470  retiredEnforceInvariants = retireFeature("EnforceInvariants"),
471  retiredSortedDirectories = retireFeature("SortedDirectories"),
476 
477 // clang-format on
478 
479 #undef REGISTER_FIX
480 #pragma pop_macro("REGISTER_FIX")
481 
482 #undef REGISTER_FEATURE
483 #pragma pop_macro("REGISTER_FEATURE")
484 
485 // All of the features should now be registered, since variables in a cpp file
486 // are initialized from top to bottom.
487 //
488 // Use initialization of one final static variable to set
489 // featureCollections::readOnly.
490 [[maybe_unused]] static const bool readOnlySet =
491  featureCollections.registrationIsDone();
492 
493 } // namespace ripple
ripple::fixRemoveNFTokenAutoTrustLine
const uint256 fixRemoveNFTokenAutoTrustLine
ripple::fixQualityUpperBound
const uint256 fixQualityUpperBound
ripple::fixNFTokenNegOffer
const uint256 fixNFTokenNegOffer
std::string
STL class.
cstring
ripple::retiredFix1528
const uint256 retiredFix1528
Definition: Feature.cpp:475
ripple::retiredSortedDirectories
const uint256 retiredSortedDirectories
Definition: Feature.cpp:471
ripple::fix1515
const uint256 fix1515
ripple::detail::numFeatures
static constexpr std::size_t numFeatures
Definition: Feature.h:77
ripple::hash_value
std::size_t hash_value(ripple::uint256 const &feature)
Definition: Feature.cpp:35
std::string::size
T size(T... args)
ripple::fixSTAmountCanonicalize
const uint256 fixSTAmountCanonicalize
ripple::fix1781
const uint256 fix1781
ripple::REGISTER_FEATURE
REGISTER_FEATURE(OwnerPaysFee, Supported::no, DefaultVote::no)
Json::check
void check(bool condition, std::string const &message)
Definition: json/Writer.h:252
ripple::readOnlySet
static const bool readOnlySet
Definition: Feature.cpp:490
ripple::retiredPayChan
const uint256 retiredPayChan
Definition: Feature.cpp:464
boost
Definition: IPAddress.h:103
ripple::retiredFix1368
const uint256 retiredFix1368
Definition: Feature.cpp:467
ripple::retiredEnforceInvariants
const uint256 retiredEnforceInvariants
Definition: Feature.cpp:470
ripple::retiredMultiSign
const uint256 retiredMultiSign
Definition: Feature.cpp:461
ripple::uint256
base_uint< 256 > uint256
Definition: base_uint.h:549
ripple::retiredTickSize
const uint256 retiredTickSize
Definition: Feature.cpp:466
ripple::detail::numUpVotedAmendments
std::size_t numUpVotedAmendments()
Amendments that this server will vote for by default.
Definition: Feature.cpp:333
ripple::base_uint< 256 >
ripple::detail::numDownVotedAmendments
std::size_t numDownVotedAmendments()
Amendments that this server won't vote for by default.
Definition: Feature.cpp:326
ripple::detail::supportedAmendments
std::map< std::string, DefaultVote > const & supportedAmendments()
Amendments that this server supports and the default voting behavior.
Definition: Feature.cpp:319
ripple::retiredEscrow
const uint256 retiredEscrow
Definition: Feature.cpp:468
ripple::DefaultVote::yes
@ yes
ripple::fix1513
const uint256 fix1513
ripple::fixCheckThreading
const uint256 fixCheckThreading
ripple::retiredFix1373
const uint256 retiredFix1373
Definition: Feature.cpp:469
ripple::registerFeature
uint256 registerFeature(std::string const &name, Supported support, DefaultVote vote)
Definition: Feature.cpp:347
ripple::fixAmendmentMajorityCalc
const uint256 fixAmendmentMajorityCalc
ripple::retiredTrustSetAuth
const uint256 retiredTrustSetAuth
Definition: Feature.cpp:462
ripple::fixTakerDryOfferRemoval
const uint256 fixTakerDryOfferRemoval
ripple::fix1623
const uint256 fix1623
ripple::featureToBitsetIndex
size_t featureToBitsetIndex(uint256 const &f)
Definition: Feature.cpp:368
ripple::fixPayChanRecipientOwnerDir
const uint256 fixPayChanRecipientOwnerDir
ripple::fixMasterKeyAsRegularKey
const uint256 fixMasterKeyAsRegularKey
ripple::REGISTER_FIX
REGISTER_FIX(fix1513, Supported::yes, DefaultVote::yes)
std::atomic< bool >
ripple::retiredCryptoConditions
const uint256 retiredCryptoConditions
Definition: Feature.cpp:465
std::map
STL class.
ripple::fixNFTokenDirV1
const uint256 fixNFTokenDirV1
ripple::fixRmSmallIncreasedQOffers
const uint256 fixRmSmallIncreasedQOffers
ripple::fixTrustLinesToSelf
const uint256 fixTrustLinesToSelf
ripple::fix1543
const uint256 fix1543
ripple::retiredFeeEscalation
const uint256 retiredFeeEscalation
Definition: Feature.cpp:463
ripple::fix1578
const uint256 fix1578
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::LogicError
void LogicError(std::string const &how) noexcept
Called when faulty logic causes a broken invariant.
Definition: contract.cpp:48
ripple::sha512Half
sha512_half_hasher::result_type sha512Half(Args const &... args)
Returns the SHA512-Half of a series of objects.
Definition: digest.h:216
ripple::bitsetIndexToFeature
uint256 bitsetIndexToFeature(size_t i)
Definition: Feature.cpp:374
ripple::DefaultVote
DefaultVote
Definition: Feature.h:69
ripple::retiredFix1523
const uint256 retiredFix1523
Definition: Feature.cpp:474
ripple::retiredFix1201
const uint256 retiredFix1201
Definition: Feature.cpp:472
std::optional
std::size_t
ripple::to_string
std::string to_string(Manifest const &m)
Format the specified manifest to a string for debugging purposes.
Definition: app/misc/impl/Manifest.cpp:41
ripple::retireFeature
uint256 retireFeature(std::string const &name)
Definition: Feature.cpp:355
ripple::retiredFix1512
const uint256 retiredFix1512
Definition: Feature.cpp:473
ripple::fix1571
const uint256 fix1571
ripple::getRegisteredFeature
std::optional< uint256 > getRegisteredFeature(std::string const &name)
Definition: Feature.cpp:341
ripple::registrationIsDone
bool registrationIsDone()
Tell FeatureCollections when registration is complete.
Definition: Feature.cpp:362
std::string::data
T data(T... args)
ripple::DefaultVote::no
@ no
ripple::featureToName
std::string featureToName(uint256 const &f)
Definition: Feature.cpp:380