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
103{
104private:
105 // Associates each trusted validator with the last votes we saw from them
106 // and an expiration for that record.
118
119public:
120 TrustedVotes() = default;
121 TrustedVotes(TrustedVotes const& rhs) = delete;
123 operator=(TrustedVotes const& rhs) = delete;
124
125 // Called when the list of trusted validators changes.
126 //
127 // Call with AmendmentTable::mutex_ locked.
128 void
130 {
131 decltype(recordedVotes_) newRecordedVotes;
132 newRecordedVotes.reserve(allTrusted.size());
133
134 // Make sure every PublicKey in allTrusted is represented in
135 // recordedVotes_. Also make sure recordedVotes_ contains
136 // no additional PublicKeys.
137 for (auto& trusted : allTrusted)
138 {
139 if (recordedVotes_.contains(trusted))
140 {
141 // Preserve this validator's previously saved voting state.
142 newRecordedVotes.insert(recordedVotes_.extract(trusted));
143 }
144 else
145 {
146 // New validators have a starting position of no on everything.
147 // Add the entry with an empty vector and unseated timeout.
148 newRecordedVotes[trusted];
149 }
150 }
151 // The votes of any no-longer-trusted validators will be destroyed
152 // when changedTrustedVotes goes out of scope.
153 recordedVotes_.swap(newRecordedVotes);
154 }
155
156 // Called when we receive the latest votes.
157 //
158 // Call with AmendmentTable::mutex_ locked.
159 void
161 Rules const& rules,
163 NetClock::time_point const closeTime,
166 {
167 // When we get an STValidation we save the upVotes it contains, but
168 // we also set an expiration for those upVotes. The following constant
169 // controls the timeout.
170 //
171 // There really is no "best" timeout to choose for when we finally
172 // lose confidence that we know how a validator is voting. But part
173 // of the point of recording validator votes is to avoid flapping of
174 // amendment votes. A 24h timeout says that we will change the local
175 // record of a validator's vote to "no" 24h after the last vote seen
176 // from that validator. So flapping due to that validator being off
177 // line will happen less frequently than every 24 hours.
178 using namespace std::chrono_literals;
179 static constexpr NetClock::duration kExpiresAfter = 24h;
180
181 auto const newTimeout = closeTime + kExpiresAfter;
182
183 // Walk all validations and replace previous votes from trusted
184 // validators with these newest votes.
185 for (auto const& val : valSet)
186 {
187 auto const pkHuman = toBase58(TokenType::NodePublic, val->getSignerPublic());
188 // If this validation comes from one of our trusted validators...
189 if (auto const iter = recordedVotes_.find(val->getSignerPublic());
190 iter != recordedVotes_.end())
191 {
192 iter->second.timeout = newTimeout;
193 if (val->isFieldPresent(sfAmendments))
194 {
195 auto const& choices = val->getFieldV256(sfAmendments);
196 iter->second.upVotes.assign(choices.begin(), choices.end());
197 JLOG(j.debug()) << "recordVotes: Validation from trusted " << pkHuman << " has "
198 << choices.size() << " amendment votes: "
199 << boost::algorithm::join(
200 iter->second.upVotes |
201 boost::adaptors::transformed(to_string<256, void>),
202 ", ");
203 // TODO: Maybe transform using to_short_string once #5126 is
204 // merged
205 //
206 // iter->second.upVotes |
207 // boost::adaptors::transformed(to_short_string<256, void>)
208 }
209 else
210 {
211 // This validator does not upVote any amendments right now.
212 iter->second.upVotes.clear();
213 JLOG(j.debug()) << "recordVotes: Validation from trusted " << pkHuman
214 << " has no amendment votes.";
215 }
216 }
217 else
218 {
219 JLOG(j.debug()) << "recordVotes: Ignoring validation from untrusted " << pkHuman;
220 }
221 }
222
223 // Now remove any expired records from recordedVotes_.
226 [&closeTime, newTimeout, &j](decltype(recordedVotes_)::value_type& votes) {
227 auto const pkHuman = toBase58(TokenType::NodePublic, votes.first);
228 if (!votes.second.timeout)
229 {
230 XRPL_ASSERT(
231 votes.second.upVotes.empty(),
232 "xrpl::TrustedVotes::recordVotes : received no "
233 "upvotes");
234 JLOG(j.debug()) << "recordVotes: Have not received any "
235 "amendment votes from "
236 << pkHuman << " since last timeout or startup";
237 }
238 else if (closeTime > votes.second.timeout)
239 {
240 JLOG(j.debug()) << "recordVotes: Timeout: Clearing votes from " << pkHuman;
241 votes.second.timeout.reset();
242 votes.second.upVotes.clear();
243 }
244 else if (votes.second.timeout != newTimeout)
245 {
246 XRPL_ASSERT(
247 votes.second.timeout < newTimeout,
248 "xrpl::TrustedVotes::recordVotes : votes not "
249 "expired");
250 using namespace std::chrono;
251 auto const age = duration_cast<minutes>(newTimeout - *votes.second.timeout);
252 JLOG(j.debug()) << "recordVotes: Using " << age.count()
253 << "min old cached votes from " << pkHuman;
254 }
255 });
256 }
257
258 // Return the information needed by AmendmentSet to determine votes.
259 //
260 // Call with AmendmentTable::mutex_ locked.
263 {
265 int available = 0;
266 for (auto& validatorVotes : recordedVotes_)
267 {
268 XRPL_ASSERT(
269 validatorVotes.second.timeout || validatorVotes.second.upVotes.empty(),
270 "xrpl::TrustedVotes::getVotes : valid votes");
271 if (validatorVotes.second.timeout)
272 ++available;
273 for (uint256 const& amendment : validatorVotes.second.upVotes)
274 {
275 ret[amendment] += 1;
276 }
277 }
278 return {available, ret};
279 }
280};
281
287{
290
296 bool enabled = false;
297
299 bool supported = false;
300
303
304 explicit AmendmentState() = default;
305};
306
309{
310private:
311 // How many yes votes each amendment received
313 // number of trusted validations
315 // number of votes needed
316 int threshold_ = 0;
317
318public:
320 Rules const& rules,
321 TrustedVotes const& trustedVotes,
323 {
324 // process validations for ledger before flag ledger.
325 auto [trustedCount, newVotes] = trustedVotes.getVotes(rules, lock);
326
327 trustedValidations_ = trustedCount;
328 votes_.swap(newVotes);
329
331 1L,
332 static_cast<long>(
335 }
336
337 [[nodiscard]] bool
338 passes(uint256 const& amendment) const
339 {
340 auto const& it = votes_.find(amendment);
341
342 if (it == votes_.end())
343 return false;
344
345 // One validator is an exception, otherwise it is not possible
346 // to gain majority.
347 if (trustedValidations_ == 1)
348 return it->second >= threshold_;
349
350 return it->second > threshold_;
351 }
352
353 [[nodiscard]] int
354 votes(uint256 const& amendment) const
355 {
356 auto const& it = votes_.find(amendment);
357
358 if (it == votes_.end())
359 return 0;
360
361 return it->second;
362 }
363
364 [[nodiscard]] int
366 {
367 return trustedValidations_;
368 }
369
370 [[nodiscard]] int
371 threshold() const
372 {
373 return threshold_;
374 }
375};
376
377//------------------------------------------------------------------------------
378
386{
387private:
389
392
393 // Record of the last votes seen from trusted validators.
395
396 // Time that an amendment must hold a majority for
398
399 // The results of the last voting round - may be empty if
400 // we haven't participated in one yet.
402
403 // True if an unsupported amendment is enabled
405
406 // Unset if no unsupported amendments reach majority,
407 // else set to the earliest time an unsupported amendment
408 // will be enabled.
410
412
413 // Database which persists veto/unveto vote
415
416 // Finds or creates state. Must be called with mutex_ locked.
418 add(uint256 const& amendment, std::scoped_lock<std::mutex> const& lock);
419
420 // Finds existing state. Must be called with mutex_ locked.
422 get(uint256 const& amendment, std::scoped_lock<std::mutex> const& lock);
423
424 AmendmentState const*
425 get(uint256 const& amendment, std::scoped_lock<std::mutex> const& lock) const;
426
427 // Injects amendment json into v. Must be called with mutex_ locked.
428 void
430 json::Value& v,
431 uint256 const& amendment,
432 AmendmentState const& state,
433 bool isAdmin,
435
436 void
437 persistVote(uint256 const& amendment, std::string const& name, AmendmentVote vote) const;
438
439public:
441 ServiceRegistry& registry,
442 std::chrono::seconds majorityTime,
443 std::vector<FeatureInfo> const& supported,
444 Section const& enabled,
445 Section const& vetoed,
446 beast::Journal journal);
447
448 uint256
449 find(std::string const& name) const override;
450
451 bool
452 veto(uint256 const& amendment) override;
453 bool
454 unVeto(uint256 const& amendment) override;
455
456 bool
457 enable(uint256 const& amendment) override;
458
459 bool
460 isEnabled(uint256 const& amendment) const override;
461 bool
462 isSupported(uint256 const& amendment) const override;
463
464 bool
465 hasUnsupportedEnabled() const override;
466
468 firstUnsupportedExpected() const override;
469
471 getJson(bool isAdmin) const override;
473 getJson(uint256 const&, bool isAdmin) const override;
474
475 bool
476 needValidatedLedger(LedgerIndex seq) const override;
477
478 void
480 LedgerIndex seq,
481 std::set<uint256> const& enabled,
482 majorityAmendments_t const& majority) override;
483
484 void
485 trustChanged(hash_set<PublicKey> const& allTrusted) override;
486
488 doValidation(std::set<uint256> const& enabledAmendments) const override;
489
491 getDesired() const override;
492
494 doVoting(
495 Rules const& rules,
496 NetClock::time_point closeTime,
497 std::set<uint256> const& enabledAmendments,
498 majorityAmendments_t const& majorityAmendments,
499 std::vector<std::shared_ptr<STValidation>> const& validations) override;
500};
501
502//------------------------------------------------------------------------------
503
505 ServiceRegistry& registry,
506 std::chrono::seconds majorityTime,
507 std::vector<FeatureInfo> const& supported,
508 Section const& enabled,
509 Section const& vetoed,
510 beast::Journal journal)
511 : majorityTime_(majorityTime), j_(journal), db_(registry.getWalletDB())
512{
514
515 // Find out if the FeatureVotes table exists in WalletDB
516 bool const featureVotesExist = [this]() {
517 auto db = db_.checkoutDb();
518 return createFeatureVotes(*db);
519 }();
520
521 // Parse supported amendments
522 for (auto const& [name, amendment, votebehavior] : supported)
523 {
524 AmendmentState& s = add(amendment, lock);
525
526 s.name = name;
527 s.supported = true;
528 switch (votebehavior)
529 {
532 break;
533
536 break;
537
540 break;
541 }
542
543 JLOG(j_.debug()) << "Amendment " << amendment << " (" << s.name
544 << ") is supported and will be "
545 << (s.vote == AmendmentVote::Up ? "up" : "down")
546 << " voted by default if not enabled on the ledger.";
547 }
548
549 hash_set<uint256> detectConflict;
550 // Parse enabled amendments from config
551 for (std::pair<uint256, std::string> const& a : parseSection(enabled))
552 {
553 if (featureVotesExist)
554 { // If the table existed, warn about duplicate config info
555 JLOG(j_.warn()) << "[amendments] section in config file ignored"
556 " in favor of data in db/wallet.db.";
557 break;
558 }
559
560 // Otherwise transfer config data into the table
561 detectConflict.insert(a.first);
562 persistVote(a.first, a.second, AmendmentVote::Up);
563 }
564
565 // Parse vetoed amendments from config
566 for (auto const& a : parseSection(vetoed))
567 {
568 if (featureVotesExist)
569 { // If the table existed, warn about duplicate config info
570 JLOG(j_.warn()) << "[veto_amendments] section in config file ignored"
571 " in favor of data in db/wallet.db.";
572 break;
573 }
574
575 // Otherwise transfer config data into the table
576 if (!detectConflict.contains(a.first))
577 {
578 persistVote(a.first, a.second, AmendmentVote::Down);
579 }
580 else
581 {
582 JLOG(j_.warn()) << "[veto_amendments] section in config has amendment " << '('
583 << a.first << ", " << a.second
584 << ") both [veto_amendments] and [amendments].";
585 }
586 }
587
588 // Read amendment votes from wallet.db
589 auto db = db_.checkoutDb();
591 *db,
592 [&](boost::optional<std::string> amendmentHash,
593 boost::optional<std::string> amendmentName,
594 boost::optional<AmendmentVote> vote) {
595 uint256 amendHash;
596 if (!amendmentHash || !amendmentName || !vote)
597 {
598 // These fields should never have nulls, but check
599 Throw<std::runtime_error>("Invalid FeatureVotes row in wallet.db");
600 }
601 if (!amendHash.parseHex(*amendmentHash))
602 {
604 "Invalid amendment ID '" + *amendmentHash + " in wallet.db");
605 }
606 if (*vote == AmendmentVote::Down)
607 {
608 // Unknown amendments are effectively vetoed already
609 if (auto s = get(amendHash, lock))
610 {
611 JLOG(j_.info()) << "Amendment {" << *amendmentName << ", " << amendHash
612 << "} is downvoted.";
613 if (!amendmentName->empty())
614 s->name = *amendmentName;
615 // An obsolete amendment's vote can never be changed
616 if (s->vote != AmendmentVote::Obsolete)
617 s->vote = *vote;
618 }
619 }
620 else // up-vote
621 {
622 AmendmentState& s = add(amendHash, lock);
623
624 JLOG(j_.debug()) << "Amendment {" << *amendmentName << ", " << amendHash
625 << "} is upvoted.";
626 if (!amendmentName->empty())
627 s.name = *amendmentName;
628 // An obsolete amendment's vote can never be changed
630 s.vote = *vote;
631 }
632 });
633}
634
637{
638 // call with the mutex held
639 return amendmentMap_[amendmentHash];
640}
641
644{
645 // Forward to the const version of get.
646 return const_cast<AmendmentState*>(std::as_const(*this).get(amendmentHash, lock));
647}
648
649AmendmentState const*
651{
652 // call with the mutex held
653 auto ret = amendmentMap_.find(amendmentHash);
654
655 if (ret == amendmentMap_.end())
656 return nullptr;
657
658 return &ret->second;
659}
660
663{
665
666 for (auto const& e : amendmentMap_)
667 {
668 if (name == e.second.name)
669 return e.first;
670 }
671
672 return {};
673}
674
675void
677 uint256 const& amendment,
678 std::string const& name,
679 AmendmentVote vote) const
680{
681 XRPL_ASSERT(
683 "xrpl::AmendmentTableImpl::persistVote : valid vote input");
684 auto db = db_.checkoutDb();
685 voteAmendment(*db, amendment, name, vote);
686}
687
688bool
690{
692 AmendmentState& s = add(amendment, lock);
693
694 if (s.vote != AmendmentVote::Up)
695 return false;
697 persistVote(amendment, s.name, s.vote);
698 return true;
699}
700
701bool
703{
705 AmendmentState* const s = get(amendment, lock);
706
707 if ((s == nullptr) || s->vote != AmendmentVote::Down)
708 return false;
710 persistVote(amendment, s->name, s->vote);
711 return true;
712}
713
714bool
716{
718 AmendmentState& s = add(amendment, lock);
719
720 if (s.enabled)
721 return false;
722
723 s.enabled = true;
724
725 if (!s.supported)
726 {
727 JLOG(j_.error()) << "Unsupported amendment " << amendment << " activated.";
728 unsupportedEnabled_ = true;
729 }
730
731 return true;
732}
733
734bool
736{
738 AmendmentState const* s = get(amendment, lock);
739 return (s != nullptr) && s->enabled;
740}
741
742bool
744{
746 AmendmentState const* s = get(amendment, lock);
747 return (s != nullptr) && s->supported;
748}
749
750bool
756
763
766{
767 // Get the list of amendments we support and do not
768 // veto, but that are not already enabled
769 std::vector<uint256> amendments;
770
771 {
773 amendments.reserve(amendmentMap_.size());
774 for (auto const& e : amendmentMap_)
775 {
776 if (e.second.supported && e.second.vote == AmendmentVote::Up &&
777 (!enabled.contains(e.first)))
778 {
779 amendments.push_back(e.first);
780 JLOG(j_.info()) << "Voting for amendment " << e.second.name;
781 }
782 }
783 }
784
785 if (!amendments.empty())
786 std::ranges::sort(amendments);
787
788 return amendments;
789}
790
793{
794 // Get the list of amendments we support and do not veto
795 return doValidation({});
796}
797
800 Rules const& rules,
801 NetClock::time_point closeTime,
802 std::set<uint256> const& enabledAmendments,
803 majorityAmendments_t const& majorityAmendments,
805{
806 JLOG(j_.trace()) << "voting at " << closeTime.time_since_epoch().count() << ": "
807 << enabledAmendments.size() << ", " << majorityAmendments.size() << ", "
808 << valSet.size();
809
811
812 // Keep a record of the votes we received.
813 previousTrustedVotes_.recordVotes(rules, valSet, closeTime, j_, lock);
814
815 // Tally the most recent votes.
817 JLOG(j_.debug()) << "Counted votes from " << vote->trustedValidations()
818 << " valid trusted validations, threshold is: " << vote->threshold();
819
820 // Map of amendments to the action to be taken for each one. The action is
821 // the value of the flags in the pseudo-transaction
823
824 // process all amendments we know of
825 for (auto const& entry : amendmentMap_)
826 {
827 if (enabledAmendments.contains(entry.first))
828 {
829 JLOG(j_.trace()) << entry.first << ": amendment already enabled";
830
831 continue;
832 }
833
834 bool const hasValMajority = vote->passes(entry.first);
835
836 auto const majorityTime = [&]() -> std::optional<NetClock::time_point> {
837 auto const it = majorityAmendments.find(entry.first);
838 if (it != majorityAmendments.end())
839 return it->second;
840 return std::nullopt;
841 }();
842
843 bool const hasLedgerMajority = majorityTime.has_value();
844
845 auto const logStr = [&entry, &vote]() {
847 ss << entry.first << " (" << entry.second.name << ") has " << vote->votes(entry.first)
848 << " votes";
849 return ss.str();
850 }();
851
852 if (hasValMajority && !hasLedgerMajority && entry.second.vote == AmendmentVote::Up)
853 {
854 // Ledger says no majority, validators say yes, and voting yes
855 // locally
856 JLOG(j_.debug()) << logStr << ": amendment got majority";
857 actions[entry.first] = tfGotMajority;
858 }
859 else if (!hasValMajority && hasLedgerMajority)
860 {
861 // Ledger says majority, validators say no
862 JLOG(j_.debug()) << logStr << ": amendment lost majority";
863 actions[entry.first] = tfLostMajority;
864 }
865 else if (
866 hasLedgerMajority && ((*majorityTime + majorityTime_) <= closeTime) &&
867 entry.second.vote == AmendmentVote::Up)
868 {
869 // Ledger says majority held
870 JLOG(j_.debug()) << logStr << ": amendment majority held";
871 actions[entry.first] = 0;
872 }
873 // Logging only below this point
874 else if (hasValMajority && hasLedgerMajority)
875 {
876 JLOG(j_.debug()) << logStr << ": amendment holding majority, waiting to be enabled";
877 }
878 else if (!hasValMajority)
879 {
880 JLOG(j_.debug()) << logStr << ": amendment does not have majority";
881 }
882 }
883
884 // Stash for reporting
885 lastVote_ = std::move(vote);
886 return actions;
887}
888
889bool
891{
893
894 // Is there a ledger in which an amendment could have been enabled
895 // between these two ledger sequences?
896
897 return ((ledgerSeq - 1) / 256) != ((lastUpdateSeq_ - 1) / 256);
898}
899
900void
902 LedgerIndex ledgerSeq,
903 std::set<uint256> const& enabled,
904 majorityAmendments_t const& majority)
905{
906 for (auto& e : enabled)
907 enable(e);
908
910
911 // Remember the ledger sequence of this update.
912 lastUpdateSeq_ = ledgerSeq;
913
914 // Since we have the whole list in `majority`, reset the time flag, even
915 // if it's currently set. If it's not set when the loop is done, then any
916 // prior unknown amendments have lost majority.
918 for (auto const& [hash, time] : majority)
919 {
920 AmendmentState const& s = add(hash, lock);
921
922 if (s.enabled)
923 continue;
924
925 if (!s.supported)
926 {
927 JLOG(j_.info()) << "Unsupported amendment " << hash << " reached majority at "
928 << to_string(time);
931 }
932 }
935}
936
937void
939{
941 previousTrustedVotes_.trustChanged(allTrusted, lock);
942}
943
944void
946 json::Value& v,
947 uint256 const& id,
948 AmendmentState const& fs,
949 bool isAdmin,
950 std::scoped_lock<std::mutex> const&) const
951{
952 if (!fs.name.empty())
953 v[jss::name] = fs.name;
954
955 v[jss::supported] = fs.supported;
956 if (!fs.enabled && isAdmin)
957 {
959 {
960 v[jss::vetoed] = "Obsolete";
961 }
962 else
963 {
964 v[jss::vetoed] = fs.vote == AmendmentVote::Down;
965 }
966 }
967 v[jss::enabled] = fs.enabled;
968
969 if (!fs.enabled && lastVote_ && isAdmin)
970 {
971 auto const votesTotal = lastVote_->trustedValidations();
972 auto const votesNeeded = lastVote_->threshold();
973 auto const votesFor = lastVote_->votes(id);
974
975 v[jss::count] = votesFor;
976 v[jss::validations] = votesTotal;
977
978 if (votesNeeded != 0)
979 v[jss::threshold] = votesNeeded;
980 }
981}
982
985{
987 {
989 for (auto const& e : amendmentMap_)
990 {
992 ret[to_string(e.first)] = json::ValueType::Object,
993 e.first,
994 e.second,
995 isAdmin,
996 lock);
997 }
998 }
999 return ret;
1000}
1001
1003AmendmentTableImpl::getJson(uint256 const& amendmentID, bool isAdmin) const
1004{
1006
1007 {
1009 AmendmentState const* a = get(amendmentID, lock);
1010 if (a != nullptr)
1011 {
1012 json::Value& jAmendment = (ret[to_string(amendmentID)] = json::ValueType::Object);
1013 injectJson(jAmendment, amendmentID, *a, isAdmin, lock);
1014 }
1015 }
1016
1017 return ret;
1018}
1019
1022 ServiceRegistry& registry,
1023 std::chrono::seconds majorityTime,
1025 Section const& enabled,
1026 Section const& vetoed,
1027 beast::Journal journal)
1028{
1030 registry, majorityTime, supported, enabled, vetoed, journal);
1031}
1032
1033} // namespace xrpl
T as_const(T... args)
A generic endpoint for log messages.
Definition Journal.h:38
Stream debug() const
Definition Journal.h:297
Represents a JSON value.
Definition json_value.h:130
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:507
std::chrono::time_point< NetClock > time_point
Definition chrono.h:46
std::chrono::duration< rep, period > duration
Definition chrono.h:45
Rules controlling protocol behavior.
Definition Rules.h:33
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
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:26
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:259
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition AccountID.cpp:93
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:253
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:633
bool createFeatureVotes(soci::session &session)
createFeatureVotes Creates the FeatureVote table if it does not exist.
Definition Wallet.cpp:229
std::map< uint256, NetClock::time_point > majorityAmendments_t
Definition View.h:73
AmendmentVote
Definition Wallet.h:126
std::unordered_map< Key, Value, Hash, Pred, Allocator > hash_map
bool isAdmin(Port const &port, json::Value const &params, beast::IP::Address const &remoteIp)
Definition Role.cpp:81
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:288
BaseUInt< 256 > uint256
Definition base_uint.h:562
XRPL_NO_SANITIZE_ADDRESS void Throw(Args &&... args)
Definition contract.h:49
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)