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 <map>
37#include <memory>
38#include <mutex>
39#include <optional>
40#include <set>
41#include <sstream>
42#include <stdexcept>
43#include <string>
44#include <utility>
45#include <vector>
46
47namespace xrpl {
48
49static std::vector<std::pair<uint256, std::string>>
50parseSection(Section const& section)
51{
52 static boost::regex const kRe1(
53 "^" // start of line
54 "(?:\\s*)" // whitespace (optional)
55 "([abcdefABCDEF0-9]{64})" // <hexadecimal amendment ID>
56 "(?:\\s+)" // whitespace
57 "(\\S+)" // <description>
58 ,
59 boost::regex_constants::optimize);
60
62
63 for (auto const& line : section.lines())
64 {
65 boost::smatch match;
66
67 if (!boost::regex_match(line, match, kRe1))
68 Throw<std::runtime_error>("Invalid entry '" + line + "' in [" + section.name() + "]");
69
70 uint256 id;
71
72 if (!id.parseHex(match[1]))
73 {
75 "Invalid amendment ID '" + match[1] + "' in [" + section.name() + "]");
76 }
77
78 names.emplace_back(id, match[2]);
79 }
80
81 return names;
82}
83
104{
105private:
106 // Associates each trusted validator with the last votes we saw from them
107 // and an expiration for that record.
120
121public:
122 TrustedVotes() = default;
123 TrustedVotes(TrustedVotes const& rhs) = delete;
125 operator=(TrustedVotes const& rhs) = delete;
126
127 // Called when the list of trusted validators changes.
128 //
129 // Call with AmendmentTable::mutex_ locked.
130 void
132 {
133 decltype(recordedVotes_) newRecordedVotes;
134 newRecordedVotes.reserve(allTrusted.size());
135
136 // Make sure every PublicKey in allTrusted is represented in
137 // recordedVotes_. Also make sure recordedVotes_ contains
138 // no additional PublicKeys.
139 for (auto& trusted : allTrusted)
140 {
141 if (recordedVotes_.contains(trusted))
142 {
143 // Preserve this validator's previously saved voting state.
144 newRecordedVotes.insert(recordedVotes_.extract(trusted));
145 }
146 else
147 {
148 // New validators have a starting position of no on everything.
149 // Add the entry with an empty vector and unseated timeout.
150 newRecordedVotes[trusted];
151 }
152 }
153 // The votes of any no-longer-trusted validators will be destroyed
154 // when changedTrustedVotes goes out of scope.
155 recordedVotes_.swap(newRecordedVotes);
156 }
157
158 // Called when we receive the latest votes.
159 //
160 // Call with AmendmentTable::mutex_ locked.
161 void
163 Rules const& rules,
165 NetClock::time_point const closeTime,
168 {
169 // When we get an STValidation we save the upVotes it contains, but
170 // we also set an expiration for those upVotes. The following constant
171 // controls the timeout.
172 //
173 // There really is no "best" timeout to choose for when we finally
174 // lose confidence that we know how a validator is voting. But part
175 // of the point of recording validator votes is to avoid flapping of
176 // amendment votes. A 24h timeout says that we will change the local
177 // record of a validator's vote to "no" 24h after the last vote seen
178 // from that validator. So flapping due to that validator being off
179 // line will happen less frequently than every 24 hours.
180 using namespace std::chrono_literals;
181 static constexpr NetClock::duration kExpiresAfter = 24h;
182
183 auto const newTimeout = closeTime + kExpiresAfter;
184
185 // Walk all validations and replace previous votes from trusted
186 // validators with these newest votes.
187 for (auto const& val : valSet)
188 {
189 auto const pkHuman = toBase58(TokenType::NodePublic, val->getSignerPublic());
190 // If this validation comes from one of our trusted validators...
191 if (auto const iter = recordedVotes_.find(val->getSignerPublic());
192 iter != recordedVotes_.end())
193 {
194 iter->second.timeout = newTimeout;
195 if (val->isFieldPresent(sfAmendments))
196 {
197 auto const& choices = val->getFieldV256(sfAmendments);
198 iter->second.upVotes.assign(choices.begin(), choices.end());
199 JLOG(j.debug()) << "recordVotes: Validation from trusted " << pkHuman << " has "
200 << choices.size() << " amendment votes: "
201 << boost::algorithm::join(
202 iter->second.upVotes |
203 boost::adaptors::transformed(to_string<256, void>),
204 ", ");
205 // TODO: Maybe transform using to_short_string once #5126 is
206 // merged
207 //
208 // iter->second.upVotes |
209 // boost::adaptors::transformed(to_short_string<256, void>)
210 }
211 else
212 {
213 // This validator does not upVote any amendments right now.
214 iter->second.upVotes.clear();
215 JLOG(j.debug()) << "recordVotes: Validation from trusted " << pkHuman
216 << " has no amendment votes.";
217 }
218 }
219 else
220 {
221 JLOG(j.debug()) << "recordVotes: Ignoring validation from untrusted " << pkHuman;
222 }
223 }
224
225 // Now remove any expired records from recordedVotes_.
228 [&closeTime, newTimeout, &j](decltype(recordedVotes_)::value_type& votes) {
229 auto const pkHuman = toBase58(TokenType::NodePublic, votes.first);
230 if (!votes.second.timeout)
231 {
232 XRPL_ASSERT(
233 votes.second.upVotes.empty(),
234 "xrpl::TrustedVotes::recordVotes : received no "
235 "upvotes");
236 JLOG(j.debug()) << "recordVotes: Have not received any "
237 "amendment votes from "
238 << pkHuman << " since last timeout or startup";
239 }
240 else if (closeTime > votes.second.timeout)
241 {
242 JLOG(j.debug()) << "recordVotes: Timeout: Clearing votes from " << pkHuman;
243 votes.second.timeout.reset();
244 votes.second.upVotes.clear();
245 }
246 else if (votes.second.timeout != newTimeout)
247 {
248 XRPL_ASSERT(
249 votes.second.timeout < newTimeout,
250 "xrpl::TrustedVotes::recordVotes : votes not "
251 "expired");
252 using namespace std::chrono;
253 auto const age = duration_cast<minutes>(newTimeout - *votes.second.timeout);
254 JLOG(j.debug()) << "recordVotes: Using " << age.count()
255 << "min old cached votes from " << pkHuman;
256 }
257 });
258 }
259
260 // Return the information needed by AmendmentSet to determine votes.
261 //
262 // Call with AmendmentTable::mutex_ locked.
265 {
267 int available = 0;
268 for (auto& validatorVotes : recordedVotes_)
269 {
270 XRPL_ASSERT(
271 validatorVotes.second.timeout || validatorVotes.second.upVotes.empty(),
272 "xrpl::TrustedVotes::getVotes : valid votes");
273 if (validatorVotes.second.timeout)
274 ++available;
275 for (uint256 const& amendment : validatorVotes.second.upVotes)
276 {
277 ret[amendment] += 1;
278 }
279 }
280 return {available, ret};
281 }
282};
283
290{
295
302 bool enabled = false;
303
307 bool supported = false;
308
313
314 explicit AmendmentState() = default;
315};
316
321{
322private:
323 // How many yes votes each amendment received
325 // number of trusted validations
327 // number of votes needed
328 int threshold_ = 0;
329
330public:
332 Rules const& rules,
333 TrustedVotes const& trustedVotes,
335 {
336 // process validations for ledger before flag ledger.
337 auto [trustedCount, newVotes] = trustedVotes.getVotes(rules, lock);
338
339 trustedValidations_ = trustedCount;
340 votes_.swap(newVotes);
341
343 1L,
344 static_cast<long>(
347 }
348
349 [[nodiscard]] bool
350 passes(uint256 const& amendment) const
351 {
352 auto const& it = votes_.find(amendment);
353
354 if (it == votes_.end())
355 return false;
356
357 // One validator is an exception, otherwise it is not possible
358 // to gain majority.
359 if (trustedValidations_ == 1)
360 return it->second >= threshold_;
361
362 return it->second > threshold_;
363 }
364
365 [[nodiscard]] int
366 votes(uint256 const& amendment) const
367 {
368 auto const& it = votes_.find(amendment);
369
370 if (it == votes_.end())
371 return 0;
372
373 return it->second;
374 }
375
376 [[nodiscard]] int
378 {
379 return trustedValidations_;
380 }
381
382 [[nodiscard]] int
383 threshold() const
384 {
385 return threshold_;
386 }
387};
388
389//------------------------------------------------------------------------------
390
399{
400private:
402
405
406 // Record of the last votes seen from trusted validators.
408
409 // Time that an amendment must hold a majority for
411
412 // The results of the last voting round - may be empty if
413 // we haven't participated in one yet.
415
416 // True if an unsupported amendment is enabled
418
419 // Unset if no unsupported amendments reach majority,
420 // else set to the earliest time an unsupported amendment
421 // will be enabled.
423
425
426 // Database which persists veto/unveto vote
428
429 // Finds or creates state. Must be called with mutex_ locked.
431 add(uint256 const& amendment, std::scoped_lock<std::mutex> const& lock);
432
433 // Finds existing state. Must be called with mutex_ locked.
435 get(uint256 const& amendment, std::scoped_lock<std::mutex> const& lock);
436
437 AmendmentState const*
438 get(uint256 const& amendment, std::scoped_lock<std::mutex> const& lock) const;
439
440 // Injects amendment json into v. Must be called with mutex_ locked.
441 void
443 json::Value& v,
444 uint256 const& amendment,
445 AmendmentState const& state,
446 bool isAdmin,
448
449 void
450 persistVote(uint256 const& amendment, std::string const& name, AmendmentVote vote) const;
451
452public:
454 ServiceRegistry& registry,
455 std::chrono::seconds majorityTime,
456 std::vector<FeatureInfo> const& supported,
457 Section const& enabled,
458 Section const& vetoed,
459 beast::Journal journal);
460
461 uint256
462 find(std::string const& name) const override;
463
464 bool
465 veto(uint256 const& amendment) override;
466 bool
467 unVeto(uint256 const& amendment) override;
468
469 bool
470 enable(uint256 const& amendment) override;
471
472 bool
473 isEnabled(uint256 const& amendment) const override;
474 bool
475 isSupported(uint256 const& amendment) const override;
476
477 bool
478 hasUnsupportedEnabled() const override;
479
481 firstUnsupportedExpected() const override;
482
484 getJson(bool isAdmin) const override;
486 getJson(uint256 const&, bool isAdmin) const override;
487
488 bool
489 needValidatedLedger(LedgerIndex seq) const override;
490
491 void
493 LedgerIndex seq,
494 std::set<uint256> const& enabled,
495 majorityAmendments_t const& majority) override;
496
497 void
498 trustChanged(hash_set<PublicKey> const& allTrusted) override;
499
501 doValidation(std::set<uint256> const& enabledAmendments) const override;
502
504 getDesired() const override;
505
507 doVoting(
508 Rules const& rules,
509 NetClock::time_point closeTime,
510 std::set<uint256> const& enabledAmendments,
511 majorityAmendments_t const& majorityAmendments,
512 std::vector<std::shared_ptr<STValidation>> const& validations) override;
513};
514
515//------------------------------------------------------------------------------
516
518 ServiceRegistry& registry,
519 std::chrono::seconds majorityTime,
520 std::vector<FeatureInfo> const& supported,
521 Section const& enabled,
522 Section const& vetoed,
523 beast::Journal journal)
524 : majorityTime_(majorityTime), j_(journal), db_(registry.getWalletDB())
525{
527
528 // Find out if the FeatureVotes table exists in WalletDB
529 bool const featureVotesExist = [this]() {
530 auto db = db_.checkoutDb();
531 return createFeatureVotes(*db);
532 }();
533
534 // Parse supported amendments
535 for (auto const& [name, amendment, votebehavior] : supported)
536 {
537 AmendmentState& s = add(amendment, lock);
538
539 s.name = name;
540 s.supported = true;
541 switch (votebehavior)
542 {
545 break;
546
549 break;
550
553 break;
554 }
555
556 JLOG(j_.debug()) << "Amendment " << amendment << " (" << s.name
557 << ") is supported and will be "
558 << (s.vote == AmendmentVote::Up ? "up" : "down")
559 << " voted by default if not enabled on the ledger.";
560 }
561
562 hash_set<uint256> detectConflict;
563 // Parse enabled amendments from config
564 for (std::pair<uint256, std::string> const& a : parseSection(enabled))
565 {
566 if (featureVotesExist)
567 { // If the table existed, warn about duplicate config info
568 JLOG(j_.warn()) << "[amendments] section in config file ignored"
569 " in favor of data in db/wallet.db.";
570 break;
571 }
572
573 // Otherwise transfer config data into the table
574 detectConflict.insert(a.first);
575 persistVote(a.first, a.second, AmendmentVote::Up);
576 }
577
578 // Parse vetoed amendments from config
579 for (auto const& a : parseSection(vetoed))
580 {
581 if (featureVotesExist)
582 { // If the table existed, warn about duplicate config info
583 JLOG(j_.warn()) << "[veto_amendments] section in config file ignored"
584 " in favor of data in db/wallet.db.";
585 break;
586 }
587
588 // Otherwise transfer config data into the table
589 if (!detectConflict.contains(a.first))
590 {
591 persistVote(a.first, a.second, AmendmentVote::Down);
592 }
593 else
594 {
595 JLOG(j_.warn()) << "[veto_amendments] section in config has amendment " << '('
596 << a.first << ", " << a.second
597 << ") both [veto_amendments] and [amendments].";
598 }
599 }
600
601 // Read amendment votes from wallet.db
602 auto db = db_.checkoutDb();
604 *db,
605 [&](boost::optional<std::string> amendmentHash,
606 boost::optional<std::string> amendmentName,
607 boost::optional<AmendmentVote> vote) {
608 uint256 amendHash;
609 if (!amendmentHash || !amendmentName || !vote)
610 {
611 // These fields should never have nulls, but check
612 Throw<std::runtime_error>("Invalid FeatureVotes row in wallet.db");
613 }
614 if (!amendHash.parseHex(*amendmentHash))
615 {
617 "Invalid amendment ID '" + *amendmentHash + " in wallet.db");
618 }
619 if (*vote == AmendmentVote::Down)
620 {
621 // Unknown amendments are effectively vetoed already
622 if (auto s = get(amendHash, lock))
623 {
624 JLOG(j_.info()) << "Amendment {" << *amendmentName << ", " << amendHash
625 << "} is downvoted.";
626 if (!amendmentName->empty())
627 s->name = *amendmentName;
628 // An obsolete amendment's vote can never be changed
629 if (s->vote != AmendmentVote::Obsolete)
630 s->vote = *vote;
631 }
632 }
633 else // up-vote
634 {
635 AmendmentState& s = add(amendHash, lock);
636
637 JLOG(j_.debug()) << "Amendment {" << *amendmentName << ", " << amendHash
638 << "} is upvoted.";
639 if (!amendmentName->empty())
640 s.name = *amendmentName;
641 // An obsolete amendment's vote can never be changed
643 s.vote = *vote;
644 }
645 });
646}
647
650{
651 // call with the mutex held
652 return amendmentMap_[amendmentHash];
653}
654
657{
658 // Forward to the const version of get.
659 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
660 return const_cast<AmendmentState*>(std::as_const(*this).get(amendmentHash, lock));
661}
662
663AmendmentState const*
665{
666 // call with the mutex held
667 auto ret = amendmentMap_.find(amendmentHash);
668
669 if (ret == amendmentMap_.end())
670 return nullptr;
671
672 return &ret->second;
673}
674
677{
679
680 for (auto const& e : amendmentMap_)
681 {
682 if (name == e.second.name)
683 return e.first;
684 }
685
686 return {};
687}
688
689void
691 uint256 const& amendment,
692 std::string const& name,
693 AmendmentVote vote) const
694{
695 XRPL_ASSERT(
697 "xrpl::AmendmentTableImpl::persistVote : valid vote input");
698 auto db = db_.checkoutDb();
699 voteAmendment(*db, amendment, name, vote);
700}
701
702bool
704{
706 AmendmentState& s = add(amendment, lock);
707
708 if (s.vote != AmendmentVote::Up)
709 return false;
711 persistVote(amendment, s.name, s.vote);
712 return true;
713}
714
715bool
717{
719 AmendmentState* const s = get(amendment, lock);
720
721 if ((s == nullptr) || s->vote != AmendmentVote::Down)
722 return false;
724 persistVote(amendment, s->name, s->vote);
725 return true;
726}
727
728bool
730{
732 AmendmentState& s = add(amendment, lock);
733
734 if (s.enabled)
735 return false;
736
737 s.enabled = true;
738
739 if (!s.supported)
740 {
741 JLOG(j_.error()) << "Unsupported amendment " << amendment << " activated.";
742 unsupportedEnabled_ = true;
743 }
744
745 return true;
746}
747
748bool
750{
752 AmendmentState const* s = get(amendment, lock);
753 return (s != nullptr) && s->enabled;
754}
755
756bool
758{
760 AmendmentState const* s = get(amendment, lock);
761 return (s != nullptr) && s->supported;
762}
763
764bool
770
777
780{
781 // Get the list of amendments we support and do not
782 // veto, but that are not already enabled
783 std::vector<uint256> amendments;
784
785 {
787 amendments.reserve(amendmentMap_.size());
788 for (auto const& e : amendmentMap_)
789 {
790 if (e.second.supported && e.second.vote == AmendmentVote::Up &&
791 (!enabled.contains(e.first)))
792 {
793 amendments.push_back(e.first);
794 JLOG(j_.info()) << "Voting for amendment " << e.second.name;
795 }
796 }
797 }
798
799 if (!amendments.empty())
800 std::ranges::sort(amendments);
801
802 return amendments;
803}
804
807{
808 // Get the list of amendments we support and do not veto
809 return doValidation({});
810}
811
814 Rules const& rules,
815 NetClock::time_point closeTime,
816 std::set<uint256> const& enabledAmendments,
817 majorityAmendments_t const& majorityAmendments,
819{
820 JLOG(j_.trace()) << "voting at " << closeTime.time_since_epoch().count() << ": "
821 << enabledAmendments.size() << ", " << majorityAmendments.size() << ", "
822 << valSet.size();
823
825
826 // Keep a record of the votes we received.
827 previousTrustedVotes_.recordVotes(rules, valSet, closeTime, j_, lock);
828
829 // Tally the most recent votes.
831 JLOG(j_.debug()) << "Counted votes from " << vote->trustedValidations()
832 << " valid trusted validations, threshold is: " << vote->threshold();
833
834 // Map of amendments to the action to be taken for each one. The action is
835 // the value of the flags in the pseudo-transaction
837
838 // process all amendments we know of
839 for (auto const& entry : amendmentMap_)
840 {
841 if (enabledAmendments.contains(entry.first))
842 {
843 JLOG(j_.trace()) << entry.first << ": amendment already enabled";
844
845 continue;
846 }
847
848 bool const hasValMajority = vote->passes(entry.first);
849
850 auto const majorityTime = [&]() -> std::optional<NetClock::time_point> {
851 auto const it = majorityAmendments.find(entry.first);
852 if (it != majorityAmendments.end())
853 return it->second;
854 return std::nullopt;
855 }();
856
857 bool const hasLedgerMajority = majorityTime.has_value();
858
859 auto const logStr = [&entry, &vote]() {
861 ss << entry.first << " (" << entry.second.name << ") has " << vote->votes(entry.first)
862 << " votes";
863 return ss.str();
864 }();
865
866 if (hasValMajority && !hasLedgerMajority && entry.second.vote == AmendmentVote::Up)
867 {
868 // Ledger says no majority, validators say yes, and voting yes
869 // locally
870 JLOG(j_.debug()) << logStr << ": amendment got majority";
871 actions[entry.first] = tfGotMajority;
872 }
873 else if (!hasValMajority && hasLedgerMajority)
874 {
875 // Ledger says majority, validators say no
876 JLOG(j_.debug()) << logStr << ": amendment lost majority";
877 actions[entry.first] = tfLostMajority;
878 }
879 else if (
880 hasLedgerMajority && ((*majorityTime + majorityTime_) <= closeTime) &&
881 entry.second.vote == AmendmentVote::Up)
882 {
883 // Ledger says majority held
884 JLOG(j_.debug()) << logStr << ": amendment majority held";
885 actions[entry.first] = 0;
886 }
887 // Logging only below this point
888 else if (hasValMajority && hasLedgerMajority)
889 {
890 JLOG(j_.debug()) << logStr << ": amendment holding majority, waiting to be enabled";
891 }
892 else if (!hasValMajority)
893 {
894 JLOG(j_.debug()) << logStr << ": amendment does not have majority";
895 }
896 }
897
898 // Stash for reporting
899 lastVote_ = std::move(vote);
900 return actions;
901}
902
903bool
905{
907
908 // Is there a ledger in which an amendment could have been enabled
909 // between these two ledger sequences?
910
911 return ((ledgerSeq - 1) / 256) != ((lastUpdateSeq_ - 1) / 256);
912}
913
914void
916 LedgerIndex ledgerSeq,
917 std::set<uint256> const& enabled,
918 majorityAmendments_t const& majority)
919{
920 for (auto& e : enabled)
921 enable(e);
922
924
925 // Remember the ledger sequence of this update.
926 lastUpdateSeq_ = ledgerSeq;
927
928 // Since we have the whole list in `majority`, reset the time flag, even
929 // if it's currently set. If it's not set when the loop is done, then any
930 // prior unknown amendments have lost majority.
932 for (auto const& [hash, time] : majority)
933 {
934 AmendmentState const& s = add(hash, lock);
935
936 if (s.enabled)
937 continue;
938
939 if (!s.supported)
940 {
941 JLOG(j_.info()) << "Unsupported amendment " << hash << " reached majority at "
942 << to_string(time);
945 }
946 }
949}
950
951void
953{
955 previousTrustedVotes_.trustChanged(allTrusted, lock);
956}
957
958void
960 json::Value& v,
961 uint256 const& id,
962 AmendmentState const& fs,
963 bool isAdmin,
964 std::scoped_lock<std::mutex> const&) const
965{
966 if (!fs.name.empty())
967 v[jss::name] = fs.name;
968
969 v[jss::supported] = fs.supported;
970 if (!fs.enabled && isAdmin)
971 {
973 {
974 v[jss::vetoed] = "Obsolete";
975 }
976 else
977 {
978 v[jss::vetoed] = fs.vote == AmendmentVote::Down;
979 }
980 }
981 v[jss::enabled] = fs.enabled;
982
983 if (!fs.enabled && lastVote_ && isAdmin)
984 {
985 auto const votesTotal = lastVote_->trustedValidations();
986 auto const votesNeeded = lastVote_->threshold();
987 auto const votesFor = lastVote_->votes(id);
988
989 v[jss::count] = votesFor;
990 v[jss::validations] = votesTotal;
991
992 if (votesNeeded != 0)
993 v[jss::threshold] = votesNeeded;
994 }
995}
996
999{
1001 {
1003 for (auto const& e : amendmentMap_)
1004 {
1005 injectJson(
1006 ret[to_string(e.first)] = json::ValueType::Object,
1007 e.first,
1008 e.second,
1009 isAdmin,
1010 lock);
1011 }
1012 }
1013 return ret;
1014}
1015
1017AmendmentTableImpl::getJson(uint256 const& amendmentID, bool isAdmin) const
1018{
1020
1021 {
1023 AmendmentState const* a = get(amendmentID, lock);
1024 if (a != nullptr)
1025 {
1026 json::Value& jAmendment = (ret[to_string(amendmentID)] = json::ValueType::Object);
1027 injectJson(jAmendment, amendmentID, *a, isAdmin, lock);
1028 }
1029 }
1030
1031 return ret;
1032}
1033
1036 ServiceRegistry& registry,
1037 std::chrono::seconds majorityTime,
1039 Section const& enabled,
1040 Section const& vetoed,
1041 beast::Journal journal)
1042{
1044 registry, majorityTime, supported, enabled, vetoed, journal);
1045}
1046
1047} // 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)
int votes(uint256 const &amendment) const
hash_map< uint256, int > votes_
bool passes(uint256 const &amendment) const
bool isSupported(uint256 const &amendment) const override
std::optional< NetClock::time_point > firstUnsupportedExpected() const override
std::optional< NetClock::time_point > firstUnsupportedExpected_
json::Value getJson(bool isAdmin) const override
beast::Journal const j_
AmendmentState & add(uint256 const &amendment, std::scoped_lock< std::mutex > const &lock)
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
AmendmentTableImpl(ServiceRegistry &registry, std::chrono::seconds majorityTime, std::vector< FeatureInfo > const &supported, Section const &enabled, Section const &vetoed, beast::Journal journal)
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_
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
AmendmentState * get(uint256 const &amendment, std::scoped_lock< std::mutex > const &lock)
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_
void injectJson(json::Value &v, uint256 const &amendment, AmendmentState const &state, bool isAdmin, std::scoped_lock< std::mutex > const &lock) const
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:29
std::string const & name() const
Returns the name of this section.
Definition BasicConfig.h:49
std::vector< std::string > const & lines() const
Returns all the lines in the section.
Definition BasicConfig.h:59
Service registry for dependency injection.
TrustedVotes records the most recent votes from trusted validators.
TrustedVotes & operator=(TrustedVotes const &rhs)=delete
std::pair< int, hash_map< uint256, int > > getVotes(Rules const &rules, std::scoped_lock< std::mutex > const &lock) const
hash_map< 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)
TrustedVotes(TrustedVotes const &rhs)=delete
TrustedVotes()=default
void trustChanged(hash_set< PublicKey > const &allTrusted, std::scoped_lock< std::mutex > const &lock)
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 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:370
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition AccountID.cpp:95
std::unordered_set< Value, Hash, Pred, Allocator > hash_set
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:651
bool isAdmin(Port const &port, json::Value const &params, beast::ip::Address const &remoteIp)
Definition Role.cpp:81
bool createFeatureVotes(soci::session &session)
createFeatureVotes Creates the FeatureVote table if it does not exist.
Definition Wallet.cpp:241
std::map< uint256, NetClock::time_point > majorityAmendments_t
Definition View.h:93
AmendmentVote
Definition Wallet.h:145
std::unordered_map< Key, Value, Hash, Pred, Allocator > hash_map
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
BaseUInt< 256 > uint256
Definition base_uint.h:580
XRPL_NO_SANITIZE_ADDRESS void Throw(Args &&... args)
Definition contract.h:52
static std::vector< std::pair< uint256, std::string > > parseSection(Section const &section)
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)