xrpld
Loading...
Searching...
No Matches
AmendmentTable.cpp
1#include <xrpl/ledger/AmendmentTable.h>
2
3#include <xrpl/basics/Log.h>
4#include <xrpl/basics/UnorderedContainers.h>
5#include <xrpl/basics/base_uint.h>
6#include <xrpl/basics/chrono.h>
7#include <xrpl/basics/contract.h>
8#include <xrpl/beast/utility/Journal.h>
9#include <xrpl/beast/utility/instrumentation.h>
10#include <xrpl/config/BasicConfig.h>
11#include <xrpl/core/ServiceRegistry.h>
12#include <xrpl/json/json_value.h>
13#include <xrpl/ledger/View.h>
14#include <xrpl/protocol/Feature.h>
15#include <xrpl/protocol/Protocol.h>
16#include <xrpl/protocol/PublicKey.h>
17#include <xrpl/protocol/Rules.h>
18#include <xrpl/protocol/SField.h>
19#include <xrpl/protocol/STValidation.h>
20#include <xrpl/protocol/SystemParameters.h>
21#include <xrpl/protocol/TxFlags.h>
22#include <xrpl/protocol/jss.h>
23#include <xrpl/protocol/tokens.h>
24#include <xrpl/server/Wallet.h>
25
26#include <boost/algorithm/string/join.hpp>
27#include <boost/optional/optional.hpp> // IWYU pragma: keep
28#include <boost/range/adaptor/transformed.hpp>
29#include <boost/regex/v5/regbase.hpp>
30#include <boost/regex/v5/regex.hpp>
31#include <boost/regex/v5/regex_match.hpp>
32
33#include <algorithm>
34#include <chrono>
35#include <cstdint>
36#include <format>
37#include <map>
38#include <memory>
39#include <mutex>
40#include <optional>
41#include <set>
42#include <sstream>
43#include <stdexcept>
44#include <string>
45#include <utility>
46#include <vector>
47
48namespace xrpl {
49
50static std::vector<std::pair<UInt256, std::string>>
51parseSection(Section const& section)
52{
53 static boost::regex const kRe1(
54 "^" // start of line
55 "(?:\\s*)" // whitespace (optional)
56 "([abcdefABCDEF0-9]{64})" // <hexadecimal amendment ID>
57 "(?:\\s+)" // whitespace
58 "(\\S+)" // <description>
59 ,
60 boost::regex_constants::optimize);
61
63
64 for (auto const& line : section.lines())
65 {
66 boost::smatch match;
67
68 if (!boost::regex_match(line, match, kRe1))
69 {
71 std::format("Invalid entry '{}' in [{}]", line, section.name()));
72 }
73
74 UInt256 id;
75
76 if (!id.parseHex(match[1]))
77 {
79 std::format("Invalid amendment ID '{}' in [{}]", match[1].str(), section.name()));
80 }
81
82 names.emplace_back(id, match[2]);
83 }
84
85 return names;
86}
87
108{
109private:
110 // Associates each trusted validator with the last votes we saw from them
111 // and an expiration for that record.
124
125public:
126 TrustedVotes() = default;
127 TrustedVotes(TrustedVotes const& rhs) = delete;
129 operator=(TrustedVotes const& rhs) = delete;
130
131 // Called when the list of trusted validators changes.
132 //
133 // Call with AmendmentTable::mutex_ locked.
134 void
136 {
137 decltype(recordedVotes_) newRecordedVotes;
138 newRecordedVotes.reserve(allTrusted.size());
139
140 // Make sure every PublicKey in allTrusted is represented in
141 // recordedVotes_. Also make sure recordedVotes_ contains
142 // no additional PublicKeys.
143 for (auto& trusted : allTrusted)
144 {
145 if (recordedVotes_.contains(trusted))
146 {
147 // Preserve this validator's previously saved voting state.
148 newRecordedVotes.insert(recordedVotes_.extract(trusted));
149 }
150 else
151 {
152 // New validators have a starting position of no on everything.
153 // Add the entry with an empty vector and unseated timeout.
154 newRecordedVotes[trusted];
155 }
156 }
157 // The votes of any no-longer-trusted validators will be destroyed
158 // when changedTrustedVotes goes out of scope.
159 recordedVotes_.swap(newRecordedVotes);
160 }
161
162 // Called when we receive the latest votes.
163 //
164 // Call with AmendmentTable::mutex_ locked.
165 void
167 Rules const& rules,
169 NetClock::time_point const closeTime,
172 {
173 // When we get an STValidation we save the upVotes it contains, but
174 // we also set an expiration for those upVotes. The following constant
175 // controls the timeout.
176 //
177 // There really is no "best" timeout to choose for when we finally
178 // lose confidence that we know how a validator is voting. But part
179 // of the point of recording validator votes is to avoid flapping of
180 // amendment votes. A 24h timeout says that we will change the local
181 // record of a validator's vote to "no" 24h after the last vote seen
182 // from that validator. So flapping due to that validator being off
183 // line will happen less frequently than every 24 hours.
184 using namespace std::chrono_literals;
185 static constexpr NetClock::duration kExpiresAfter = 24h;
186
187 auto const newTimeout = closeTime + kExpiresAfter;
188
189 // Walk all validations and replace previous votes from trusted
190 // validators with these newest votes.
191 for (auto const& val : valSet)
192 {
193 auto const pkHuman = toBase58(TokenType::NodePublic, val->getSignerPublic());
194 // If this validation comes from one of our trusted validators...
195 if (auto const iter = recordedVotes_.find(val->getSignerPublic());
196 iter != recordedVotes_.end())
197 {
198 iter->second.timeout = newTimeout;
199 if (val->isFieldPresent(sfAmendments))
200 {
201 auto const& choices = val->getFieldV256(sfAmendments);
202 iter->second.upVotes.assign(choices.begin(), choices.end());
203 JLOG(j.debug()) << "recordVotes: Validation from trusted " << pkHuman << " has "
204 << choices.size() << " amendment votes: "
205 << boost::algorithm::join(
206 iter->second.upVotes |
207 boost::adaptors::transformed(to_string<256, void>),
208 ", ");
209 // TODO: Maybe transform using to_short_string once #5126 is
210 // merged
211 //
212 // iter->second.upVotes |
213 // boost::adaptors::transformed(to_short_string<256, void>)
214 }
215 else
216 {
217 // This validator does not upVote any amendments right now.
218 iter->second.upVotes.clear();
219 JLOG(j.debug()) << "recordVotes: Validation from trusted " << pkHuman
220 << " has no amendment votes.";
221 }
222 }
223 else
224 {
225 JLOG(j.debug()) << "recordVotes: Ignoring validation from untrusted " << pkHuman;
226 }
227 }
228
229 // Now remove any expired records from recordedVotes_.
232 [&closeTime, newTimeout, &j](decltype(recordedVotes_)::value_type& votes) {
233 auto const pkHuman = toBase58(TokenType::NodePublic, votes.first);
234 if (!votes.second.timeout)
235 {
236 XRPL_ASSERT(
237 votes.second.upVotes.empty(),
238 "xrpl::TrustedVotes::recordVotes : received no "
239 "upvotes");
240 JLOG(j.debug()) << "recordVotes: Have not received any "
241 "amendment votes from "
242 << pkHuman << " since last timeout or startup";
243 }
244 else if (closeTime > votes.second.timeout)
245 {
246 JLOG(j.debug()) << "recordVotes: Timeout: Clearing votes from " << pkHuman;
247 votes.second.timeout.reset();
248 votes.second.upVotes.clear();
249 }
250 else if (votes.second.timeout != newTimeout)
251 {
252 XRPL_ASSERT(
253 votes.second.timeout < newTimeout,
254 "xrpl::TrustedVotes::recordVotes : votes not "
255 "expired");
256 using namespace std::chrono;
257 auto const age = duration_cast<minutes>(newTimeout - *votes.second.timeout);
258 JLOG(j.debug()) << "recordVotes: Using " << age.count()
259 << "min old cached votes from " << pkHuman;
260 }
261 });
262 }
263
264 // Return the information needed by AmendmentSet to determine votes.
265 //
266 // Call with AmendmentTable::mutex_ locked.
269 {
271 int available = 0;
272 for (auto& validatorVotes : recordedVotes_)
273 {
274 XRPL_ASSERT(
275 validatorVotes.second.timeout || validatorVotes.second.upVotes.empty(),
276 "xrpl::TrustedVotes::getVotes : valid votes");
277 if (validatorVotes.second.timeout)
278 ++available;
279 for (UInt256 const& amendment : validatorVotes.second.upVotes)
280 {
281 ret[amendment] += 1;
282 }
283 }
284 return {available, ret};
285 }
286};
287
294{
299
306 bool enabled = false;
307
311 bool supported = false;
312
317
318 explicit AmendmentState() = default;
319};
320
325{
326private:
327 // How many yes votes each amendment received
329 // number of trusted validations
331 // number of votes needed
332 int threshold_ = 0;
333
334public:
336 Rules const& rules,
337 TrustedVotes const& trustedVotes,
339 {
340 // process validations for ledger before flag ledger.
341 auto [trustedCount, newVotes] = trustedVotes.getVotes(rules, lock);
342
343 trustedValidations_ = trustedCount;
344 votes_.swap(newVotes);
345
347 1L,
348 static_cast<long>(
351 }
352
353 [[nodiscard]] bool
354 passes(UInt256 const& amendment) const
355 {
356 auto const& it = votes_.find(amendment);
357
358 if (it == votes_.end())
359 return false;
360
361 // One validator is an exception, otherwise it is not possible
362 // to gain majority.
363 if (trustedValidations_ == 1)
364 return it->second >= threshold_;
365
366 return it->second > threshold_;
367 }
368
369 [[nodiscard]] int
370 votes(UInt256 const& amendment) const
371 {
372 auto const& it = votes_.find(amendment);
373
374 if (it == votes_.end())
375 return 0;
376
377 return it->second;
378 }
379
380 [[nodiscard]] int
382 {
383 return trustedValidations_;
384 }
385
386 [[nodiscard]] int
387 threshold() const
388 {
389 return threshold_;
390 }
391};
392
393//------------------------------------------------------------------------------
394
403{
404private:
406
409
410 // Record of the last votes seen from trusted validators.
412
413 // Time that an amendment must hold a majority for
415
416 // The results of the last voting round - may be empty if
417 // we haven't participated in one yet.
419
420 // True if an unsupported amendment is enabled
422
423 // Unset if no unsupported amendments reach majority,
424 // else set to the earliest time an unsupported amendment
425 // will be enabled.
427
429
430 // Database which persists veto/unveto vote
432
433 // Finds or creates state. Must be called with mutex_ locked.
435 add(UInt256 const& amendment, std::scoped_lock<std::mutex> const& lock);
436
437 // Finds existing state. Must be called with mutex_ locked.
439 get(UInt256 const& amendment, std::scoped_lock<std::mutex> const& lock);
440
441 AmendmentState const*
442 get(UInt256 const& amendment, std::scoped_lock<std::mutex> const& lock) const;
443
444 // Injects amendment json into v. Must be called with mutex_ locked.
445 void
447 json::Value& v,
448 UInt256 const& amendment,
449 AmendmentState const& state,
450 bool isAdmin,
452
453 void
454 persistVote(UInt256 const& amendment, std::string const& name, AmendmentVote vote) const;
455
456public:
458 ServiceRegistry& registry,
459 std::chrono::seconds majorityTime,
460 std::vector<FeatureInfo> const& supported,
461 Section const& enabled,
462 Section const& vetoed,
463 beast::Journal journal);
464
465 UInt256
466 find(std::string const& name) const override;
467
468 bool
469 veto(UInt256 const& amendment) override;
470 bool
471 unVeto(UInt256 const& amendment) override;
472
473 bool
474 enable(UInt256 const& amendment) override;
475
476 bool
477 isEnabled(UInt256 const& amendment) const override;
478 bool
479 isSupported(UInt256 const& amendment) const override;
480
481 bool
482 hasUnsupportedEnabled() const override;
483
485 firstUnsupportedExpected() const override;
486
488 getJson(bool isAdmin) const override;
490 getJson(UInt256 const&, bool isAdmin) const override;
491
492 bool
493 needValidatedLedger(LedgerIndex seq) const override;
494
495 void
497 LedgerIndex seq,
498 std::set<UInt256> const& enabled,
499 MajorityAmendmentsT const& majority) override;
500
501 void
502 trustChanged(HashSet<PublicKey> const& allTrusted) override;
503
505 doValidation(std::set<UInt256> const& enabledAmendments) const override;
506
508 getDesired() const override;
509
511 doVoting(
512 Rules const& rules,
513 NetClock::time_point closeTime,
514 std::set<UInt256> const& enabledAmendments,
515 MajorityAmendmentsT const& majorityAmendments,
516 std::vector<std::shared_ptr<STValidation>> const& validations) override;
517};
518
519//------------------------------------------------------------------------------
520
522 ServiceRegistry& registry,
523 std::chrono::seconds majorityTime,
524 std::vector<FeatureInfo> const& supported,
525 Section const& enabled,
526 Section const& vetoed,
527 beast::Journal journal)
528 : majorityTime_(majorityTime), j_(journal), db_(registry.getWalletDB())
529{
531
532 // Find out if the FeatureVotes table exists in WalletDB
533 bool const featureVotesExist = [this]() {
534 auto db = db_.checkoutDb();
535 return createFeatureVotes(*db);
536 }();
537
538 // Parse supported amendments
539 for (auto const& [name, amendment, votebehavior] : supported)
540 {
541 AmendmentState& s = add(amendment, lock);
542
543 s.name = name;
544 s.supported = true;
545 switch (votebehavior)
546 {
549 break;
550
553 break;
554
557 break;
558 }
559
560 JLOG(j_.debug()) << "Amendment " << amendment << " (" << s.name
561 << ") is supported and will be "
562 << (s.vote == AmendmentVote::Up ? "up" : "down")
563 << " voted by default if not enabled on the ledger.";
564 }
565
566 HashSet<UInt256> detectConflict;
567 // Parse enabled amendments from config
568 for (std::pair<UInt256, std::string> const& a : parseSection(enabled))
569 {
570 if (featureVotesExist)
571 { // If the table existed, warn about duplicate config info
572 JLOG(j_.warn()) << "[amendments] section in config file ignored"
573 " in favor of data in db/wallet.db.";
574 break;
575 }
576
577 // Otherwise transfer config data into the table
578 detectConflict.insert(a.first);
579 persistVote(a.first, a.second, AmendmentVote::Up);
580 }
581
582 // Parse vetoed amendments from config
583 for (auto const& a : parseSection(vetoed))
584 {
585 if (featureVotesExist)
586 { // If the table existed, warn about duplicate config info
587 JLOG(j_.warn()) << "[veto_amendments] section in config file ignored"
588 " in favor of data in db/wallet.db.";
589 break;
590 }
591
592 // Otherwise transfer config data into the table
593 if (!detectConflict.contains(a.first))
594 {
595 persistVote(a.first, a.second, AmendmentVote::Down);
596 }
597 else
598 {
599 JLOG(j_.warn()) << "[veto_amendments] section in config has amendment " << '('
600 << a.first << ", " << a.second
601 << ") both [veto_amendments] and [amendments].";
602 }
603 }
604
605 // Read amendment votes from wallet.db
606 auto db = db_.checkoutDb();
608 *db,
609 [&](boost::optional<std::string> amendmentHash,
610 boost::optional<std::string> amendmentName,
611 boost::optional<AmendmentVote> vote) {
612 UInt256 amendHash;
613 if (!amendmentHash || !amendmentName || !vote)
614 {
615 // These fields should never have nulls, but check
616 Throw<std::runtime_error>("Invalid FeatureVotes row in wallet.db");
617 }
618 if (!amendHash.parseHex(*amendmentHash))
619 {
621 std::format("Invalid amendment ID '{}' in wallet.db", *amendmentHash));
622 }
623 if (*vote == AmendmentVote::Down)
624 {
625 // Unknown amendments are effectively vetoed already
626 if (auto s = get(amendHash, lock))
627 {
628 JLOG(j_.info()) << "Amendment {" << *amendmentName << ", " << amendHash
629 << "} is downvoted.";
630 if (!amendmentName->empty())
631 s->name = *amendmentName;
632 // An obsolete amendment's vote can never be changed
633 if (s->vote != AmendmentVote::Obsolete)
634 s->vote = *vote;
635 }
636 }
637 else // up-vote
638 {
639 AmendmentState& s = add(amendHash, lock);
640
641 JLOG(j_.debug()) << "Amendment {" << *amendmentName << ", " << amendHash
642 << "} is upvoted.";
643 if (!amendmentName->empty())
644 s.name = *amendmentName;
645 // An obsolete amendment's vote can never be changed
647 s.vote = *vote;
648 }
649 });
650}
651
654{
655 // call with the mutex held
656 return amendmentMap_[amendmentHash];
657}
658
661{
662 // Forward to the const version of get.
663 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
664 return const_cast<AmendmentState*>(std::as_const(*this).get(amendmentHash, lock));
665}
666
667AmendmentState const*
669{
670 // call with the mutex held
671 auto ret = amendmentMap_.find(amendmentHash);
672
673 if (ret == amendmentMap_.end())
674 return nullptr;
675
676 return &ret->second;
677}
678
681{
683
684 for (auto const& e : amendmentMap_)
685 {
686 if (name == e.second.name)
687 return e.first;
688 }
689
690 return {};
691}
692
693void
695 UInt256 const& amendment,
696 std::string const& name,
697 AmendmentVote vote) const
698{
699 XRPL_ASSERT(
701 "xrpl::AmendmentTableImpl::persistVote : valid vote input");
702 auto db = db_.checkoutDb();
703 voteAmendment(*db, amendment, name, vote);
704}
705
706bool
708{
710 AmendmentState& s = add(amendment, lock);
711
712 if (s.vote != AmendmentVote::Up)
713 return false;
715 persistVote(amendment, s.name, s.vote);
716 return true;
717}
718
719bool
721{
723 AmendmentState* const s = get(amendment, lock);
724
725 if ((s == nullptr) || s->vote != AmendmentVote::Down)
726 return false;
728 persistVote(amendment, s->name, s->vote);
729 return true;
730}
731
732bool
734{
736 AmendmentState& s = add(amendment, lock);
737
738 if (s.enabled)
739 return false;
740
741 s.enabled = true;
742
743 if (!s.supported)
744 {
745 JLOG(j_.error()) << "Unsupported amendment " << amendment << " activated.";
746 unsupportedEnabled_ = true;
747 }
748
749 return true;
750}
751
752bool
754{
756 AmendmentState const* s = get(amendment, lock);
757 return (s != nullptr) && s->enabled;
758}
759
760bool
762{
764 AmendmentState const* s = get(amendment, lock);
765 return (s != nullptr) && s->supported;
766}
767
768bool
774
781
784{
785 // Get the list of amendments we support and do not
786 // veto, but that are not already enabled
787 std::vector<UInt256> amendments;
788
789 {
791 amendments.reserve(amendmentMap_.size());
792 for (auto const& e : amendmentMap_)
793 {
794 if (e.second.supported && e.second.vote == AmendmentVote::Up &&
795 (!enabled.contains(e.first)))
796 {
797 amendments.push_back(e.first);
798 JLOG(j_.info()) << "Voting for amendment " << e.second.name;
799 }
800 }
801 }
802
803 if (!amendments.empty())
804 std::ranges::sort(amendments);
805
806 return amendments;
807}
808
811{
812 // Get the list of amendments we support and do not veto
813 return doValidation({});
814}
815
818 Rules const& rules,
819 NetClock::time_point closeTime,
820 std::set<UInt256> const& enabledAmendments,
821 MajorityAmendmentsT const& majorityAmendments,
823{
824 JLOG(j_.trace()) << "voting at " << closeTime.time_since_epoch().count() << ": "
825 << enabledAmendments.size() << ", " << majorityAmendments.size() << ", "
826 << valSet.size();
827
829
830 // Keep a record of the votes we received.
831 previousTrustedVotes_.recordVotes(rules, valSet, closeTime, j_, lock);
832
833 // Tally the most recent votes.
835 JLOG(j_.debug()) << "Counted votes from " << vote->trustedValidations()
836 << " valid trusted validations, threshold is: " << vote->threshold();
837
838 // Map of amendments to the action to be taken for each one. The action is
839 // the value of the flags in the pseudo-transaction
841
842 // process all amendments we know of
843 for (auto const& entry : amendmentMap_)
844 {
845 if (enabledAmendments.contains(entry.first))
846 {
847 JLOG(j_.trace()) << entry.first << ": amendment already enabled";
848
849 continue;
850 }
851
852 bool const hasValMajority = vote->passes(entry.first);
853
854 auto const majorityTime = [&]() -> std::optional<NetClock::time_point> {
855 auto const it = majorityAmendments.find(entry.first);
856 if (it != majorityAmendments.end())
857 return it->second;
858 return std::nullopt;
859 }();
860
861 bool const hasLedgerMajority = majorityTime.has_value();
862
863 auto const logStr = [&entry, &vote]() {
865 ss << entry.first << " (" << entry.second.name << ") has " << vote->votes(entry.first)
866 << " votes";
867 return ss.str();
868 }();
869
870 if (hasValMajority && !hasLedgerMajority && entry.second.vote == AmendmentVote::Up)
871 {
872 // Ledger says no majority, validators say yes, and voting yes
873 // locally
874 JLOG(j_.debug()) << logStr << ": amendment got majority";
875 actions[entry.first] = tfGotMajority;
876 }
877 else if (!hasValMajority && hasLedgerMajority)
878 {
879 // Ledger says majority, validators say no
880 JLOG(j_.debug()) << logStr << ": amendment lost majority";
881 actions[entry.first] = tfLostMajority;
882 }
883 else if (
884 hasLedgerMajority && ((*majorityTime + majorityTime_) <= closeTime) &&
885 entry.second.vote == AmendmentVote::Up)
886 {
887 // Ledger says majority held
888 JLOG(j_.debug()) << logStr << ": amendment majority held";
889 actions[entry.first] = 0;
890 }
891 // Logging only below this point
892 else if (hasValMajority && hasLedgerMajority)
893 {
894 JLOG(j_.debug()) << logStr << ": amendment holding majority, waiting to be enabled";
895 }
896 else if (!hasValMajority)
897 {
898 JLOG(j_.debug()) << logStr << ": amendment does not have majority";
899 }
900 }
901
902 // Stash for reporting
903 lastVote_ = std::move(vote);
904 return actions;
905}
906
907bool
909{
911
912 // Is there a ledger in which an amendment could have been enabled
913 // between these two ledger sequences?
914
915 return ((ledgerSeq - 1) / 256) != ((lastUpdateSeq_ - 1) / 256);
916}
917
918void
920 LedgerIndex ledgerSeq,
921 std::set<UInt256> const& enabled,
922 MajorityAmendmentsT const& majority)
923{
924 for (auto& e : enabled)
925 enable(e);
926
928
929 // Remember the ledger sequence of this update.
930 lastUpdateSeq_ = ledgerSeq;
931
932 // Since we have the whole list in `majority`, reset the time flag, even
933 // if it's currently set. If it's not set when the loop is done, then any
934 // prior unknown amendments have lost majority.
936 for (auto const& [hash, time] : majority)
937 {
938 AmendmentState const& s = add(hash, lock);
939
940 if (s.enabled)
941 continue;
942
943 if (!s.supported)
944 {
945 JLOG(j_.info()) << "Unsupported amendment " << hash << " reached majority at "
946 << to_string(time);
949 }
950 }
953}
954
955void
957{
959 previousTrustedVotes_.trustChanged(allTrusted, lock);
960}
961
962void
964 json::Value& v,
965 UInt256 const& id,
966 AmendmentState const& fs,
967 bool isAdmin,
968 std::scoped_lock<std::mutex> const&) const
969{
970 if (!fs.name.empty())
971 v[jss::name] = fs.name;
972
973 v[jss::supported] = fs.supported;
974 if (!fs.enabled && isAdmin)
975 {
977 {
978 v[jss::vetoed] = "Obsolete";
979 }
980 else
981 {
982 v[jss::vetoed] = fs.vote == AmendmentVote::Down;
983 }
984 }
985 v[jss::enabled] = fs.enabled;
986
987 if (!fs.enabled && lastVote_ && isAdmin)
988 {
989 auto const votesTotal = lastVote_->trustedValidations();
990 auto const votesNeeded = lastVote_->threshold();
991 auto const votesFor = lastVote_->votes(id);
992
993 v[jss::count] = votesFor;
994 v[jss::validations] = votesTotal;
995
996 if (votesNeeded != 0)
997 v[jss::threshold] = votesNeeded;
998 }
999}
1000
1003{
1005 {
1007 for (auto const& e : amendmentMap_)
1008 {
1009 injectJson(
1010 ret[to_string(e.first)] = json::ValueType::Object,
1011 e.first,
1012 e.second,
1013 isAdmin,
1014 lock);
1015 }
1016 }
1017 return ret;
1018}
1019
1021AmendmentTableImpl::getJson(UInt256 const& amendmentID, bool isAdmin) const
1022{
1024
1025 {
1027 AmendmentState const* a = get(amendmentID, lock);
1028 if (a != nullptr)
1029 {
1030 json::Value& jAmendment = (ret[to_string(amendmentID)] = json::ValueType::Object);
1031 injectJson(jAmendment, amendmentID, *a, isAdmin, lock);
1032 }
1033 }
1034
1035 return ret;
1036}
1037
1040 ServiceRegistry& registry,
1041 std::chrono::seconds majorityTime,
1043 Section const& enabled,
1044 Section const& vetoed,
1045 beast::Journal journal)
1046{
1048 registry, majorityTime, supported, enabled, vetoed, journal);
1049}
1050
1051} // namespace xrpl
T as_const(T... args)
A generic endpoint for log messages.
Definition Journal.h:44
Stream debug() const
Definition Journal.h:344
Represents a JSON value.
Definition json_value.h:117
int trustedValidations() const
AmendmentSet(Rules const &rules, TrustedVotes const &trustedVotes, std::scoped_lock< std::mutex > const &lock)
bool passes(UInt256 const &amendment) const
HashMap< UInt256, int > votes_
int votes(UInt256 const &amendment) const
std::vector< UInt256 > doValidation(std::set< UInt256 > const &enabledAmendments) const override
void persistVote(UInt256 const &amendment, std::string const &name, AmendmentVote vote) const
std::optional< NetClock::time_point > firstUnsupportedExpected() const override
std::optional< NetClock::time_point > firstUnsupportedExpected_
bool enable(UInt256 const &amendment) override
bool veto(UInt256 const &amendment) override
void doValidatedLedger(LedgerIndex seq, std::set< UInt256 > const &enabled, MajorityAmendmentsT const &majority) override
json::Value getJson(bool isAdmin) const override
beast::Journal const j_
void injectJson(json::Value &v, UInt256 const &amendment, AmendmentState const &state, bool isAdmin, std::scoped_lock< std::mutex > const &lock) const
AmendmentState & add(UInt256 const &amendment, std::scoped_lock< std::mutex > const &lock)
bool hasUnsupportedEnabled() const override
returns true if one or more amendments on the network have been enabled that this server does not sup...
bool isEnabled(UInt256 const &amendment) const override
void trustChanged(HashSet< PublicKey > const &allTrusted) override
bool isSupported(UInt256 const &amendment) const override
AmendmentTableImpl(ServiceRegistry &registry, std::chrono::seconds majorityTime, std::vector< FeatureInfo > const &supported, Section const &enabled, Section const &vetoed, beast::Journal journal)
HashMap< UInt256, AmendmentState > amendmentMap_
std::unique_ptr< AmendmentSet > lastVote_
std::chrono::seconds const majorityTime_
UInt256 find(std::string const &name) const override
std::map< UInt256, std::uint32_t > doVoting(Rules const &rules, NetClock::time_point closeTime, std::set< UInt256 > const &enabledAmendments, MajorityAmendmentsT const &majorityAmendments, std::vector< std::shared_ptr< STValidation > > const &validations) override
std::vector< UInt256 > getDesired() const override
AmendmentState * get(UInt256 const &amendment, std::scoped_lock< std::mutex > const &lock)
bool needValidatedLedger(LedgerIndex seq) const override
Called to determine whether the amendment logic needs to process a new validated ledger.
bool unVeto(UInt256 const &amendment) override
The amendment table stores the list of enabled and potential amendments.
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition base_uint.h:525
std::chrono::time_point< NetClock > time_point
Definition chrono.h:48
std::chrono::duration< rep, period > duration
Definition chrono.h:47
Rules controlling protocol behavior.
Definition Rules.h:40
Holds a collection of configuration values.
Definition BasicConfig.h:28
std::string const & name() const
Returns the name of this section.
Definition BasicConfig.h:48
std::vector< std::string > const & lines() const
Returns all the lines in the section.
Definition BasicConfig.h:58
Service registry for dependency injection.
TrustedVotes records the most recent votes from trusted validators.
TrustedVotes & operator=(TrustedVotes const &rhs)=delete
void trustChanged(HashSet< PublicKey > const &allTrusted, std::scoped_lock< std::mutex > const &lock)
HashMap< PublicKey, UpvotesAndTimeout > recordedVotes_
void recordVotes(Rules const &rules, std::vector< std::shared_ptr< STValidation > > const &valSet, NetClock::time_point const closeTime, beast::Journal j, std::scoped_lock< std::mutex > const &lock)
std::pair< int, HashMap< UInt256, int > > getVotes(Rules const &rules, std::scoped_lock< std::mutex > const &lock) const
TrustedVotes(TrustedVotes const &rhs)=delete
TrustedVotes()=default
T contains(T... args)
T duration_cast(T... args)
T emplace_back(T... args)
T empty(T... args)
T end(T... args)
T find(T... args)
T for_each(T... args)
T format(T... args)
T insert(T... args)
T lock(T... args)
T make_unique(T... args)
T max(T... args)
@ Object
object value (collection of name/value pairs).
Definition json_value.h:29
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
std::unique_ptr< AmendmentTable > makeAmendmentTable(ServiceRegistry &registry, std::chrono::seconds majorityTime, std::vector< AmendmentTable::FeatureInfo > const &supported, Section const &enabled, Section const &vetoed, beast::Journal journal)
std::uint32_t LedgerIndex
A ledger index.
Definition Protocol.h:382
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition AccountID.cpp:95
void readAmendments(soci::session &session, std::function< void(boost::optional< std::string > amendmentHash, boost::optional< std::string > amendmentName, boost::optional< AmendmentVote > vote)> const &callback)
readAmendments Reads all amendments from the FeatureVotes table.
Definition Wallet.cpp:265
constexpr std::ratio< 80, 100 > kAmendmentMajorityCalcThreshold
The minimum amount of support an amendment should have.
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:657
BaseUInt< 256 > UInt256
Definition base_uint.h:580
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:300
static std::vector< std::pair< UInt256, std::string > > parseSection(Section const &section)
bool isAdmin(Port const &port, json::Value const &params, beast::ip::Address const &remoteIp)
Definition Role.cpp:81
std::map< UInt256, NetClock::time_point > MajorityAmendmentsT
Definition View.h:121
std::unordered_set< Value, Hash, Pred, Allocator > HashSet
bool createFeatureVotes(soci::session &session)
createFeatureVotes Creates the FeatureVote table if it does not exist.
Definition Wallet.cpp:241
AmendmentVote
Definition Wallet.h:145
std::unordered_map< Key, Value, Hash, Pred, Allocator > HashMap
XRPL_NO_SANITIZE_ADDRESS void Throw(Args &&... args)
Definition contract.h:52
T has_value(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 time(T... args)
T time_since_epoch(T... args)