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 class FeatureCollections
70 {
71  struct Feature
72  {
73  std::string name;
74  uint256 feature;
75 
76  Feature() = delete;
77  explicit Feature(std::string const& name_, uint256 const& feature_)
78  : name(name_), feature(feature_)
79  {
80  }
81 
82  // These structs are used by the `features` multi_index_container to
83  // provide access to the features collection by size_t index, string
84  // name, and uint256 feature identifier
85  struct byIndex
86  {
87  };
88  struct byName
89  {
90  };
91  struct byFeature
92  {
93  };
94  };
95 
96  // Intermediate types to help with readability
97  template <class tag, typename Type, Type Feature::*PtrToMember>
98  using feature_hashed_unique = boost::multi_index::hashed_unique<
99  boost::multi_index::tag<tag>,
100  boost::multi_index::member<Feature, Type, PtrToMember>>;
101 
102  // Intermediate types to help with readability
103  using feature_indexing = boost::multi_index::indexed_by<
104  boost::multi_index::random_access<
105  boost::multi_index::tag<Feature::byIndex>>,
106  feature_hashed_unique<Feature::byFeature, uint256, &Feature::feature>,
107  feature_hashed_unique<Feature::byName, std::string, &Feature::name>>;
108 
109  // This multi_index_container provides access to the features collection by
110  // name, index, and uint256 feature identifier
111  boost::multi_index::multi_index_container<Feature, feature_indexing>
112  features;
114  std::size_t upVotes = 0;
115  std::size_t downVotes = 0;
116  mutable std::atomic<bool> readOnly = false;
117 
118  // These helper functions provide access to the features collection by name,
119  // index, and uint256 feature identifier, so the details of
120  // multi_index_container can be hidden
121  Feature const&
122  getByIndex(size_t i) const
123  {
124  if (i >= features.size())
125  LogicError("Invalid FeatureBitset index");
126  const auto& sequence = features.get<Feature::byIndex>();
127  return sequence[i];
128  }
129  size_t
130  getIndex(Feature const& feature) const
131  {
132  const auto& sequence = features.get<Feature::byIndex>();
133  auto const it_to = sequence.iterator_to(feature);
134  return it_to - sequence.begin();
135  }
136  Feature const*
137  getByFeature(uint256 const& feature) const
138  {
139  const auto& feature_index = features.get<Feature::byFeature>();
140  auto const feature_it = feature_index.find(feature);
141  return feature_it == feature_index.end() ? nullptr : &*feature_it;
142  }
143  Feature const*
144  getByName(std::string const& name) const
145  {
146  const auto& name_index = features.get<Feature::byName>();
147  auto const name_it = name_index.find(name);
148  return name_it == name_index.end() ? nullptr : &*name_it;
149  }
150 
151 public:
152  FeatureCollections();
153 
155  getRegisteredFeature(std::string const& name) const;
156 
157  uint256
159  std::string const& name,
160  Supported support,
161  DefaultVote vote);
162 
164  bool
166 
168  featureToBitsetIndex(uint256 const& f) const;
169 
170  uint256 const&
171  bitsetIndexToFeature(size_t i) const;
172 
174  featureToName(uint256 const& f) const;
175 
180  supportedAmendments() const
181  {
182  return supported;
183  }
184 
187  numDownVotedAmendments() const
188  {
189  return downVotes;
190  }
191 
194  numUpVotedAmendments() const
195  {
196  return upVotes;
197  }
198 };
199 
200 //------------------------------------------------------------------------------
201 
202 FeatureCollections::FeatureCollections()
203 {
204  features.reserve(ripple::detail::numFeatures);
205 }
206 
208 FeatureCollections::getRegisteredFeature(std::string const& name) const
209 {
210  assert(readOnly);
211  Feature const* feature = getByName(name);
212  if (feature)
213  return feature->feature;
214  return std::nullopt;
215 }
216 
217 void
218 check(bool condition, const char* logicErrorMessage)
219 {
220  if (!condition)
221  LogicError(logicErrorMessage);
222 }
223 
224 uint256
225 FeatureCollections::registerFeature(
226  std::string const& name,
227  Supported support,
228  DefaultVote vote)
229 {
230  check(!readOnly, "Attempting to register a feature after startup.");
231  check(
232  support == Supported::yes || vote == DefaultVote::no,
233  "Invalid feature parameters. Must be supported to be up-voted.");
234  Feature const* i = getByName(name);
235  if (!i)
236  {
237  // If this check fails, and you just added a feature, increase the
238  // numFeatures value in Feature.h
239  check(
240  features.size() < detail::numFeatures,
241  "More features defined than allocated. Adjust numFeatures in "
242  "Feature.h.");
243 
244  auto const f = sha512Half(Slice(name.data(), name.size()));
245 
246  features.emplace_back(name, f);
247 
248  if (support == Supported::yes)
249  {
250  supported.emplace(name, vote);
251 
252  if (vote == DefaultVote::yes)
253  ++upVotes;
254  else
255  ++downVotes;
256  }
257  check(
258  upVotes + downVotes == supported.size(),
259  "Feature counting logic broke");
260  check(
261  supported.size() <= features.size(),
262  "More supported features than defined features");
263  return f;
264  }
265  else
266  // Each feature should only be registered once
267  LogicError("Duplicate feature registration");
268 }
269 
271 bool
272 FeatureCollections::registrationIsDone()
273 {
274  readOnly = true;
275  return true;
276 }
277 
278 size_t
279 FeatureCollections::featureToBitsetIndex(uint256 const& f) const
280 {
281  assert(readOnly);
282 
283  Feature const* feature = getByFeature(f);
284  if (!feature)
285  LogicError("Invalid Feature ID");
286 
287  return getIndex(*feature);
288 }
289 
290 uint256 const&
291 FeatureCollections::bitsetIndexToFeature(size_t i) const
292 {
293  assert(readOnly);
294  Feature const& feature = getByIndex(i);
295  return feature.feature;
296 }
297 
299 FeatureCollections::featureToName(uint256 const& f) const
300 {
301  assert(readOnly);
302  Feature const* feature = getByFeature(f);
303  return feature ? feature->name : to_string(f);
304 }
305 
306 static FeatureCollections featureCollections;
307 
308 } // namespace
309 
315 {
316  return featureCollections.supportedAmendments();
317 }
318 
322 {
323  return featureCollections.numDownVotedAmendments();
324 }
325 
329 {
330  return featureCollections.numUpVotedAmendments();
331 }
332 
333 //------------------------------------------------------------------------------
334 
337 {
338  return featureCollections.getRegisteredFeature(name);
339 }
340 
341 uint256
342 registerFeature(std::string const& name, Supported support, DefaultVote vote)
343 {
344  return featureCollections.registerFeature(name, support, vote);
345 }
346 
347 // Retired features are in the ledger and have no code controlled by the
348 // feature. They need to be supported, but do not need to be voted on.
349 uint256
351 {
352  return registerFeature(name, Supported::yes, DefaultVote::no);
353 }
354 
356 bool
358 {
359  return featureCollections.registrationIsDone();
360 }
361 
362 size_t
364 {
365  return featureCollections.featureToBitsetIndex(f);
366 }
367 
368 uint256
370 {
371  return featureCollections.bitsetIndexToFeature(i);
372 }
373 
376 {
377  return featureCollections.featureToName(f);
378 }
379 
380 #pragma push_macro("REGISTER_FEATURE")
381 #undef REGISTER_FEATURE
382 
388 #define REGISTER_FEATURE(fName, supported, defaultvote) \
389  uint256 const feature##fName = \
390  registerFeature(#fName, supported, defaultvote)
391 
392 #pragma push_macro("REGISTER_FIX")
393 #undef REGISTER_FIX
394 
400 #define REGISTER_FIX(fName, supported, defaultvote) \
401  uint256 const fName = registerFeature(#fName, supported, defaultvote)
402 
403 // clang-format off
404 
405 // All known amendments must be registered either here or below with the
406 // "retired" amendments
407 REGISTER_FEATURE(OwnerPaysFee, Supported::no, DefaultVote::no);
408 REGISTER_FEATURE(Flow, Supported::yes, DefaultVote::yes);
409 REGISTER_FEATURE(FlowCross, Supported::yes, DefaultVote::yes);
410 REGISTER_FEATURE(CryptoConditionsSuite, Supported::yes, DefaultVote::no);
411 REGISTER_FIX (fix1513, Supported::yes, DefaultVote::yes);
412 REGISTER_FEATURE(DepositAuth, Supported::yes, DefaultVote::yes);
413 REGISTER_FEATURE(Checks, Supported::yes, DefaultVote::yes);
414 REGISTER_FIX (fix1571, Supported::yes, DefaultVote::yes);
415 REGISTER_FIX (fix1543, Supported::yes, DefaultVote::yes);
416 REGISTER_FIX (fix1623, Supported::yes, DefaultVote::yes);
417 REGISTER_FEATURE(DepositPreauth, Supported::yes, DefaultVote::yes);
418 // Use liquidity from strands that consume max offers, but mark as dry
419 REGISTER_FIX (fix1515, Supported::yes, DefaultVote::yes);
420 REGISTER_FIX (fix1578, Supported::yes, DefaultVote::yes);
421 REGISTER_FEATURE(MultiSignReserve, Supported::yes, DefaultVote::yes);
426 REGISTER_FEATURE(DeletableAccounts, Supported::yes, DefaultVote::yes);
427 // fixQualityUpperBound should be activated before FlowCross
429 REGISTER_FEATURE(RequireFullyCanonicalSig, Supported::yes, DefaultVote::yes);
430 // fix1781: XRPEndpointSteps should be included in the circular payment check
431 REGISTER_FIX (fix1781, Supported::yes, DefaultVote::yes);
432 REGISTER_FEATURE(HardenedValidations, Supported::yes, DefaultVote::yes);
434 REGISTER_FEATURE(NegativeUNL, Supported::yes, DefaultVote::no);
435 REGISTER_FEATURE(TicketBatch, Supported::yes, DefaultVote::yes);
436 REGISTER_FEATURE(FlowSortStrands, Supported::yes, DefaultVote::yes);
439 REGISTER_FEATURE(CheckCashMakesTrustLine, Supported::yes, DefaultVote::no);
440 REGISTER_FEATURE(NonFungibleTokensV1, Supported::yes, DefaultVote::no);
441 REGISTER_FEATURE(ExpandedSignerList, Supported::yes, DefaultVote::no);
442 REGISTER_FIX (fixNFTokenDirV1, Supported::yes, DefaultVote::no);
443 
444 // The following amendments have been active for at least two years. Their
445 // pre-amendment code has been removed and the identifiers are deprecated.
446 // All known amendments and amendments that may appear in a validated
447 // ledger must be registered either here or above with the "active" amendments
448 [[deprecated("The referenced amendment has been retired"), maybe_unused]]
449 uint256 const
454  retiredCryptoConditions = retireFeature("CryptoConditions"),
459  retiredEnforceInvariants = retireFeature("EnforceInvariants"),
460  retiredSortedDirectories = retireFeature("SortedDirectories"),
465 
466 // clang-format on
467 
468 #undef REGISTER_FIX
469 #pragma pop_macro("REGISTER_FIX")
470 
471 #undef REGISTER_FEATURE
472 #pragma pop_macro("REGISTER_FEATURE")
473 
474 // All of the features should now be registered, since variables in a cpp file
475 // are initialized from top to bottom.
476 //
477 // Use initialization of one final static variable to set
478 // featureCollections::readOnly.
479 [[maybe_unused]] static const bool readOnlySet =
480  featureCollections.registrationIsDone();
481 
482 } // namespace ripple
ripple::fixQualityUpperBound
const uint256 fixQualityUpperBound
std::string
STL class.
cstring
ripple::retiredFix1528
const uint256 retiredFix1528
Definition: Feature.cpp:464
ripple::retiredSortedDirectories
const uint256 retiredSortedDirectories
Definition: Feature.cpp:460
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:479
ripple::retiredPayChan
const uint256 retiredPayChan
Definition: Feature.cpp:453
boost
Definition: IPAddress.h:103
ripple::retiredFix1368
const uint256 retiredFix1368
Definition: Feature.cpp:456
ripple::retiredEnforceInvariants
const uint256 retiredEnforceInvariants
Definition: Feature.cpp:459
ripple::retiredMultiSign
const uint256 retiredMultiSign
Definition: Feature.cpp:450
ripple::uint256
base_uint< 256 > uint256
Definition: base_uint.h:543
ripple::retiredTickSize
const uint256 retiredTickSize
Definition: Feature.cpp:455
ripple::detail::numUpVotedAmendments
std::size_t numUpVotedAmendments()
Amendments that this server will vote for by default.
Definition: Feature.cpp:328
ripple::base_uint< 256 >
ripple::detail::numDownVotedAmendments
std::size_t numDownVotedAmendments()
Amendments that this server won't vote for by default.
Definition: Feature.cpp:321
ripple::detail::supportedAmendments
std::map< std::string, DefaultVote > const & supportedAmendments()
Amendments that this server supports and the default voting behavior.
Definition: Feature.cpp:314
ripple::retiredEscrow
const uint256 retiredEscrow
Definition: Feature.cpp:457
ripple::DefaultVote::yes
@ yes
ripple::fix1513
const uint256 fix1513
ripple::fixCheckThreading
const uint256 fixCheckThreading
ripple::retiredFix1373
const uint256 retiredFix1373
Definition: Feature.cpp:458
ripple::registerFeature
uint256 registerFeature(std::string const &name, Supported support, DefaultVote vote)
Definition: Feature.cpp:342
ripple::fixAmendmentMajorityCalc
const uint256 fixAmendmentMajorityCalc
ripple::retiredTrustSetAuth
const uint256 retiredTrustSetAuth
Definition: Feature.cpp:451
ripple::fixTakerDryOfferRemoval
const uint256 fixTakerDryOfferRemoval
ripple::fix1623
const uint256 fix1623
ripple::featureToBitsetIndex
size_t featureToBitsetIndex(uint256 const &f)
Definition: Feature.cpp:363
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:454
std::map
STL class.
ripple::fixNFTokenDirV1
const uint256 fixNFTokenDirV1
ripple::fixRmSmallIncreasedQOffers
const uint256 fixRmSmallIncreasedQOffers
ripple::fix1543
const uint256 fix1543
ripple::retiredFeeEscalation
const uint256 retiredFeeEscalation
Definition: Feature.cpp:452
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:369
ripple::DefaultVote
DefaultVote
Definition: Feature.h:69
ripple::retiredFix1523
const uint256 retiredFix1523
Definition: Feature.cpp:463
ripple::retiredFix1201
const uint256 retiredFix1201
Definition: Feature.cpp:461
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:350
ripple::retiredFix1512
const uint256 retiredFix1512
Definition: Feature.cpp:462
ripple::fix1571
const uint256 fix1571
ripple::getRegisteredFeature
std::optional< uint256 > getRegisteredFeature(std::string const &name)
Definition: Feature.cpp:336
ripple::registrationIsDone
bool registrationIsDone()
Tell FeatureCollections when registration is complete.
Definition: Feature.cpp:357
std::string::data
T data(T... args)
ripple::DefaultVote::no
@ no
ripple::featureToName
std::string featureToName(uint256 const &f)
Definition: Feature.cpp:375