-
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 8 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> | ||
|
@@ -97,25 +98,71 @@ struct AmendmentSet | |
private: | ||
// How many yes votes each amendment received | ||
hash_map<uint256, int> votes_; | ||
Rules const& rules_; | ||
|
||
public: | ||
// number of trusted validations | ||
int mTrustedValidations = 0; | ||
|
||
// number of votes needed | ||
int mThreshold = 0; | ||
AmendmentSet( | ||
Rules const& rules, | ||
std::vector<std::shared_ptr<STValidation>> const& valSet) | ||
: rules_(rules) | ||
{ | ||
// process validations for ledger before flag ledger | ||
for (auto const& val : valSet) | ||
{ | ||
if (val->isTrusted()) | ||
{ | ||
if (val->isFieldPresent(sfAmendments)) | ||
{ | ||
auto const choices = val->getFieldV256(sfAmendments); | ||
std::for_each( | ||
choices.begin(), | ||
choices.end(), | ||
[&](auto const& amendment) { ++votes_[amendment]; }); | ||
} | ||
|
||
++mTrustedValidations; | ||
} | ||
} | ||
|
||
AmendmentSet() = default; | ||
mThreshold = !rules_.enabled(fixAmendmentMajorityCalc) | ||
? std::max( | ||
1L, | ||
(mTrustedValidations * | ||
amendmentSuperMajorityThresholdPre3396.num) / | ||
amendmentSuperMajorityThresholdPre3396.den) | ||
: std::max( | ||
1L, | ||
(mTrustedValidations * | ||
amendmentSuperMajorityThresholdPost3396.num) / | ||
amendmentSuperMajorityThresholdPost3396.den); | ||
} | ||
|
||
void | ||
tally(std::set<uint256> const& amendments) | ||
bool | ||
passes(uint256 const& amendment) const | ||
{ | ||
++mTrustedValidations; | ||
auto const& it = votes_.find(amendment); | ||
|
||
for (auto const& amendment : amendments) | ||
++votes_[amendment]; | ||
if (it == votes_.end()) | ||
return false; | ||
|
||
// Before this fix, it was possible for an amendment to activate with a | ||
// percentage slightly less than 80% because we compared for "greater | ||
// than or equal to" instead of strictly "greater than". | ||
// One validator is an exception, otherwise it is not possible | ||
// to gain majority. | ||
if (!rules_.enabled(fixAmendmentMajorityCalc) || | ||
mTrustedValidations == 1) | ||
return it->second >= mThreshold; | ||
|
||
return it->second > mThreshold; | ||
} | ||
|
||
// number of trusted validations | ||
int mTrustedValidations = 0; | ||
|
||
// number of votes needed | ||
int mThreshold = 0; | ||
|
||
int | ||
votes(uint256 const& amendment) const | ||
{ | ||
|
@@ -138,7 +185,7 @@ struct AmendmentSet | |
*/ | ||
class AmendmentTableImpl final : public AmendmentTable | ||
{ | ||
protected: | ||
private: | ||
mutable std::mutex mutex_; | ||
|
||
hash_map<uint256, AmendmentState> amendmentMap_; | ||
|
@@ -147,10 +194,6 @@ class AmendmentTableImpl final : public AmendmentTable | |
// Time that an amendment must hold a majority for | ||
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_; | ||
|
||
// The results of the last voting round - may be empty if | ||
// we haven't participated in one yet. | ||
std::unique_ptr<AmendmentSet> lastVote_; | ||
|
@@ -187,7 +230,6 @@ class AmendmentTableImpl final : public AmendmentTable | |
public: | ||
AmendmentTableImpl( | ||
std::chrono::seconds majorityTime, | ||
int majorityFraction, | ||
Section const& supported, | ||
Section const& enabled, | ||
Section const& vetoed, | ||
|
@@ -237,6 +279,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,19 +290,15 @@ class AmendmentTableImpl final : public AmendmentTable | |
|
||
AmendmentTableImpl::AmendmentTableImpl( | ||
std::chrono::seconds majorityTime, | ||
int majorityFraction, | ||
Section const& supported, | ||
Section const& enabled, | ||
Section const& vetoed, | ||
beast::Journal journal) | ||
: lastUpdateSeq_(0) | ||
, majorityTime_(majorityTime) | ||
, majorityFraction_(majorityFraction) | ||
, unsupportedEnabled_(false) | ||
, j_(journal) | ||
{ | ||
assert(majorityFraction_ != 0); | ||
|
||
std::lock_guard sl(mutex_); | ||
|
||
for (auto const& a : parseSection(supported)) | ||
|
@@ -461,6 +500,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, | ||
|
@@ -470,27 +510,7 @@ AmendmentTableImpl::doVoting( | |
<< ": " << enabledAmendments.size() << ", " | ||
<< majorityAmendments.size() << ", " << valSet.size(); | ||
|
||
auto vote = std::make_unique<AmendmentSet>(); | ||
|
||
// process validations for ledger before flag ledger | ||
for (auto const& val : valSet) | ||
{ | ||
if (val->isTrusted()) | ||
{ | ||
std::set<uint256> ballot; | ||
|
||
if (val->isFieldPresent(sfAmendments)) | ||
{ | ||
auto const choices = val->getFieldV256(sfAmendments); | ||
ballot.insert(choices.begin(), choices.end()); | ||
} | ||
|
||
vote->tally(ballot); | ||
} | ||
} | ||
|
||
vote->mThreshold = | ||
std::max(1, (vote->mTrustedValidations * majorityFraction_) / 256); | ||
auto vote = std::make_unique<AmendmentSet>(rules, valSet); | ||
|
||
JLOG(j_.debug()) << "Received " << vote->mTrustedValidations | ||
<< " trusted validations, threshold is: " | ||
|
@@ -507,8 +527,7 @@ AmendmentTableImpl::doVoting( | |
{ | ||
NetClock::time_point majorityTime = {}; | ||
|
||
bool const hasValMajority = | ||
(vote->votes(entry.first) >= vote->mThreshold); | ||
bool const hasValMajority = vote->passes(entry.first); | ||
|
||
{ | ||
auto const it = majorityAmendments.find(entry.first); | ||
|
@@ -666,14 +685,13 @@ AmendmentTableImpl::getJson(uint256 const& amendmentID) const | |
std::unique_ptr<AmendmentTable> | ||
make_AmendmentTable( | ||
std::chrono::seconds majorityTime, | ||
int majorityFraction, | ||
Section const& supported, | ||
Section const& enabled, | ||
Section const& vetoed, | ||
beast::Journal journal) | ||
{ | ||
return std::make_unique<AmendmentTableImpl>( | ||
majorityTime, majorityFraction, supported, enabled, vetoed, journal); | ||
majorityTime, supported, enabled, vetoed, journal); | ||
} | ||
|
||
} // namespace ripple |
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 |
---|---|---|
|
@@ -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 fixAmendmentMajorityCalc amendment activates. | ||
*/ | ||
constexpr std::ratio<204, 256> amendmentSuperMajorityThresholdPre3396; | ||
|
||
constexpr std::ratio<80, 100> amendmentSuperMajorityThresholdPost3396; | ||
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. Now that the
would be a little easier to figure out. You may have better ideas than these. 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 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 like the way that the decision making code has moved into
AmendmentSet
. However I find it a bit disorienting to readAmendmentSet
in its current hybrid struct/class form. I suggest that you pushAmendmentSet
all the way over to being a proper class.class
, not astruct
.mTrustedValidations
andmThreshold
private and move them up to the top of the class declaration next tovotes_
andrules_
.mTrustedValidations
andmThreshold
totrustedValidations_
andthreshold_
respectively.const
accessor methodstrustedValidations()
andthreshold()
.mTrustedValidations
andmThreshold
to use the newly added accessor methods.These changes don't affect functionality, they just make the class more self consistent and easier to comprehend IMO. So your call.
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