rippled
Loading...
Searching...
No Matches
AmendmentTable.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012, 2013 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <xrpld/app/main/Application.h>
21#include <xrpld/app/misc/AmendmentTable.h>
22#include <xrpld/app/rdb/Wallet.h>
23#include <xrpld/core/ConfigSections.h>
24#include <xrpl/protocol/Feature.h>
25#include <xrpl/protocol/STValidation.h>
26#include <xrpl/protocol/TxFlags.h>
27#include <xrpl/protocol/jss.h>
28
29#include <boost/algorithm/string.hpp>
30#include <boost/format.hpp>
31#include <boost/range/adaptor/transformed.hpp>
32#include <boost/regex.hpp>
33#include <algorithm>
34#include <mutex>
35
36namespace ripple {
37
39parseSection(Section const& section)
40{
41 static boost::regex const re1(
42 "^" // start of line
43 "(?:\\s*)" // whitespace (optional)
44 "([abcdefABCDEF0-9]{64})" // <hexadecimal amendment ID>
45 "(?:\\s+)" // whitespace
46 "(\\S+)" // <description>
47 ,
48 boost::regex_constants::optimize);
49
51
52 for (auto const& line : section.lines())
53 {
54 boost::smatch match;
55
56 if (!boost::regex_match(line, match, re1))
57 Throw<std::runtime_error>(
58 "Invalid entry '" + line + "' in [" + section.name() + "]");
59
60 uint256 id;
61
62 if (!id.parseHex(match[1]))
63 Throw<std::runtime_error>(
64 "Invalid amendment ID '" + match[1] + "' in [" +
65 section.name() + "]");
66
67 names.push_back(std::make_pair(id, match[2]));
68 }
69
70 return names;
71}
72
92{
93private:
94 // Associates each trusted validator with the last votes we saw from them
95 // and an expiration for that record.
97 {
105 };
107
108public:
109 TrustedVotes() = default;
110 TrustedVotes(TrustedVotes const& rhs) = delete;
112 operator=(TrustedVotes const& rhs) = delete;
113
114 // Called when the list of trusted validators changes.
115 //
116 // Call with AmendmentTable::mutex_ locked.
117 void
119 hash_set<PublicKey> const& allTrusted,
120 std::lock_guard<std::mutex> const& lock)
121 {
122 decltype(recordedVotes_) newRecordedVotes;
123 newRecordedVotes.reserve(allTrusted.size());
124
125 // Make sure every PublicKey in allTrusted is represented in
126 // recordedVotes_. Also make sure recordedVotes_ contains
127 // no additional PublicKeys.
128 for (auto& trusted : allTrusted)
129 {
130 if (recordedVotes_.contains(trusted))
131 {
132 // Preserve this validator's previously saved voting state.
133 newRecordedVotes.insert(recordedVotes_.extract(trusted));
134 }
135 else
136 {
137 // New validators have a starting position of no on everything.
138 // Add the entry with an empty vector and unseated timeout.
139 newRecordedVotes[trusted];
140 }
141 }
142 // The votes of any no-longer-trusted validators will be destroyed
143 // when changedTrustedVotes goes out of scope.
144 recordedVotes_.swap(newRecordedVotes);
145 }
146
147 // Called when we receive the latest votes.
148 //
149 // Call with AmendmentTable::mutex_ locked.
150 void
152 Rules const& rules,
154 NetClock::time_point const closeTime,
156 std::lock_guard<std::mutex> const& lock)
157 {
158 // When we get an STValidation we save the upVotes it contains, but
159 // we also set an expiration for those upVotes. The following constant
160 // controls the timeout.
161 //
162 // There really is no "best" timeout to choose for when we finally
163 // lose confidence that we know how a validator is voting. But part
164 // of the point of recording validator votes is to avoid flapping of
165 // amendment votes. A 24h timeout says that we will change the local
166 // record of a validator's vote to "no" 24h after the last vote seen
167 // from that validator. So flapping due to that validator being off
168 // line will happen less frequently than every 24 hours.
169 using namespace std::chrono_literals;
170 static constexpr NetClock::duration expiresAfter = 24h;
171
172 auto const newTimeout = closeTime + expiresAfter;
173
174 // Walk all validations and replace previous votes from trusted
175 // validators with these newest votes.
176 for (auto const& val : valSet)
177 {
178 auto const pkHuman =
179 toBase58(TokenType::NodePublic, val->getSignerPublic());
180 // If this validation comes from one of our trusted validators...
181 if (auto const iter = recordedVotes_.find(val->getSignerPublic());
182 iter != recordedVotes_.end())
183 {
184 iter->second.timeout = newTimeout;
185 if (val->isFieldPresent(sfAmendments))
186 {
187 auto const& choices = val->getFieldV256(sfAmendments);
188 iter->second.upVotes.assign(choices.begin(), choices.end());
189 JLOG(j.debug())
190 << "recordVotes: Validation from trusted " << pkHuman
191 << " has " << choices.size() << " amendment votes: "
192 << boost::algorithm::join(
193 iter->second.upVotes |
194 boost::adaptors::transformed(
195 to_string<256, void>),
196 ", ");
197 // TODO: Maybe transform using to_short_string once #5126 is
198 // merged
199 //
200 // iter->second.upVotes |
201 // boost::adaptors::transformed(to_short_string<256, void>)
202 }
203 else
204 {
205 // This validator does not upVote any amendments right now.
206 iter->second.upVotes.clear();
207 JLOG(j.debug()) << "recordVotes: Validation from trusted "
208 << pkHuman << " has no amendment votes.";
209 }
210 }
211 else
212 {
213 JLOG(j.debug())
214 << "recordVotes: Ignoring validation from untrusted "
215 << pkHuman;
216 }
217 }
218
219 // Now remove any expired records from recordedVotes_.
221 recordedVotes_.begin(),
222 recordedVotes_.end(),
223 [&closeTime, newTimeout, &j](
224 decltype(recordedVotes_)::value_type& votes) {
225 auto const pkHuman =
226 toBase58(TokenType::NodePublic, votes.first);
227 if (!votes.second.timeout)
228 {
229 assert(votes.second.upVotes.empty());
230 JLOG(j.debug())
231 << "recordVotes: Have not received any "
232 "amendment votes from "
233 << pkHuman << " since last timeout or startup";
234 }
235 else if (closeTime > votes.second.timeout)
236 {
237 JLOG(j.debug())
238 << "recordVotes: Timeout: Clearing votes from "
239 << pkHuman;
240 votes.second.timeout.reset();
241 votes.second.upVotes.clear();
242 }
243 else if (votes.second.timeout != newTimeout)
244 {
245 assert(votes.second.timeout < newTimeout);
246 using namespace std::chrono;
247 auto const age = duration_cast<minutes>(
248 newTimeout - *votes.second.timeout);
249 JLOG(j.debug()) << "recordVotes: Using " << age.count()
250 << "min old cached votes from " << pkHuman;
251 }
252 });
253 }
254
255 // Return the information needed by AmendmentSet to determine votes.
256 //
257 // Call with AmendmentTable::mutex_ locked.
259 getVotes(Rules const& rules, std::lock_guard<std::mutex> const& lock) const
260 {
262 int available = 0;
263 for (auto& validatorVotes : recordedVotes_)
264 {
265 assert(
266 validatorVotes.second.timeout ||
267 validatorVotes.second.upVotes.empty());
268 if (validatorVotes.second.timeout)
269 ++available;
270 for (uint256 const& amendment : validatorVotes.second.upVotes)
271 {
272 ret[amendment] += 1;
273 }
274 }
275 return {available, ret};
276 }
277};
278
284{
286 AmendmentVote vote = AmendmentVote::down;
287
293 bool enabled = false;
294
296 bool supported = false;
297
300
301 explicit AmendmentState() = default;
302};
303
306{
307private:
308 // How many yes votes each amendment received
310 Rules const& rules_;
311 // number of trusted validations
312 int trustedValidations_ = 0;
313 // number of votes needed
314 int threshold_ = 0;
315
316 void
317 computeThreshold(int trustedValidations, Rules const& rules)
318 {
319 threshold_ = !rules_.enabled(fixAmendmentMajorityCalc)
320 ? std::max(
321 1L,
322 static_cast<long>(
323 (trustedValidations_ *
326 : std::max(
327 1L,
328 static_cast<long>(
329 (trustedValidations_ *
332 }
333
334public:
336 Rules const& rules,
337 TrustedVotes const& trustedVotes,
338 std::lock_guard<std::mutex> const& lock)
339 : rules_(rules)
340 {
341 // process validations for ledger before flag ledger.
342 auto [trustedCount, newVotes] = trustedVotes.getVotes(rules, lock);
343
344 trustedValidations_ = trustedCount;
345 votes_.swap(newVotes);
346
347 computeThreshold(trustedValidations_, rules);
348 }
349
350 bool
351 passes(uint256 const& amendment) const
352 {
353 auto const& it = votes_.find(amendment);
354
355 if (it == votes_.end())
356 return false;
357
358 // Before this fix, it was possible for an amendment to activate with a
359 // percentage slightly less than 80% because we compared for "greater
360 // than or equal to" instead of strictly "greater than".
361 // One validator is an exception, otherwise it is not possible
362 // to gain majority.
363 if (!rules_.enabled(fixAmendmentMajorityCalc) ||
364 trustedValidations_ == 1)
365 return it->second >= threshold_;
366
367 return it->second > threshold_;
368 }
369
370 int
371 votes(uint256 const& amendment) const
372 {
373 auto const& it = votes_.find(amendment);
374
375 if (it == votes_.end())
376 return 0;
377
378 return it->second;
379 }
380
381 int
383 {
384 return trustedValidations_;
385 }
386
387 int
388 threshold() const
389 {
390 return threshold_;
391 }
392};
393
394//------------------------------------------------------------------------------
395
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::lock_guard<std::mutex> const& lock);
436
437 // Finds existing state. Must be called with mutex_ locked.
439 get(uint256 const& amendment, std::lock_guard<std::mutex> const& lock);
440
441 AmendmentState const*
442 get(uint256 const& amendment,
443 std::lock_guard<std::mutex> const& lock) const;
444
445 // Injects amendment json into v. Must be called with mutex_ locked.
446 void
447 injectJson(
448 Json::Value& v,
449 uint256 const& amendment,
450 AmendmentState const& state,
451 bool isAdmin,
452 std::lock_guard<std::mutex> const& lock) const;
453
454 void
455 persistVote(
456 uint256 const& amendment,
457 std::string const& name,
458 AmendmentVote vote) const;
459
460public:
462 Application& app,
463 std::chrono::seconds majorityTime,
464 std::vector<FeatureInfo> const& supported,
465 Section const& enabled,
466 Section const& vetoed,
467 beast::Journal journal);
468
469 uint256
470 find(std::string const& name) const override;
471
472 bool
473 veto(uint256 const& amendment) override;
474 bool
475 unVeto(uint256 const& amendment) override;
476
477 bool
478 enable(uint256 const& amendment) override;
479
480 bool
481 isEnabled(uint256 const& amendment) const override;
482 bool
483 isSupported(uint256 const& amendment) const override;
484
485 bool
486 hasUnsupportedEnabled() const override;
487
489 firstUnsupportedExpected() const override;
490
492 getJson(bool isAdmin) const override;
494 getJson(uint256 const&, bool isAdmin) const override;
495
496 bool
497 needValidatedLedger(LedgerIndex seq) const override;
498
499 void
500 doValidatedLedger(
501 LedgerIndex seq,
502 std::set<uint256> const& enabled,
503 majorityAmendments_t const& majority) override;
504
505 void
506 trustChanged(hash_set<PublicKey> const& allTrusted) override;
507
509 doValidation(std::set<uint256> const& enabledAmendments) const override;
510
512 getDesired() const override;
513
515 doVoting(
516 Rules const& rules,
517 NetClock::time_point closeTime,
518 std::set<uint256> const& enabledAmendments,
519 majorityAmendments_t const& majorityAmendments,
520 std::vector<std::shared_ptr<STValidation>> const& validations) override;
521};
522
523//------------------------------------------------------------------------------
524
525AmendmentTableImpl::AmendmentTableImpl(
526 Application& app,
527 std::chrono::seconds majorityTime,
528 std::vector<FeatureInfo> const& supported,
529 Section const& enabled,
530 Section const& vetoed,
531 beast::Journal journal)
532 : lastUpdateSeq_(0)
533 , majorityTime_(majorityTime)
534 , unsupportedEnabled_(false)
535 , j_(journal)
536 , db_(app.getWalletDB())
537{
539
540 // Find out if the FeatureVotes table exists in WalletDB
541 bool const featureVotesExist = [this]() {
542 auto db = db_.checkoutDb();
543 return createFeatureVotes(*db);
544 }();
545
546 // Parse supported amendments
547 for (auto const& [name, amendment, votebehavior] : supported)
548 {
549 AmendmentState& s = add(amendment, lock);
550
551 s.name = name;
552 s.supported = true;
553 switch (votebehavior)
554 {
557 break;
558
561 break;
562
565 break;
566 }
567
568 JLOG(j_.debug()) << "Amendment " << amendment << " (" << s.name
569 << ") is supported and will be "
570 << (s.vote == AmendmentVote::up ? "up" : "down")
571 << " voted by default if not enabled on the ledger.";
572 }
573
574 hash_set<uint256> detect_conflict;
575 // Parse enabled amendments from config
576 for (std::pair<uint256, std::string> const& a : parseSection(enabled))
577 {
578 if (featureVotesExist)
579 { // If the table existed, warn about duplicate config info
580 JLOG(j_.warn()) << "[amendments] section in config file ignored"
581 " in favor of data in db/wallet.db.";
582 break;
583 }
584 else
585 { // Otherwise transfer config data into the table
586 detect_conflict.insert(a.first);
587 persistVote(a.first, a.second, AmendmentVote::up);
588 }
589 }
590
591 // Parse vetoed amendments from config
592 for (auto const& a : parseSection(vetoed))
593 {
594 if (featureVotesExist)
595 { // If the table existed, warn about duplicate config info
596 JLOG(j_.warn())
597 << "[veto_amendments] section in config file ignored"
598 " in favor of data in db/wallet.db.";
599 break;
600 }
601 else
602 { // Otherwise transfer config data into the table
603 if (detect_conflict.count(a.first) == 0)
604 {
605 persistVote(a.first, a.second, AmendmentVote::down);
606 }
607 else
608 {
609 JLOG(j_.warn())
610 << "[veto_amendments] section in config has amendment "
611 << '(' << a.first << ", " << a.second
612 << ") both [veto_amendments] and [amendments].";
613 }
614 }
615 }
616
617 // Read amendment votes from wallet.db
618 auto db = db_.checkoutDb();
620 *db,
621 [&](boost::optional<std::string> amendment_hash,
622 boost::optional<std::string> amendment_name,
623 boost::optional<AmendmentVote> vote) {
624 uint256 amend_hash;
625 if (!amendment_hash || !amendment_name || !vote)
626 {
627 // These fields should never have nulls, but check
628 Throw<std::runtime_error>(
629 "Invalid FeatureVotes row in wallet.db");
630 }
631 if (!amend_hash.parseHex(*amendment_hash))
632 {
633 Throw<std::runtime_error>(
634 "Invalid amendment ID '" + *amendment_hash +
635 " in wallet.db");
636 }
637 if (*vote == AmendmentVote::down)
638 {
639 // Unknown amendments are effectively vetoed already
640 if (auto s = get(amend_hash, lock))
641 {
642 JLOG(j_.info()) << "Amendment {" << *amendment_name << ", "
643 << amend_hash << "} is downvoted.";
644 if (!amendment_name->empty())
645 s->name = *amendment_name;
646 // An obsolete amendment's vote can never be changed
647 if (s->vote != AmendmentVote::obsolete)
648 s->vote = *vote;
649 }
650 }
651 else // up-vote
652 {
653 AmendmentState& s = add(amend_hash, lock);
654
655 JLOG(j_.debug()) << "Amendment {" << *amendment_name << ", "
656 << amend_hash << "} is upvoted.";
657 if (!amendment_name->empty())
658 s.name = *amendment_name;
659 // An obsolete amendment's vote can never be changed
661 s.vote = *vote;
662 }
663 });
664}
665
668 uint256 const& amendmentHash,
670{
671 // call with the mutex held
672 return amendmentMap_[amendmentHash];
673}
674
677 uint256 const& amendmentHash,
678 std::lock_guard<std::mutex> const& lock)
679{
680 // Forward to the const version of get.
681 return const_cast<AmendmentState*>(
682 std::as_const(*this).get(amendmentHash, lock));
683}
684
685AmendmentState const*
687 uint256 const& amendmentHash,
688 std::lock_guard<std::mutex> const&) const
689{
690 // call with the mutex held
691 auto ret = amendmentMap_.find(amendmentHash);
692
693 if (ret == amendmentMap_.end())
694 return nullptr;
695
696 return &ret->second;
697}
698
701{
703
704 for (auto const& e : amendmentMap_)
705 {
706 if (name == e.second.name)
707 return e.first;
708 }
709
710 return {};
711}
712
713void
715 uint256 const& amendment,
716 std::string const& name,
717 AmendmentVote vote) const
718{
719 XRPL_ASSERT(
721 "ripple::AmendmentTableImpl::persistVote : valid vote input");
722 auto db = db_.checkoutDb();
723 voteAmendment(*db, amendment, name, vote);
724}
725
726bool
728{
730 AmendmentState& s = add(amendment, lock);
731
732 if (s.vote != AmendmentVote::up)
733 return false;
735 persistVote(amendment, s.name, s.vote);
736 return true;
737}
738
739bool
741{
743 AmendmentState* const s = get(amendment, lock);
744
745 if (!s || s->vote != AmendmentVote::down)
746 return false;
748 persistVote(amendment, s->name, s->vote);
749 return true;
750}
751
752bool
754{
756 AmendmentState& s = add(amendment, lock);
757
758 if (s.enabled)
759 return false;
760
761 s.enabled = true;
762
763 if (!s.supported)
764 {
765 JLOG(j_.error()) << "Unsupported amendment " << amendment
766 << " activated.";
767 unsupportedEnabled_ = true;
768 }
769
770 return true;
771}
772
773bool
775{
777 AmendmentState const* s = get(amendment, lock);
778 return s && s->enabled;
779}
780
781bool
783{
785 AmendmentState const* s = get(amendment, lock);
786 return s && s->supported;
787}
788
789bool
791{
793 return unsupportedEnabled_;
794}
795
798{
801}
802
805{
806 // Get the list of amendments we support and do not
807 // veto, but that are not already enabled
808 std::vector<uint256> amendments;
809
810 {
812 amendments.reserve(amendmentMap_.size());
813 for (auto const& e : amendmentMap_)
814 {
815 if (e.second.supported && e.second.vote == AmendmentVote::up &&
816 (enabled.count(e.first) == 0))
817 {
818 amendments.push_back(e.first);
819 JLOG(j_.info()) << "Voting for amendment " << e.second.name;
820 }
821 }
822 }
823
824 if (!amendments.empty())
825 std::sort(amendments.begin(), amendments.end());
826
827 return amendments;
828}
829
832{
833 // Get the list of amendments we support and do not veto
834 return doValidation({});
835}
836
839 Rules const& rules,
840 NetClock::time_point closeTime,
841 std::set<uint256> const& enabledAmendments,
842 majorityAmendments_t const& majorityAmendments,
844{
845 JLOG(j_.trace()) << "voting at " << closeTime.time_since_epoch().count()
846 << ": " << enabledAmendments.size() << ", "
847 << majorityAmendments.size() << ", " << valSet.size();
848
850
851 // Keep a record of the votes we received.
852 previousTrustedVotes_.recordVotes(rules, valSet, closeTime, j_, lock);
853
854 // Tally the most recent votes.
855 auto vote =
856 std::make_unique<AmendmentSet>(rules, previousTrustedVotes_, lock);
857 JLOG(j_.debug()) << "Counted votes from " << vote->trustedValidations()
858 << " valid trusted validations, threshold is: "
859 << vote->threshold();
860
861 // Map of amendments to the action to be taken for each one. The action is
862 // the value of the flags in the pseudo-transaction
864
865 // process all amendments we know of
866 for (auto const& entry : amendmentMap_)
867 {
868 if (enabledAmendments.contains(entry.first))
869 {
870 JLOG(j_.trace()) << entry.first << ": amendment already enabled";
871
872 continue;
873 }
874
875 bool const hasValMajority = vote->passes(entry.first);
876
877 auto const majorityTime = [&]() -> std::optional<NetClock::time_point> {
878 auto const it = majorityAmendments.find(entry.first);
879 if (it != majorityAmendments.end())
880 return it->second;
881 return std::nullopt;
882 }();
883
884 bool const hasLedgerMajority = majorityTime.has_value();
885
886 auto const logStr = [&entry, &vote]() {
888 ss << entry.first << " (" << entry.second.name << ") has "
889 << vote->votes(entry.first) << " votes";
890 return ss.str();
891 }();
892
893 if (hasValMajority && !hasLedgerMajority &&
894 entry.second.vote == AmendmentVote::up)
895 {
896 // Ledger says no majority, validators say yes, and voting yes
897 // locally
898 JLOG(j_.debug()) << logStr << ": amendment got majority";
899 actions[entry.first] = tfGotMajority;
900 }
901 else if (!hasValMajority && hasLedgerMajority)
902 {
903 // Ledger says majority, validators say no
904 JLOG(j_.debug()) << logStr << ": amendment lost majority";
905 actions[entry.first] = tfLostMajority;
906 }
907 else if (
908 hasLedgerMajority &&
909 ((*majorityTime + majorityTime_) <= closeTime) &&
910 entry.second.vote == AmendmentVote::up)
911 {
912 // Ledger says majority held
913 JLOG(j_.debug()) << logStr << ": amendment majority held";
914 actions[entry.first] = 0;
915 }
916 // Logging only below this point
917 else if (hasValMajority && hasLedgerMajority)
918 {
919 JLOG(j_.debug())
920 << logStr
921 << ": amendment holding majority, waiting to be enabled";
922 }
923 else if (!hasValMajority)
924 {
925 JLOG(j_.debug()) << logStr << ": amendment does not have majority";
926 }
927 }
928
929 // Stash for reporting
930 lastVote_ = std::move(vote);
931 return actions;
932}
933
934bool
936{
938
939 // Is there a ledger in which an amendment could have been enabled
940 // between these two ledger sequences?
941
942 return ((ledgerSeq - 1) / 256) != ((lastUpdateSeq_ - 1) / 256);
943}
944
945void
947 LedgerIndex ledgerSeq,
948 std::set<uint256> const& enabled,
949 majorityAmendments_t const& majority)
950{
951 for (auto& e : enabled)
952 enable(e);
953
955
956 // Remember the ledger sequence of this update.
957 lastUpdateSeq_ = ledgerSeq;
958
959 // Since we have the whole list in `majority`, reset the time flag, even
960 // if it's currently set. If it's not set when the loop is done, then any
961 // prior unknown amendments have lost majority.
963 for (auto const& [hash, time] : majority)
964 {
965 AmendmentState& s = add(hash, lock);
966
967 if (s.enabled)
968 continue;
969
970 if (!s.supported)
971 {
972 JLOG(j_.info()) << "Unsupported amendment " << hash
973 << " reached majority at " << to_string(time);
976 }
977 }
980}
981
982void
984{
986 previousTrustedVotes_.trustChanged(allTrusted, lock);
987}
988
989void
991 Json::Value& v,
992 const uint256& id,
993 const AmendmentState& fs,
994 bool isAdmin,
995 std::lock_guard<std::mutex> const&) const
996{
997 if (!fs.name.empty())
998 v[jss::name] = fs.name;
999
1000 v[jss::supported] = fs.supported;
1001 if (!fs.enabled && isAdmin)
1002 {
1003 if (fs.vote == AmendmentVote::obsolete)
1004 v[jss::vetoed] = "Obsolete";
1005 else
1006 v[jss::vetoed] = fs.vote == AmendmentVote::down;
1007 }
1008 v[jss::enabled] = fs.enabled;
1009
1010 if (!fs.enabled && lastVote_ && isAdmin)
1011 {
1012 auto const votesTotal = lastVote_->trustedValidations();
1013 auto const votesNeeded = lastVote_->threshold();
1014 auto const votesFor = lastVote_->votes(id);
1015
1016 v[jss::count] = votesFor;
1017 v[jss::validations] = votesTotal;
1018
1019 if (votesNeeded)
1020 v[jss::threshold] = votesNeeded;
1021 }
1022}
1023
1026{
1028 {
1029 std::lock_guard lock(mutex_);
1030 for (auto const& e : amendmentMap_)
1031 {
1032 injectJson(
1033 ret[to_string(e.first)] = Json::objectValue,
1034 e.first,
1035 e.second,
1036 isAdmin,
1037 lock);
1038 }
1039 }
1040 return ret;
1041}
1042
1044AmendmentTableImpl::getJson(uint256 const& amendmentID, bool isAdmin) const
1045{
1047
1048 {
1049 std::lock_guard lock(mutex_);
1050 AmendmentState const* a = get(amendmentID, lock);
1051 if (a)
1052 {
1053 Json::Value& jAmendment =
1054 (ret[to_string(amendmentID)] = Json::objectValue);
1055 injectJson(jAmendment, amendmentID, *a, isAdmin, lock);
1056 }
1057 }
1058
1059 return ret;
1060}
1061
1064 Application& app,
1065 std::chrono::seconds majorityTime,
1067 Section const& enabled,
1068 Section const& vetoed,
1069 beast::Journal journal)
1070{
1071 return std::make_unique<AmendmentTableImpl>(
1072 app, majorityTime, supported, enabled, vetoed, journal);
1073}
1074
1075} // namespace ripple
T as_const(T... args)
Represents a JSON value.
Definition: json_value.h:147
A generic endpoint for log messages.
Definition: Journal.h:59
Stream error() const
Definition: Journal.h:335
Stream debug() const
Definition: Journal.h:317
Stream info() const
Definition: Journal.h:323
Stream trace() const
Severity stream access functions.
Definition: Journal.h:311
Stream warn() const
Definition: Journal.h:329
The status of all amendments requested in a given window.
int votes(uint256 const &amendment) const
int trustedValidations() const
void computeThreshold(int trustedValidations, Rules const &rules)
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
beast::Journal const j_
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()
Definition: DatabaseCon.h:188
Rules controlling protocol behavior.
Definition: Rules.h:35
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition: Rules.cpp:122
Holds a collection of configuration values.
Definition: BasicConfig.h:46
std::string const & name() const
Returns the name of this section.
Definition: BasicConfig.h:62
std::vector< std::string > const & lines() const
Returns all the lines in the section.
Definition: BasicConfig.h:71
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:502
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 make_pair(T... args)
T max(T... args)
@ objectValue
object value (collection of name/value pairs).
Definition: json_value.h:43
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
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:106
AmendmentVote
Definition: Wallet.h:152
constexpr std::uint32_t tfGotMajority
Definition: TxFlags.h:124
bool isAdmin(Port const &port, Json::Value const &params, beast::IP::Address const &remoteIp)
Definition: Role.cpp:84
bool createFeatureVotes(soci::session &session)
createFeatureVotes Creates the FeatureVote table if it does not exist.
Definition: Wallet.cpp:228
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:252
constexpr std::ratio< 80, 100 > postFixAmendmentMajorityCalcThreshold
constexpr std::ratio< 204, 256 > preFixAmendmentMajorityCalcThreshold
The minimum amount of support an amendment should have.
std::string to_string(base_uint< Bits, Tag > const &a)
Definition: base_uint.h:629
T get(Section const &section, std::string const &name, T const &defaultValue=T{})
Retrieve a key/value pair from a section.
Definition: BasicConfig.h:356
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::uint32_t tfLostMajority
Definition: TxFlags.h:125
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
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)