rippled
Loading...
Searching...
No Matches
AmendmentTable.cpp
1#include <xrpl/core/ServiceRegistry.h>
2#include <xrpl/ledger/AmendmentTable.h>
3#include <xrpl/protocol/Feature.h>
4#include <xrpl/protocol/STValidation.h>
5#include <xrpl/protocol/TxFlags.h>
6#include <xrpl/protocol/jss.h>
7#include <xrpl/server/Wallet.h>
8
9#include <boost/algorithm/string.hpp>
10#include <boost/format.hpp>
11#include <boost/range/adaptor/transformed.hpp>
12#include <boost/regex.hpp>
13
14#include <algorithm>
15#include <mutex>
16
17namespace xrpl {
18
20parseSection(Section const& section)
21{
22 static boost::regex const re1(
23 "^" // start of line
24 "(?:\\s*)" // whitespace (optional)
25 "([abcdefABCDEF0-9]{64})" // <hexadecimal amendment ID>
26 "(?:\\s+)" // whitespace
27 "(\\S+)" // <description>
28 ,
29 boost::regex_constants::optimize);
30
32
33 for (auto const& line : section.lines())
34 {
35 boost::smatch match;
36
37 if (!boost::regex_match(line, match, re1))
38 Throw<std::runtime_error>("Invalid entry '" + line + "' in [" + section.name() + "]");
39
40 uint256 id;
41
42 if (!id.parseHex(match[1]))
43 {
44 Throw<std::runtime_error>(
45 "Invalid amendment ID '" + match[1] + "' in [" + section.name() + "]");
46 }
47
48 names.push_back(std::make_pair(id, match[2]));
49 }
50
51 return names;
52}
53
73{
74private:
75 // Associates each trusted validator with the last votes we saw from them
76 // and an expiration for that record.
88
89public:
90 TrustedVotes() = default;
91 TrustedVotes(TrustedVotes const& rhs) = delete;
93 operator=(TrustedVotes const& rhs) = delete;
94
95 // Called when the list of trusted validators changes.
96 //
97 // Call with AmendmentTable::mutex_ locked.
98 void
100 {
101 decltype(recordedVotes_) newRecordedVotes;
102 newRecordedVotes.reserve(allTrusted.size());
103
104 // Make sure every PublicKey in allTrusted is represented in
105 // recordedVotes_. Also make sure recordedVotes_ contains
106 // no additional PublicKeys.
107 for (auto& trusted : allTrusted)
108 {
109 if (recordedVotes_.contains(trusted))
110 {
111 // Preserve this validator's previously saved voting state.
112 newRecordedVotes.insert(recordedVotes_.extract(trusted));
113 }
114 else
115 {
116 // New validators have a starting position of no on everything.
117 // Add the entry with an empty vector and unseated timeout.
118 newRecordedVotes[trusted];
119 }
120 }
121 // The votes of any no-longer-trusted validators will be destroyed
122 // when changedTrustedVotes goes out of scope.
123 recordedVotes_.swap(newRecordedVotes);
124 }
125
126 // Called when we receive the latest votes.
127 //
128 // Call with AmendmentTable::mutex_ locked.
129 void
131 Rules const& rules,
133 NetClock::time_point const closeTime,
135 std::lock_guard<std::mutex> const& lock)
136 {
137 // When we get an STValidation we save the upVotes it contains, but
138 // we also set an expiration for those upVotes. The following constant
139 // controls the timeout.
140 //
141 // There really is no "best" timeout to choose for when we finally
142 // lose confidence that we know how a validator is voting. But part
143 // of the point of recording validator votes is to avoid flapping of
144 // amendment votes. A 24h timeout says that we will change the local
145 // record of a validator's vote to "no" 24h after the last vote seen
146 // from that validator. So flapping due to that validator being off
147 // line will happen less frequently than every 24 hours.
148 using namespace std::chrono_literals;
149 static constexpr NetClock::duration expiresAfter = 24h;
150
151 auto const newTimeout = closeTime + expiresAfter;
152
153 // Walk all validations and replace previous votes from trusted
154 // validators with these newest votes.
155 for (auto const& val : valSet)
156 {
157 auto const pkHuman = toBase58(TokenType::NodePublic, val->getSignerPublic());
158 // If this validation comes from one of our trusted validators...
159 if (auto const iter = recordedVotes_.find(val->getSignerPublic());
160 iter != recordedVotes_.end())
161 {
162 iter->second.timeout = newTimeout;
163 if (val->isFieldPresent(sfAmendments))
164 {
165 auto const& choices = val->getFieldV256(sfAmendments);
166 iter->second.upVotes.assign(choices.begin(), choices.end());
167 JLOG(j.debug()) << "recordVotes: Validation from trusted " << pkHuman << " has "
168 << choices.size() << " amendment votes: "
169 << boost::algorithm::join(
170 iter->second.upVotes |
171 boost::adaptors::transformed(to_string<256, void>),
172 ", ");
173 // TODO: Maybe transform using to_short_string once #5126 is
174 // merged
175 //
176 // iter->second.upVotes |
177 // boost::adaptors::transformed(to_short_string<256, void>)
178 }
179 else
180 {
181 // This validator does not upVote any amendments right now.
182 iter->second.upVotes.clear();
183 JLOG(j.debug()) << "recordVotes: Validation from trusted " << pkHuman
184 << " has no amendment votes.";
185 }
186 }
187 else
188 {
189 JLOG(j.debug()) << "recordVotes: Ignoring validation from untrusted " << pkHuman;
190 }
191 }
192
193 // Now remove any expired records from recordedVotes_.
195 recordedVotes_.begin(),
196 recordedVotes_.end(),
197 [&closeTime, newTimeout, &j](decltype(recordedVotes_)::value_type& votes) {
198 auto const pkHuman = toBase58(TokenType::NodePublic, votes.first);
199 if (!votes.second.timeout)
200 {
201 XRPL_ASSERT(
202 votes.second.upVotes.empty(),
203 "xrpl::TrustedVotes::recordVotes : received no "
204 "upvotes");
205 JLOG(j.debug()) << "recordVotes: Have not received any "
206 "amendment votes from "
207 << pkHuman << " since last timeout or startup";
208 }
209 else if (closeTime > votes.second.timeout)
210 {
211 JLOG(j.debug()) << "recordVotes: Timeout: Clearing votes from " << pkHuman;
212 votes.second.timeout.reset();
213 votes.second.upVotes.clear();
214 }
215 else if (votes.second.timeout != newTimeout)
216 {
217 XRPL_ASSERT(
218 votes.second.timeout < newTimeout,
219 "xrpl::TrustedVotes::recordVotes : votes not "
220 "expired");
221 using namespace std::chrono;
222 auto const age = duration_cast<minutes>(newTimeout - *votes.second.timeout);
223 JLOG(j.debug()) << "recordVotes: Using " << age.count()
224 << "min old cached votes from " << pkHuman;
225 }
226 });
227 }
228
229 // Return the information needed by AmendmentSet to determine votes.
230 //
231 // Call with AmendmentTable::mutex_ locked.
233 getVotes(Rules const& rules, std::lock_guard<std::mutex> const& lock) const
234 {
236 int available = 0;
237 for (auto& validatorVotes : recordedVotes_)
238 {
239 XRPL_ASSERT(
240 validatorVotes.second.timeout || validatorVotes.second.upVotes.empty(),
241 "xrpl::TrustedVotes::getVotes : valid votes");
242 if (validatorVotes.second.timeout)
243 ++available;
244 for (uint256 const& amendment : validatorVotes.second.upVotes)
245 {
246 ret[amendment] += 1;
247 }
248 }
249 return {available, ret};
250 }
251};
252
258{
260 AmendmentVote vote = AmendmentVote::down;
261
267 bool enabled = false;
268
270 bool supported = false;
271
274
275 explicit AmendmentState() = default;
276};
277
280{
281private:
282 // How many yes votes each amendment received
284 // number of trusted validations
285 int trustedValidations_ = 0;
286 // number of votes needed
287 int threshold_ = 0;
288
289public:
291 Rules const& rules,
292 TrustedVotes const& trustedVotes,
293 std::lock_guard<std::mutex> const& lock)
294 {
295 // process validations for ledger before flag ledger.
296 auto [trustedCount, newVotes] = trustedVotes.getVotes(rules, lock);
297
298 trustedValidations_ = trustedCount;
299 votes_.swap(newVotes);
300
301 threshold_ = std::max(
302 1L,
303 static_cast<long>(
304 (trustedValidations_ * amendmentMajorityCalcThreshold.num) /
306 }
307
308 bool
309 passes(uint256 const& amendment) const
310 {
311 auto const& it = votes_.find(amendment);
312
313 if (it == votes_.end())
314 return false;
315
316 // One validator is an exception, otherwise it is not possible
317 // to gain majority.
318 if (trustedValidations_ == 1)
319 return it->second >= threshold_;
320
321 return it->second > threshold_;
322 }
323
324 int
325 votes(uint256 const& amendment) const
326 {
327 auto const& it = votes_.find(amendment);
328
329 if (it == votes_.end())
330 return 0;
331
332 return it->second;
333 }
334
335 int
337 {
338 return trustedValidations_;
339 }
340
341 int
342 threshold() const
343 {
344 return threshold_;
345 }
346};
347
348//------------------------------------------------------------------------------
349
357{
358private:
360
362 std::uint32_t lastUpdateSeq_{0};
363
364 // Record of the last votes seen from trusted validators.
366
367 // Time that an amendment must hold a majority for
369
370 // The results of the last voting round - may be empty if
371 // we haven't participated in one yet.
373
374 // True if an unsupported amendment is enabled
375 bool unsupportedEnabled_{false};
376
377 // Unset if no unsupported amendments reach majority,
378 // else set to the earliest time an unsupported amendment
379 // will be enabled.
381
383
384 // Database which persists veto/unveto vote
386
387 // Finds or creates state. Must be called with mutex_ locked.
389 add(uint256 const& amendment, std::lock_guard<std::mutex> const& lock);
390
391 // Finds existing state. Must be called with mutex_ locked.
393 get(uint256 const& amendment, std::lock_guard<std::mutex> const& lock);
394
395 AmendmentState const*
396 get(uint256 const& amendment, std::lock_guard<std::mutex> const& lock) const;
397
398 // Injects amendment json into v. Must be called with mutex_ locked.
399 void
400 injectJson(
401 Json::Value& v,
402 uint256 const& amendment,
403 AmendmentState const& state,
404 bool isAdmin,
405 std::lock_guard<std::mutex> const& lock) const;
406
407 void
408 persistVote(uint256 const& amendment, std::string const& name, AmendmentVote vote) const;
409
410public:
412 ServiceRegistry& registry,
413 std::chrono::seconds majorityTime,
414 std::vector<FeatureInfo> const& supported,
415 Section const& enabled,
416 Section const& vetoed,
417 beast::Journal journal);
418
419 uint256
420 find(std::string const& name) const override;
421
422 bool
423 veto(uint256 const& amendment) override;
424 bool
425 unVeto(uint256 const& amendment) override;
426
427 bool
428 enable(uint256 const& amendment) override;
429
430 bool
431 isEnabled(uint256 const& amendment) const override;
432 bool
433 isSupported(uint256 const& amendment) const override;
434
435 bool
436 hasUnsupportedEnabled() const override;
437
439 firstUnsupportedExpected() const override;
440
442 getJson(bool isAdmin) const override;
444 getJson(uint256 const&, bool isAdmin) const override;
445
446 bool
447 needValidatedLedger(LedgerIndex seq) const override;
448
449 void
450 doValidatedLedger(
451 LedgerIndex seq,
452 std::set<uint256> const& enabled,
453 majorityAmendments_t const& majority) override;
454
455 void
456 trustChanged(hash_set<PublicKey> const& allTrusted) override;
457
459 doValidation(std::set<uint256> const& enabledAmendments) const override;
460
462 getDesired() const override;
463
465 doVoting(
466 Rules const& rules,
467 NetClock::time_point closeTime,
468 std::set<uint256> const& enabledAmendments,
469 majorityAmendments_t const& majorityAmendments,
470 std::vector<std::shared_ptr<STValidation>> const& validations) override;
471};
472
473//------------------------------------------------------------------------------
474
475AmendmentTableImpl::AmendmentTableImpl(
476 ServiceRegistry& registry,
477 std::chrono::seconds majorityTime,
478 std::vector<FeatureInfo> const& supported,
479 Section const& enabled,
480 Section const& vetoed,
481 beast::Journal journal)
482 : majorityTime_(majorityTime), j_(journal), db_(registry.getWalletDB())
483{
485
486 // Find out if the FeatureVotes table exists in WalletDB
487 bool const featureVotesExist = [this]() {
488 auto db = db_.checkoutDb();
489 return createFeatureVotes(*db);
490 }();
491
492 // Parse supported amendments
493 for (auto const& [name, amendment, votebehavior] : supported)
494 {
495 AmendmentState& s = add(amendment, lock);
496
497 s.name = name;
498 s.supported = true;
499 switch (votebehavior)
500 {
503 break;
504
507 break;
508
511 break;
512 }
513
514 JLOG(j_.debug()) << "Amendment " << amendment << " (" << s.name
515 << ") is supported and will be "
516 << (s.vote == AmendmentVote::up ? "up" : "down")
517 << " voted by default if not enabled on the ledger.";
518 }
519
520 hash_set<uint256> detect_conflict;
521 // Parse enabled amendments from config
522 for (std::pair<uint256, std::string> const& a : parseSection(enabled))
523 {
524 if (featureVotesExist)
525 { // If the table existed, warn about duplicate config info
526 JLOG(j_.warn()) << "[amendments] section in config file ignored"
527 " in favor of data in db/wallet.db.";
528 break;
529 }
530
531 // Otherwise transfer config data into the table
532 detect_conflict.insert(a.first);
533 persistVote(a.first, a.second, AmendmentVote::up);
534 }
535
536 // Parse vetoed amendments from config
537 for (auto const& a : parseSection(vetoed))
538 {
539 if (featureVotesExist)
540 { // If the table existed, warn about duplicate config info
541 JLOG(j_.warn()) << "[veto_amendments] section in config file ignored"
542 " in favor of data in db/wallet.db.";
543 break;
544 }
545
546 // Otherwise transfer config data into the table
547 if (!detect_conflict.contains(a.first))
548 {
549 persistVote(a.first, a.second, AmendmentVote::down);
550 }
551 else
552 {
553 JLOG(j_.warn()) << "[veto_amendments] section in config has amendment " << '('
554 << a.first << ", " << a.second
555 << ") both [veto_amendments] and [amendments].";
556 }
557 }
558
559 // Read amendment votes from wallet.db
560 auto db = db_.checkoutDb();
562 *db,
563 [&](boost::optional<std::string> amendment_hash,
564 boost::optional<std::string> amendment_name,
565 boost::optional<AmendmentVote> vote) {
566 uint256 amend_hash;
567 if (!amendment_hash || !amendment_name || !vote)
568 {
569 // These fields should never have nulls, but check
570 Throw<std::runtime_error>("Invalid FeatureVotes row in wallet.db");
571 }
572 if (!amend_hash.parseHex(*amendment_hash))
573 {
574 Throw<std::runtime_error>(
575 "Invalid amendment ID '" + *amendment_hash + " in wallet.db");
576 }
577 if (*vote == AmendmentVote::down)
578 {
579 // Unknown amendments are effectively vetoed already
580 if (auto s = get(amend_hash, lock))
581 {
582 JLOG(j_.info()) << "Amendment {" << *amendment_name << ", " << amend_hash
583 << "} is downvoted.";
584 if (!amendment_name->empty())
585 s->name = *amendment_name;
586 // An obsolete amendment's vote can never be changed
587 if (s->vote != AmendmentVote::obsolete)
588 s->vote = *vote;
589 }
590 }
591 else // up-vote
592 {
593 AmendmentState& s = add(amend_hash, lock);
594
595 JLOG(j_.debug()) << "Amendment {" << *amendment_name << ", " << amend_hash
596 << "} is upvoted.";
597 if (!amendment_name->empty())
598 s.name = *amendment_name;
599 // An obsolete amendment's vote can never be changed
601 s.vote = *vote;
602 }
603 });
604}
605
608{
609 // call with the mutex held
610 return amendmentMap_[amendmentHash];
611}
612
615{
616 // Forward to the const version of get.
617 return const_cast<AmendmentState*>(std::as_const(*this).get(amendmentHash, lock));
618}
619
620AmendmentState const*
622{
623 // call with the mutex held
624 auto ret = amendmentMap_.find(amendmentHash);
625
626 if (ret == amendmentMap_.end())
627 return nullptr;
628
629 return &ret->second;
630}
631
634{
635 std::lock_guard const lock(mutex_);
636
637 for (auto const& e : amendmentMap_)
638 {
639 if (name == e.second.name)
640 return e.first;
641 }
642
643 return {};
644}
645
646void
648 uint256 const& amendment,
649 std::string const& name,
650 AmendmentVote vote) const
651{
652 XRPL_ASSERT(
654 "xrpl::AmendmentTableImpl::persistVote : valid vote input");
655 auto db = db_.checkoutDb();
656 voteAmendment(*db, amendment, name, vote);
657}
658
659bool
661{
662 std::lock_guard const lock(mutex_);
663 AmendmentState& s = add(amendment, lock);
664
665 if (s.vote != AmendmentVote::up)
666 return false;
668 persistVote(amendment, s.name, s.vote);
669 return true;
670}
671
672bool
674{
675 std::lock_guard const lock(mutex_);
676 AmendmentState* const s = get(amendment, lock);
677
678 if ((s == nullptr) || s->vote != AmendmentVote::down)
679 return false;
681 persistVote(amendment, s->name, s->vote);
682 return true;
683}
684
685bool
687{
688 std::lock_guard const lock(mutex_);
689 AmendmentState& s = add(amendment, lock);
690
691 if (s.enabled)
692 return false;
693
694 s.enabled = true;
695
696 if (!s.supported)
697 {
698 JLOG(j_.error()) << "Unsupported amendment " << amendment << " activated.";
699 unsupportedEnabled_ = true;
700 }
701
702 return true;
703}
704
705bool
707{
708 std::lock_guard const lock(mutex_);
709 AmendmentState const* s = get(amendment, lock);
710 return (s != nullptr) && s->enabled;
711}
712
713bool
715{
716 std::lock_guard const lock(mutex_);
717 AmendmentState const* s = get(amendment, lock);
718 return (s != nullptr) && s->supported;
719}
720
721bool
727
734
737{
738 // Get the list of amendments we support and do not
739 // veto, but that are not already enabled
740 std::vector<uint256> amendments;
741
742 {
743 std::lock_guard const lock(mutex_);
744 amendments.reserve(amendmentMap_.size());
745 for (auto const& e : amendmentMap_)
746 {
747 if (e.second.supported && e.second.vote == AmendmentVote::up &&
748 (!enabled.contains(e.first)))
749 {
750 amendments.push_back(e.first);
751 JLOG(j_.info()) << "Voting for amendment " << e.second.name;
752 }
753 }
754 }
755
756 if (!amendments.empty())
757 std::sort(amendments.begin(), amendments.end());
758
759 return amendments;
760}
761
764{
765 // Get the list of amendments we support and do not veto
766 return doValidation({});
767}
768
771 Rules const& rules,
772 NetClock::time_point closeTime,
773 std::set<uint256> const& enabledAmendments,
774 majorityAmendments_t const& majorityAmendments,
776{
777 JLOG(j_.trace()) << "voting at " << closeTime.time_since_epoch().count() << ": "
778 << enabledAmendments.size() << ", " << majorityAmendments.size() << ", "
779 << valSet.size();
780
781 std::lock_guard const lock(mutex_);
782
783 // Keep a record of the votes we received.
784 previousTrustedVotes_.recordVotes(rules, valSet, closeTime, j_, lock);
785
786 // Tally the most recent votes.
788 JLOG(j_.debug()) << "Counted votes from " << vote->trustedValidations()
789 << " valid trusted validations, threshold is: " << vote->threshold();
790
791 // Map of amendments to the action to be taken for each one. The action is
792 // the value of the flags in the pseudo-transaction
794
795 // process all amendments we know of
796 for (auto const& entry : amendmentMap_)
797 {
798 if (enabledAmendments.contains(entry.first))
799 {
800 JLOG(j_.trace()) << entry.first << ": amendment already enabled";
801
802 continue;
803 }
804
805 bool const hasValMajority = vote->passes(entry.first);
806
807 auto const majorityTime = [&]() -> std::optional<NetClock::time_point> {
808 auto const it = majorityAmendments.find(entry.first);
809 if (it != majorityAmendments.end())
810 return it->second;
811 return std::nullopt;
812 }();
813
814 bool const hasLedgerMajority = majorityTime.has_value();
815
816 auto const logStr = [&entry, &vote]() {
818 ss << entry.first << " (" << entry.second.name << ") has " << vote->votes(entry.first)
819 << " votes";
820 return ss.str();
821 }();
822
823 if (hasValMajority && !hasLedgerMajority && entry.second.vote == AmendmentVote::up)
824 {
825 // Ledger says no majority, validators say yes, and voting yes
826 // locally
827 JLOG(j_.debug()) << logStr << ": amendment got majority";
828 actions[entry.first] = tfGotMajority;
829 }
830 else if (!hasValMajority && hasLedgerMajority)
831 {
832 // Ledger says majority, validators say no
833 JLOG(j_.debug()) << logStr << ": amendment lost majority";
834 actions[entry.first] = tfLostMajority;
835 }
836 else if (
837 hasLedgerMajority && ((*majorityTime + majorityTime_) <= closeTime) &&
838 entry.second.vote == AmendmentVote::up)
839 {
840 // Ledger says majority held
841 JLOG(j_.debug()) << logStr << ": amendment majority held";
842 actions[entry.first] = 0;
843 }
844 // Logging only below this point
845 else if (hasValMajority && hasLedgerMajority)
846 {
847 JLOG(j_.debug()) << logStr << ": amendment holding majority, waiting to be enabled";
848 }
849 else if (!hasValMajority)
850 {
851 JLOG(j_.debug()) << logStr << ": amendment does not have majority";
852 }
853 }
854
855 // Stash for reporting
856 lastVote_ = std::move(vote);
857 return actions;
858}
859
860bool
862{
863 std::lock_guard const lock(mutex_);
864
865 // Is there a ledger in which an amendment could have been enabled
866 // between these two ledger sequences?
867
868 return ((ledgerSeq - 1) / 256) != ((lastUpdateSeq_ - 1) / 256);
869}
870
871void
873 LedgerIndex ledgerSeq,
874 std::set<uint256> const& enabled,
875 majorityAmendments_t const& majority)
876{
877 for (auto& e : enabled)
878 enable(e);
879
880 std::lock_guard const lock(mutex_);
881
882 // Remember the ledger sequence of this update.
883 lastUpdateSeq_ = ledgerSeq;
884
885 // Since we have the whole list in `majority`, reset the time flag, even
886 // if it's currently set. If it's not set when the loop is done, then any
887 // prior unknown amendments have lost majority.
889 for (auto const& [hash, time] : majority)
890 {
891 AmendmentState const& s = add(hash, lock);
892
893 if (s.enabled)
894 continue;
895
896 if (!s.supported)
897 {
898 JLOG(j_.info()) << "Unsupported amendment " << hash << " reached majority at "
899 << to_string(time);
902 }
903 }
906}
907
908void
910{
911 std::lock_guard const lock(mutex_);
912 previousTrustedVotes_.trustChanged(allTrusted, lock);
913}
914
915void
917 Json::Value& v,
918 uint256 const& id,
919 AmendmentState const& fs,
920 bool isAdmin,
921 std::lock_guard<std::mutex> const&) const
922{
923 if (!fs.name.empty())
924 v[jss::name] = fs.name;
925
926 v[jss::supported] = fs.supported;
927 if (!fs.enabled && isAdmin)
928 {
930 {
931 v[jss::vetoed] = "Obsolete";
932 }
933 else
934 {
935 v[jss::vetoed] = fs.vote == AmendmentVote::down;
936 }
937 }
938 v[jss::enabled] = fs.enabled;
939
940 if (!fs.enabled && lastVote_ && isAdmin)
941 {
942 auto const votesTotal = lastVote_->trustedValidations();
943 auto const votesNeeded = lastVote_->threshold();
944 auto const votesFor = lastVote_->votes(id);
945
946 v[jss::count] = votesFor;
947 v[jss::validations] = votesTotal;
948
949 if (votesNeeded != 0)
950 v[jss::threshold] = votesNeeded;
951 }
952}
953
956{
958 {
959 std::lock_guard const lock(mutex_);
960 for (auto const& e : amendmentMap_)
961 {
963 ret[to_string(e.first)] = Json::objectValue, e.first, e.second, isAdmin, lock);
964 }
965 }
966 return ret;
967}
968
970AmendmentTableImpl::getJson(uint256 const& amendmentID, bool isAdmin) const
971{
973
974 {
975 std::lock_guard const lock(mutex_);
976 AmendmentState const* a = get(amendmentID, lock);
977 if (a != nullptr)
978 {
979 Json::Value& jAmendment = (ret[to_string(amendmentID)] = Json::objectValue);
980 injectJson(jAmendment, amendmentID, *a, isAdmin, lock);
981 }
982 }
983
984 return ret;
985}
986
989 ServiceRegistry& registry,
990 std::chrono::seconds majorityTime,
992 Section const& enabled,
993 Section const& vetoed,
994 beast::Journal journal)
995{
997 registry, majorityTime, supported, enabled, vetoed, journal);
998}
999
1000} // namespace xrpl
T as_const(T... args)
Represents a JSON value.
Definition json_value.h:130
A generic endpoint for log messages.
Definition Journal.h:40
Stream error() const
Definition Journal.h:319
Stream debug() const
Definition Journal.h:301
Stream info() const
Definition Journal.h:307
Stream trace() const
Severity stream access functions.
Definition Journal.h:295
Stream warn() const
Definition Journal.h:313
The status of all amendments requested in a given window.
int trustedValidations() const
int votes(uint256 const &amendment) const
hash_map< uint256, int > votes_
AmendmentSet(Rules const &rules, TrustedVotes const &trustedVotes, std::lock_guard< std::mutex > const &lock)
bool passes(uint256 const &amendment) const
Track the list of "amendments".
Json::Value getJson(bool isAdmin) const override
bool isSupported(uint256 const &amendment) const override
std::optional< NetClock::time_point > firstUnsupportedExpected() const override
std::optional< NetClock::time_point > firstUnsupportedExpected_
void injectJson(Json::Value &v, uint256 const &amendment, AmendmentState const &state, bool isAdmin, std::lock_guard< std::mutex > const &lock) const
beast::Journal const j_
std::vector< uint256 > doValidation(std::set< uint256 > const &enabledAmendments) const override
bool veto(uint256 const &amendment) override
bool hasUnsupportedEnabled() const override
returns true if one or more amendments on the network have been enabled that this server does not sup...
void persistVote(uint256 const &amendment, std::string const &name, AmendmentVote vote) const
AmendmentState & add(uint256 const &amendment, std::lock_guard< std::mutex > const &lock)
std::unique_ptr< AmendmentSet > lastVote_
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 isEnabled(uint256 const &amendment) const override
std::chrono::seconds const majorityTime_
AmendmentState * get(uint256 const &amendment, std::lock_guard< std::mutex > const &lock)
bool enable(uint256 const &amendment) override
bool unVeto(uint256 const &amendment) override
uint256 find(std::string const &name) const override
std::vector< uint256 > getDesired() const override
void trustChanged(hash_set< PublicKey > const &allTrusted) override
void doValidatedLedger(LedgerIndex seq, std::set< uint256 > const &enabled, majorityAmendments_t const &majority) override
bool needValidatedLedger(LedgerIndex seq) const override
Called to determine whether the amendment logic needs to process a new validated ledger.
hash_map< uint256, AmendmentState > amendmentMap_
The amendment table stores the list of enabled and potential amendments.
LockedSociSession checkoutDb()
Rules controlling protocol behavior.
Definition Rules.h:18
Holds a collection of configuration values.
Definition BasicConfig.h:24
std::string const & name() const
Returns the name of this section.
Definition BasicConfig.h:40
std::vector< std::string > const & lines() const
Returns all the lines in the section.
Definition BasicConfig.h:49
Service registry for dependency injection.
TrustedVotes records the most recent votes from trusted validators.
TrustedVotes & operator=(TrustedVotes const &rhs)=delete
hash_map< PublicKey, UpvotesAndTimeout > recordedVotes_
std::pair< int, hash_map< uint256, int > > getVotes(Rules const &rules, std::lock_guard< std::mutex > const &lock) const
void trustChanged(hash_set< PublicKey > const &allTrusted, std::lock_guard< std::mutex > const &lock)
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)
TrustedVotes(TrustedVotes const &rhs)=delete
TrustedVotes()=default
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition base_uint.h:476
T contains(T... args)
T empty(T... args)
T end(T... args)
T find(T... args)
T for_each(T... args)
T insert(T... args)
T is_same_v
T make_pair(T... args)
T max(T... args)
@ objectValue
object value (collection of name/value pairs).
Definition json_value.h:26
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
Json::Value getJson(LedgerFill const &fill)
Return a new Json::Value representing the ledger with given options.
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:602
T get(Section const &section, std::string const &name, T const &defaultValue=T{})
Retrieve a key/value pair from a section.
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition AccountID.cpp:92
std::unique_ptr< AmendmentTable > make_AmendmentTable(ServiceRegistry &registry, std::chrono::seconds majorityTime, std::vector< AmendmentTable::FeatureInfo > const &supported, Section const &enabled, Section const &vetoed, beast::Journal journal)
bool isAdmin(Port const &port, Json::Value const &params, beast::IP::Address const &remoteIp)
Definition Role.cpp:64
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.
Definition Wallet.cpp:223
bool createFeatureVotes(soci::session &session)
createFeatureVotes Creates the FeatureVote table if it does not exist.
Definition Wallet.cpp:199
AmendmentVote
Definition Wallet.h:126
constexpr std::ratio< 80, 100 > amendmentMajorityCalcThreshold
The minimum amount of support an amendment should have.
void voteAmendment(soci::session &session, uint256 const &amendment, std::string const &name, AmendmentVote vote)
voteAmendment Set the veto value for a particular amendment.
Definition Wallet.cpp:258
static std::vector< std::pair< uint256, std::string > > parseSection(Section const &section)
T push_back(T... args)
T size(T... args)
T sort(T... args)
T str(T... args)
Current state of an amendment.
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.
bool supported
Indicates an amendment that this server has code support for.
AmendmentState()=default
bool enabled
Indicates that the amendment has been enabled.
std::optional< NetClock::time_point > timeout
An unseated timeout indicates that either.
T swap(T... args)
T time_since_epoch(T... args)