-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Amendment table clean up (Improve amendment processing and activation logic) #3428
Changes from 4 commits
29b74b2
7d90194
492272c
7482d74
5e989ec
63370d1
e0f516f
9f6aefc
f3c752c
2c429dd
69ea2d4
d7cb90c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,6 +21,7 @@ | |
#include <ripple/app/misc/AmendmentTable.h> | ||
#include <ripple/core/ConfigSections.h> | ||
#include <ripple/core/DatabaseCon.h> | ||
#include <ripple/protocol/Feature.h> | ||
#include <ripple/protocol/STValidation.h> | ||
#include <ripple/protocol/TxFlags.h> | ||
#include <ripple/protocol/jss.h> | ||
|
@@ -148,8 +149,8 @@ class AmendmentTableImpl final : public AmendmentTable | |
std::chrono::seconds const majorityTime_; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can't comment on a line that hasn't been modified. And I know this is not your doing. I'm looking at the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
|
||
// The amount of support that an amendment must receive | ||
// 0 = 0% and 256 = 100% | ||
int const majorityFraction_; | ||
// 0 = 0% and 256 = 100%(old) 8 = 100%(new) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this comment is wrong? Doesn't the current implementation result in "10 = 100%(new) "? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's right. But I think the comment with the arithmetic is unnecessary here, especially if we use the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removed the comment |
||
MajorityFraction const majorityFraction_; | ||
|
||
// The results of the last voting round - may be empty if | ||
// we haven't participated in one yet. | ||
|
@@ -187,7 +188,7 @@ class AmendmentTableImpl final : public AmendmentTable | |
public: | ||
AmendmentTableImpl( | ||
std::chrono::seconds majorityTime, | ||
int majorityFraction, | ||
MajorityFraction const& majorityFraction, | ||
Section const& supported, | ||
Section const& enabled, | ||
Section const& vetoed, | ||
|
@@ -237,6 +238,7 @@ class AmendmentTableImpl final : public AmendmentTable | |
|
||
std::map<uint256, std::uint32_t> | ||
doVoting( | ||
Rules const& rules, | ||
NetClock::time_point closeTime, | ||
std::set<uint256> const& enabledAmendments, | ||
majorityAmendments_t const& majorityAmendments, | ||
|
@@ -247,7 +249,7 @@ class AmendmentTableImpl final : public AmendmentTable | |
|
||
AmendmentTableImpl::AmendmentTableImpl( | ||
std::chrono::seconds majorityTime, | ||
int majorityFraction, | ||
MajorityFraction const& majorityFraction, | ||
Section const& supported, | ||
Section const& enabled, | ||
Section const& vetoed, | ||
|
@@ -258,7 +260,7 @@ AmendmentTableImpl::AmendmentTableImpl( | |
, unsupportedEnabled_(false) | ||
, j_(journal) | ||
{ | ||
assert(majorityFraction_ != 0); | ||
assert(majorityFraction_.old_ != 0 && majorityFraction_.new_ != 0); | ||
|
||
std::lock_guard sl(mutex_); | ||
|
||
|
@@ -461,6 +463,7 @@ AmendmentTableImpl::getDesired() const | |
|
||
std::map<uint256, std::uint32_t> | ||
AmendmentTableImpl::doVoting( | ||
Rules const& rules, | ||
NetClock::time_point closeTime, | ||
std::set<uint256> const& enabledAmendments, | ||
majorityAmendments_t const& majorityAmendments, | ||
|
@@ -489,8 +492,10 @@ AmendmentTableImpl::doVoting( | |
} | ||
} | ||
|
||
vote->mThreshold = | ||
std::max(1, (vote->mTrustedValidations * majorityFraction_) / 256); | ||
vote->mThreshold = rules.enabled(ripple::fix3396) | ||
? std::max(1, (vote->mTrustedValidations * majorityFraction_.new_) / 10) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are two different places where I'd like to suggest consolidating those two places into a single place; here. You can do that by rounding up the division instead of doing the usual C/C++ truncate toward zero. It's worth noting that this change, either way it is done, has non-trivial impact on how amendments behave on a network with a small number of validators. It means that with small numbers of trusted validators there must be 100% agreement before any amendment will pass. For example, if we require at least 80% agreement between validators, then with 2, 3, or 4 trusted validators there must be 100% agreement for any amendment to pass. I agree that's the right call to make given the current state of the network. I think by doing the rounding up here, rather than tweaking the comparison on line 515, you'll make the intent easier to understand for future maintainers. There are a couple of different ways to achieve rounding up. Here's one way to do it that avoids floating point:
There are other ways to code it as well. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. While we're here, I have a mild preference for using 100 as the divisor rather than 10. That would allow you to rename amendmentMajorityFraction = 8 to amendmentMajorityPercent = 80; which is way easier to read and understand. I think people accustomed to western math will also recognize percentages much faster than they will recognize fractions of 10. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I appreciate the well-thought out comment (and agree with the 8 -> 80 change), but I find the division code confusing to read. Once I read it carefully, I understand what's happening, but I'm not confident it behaves identically pre-amendment. I am hoping we can do something even better: We should push all this logic into
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
: std::max( | ||
1, (vote->mTrustedValidations * majorityFraction_.old_) / 256); | ||
|
||
JLOG(j_.debug()) << "Received " << vote->mTrustedValidations | ||
<< " trusted validations, threshold is: " | ||
|
@@ -507,8 +512,9 @@ AmendmentTableImpl::doVoting( | |
{ | ||
NetClock::time_point majorityTime = {}; | ||
|
||
bool const hasValMajority = | ||
(vote->votes(entry.first) >= vote->mThreshold); | ||
bool const hasValMajority = rules.enabled(ripple::fix3396) | ||
? (vote->votes(entry.first) > vote->mThreshold) | ||
: (vote->votes(entry.first) >= vote->mThreshold); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you go with rounding up when calculating the threshold, then this change should be reverted. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated per Nik's suggestion of pushing the logic into AmendmentSet. |
||
|
||
{ | ||
auto const it = majorityAmendments.find(entry.first); | ||
|
@@ -666,7 +672,7 @@ AmendmentTableImpl::getJson(uint256 const& amendmentID) const | |
std::unique_ptr<AmendmentTable> | ||
make_AmendmentTable( | ||
std::chrono::seconds majorityTime, | ||
int majorityFraction, | ||
MajorityFraction const& majorityFraction, | ||
Section const& supported, | ||
Section const& enabled, | ||
Section const& vetoed, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -480,6 +480,37 @@ Config::loadFromString(std::string const& fileContents) | |
if (getSingleSection(secConfig, SECTION_COMPRESSION, strTemp, j_)) | ||
COMPRESSION = beast::lexicalCastThrow<bool>(strTemp); | ||
|
||
if (getSingleSection( | ||
secConfig, SECTION_AMENDMENT_MAJORITY_TIME, strTemp, j_)) | ||
{ | ||
using namespace std::chrono; | ||
boost::regex const re( | ||
"^\\s*(\\d+)\\s*(minutes|hours|days|weeks)\\s*(\\s+.*)?$"); | ||
boost::smatch match; | ||
if (!boost::regex_match(strTemp, match, re)) | ||
Throw<std::runtime_error>( | ||
"Invalid " SECTION_AMENDMENT_MAJORITY_TIME | ||
", must be: [0-9]+ [minutes|hours|days|weeks]"); | ||
|
||
std::uint32_t duration = | ||
beast::lexicalCastThrow<std::uint32_t>(match[1].str()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would not allow There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
|
||
if (boost::iequals(match[2], "minutes")) | ||
AMENDMENT_MAJORITY_TIME = minutes(duration); | ||
else if (boost::iequals(match[2], "hours")) | ||
AMENDMENT_MAJORITY_TIME = hours(duration); | ||
else if (boost::iequals(match[2], "days")) | ||
AMENDMENT_MAJORITY_TIME = days(duration); | ||
else if (boost::iequals(match[2], "weeks")) | ||
AMENDMENT_MAJORITY_TIME = weeks(duration); | ||
|
||
if (AMENDMENT_MAJORITY_TIME < minutes(15)) | ||
Throw<std::runtime_error>( | ||
"Invalid " SECTION_AMENDMENT_MAJORITY_TIME | ||
", the minimum amount of time an amendment must hold a " | ||
"majority is 15 minutes"); | ||
} | ||
|
||
// Do not load trusted validator configuration for standalone mode | ||
if (!RUN_STANDALONE) | ||
{ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -111,7 +111,8 @@ class FeatureCollections | |
"RequireFullyCanonicalSig", | ||
"fix1781", // XRPEndpointSteps should be included in the circular | ||
// payment check | ||
"HardenedValidations"}; | ||
"HardenedValidations", | ||
"fix3396"}; // Fix Amendment majority calculation | ||
|
||
std::vector<uint256> features; | ||
boost::container::flat_map<uint256, std::size_t> featureToIndex; | ||
|
@@ -367,6 +368,7 @@ extern uint256 const fixQualityUpperBound; | |
extern uint256 const featureRequireFullyCanonicalSig; | ||
extern uint256 const fix1781; | ||
extern uint256 const featureHardenedValidations; | ||
extern uint256 const fix3396; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suggest renaming this to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
|
||
} // namespace ripple | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,6 +21,7 @@ | |
#define RIPPLE_PROTOCOL_SYSTEMPARAMETERS_H_INCLUDED | ||
|
||
#include <ripple/basics/XRPAmount.h> | ||
#include <ripple/basics/chrono.h> | ||
nbougalis marked this conversation as resolved.
Show resolved
Hide resolved
|
||
#include <cstdint> | ||
#include <string> | ||
|
||
|
@@ -59,6 +60,18 @@ systemCurrencyCode() | |
/** The XRP ledger network's earliest allowed sequence */ | ||
static std::uint32_t constexpr XRP_LEDGER_EARLIEST_SEQ{32570}; | ||
|
||
/** The minimum amount of support an amendment should have. | ||
|
||
@note This value is used by legacy code and will become obsolete | ||
once the fix3396 amendment activates. | ||
*/ | ||
constexpr int const amendmentMajorityFractionOld = 204; | ||
|
||
constexpr int const amendmentMajorityFraction = 8; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See comments about changing the new divisor from 10 to 100. That would allow this value to be named |
||
|
||
/** The minimum amount of time an amendment must hold a majority */ | ||
constexpr std::chrono::seconds const defaultAmendmentMajorityTime = weeks{2}; | ||
|
||
} // namespace ripple | ||
|
||
#endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not keen on the names
old_
andnew_
. If I'm reading the code correctly, the way the numbers are interpreted is significantly different. Perhaps...old_
->per256
new_
->per10fix3396
Or perhaps you have a better idea for more descriptive names...
Also, in general, around this code base, if a member variable is public (like in
struct MajorityFraction
) then we don't put trailing underscores on it. The trailing underscores are usually reserved for private or protected data.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's best to use
std::ratio
here:The nice thing is that
std::ratio
tells you exactly what's happening and reduces the that risk that someone changes the numerator, while leaving the denominator (which is currently hidden inAmendmentTableImpl
) unchanged.Of course, it's important to note that the compiler actually reduces
std::ratio<204, 256>
from204/256
to51/64
andstd::ratio<80,100>
from80/100
to4/5
but even with those reductions the arithmetic should be precise and match the existing math. See this gist.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done