-
Notifications
You must be signed in to change notification settings - Fork 1.5k
/
Feature.cpp
503 lines (436 loc) · 17.8 KB
/
Feature.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <ripple/protocol/Feature.h>
#include <ripple/basics/Slice.h>
#include <ripple/basics/contract.h>
#include <ripple/protocol/digest.h>
#include <boost/container_hash/hash.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/key_extractors.hpp>
#include <boost/multi_index/random_access_index.hpp>
#include <boost/multi_index_container.hpp>
#include <cstring>
namespace ripple {
inline std::size_t
hash_value(ripple::uint256 const& feature)
{
std::size_t seed = 0;
using namespace boost;
for (auto const& n : feature)
hash_combine(seed, n);
return seed;
}
namespace {
enum class Supported : bool { no = false, yes };
// *NOTE*
//
// Features, or Amendments as they are called elsewhere, are enabled on the
// network at some specific time based on Validator voting. Features are
// enabled using run-time conditionals based on the state of the amendment.
// There is value in retaining that conditional code for some time after
// the amendment is enabled to make it simple to replay old transactions.
// However, once an amendment has been enabled for, say, more than two years
// then retaining that conditional code has less value since it is
// uncommon to replay such old transactions.
//
// Starting in January of 2020 Amendment conditionals from before January
// 2018 are being removed. So replaying any ledger from before January
// 2018 needs to happen on an older version of the server code. There's
// a log message in Application.cpp that warns about replaying old ledgers.
//
// At some point in the future someone may wish to remove amendment
// conditional code for amendments that were enabled after January 2018.
// When that happens then the log message in Application.cpp should be
// updated.
//
// Generally, amendments which introduce new features should be set as
// "VoteBehavior::DefaultNo" whereas in rare cases, amendments that fix
// critical bugs should be set as "VoteBehavior::DefaultYes", if off-chain
// consensus is reached amongst reviewers, validator operators, and other
// participants.
class FeatureCollections
{
struct Feature
{
std::string name;
uint256 feature;
Feature() = delete;
explicit Feature(std::string const& name_, uint256 const& feature_)
: name(name_), feature(feature_)
{
}
// These structs are used by the `features` multi_index_container to
// provide access to the features collection by size_t index, string
// name, and uint256 feature identifier
struct byIndex
{
};
struct byName
{
};
struct byFeature
{
};
};
// Intermediate types to help with readability
template <class tag, typename Type, Type Feature::*PtrToMember>
using feature_hashed_unique = boost::multi_index::hashed_unique<
boost::multi_index::tag<tag>,
boost::multi_index::member<Feature, Type, PtrToMember>>;
// Intermediate types to help with readability
using feature_indexing = boost::multi_index::indexed_by<
boost::multi_index::random_access<
boost::multi_index::tag<Feature::byIndex>>,
feature_hashed_unique<Feature::byFeature, uint256, &Feature::feature>,
feature_hashed_unique<Feature::byName, std::string, &Feature::name>>;
// This multi_index_container provides access to the features collection by
// name, index, and uint256 feature identifier
boost::multi_index::multi_index_container<Feature, feature_indexing>
features;
std::map<std::string, VoteBehavior> supported;
std::size_t upVotes = 0;
std::size_t downVotes = 0;
mutable std::atomic<bool> readOnly = false;
// These helper functions provide access to the features collection by name,
// index, and uint256 feature identifier, so the details of
// multi_index_container can be hidden
Feature const&
getByIndex(size_t i) const
{
if (i >= features.size())
LogicError("Invalid FeatureBitset index");
const auto& sequence = features.get<Feature::byIndex>();
return sequence[i];
}
size_t
getIndex(Feature const& feature) const
{
const auto& sequence = features.get<Feature::byIndex>();
auto const it_to = sequence.iterator_to(feature);
return it_to - sequence.begin();
}
Feature const*
getByFeature(uint256 const& feature) const
{
const auto& feature_index = features.get<Feature::byFeature>();
auto const feature_it = feature_index.find(feature);
return feature_it == feature_index.end() ? nullptr : &*feature_it;
}
Feature const*
getByName(std::string const& name) const
{
const auto& name_index = features.get<Feature::byName>();
auto const name_it = name_index.find(name);
return name_it == name_index.end() ? nullptr : &*name_it;
}
public:
FeatureCollections();
std::optional<uint256>
getRegisteredFeature(std::string const& name) const;
uint256
registerFeature(
std::string const& name,
Supported support,
VoteBehavior vote);
/** Tell FeatureCollections when registration is complete. */
bool
registrationIsDone();
std::size_t
featureToBitsetIndex(uint256 const& f) const;
uint256 const&
bitsetIndexToFeature(size_t i) const;
std::string
featureToName(uint256 const& f) const;
/** Amendments that this server supports.
Whether they are enabled depends on the Rules defined in the validated
ledger */
std::map<std::string, VoteBehavior> const&
supportedAmendments() const
{
return supported;
}
/** Amendments that this server WON'T vote for by default. */
std::size_t
numDownVotedAmendments() const
{
return downVotes;
}
/** Amendments that this server WILL vote for by default. */
std::size_t
numUpVotedAmendments() const
{
return upVotes;
}
};
//------------------------------------------------------------------------------
FeatureCollections::FeatureCollections()
{
features.reserve(ripple::detail::numFeatures);
}
std::optional<uint256>
FeatureCollections::getRegisteredFeature(std::string const& name) const
{
assert(readOnly);
Feature const* feature = getByName(name);
if (feature)
return feature->feature;
return std::nullopt;
}
void
check(bool condition, const char* logicErrorMessage)
{
if (!condition)
LogicError(logicErrorMessage);
}
uint256
FeatureCollections::registerFeature(
std::string const& name,
Supported support,
VoteBehavior vote)
{
check(!readOnly, "Attempting to register a feature after startup.");
check(
support == Supported::yes || vote == VoteBehavior::DefaultNo,
"Invalid feature parameters. Must be supported to be up-voted.");
Feature const* i = getByName(name);
if (!i)
{
// If this check fails, and you just added a feature, increase the
// numFeatures value in Feature.h
check(
features.size() < detail::numFeatures,
"More features defined than allocated. Adjust numFeatures in "
"Feature.h.");
auto const f = sha512Half(Slice(name.data(), name.size()));
features.emplace_back(name, f);
if (support == Supported::yes)
{
supported.emplace(name, vote);
if (vote == VoteBehavior::DefaultYes)
++upVotes;
else
++downVotes;
}
check(
upVotes + downVotes == supported.size(),
"Feature counting logic broke");
check(
supported.size() <= features.size(),
"More supported features than defined features");
return f;
}
else
// Each feature should only be registered once
LogicError("Duplicate feature registration");
}
/** Tell FeatureCollections when registration is complete. */
bool
FeatureCollections::registrationIsDone()
{
readOnly = true;
return true;
}
size_t
FeatureCollections::featureToBitsetIndex(uint256 const& f) const
{
assert(readOnly);
Feature const* feature = getByFeature(f);
if (!feature)
LogicError("Invalid Feature ID");
return getIndex(*feature);
}
uint256 const&
FeatureCollections::bitsetIndexToFeature(size_t i) const
{
assert(readOnly);
Feature const& feature = getByIndex(i);
return feature.feature;
}
std::string
FeatureCollections::featureToName(uint256 const& f) const
{
assert(readOnly);
Feature const* feature = getByFeature(f);
return feature ? feature->name : to_string(f);
}
static FeatureCollections featureCollections;
} // namespace
/** Amendments that this server supports.
Whether they are enabled depends on the Rules defined in the validated
ledger */
std::map<std::string, VoteBehavior> const&
detail::supportedAmendments()
{
return featureCollections.supportedAmendments();
}
/** Amendments that this server won't vote for by default. */
std::size_t
detail::numDownVotedAmendments()
{
return featureCollections.numDownVotedAmendments();
}
/** Amendments that this server will vote for by default. */
std::size_t
detail::numUpVotedAmendments()
{
return featureCollections.numUpVotedAmendments();
}
//------------------------------------------------------------------------------
std::optional<uint256>
getRegisteredFeature(std::string const& name)
{
return featureCollections.getRegisteredFeature(name);
}
uint256
registerFeature(std::string const& name, Supported support, VoteBehavior vote)
{
return featureCollections.registerFeature(name, support, vote);
}
// Retired features are in the ledger and have no code controlled by the
// feature. They need to be supported, but do not need to be voted on.
uint256
retireFeature(std::string const& name)
{
return registerFeature(name, Supported::yes, VoteBehavior::Obsolete);
}
/** Tell FeatureCollections when registration is complete. */
bool
registrationIsDone()
{
return featureCollections.registrationIsDone();
}
size_t
featureToBitsetIndex(uint256 const& f)
{
return featureCollections.featureToBitsetIndex(f);
}
uint256
bitsetIndexToFeature(size_t i)
{
return featureCollections.bitsetIndexToFeature(i);
}
std::string
featureToName(uint256 const& f)
{
return featureCollections.featureToName(f);
}
#pragma push_macro("REGISTER_FEATURE")
#undef REGISTER_FEATURE
/**
Takes the name of a feature, whether it's supported, and the default vote. Will
register the feature, and create a variable whose name is "feature" plus the
feature name.
*/
#define REGISTER_FEATURE(fName, supported, votebehavior) \
uint256 const feature##fName = \
registerFeature(#fName, supported, votebehavior)
#pragma push_macro("REGISTER_FIX")
#undef REGISTER_FIX
/**
Takes the name of a feature, whether it's supported, and the default vote. Will
register the feature, and create a variable whose name is the unmodified feature
name.
*/
#define REGISTER_FIX(fName, supported, votebehavior) \
uint256 const fName = registerFeature(#fName, supported, votebehavior)
// clang-format off
// All known amendments must be registered either here or below with the
// "retired" amendments
REGISTER_FEATURE(OwnerPaysFee, Supported::no, VoteBehavior::DefaultNo);
REGISTER_FEATURE(Flow, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FEATURE(FlowCross, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FIX (fix1513, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FEATURE(DepositAuth, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FEATURE(Checks, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FIX (fix1571, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FIX (fix1543, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FIX (fix1623, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FEATURE(DepositPreauth, Supported::yes, VoteBehavior::DefaultYes);
// Use liquidity from strands that consume max offers, but mark as dry
REGISTER_FIX (fix1515, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FIX (fix1578, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FEATURE(MultiSignReserve, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FIX (fixTakerDryOfferRemoval, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FIX (fixMasterKeyAsRegularKey, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FIX (fixCheckThreading, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FIX (fixPayChanRecipientOwnerDir, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FEATURE(DeletableAccounts, Supported::yes, VoteBehavior::DefaultYes);
// fixQualityUpperBound should be activated before FlowCross
REGISTER_FIX (fixQualityUpperBound, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FEATURE(RequireFullyCanonicalSig, Supported::yes, VoteBehavior::DefaultYes);
// fix1781: XRPEndpointSteps should be included in the circular payment check
REGISTER_FIX (fix1781, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FEATURE(HardenedValidations, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FIX (fixAmendmentMajorityCalc, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FEATURE(NegativeUNL, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FEATURE(TicketBatch, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FEATURE(FlowSortStrands, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FIX (fixSTAmountCanonicalize, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FIX (fixRmSmallIncreasedQOffers, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FEATURE(CheckCashMakesTrustLine, Supported::yes, VoteBehavior::DefaultNo);
REGISTER_FEATURE(ExpandedSignerList, Supported::yes, VoteBehavior::DefaultNo);
REGISTER_FEATURE(NonFungibleTokensV1_1, Supported::yes, VoteBehavior::DefaultNo);
REGISTER_FIX (fixTrustLinesToSelf, Supported::yes, VoteBehavior::DefaultNo);
// The following amendments are obsolete, but must remain supported because they could
// potentially get enabled.
//
// Obsolete features are (usually) not in the ledger, and may have code
// controlled by the feature. They need to be supported because at some
// time in the past, the feature was supported and votable, but never
// passed. So the feature needs to be supported in case it is ever
// enabled (added to the ledger).
//
// If a feature remains obsolete for long enough that no clients are able
// to vote for it, the feature can be removed (entirely?) from the code.
REGISTER_FEATURE(CryptoConditionsSuite, Supported::yes, VoteBehavior::Obsolete);
REGISTER_FEATURE(NonFungibleTokensV1, Supported::yes, VoteBehavior::Obsolete);
REGISTER_FIX (fixNFTokenDirV1, Supported::yes, VoteBehavior::Obsolete);
REGISTER_FIX (fixNFTokenNegOffer, Supported::yes, VoteBehavior::Obsolete);
// The following amendments have been active for at least two years. Their
// pre-amendment code has been removed and the identifiers are deprecated.
// All known amendments and amendments that may appear in a validated
// ledger must be registered either here or above with the "active" amendments
[[deprecated("The referenced amendment has been retired"), maybe_unused]]
uint256 const
retiredMultiSign = retireFeature("MultiSign"),
retiredTrustSetAuth = retireFeature("TrustSetAuth"),
retiredFeeEscalation = retireFeature("FeeEscalation"),
retiredPayChan = retireFeature("PayChan"),
retiredCryptoConditions = retireFeature("CryptoConditions"),
retiredTickSize = retireFeature("TickSize"),
retiredFix1368 = retireFeature("fix1368"),
retiredEscrow = retireFeature("Escrow"),
retiredFix1373 = retireFeature("fix1373"),
retiredEnforceInvariants = retireFeature("EnforceInvariants"),
retiredSortedDirectories = retireFeature("SortedDirectories"),
retiredFix1201 = retireFeature("fix1201"),
retiredFix1512 = retireFeature("fix1512"),
retiredFix1523 = retireFeature("fix1523"),
retiredFix1528 = retireFeature("fix1528");
// clang-format on
#undef REGISTER_FIX
#pragma pop_macro("REGISTER_FIX")
#undef REGISTER_FEATURE
#pragma pop_macro("REGISTER_FEATURE")
// All of the features should now be registered, since variables in a cpp file
// are initialized from top to bottom.
//
// Use initialization of one final static variable to set
// featureCollections::readOnly.
[[maybe_unused]] static const bool readOnlySet =
featureCollections.registrationIsDone();
} // namespace ripple