20#include <xrpld/app/main/Application.h> 
   21#include <xrpld/app/misc/AmendmentTable.h> 
   22#include <xrpld/app/rdb/Wallet.h> 
   23#include <xrpld/core/ConfigSections.h> 
   25#include <xrpl/protocol/Feature.h> 
   26#include <xrpl/protocol/STValidation.h> 
   27#include <xrpl/protocol/TxFlags.h> 
   28#include <xrpl/protocol/jss.h> 
   30#include <boost/algorithm/string.hpp> 
   31#include <boost/format.hpp> 
   32#include <boost/range/adaptor/transformed.hpp> 
   33#include <boost/regex.hpp> 
   43    static boost::regex 
const re1(
 
   46        "([abcdefABCDEF0-9]{64})"   
   50        boost::regex_constants::optimize);
 
   54    for (
auto const& line : section.
lines())
 
   58        if (!boost::regex_match(line, 
match, re1))
 
   59            Throw<std::runtime_error>(
 
   60                "Invalid entry '" + line + 
"' in [" + section.
name() + 
"]");
 
   64        if (!
id.parseHex(
match[1]))
 
   65            Throw<std::runtime_error>(
 
   66                "Invalid amendment ID '" + 
match[1] + 
"' in [" +
 
   67                section.
name() + 
"]");
 
 
  125        newRecordedVotes.reserve(allTrusted.
size());
 
  130        for (
auto& trusted : allTrusted)
 
  141                newRecordedVotes[trusted];
 
 
  171        using namespace std::chrono_literals;
 
  174        auto const newTimeout = closeTime + expiresAfter;
 
  178        for (
auto const& val : valSet)
 
  183            if (
auto const iter = 
recordedVotes_.find(val->getSignerPublic());
 
  186                iter->second.timeout = newTimeout;
 
  187                if (val->isFieldPresent(sfAmendments))
 
  189                    auto const& choices = val->getFieldV256(sfAmendments);
 
  190                    iter->second.upVotes.assign(choices.begin(), choices.end());
 
  192                        << 
"recordVotes: Validation from trusted " << pkHuman
 
  193                        << 
" has " << choices.size() << 
" amendment votes: " 
  194                        << boost::algorithm::join(
 
  195                               iter->second.upVotes |
 
  196                                   boost::adaptors::transformed(
 
  197                                       to_string<256, void>),
 
  208                    iter->second.upVotes.clear();
 
  209                    JLOG(j.
debug()) << 
"recordVotes: Validation from trusted " 
  210                                    << pkHuman << 
" has no amendment votes.";
 
  216                    << 
"recordVotes: Ignoring validation from untrusted " 
  225            [&closeTime, newTimeout, &j](
 
  228                    toBase58(TokenType::NodePublic, votes.first);
 
  229                if (!votes.second.timeout)
 
  232                        votes.second.upVotes.empty(),
 
  233                        "ripple::TrustedVotes::recordVotes : received no " 
  236                        << 
"recordVotes: Have not received any " 
  237                           "amendment votes from " 
  238                        << pkHuman << 
" since last timeout or startup";
 
  240                else if (closeTime > votes.second.timeout)
 
  243                        << 
"recordVotes: Timeout: Clearing votes from " 
  245                    votes.second.timeout.reset();
 
  246                    votes.second.upVotes.clear();
 
  248                else if (votes.second.timeout != newTimeout)
 
  251                        votes.second.timeout < newTimeout,
 
  252                        "ripple::TrustedVotes::recordVotes : votes not " 
  254                    using namespace std::chrono;
 
  255                    auto const age = duration_cast<minutes>(
 
  256                        newTimeout - *votes.second.timeout);
 
  257                    JLOG(j.debug()) << 
"recordVotes: Using " << age.count()
 
  258                                    << 
"min old cached votes from " << pkHuman;
 
 
  271        for (
auto& validatorVotes : recordedVotes_)
 
  274                validatorVotes.second.timeout ||
 
  275                    validatorVotes.second.upVotes.empty(),
 
  276                "ripple::TrustedVotes::getVotes : valid votes");
 
  277            if (validatorVotes.second.timeout)
 
  279            for (
uint256 const& amendment : validatorVotes.second.upVotes)
 
 
 
  302    bool enabled = 
false;
 
  305    bool supported = 
false;
 
 
  320    int trustedValidations_ = 0;
 
  331        auto [trustedCount, newVotes] = trustedVotes.
getVotes(rules, lock);
 
  333        trustedValidations_ = trustedCount;
 
  334        votes_.
swap(newVotes);
 
 
  346        auto const& it = votes_.
find(amendment);
 
  348        if (it == votes_.
end())
 
  353        if (trustedValidations_ == 1)
 
  354            return it->second >= threshold_;
 
  356        return it->second > threshold_;
 
 
  362        auto const& it = votes_.
find(amendment);
 
  364        if (it == votes_.
end())
 
 
  373        return trustedValidations_;
 
 
 
  462    veto(
uint256 const& amendment) 
override;
 
  464    unVeto(
uint256 const& amendment) 
override;
 
  467    enable(
uint256 const& amendment) 
override;
 
  470    isEnabled(
uint256 const& amendment) 
const override;
 
  472    isSupported(
uint256 const& amendment) 
const override;
 
  475    hasUnsupportedEnabled() 
const override;
 
  478    firstUnsupportedExpected() 
const override;
 
  481    getJson(
bool isAdmin) 
const override;
 
  486    needValidatedLedger(
LedgerIndex seq) 
const override;
 
  501    getDesired() 
const override;
 
 
  514AmendmentTableImpl::AmendmentTableImpl(
 
  522    , majorityTime_(majorityTime)
 
  523    , unsupportedEnabled_(false)
 
  525    , db_(app.getWalletDB())
 
  530    bool const featureVotesExist = [
this]() {
 
  536    for (
auto const& [name, amendment, votebehavior] : supported)
 
  542        switch (votebehavior)
 
  557        JLOG(
j_.
debug()) << 
"Amendment " << amendment << 
" (" << s.
name 
  558                         << 
") is supported and will be " 
  560                         << 
" voted by default if not enabled on the ledger.";
 
  567        if (featureVotesExist)
 
  569            JLOG(
j_.
warn()) << 
"[amendments] section in config file ignored" 
  570                               " in favor of data in db/wallet.db.";
 
  575            detect_conflict.
insert(a.first);
 
  583        if (featureVotesExist)
 
  586                << 
"[veto_amendments] section in config file ignored" 
  587                   " in favor of data in db/wallet.db.";
 
  592            if (detect_conflict.
count(a.first) == 0)
 
  599                    << 
"[veto_amendments] section in config has amendment " 
  600                    << 
'(' << a.first << 
", " << a.second
 
  601                    << 
") both [veto_amendments] and [amendments].";
 
  610        [&](boost::optional<std::string> amendment_hash,
 
  611            boost::optional<std::string> amendment_name,
 
  612            boost::optional<AmendmentVote> vote) {
 
  614            if (!amendment_hash || !amendment_name || !vote)
 
  617                Throw<std::runtime_error>(
 
  618                    "Invalid FeatureVotes row in wallet.db");
 
  620            if (!amend_hash.
parseHex(*amendment_hash))
 
  622                Throw<std::runtime_error>(
 
  623                    "Invalid amendment ID '" + *amendment_hash +
 
  629                if (
auto s = 
get(amend_hash, lock))
 
  631                    JLOG(
j_.
info()) << 
"Amendment {" << *amendment_name << 
", " 
  632                                    << amend_hash << 
"} is downvoted.";
 
  633                    if (!amendment_name->empty())
 
  634                        s->name = *amendment_name;
 
  644                JLOG(
j_.
debug()) << 
"Amendment {" << *amendment_name << 
", " 
  645                                 << amend_hash << 
"} is upvoted.";
 
  646                if (!amendment_name->empty())
 
  647                    s.
name = *amendment_name;
 
 
  695        if (name == e.second.name)
 
 
  710        "ripple::AmendmentTableImpl::persistVote : valid vote input");
 
 
  754        JLOG(
j_.
error()) << 
"Unsupported amendment " << amendment
 
 
  805                (enabled.
count(e.first) == 0))
 
  807                amendments.push_back(e.first);
 
  808                JLOG(
j_.
info()) << 
"Voting for amendment " << e.second.name;
 
  813    if (!amendments.empty())
 
  814        std::sort(amendments.begin(), amendments.end());
 
 
  835                     << 
": " << enabledAmendments.
size() << 
", " 
  836                     << majorityAmendments.
size() << 
", " << valSet.size();
 
  846    JLOG(
j_.
debug()) << 
"Counted votes from " << vote->trustedValidations()
 
  847                     << 
" valid trusted validations, threshold is: " 
  848                     << vote->threshold();
 
  857        if (enabledAmendments.
contains(entry.first))
 
  859            JLOG(
j_.
trace()) << entry.first << 
": amendment already enabled";
 
  864        bool const hasValMajority = vote->passes(entry.first);
 
  867            auto const it = majorityAmendments.
find(entry.first);
 
  868            if (it != majorityAmendments.
end())
 
  873        bool const hasLedgerMajority = majorityTime.has_value();
 
  875        auto const logStr = [&entry, &vote]() {
 
  877            ss << entry.first << 
" (" << entry.second.name << 
") has " 
  878               << vote->votes(entry.first) << 
" votes";
 
  882        if (hasValMajority && !hasLedgerMajority &&
 
  887            JLOG(
j_.
debug()) << logStr << 
": amendment got majority";
 
  890        else if (!hasValMajority && hasLedgerMajority)
 
  893            JLOG(
j_.
debug()) << logStr << 
": amendment lost majority";
 
  902            JLOG(
j_.
debug()) << logStr << 
": amendment majority held";
 
  903            actions[entry.first] = 0;
 
  906        else if (hasValMajority && hasLedgerMajority)
 
  910                << 
": amendment holding majority, waiting to be enabled";
 
  912        else if (!hasValMajority)
 
  914            JLOG(
j_.
debug()) << logStr << 
": amendment does not have majority";
 
 
  940    for (
auto& e : enabled)
 
  952    for (
auto const& [hash, time] : majority)
 
  961            JLOG(
j_.
info()) << 
"Unsupported amendment " << hash
 
  962                            << 
" reached majority at " << 
to_string(time);
 
 
  987        v[jss::name] = fs.
name;
 
  993            v[jss::vetoed] = 
"Obsolete";
 
 1001        auto const votesTotal = 
lastVote_->trustedValidations();
 
 1002        auto const votesNeeded = 
lastVote_->threshold();
 
 1003        auto const votesFor = 
lastVote_->votes(
id);
 
 1005        v[jss::count] = votesFor;
 
 1006        v[jss::validations] = votesTotal;
 
 1009            v[jss::threshold] = votesNeeded;
 
 
 1061        app, majorityTime, supported, enabled, vetoed, journal);
 
 
A generic endpoint for log messages.
 
Stream trace() const
Severity stream access functions.
 
The status of all amendments requested in a given window.
 
int votes(uint256 const &amendment) const
 
int trustedValidations() const
 
bool passes(uint256 const &amendment) const
 
hash_map< uint256, int > votes_
 
AmendmentSet(Rules const &rules, TrustedVotes const &trustedVotes, std::lock_guard< std::mutex > const &lock)
 
Track the list of "amendments".
 
uint256 find(std::string const &name) const override
 
bool unVeto(uint256 const &amendment) override
 
bool enable(uint256 const &amendment) override
 
bool needValidatedLedger(LedgerIndex seq) const override
Called to determine whether the amendment logic needs to process a new validated ledger.
 
std::optional< NetClock::time_point > firstUnsupportedExpected_
 
std::vector< uint256 > getDesired() const override
 
bool veto(uint256 const &amendment) override
 
std::unique_ptr< AmendmentSet > lastVote_
 
std::chrono::seconds const majorityTime_
 
void doValidatedLedger(LedgerIndex seq, std::set< uint256 > const &enabled, majorityAmendments_t const &majority) override
 
bool isEnabled(uint256 const &amendment) const override
 
AmendmentState & add(uint256 const &amendment, std::lock_guard< std::mutex > const &lock)
 
TrustedVotes previousTrustedVotes_
 
hash_map< uint256, AmendmentState > amendmentMap_
 
void injectJson(Json::Value &v, uint256 const &amendment, AmendmentState const &state, bool isAdmin, std::lock_guard< std::mutex > const &lock) const
 
Json::Value getJson(bool isAdmin) const override
 
void trustChanged(hash_set< PublicKey > const &allTrusted) override
 
std::vector< uint256 > doValidation(std::set< uint256 > const &enabledAmendments) const override
 
AmendmentState * get(uint256 const &amendment, std::lock_guard< std::mutex > const &lock)
 
void persistVote(uint256 const &amendment, std::string const &name, AmendmentVote vote) const
 
bool hasUnsupportedEnabled() const override
returns true if one or more amendments on the network have been enabled that this server does not sup...
 
std::map< uint256, std::uint32_t > doVoting(Rules const &rules, NetClock::time_point closeTime, std::set< uint256 > const &enabledAmendments, majorityAmendments_t const &majorityAmendments, std::vector< std::shared_ptr< STValidation > > const &validations) override
 
bool isSupported(uint256 const &amendment) const override
 
std::optional< NetClock::time_point > firstUnsupportedExpected() const override
 
std::uint32_t lastUpdateSeq_
 
The amendment table stores the list of enabled and potential amendments.
 
LockedSociSession checkoutDb()
 
Rules controlling protocol behavior.
 
Holds a collection of configuration values.
 
std::string const & name() const
Returns the name of this section.
 
std::vector< std::string > const & lines() const
Returns all the lines in the section.
 
TrustedVotes records the most recent votes from trusted validators.
 
void recordVotes(Rules const &rules, std::vector< std::shared_ptr< STValidation > > const &valSet, NetClock::time_point const closeTime, beast::Journal j, std::lock_guard< std::mutex > const &lock)
 
std::pair< int, hash_map< uint256, int > > getVotes(Rules const &rules, std::lock_guard< std::mutex > const &lock) const
 
hash_map< PublicKey, UpvotesAndTimeout > recordedVotes_
 
TrustedVotes & operator=(TrustedVotes const &rhs)=delete
 
void trustChanged(hash_set< PublicKey > const &allTrusted, std::lock_guard< std::mutex > const &lock)
 
TrustedVotes(TrustedVotes const &rhs)=delete
 
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
 
@ objectValue
object value (collection of name/value pairs).
 
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
 
static std::vector< std::pair< uint256, std::string > > parseSection(Section const §ion)
 
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
 
constexpr std::uint32_t tfGotMajority
 
bool isAdmin(Port const &port, Json::Value const ¶ms, beast::IP::Address const &remoteIp)
 
bool createFeatureVotes(soci::session &session)
createFeatureVotes Creates the FeatureVote table if it does not exist.
 
void readAmendments(soci::session &session, std::function< void(boost::optional< std::string > amendment_hash, boost::optional< std::string > amendment_name, boost::optional< AmendmentVote > vote)> const &callback)
readAmendments Reads all amendments from the FeatureVotes table.
 
std::string to_string(base_uint< Bits, Tag > const &a)
 
T get(Section const §ion, std::string const &name, T const &defaultValue=T{})
Retrieve a key/value pair from a section.
 
Json::Value getJson(LedgerFill const &fill)
Return a new Json::Value representing the ledger with given options.
 
std::unique_ptr< AmendmentTable > make_AmendmentTable(Application &app, std::chrono::seconds majorityTime, std::vector< AmendmentTable::FeatureInfo > const &supported, Section const &enabled, Section const &vetoed, beast::Journal journal)
 
constexpr std::ratio< 80, 100 > amendmentMajorityCalcThreshold
The minimum amount of support an amendment should have.
 
constexpr std::uint32_t tfLostMajority
 
void voteAmendment(soci::session &session, uint256 const &amendment, std::string const &name, AmendmentVote vote)
voteAmendment Set the veto value for a particular amendment.
 
Current state of an amendment.
 
bool supported
Indicates an amendment that this server has code support for.
 
bool enabled
Indicates that the amendment has been enabled.
 
std::string name
The name of this amendment, possibly empty.
 
AmendmentVote vote
If an amendment is down-voted, a server will not vote to enable it.
 
std::vector< uint256 > upVotes
 
std::optional< NetClock::time_point > timeout
An unseated timeout indicates that either.
 
T time_since_epoch(T... args)