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