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 XRPL_ASSERT(
230 votes.second.upVotes.empty(),
231 "ripple::TrustedVotes::recordVotes : received no "
232 "upvotes");
233 JLOG(j.debug())
234 << "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())
241 << "recordVotes: Timeout: Clearing votes from "
242 << 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 "ripple::TrustedVotes::recordVotes : votes not "
251 "expired");
252 using namespace std::chrono;
253 auto const age = duration_cast<minutes>(
254 newTimeout - *votes.second.timeout);
255 JLOG(j.debug()) << "recordVotes: Using " << age.count()
256 << "min old cached votes from " << pkHuman;
257 }
258 });
259 }
260
261 // Return the information needed by AmendmentSet to determine votes.
262 //
263 // Call with AmendmentTable::mutex_ locked.
265 getVotes(Rules const& rules, std::lock_guard<std::mutex> const& lock) const
266 {
268 int available = 0;
269 for (auto& validatorVotes : recordedVotes_)
270 {
271 XRPL_ASSERT(
272 validatorVotes.second.timeout ||
273 validatorVotes.second.upVotes.empty(),
274 "ripple::TrustedVotes::getVotes : valid votes");
275 if (validatorVotes.second.timeout)
276 ++available;
277 for (uint256 const& amendment : validatorVotes.second.upVotes)
278 {
279 ret[amendment] += 1;
280 }
281 }
282 return {available, ret};
283 }
284};
285
291{
293 AmendmentVote vote = AmendmentVote::down;
294
300 bool enabled = false;
301
303 bool supported = false;
304
307
308 explicit AmendmentState() = default;
309};
310
313{
314private:
315 // How many yes votes each amendment received
317 Rules const& rules_;
318 // number of trusted validations
319 int trustedValidations_ = 0;
320 // number of votes needed
321 int threshold_ = 0;
322
323 void
324 computeThreshold(int trustedValidations, Rules const& rules)
325 {
326 threshold_ = !rules_.enabled(fixAmendmentMajorityCalc)
327 ? std::max(
328 1L,
329 static_cast<long>(
330 (trustedValidations_ *
333 : std::max(
334 1L,
335 static_cast<long>(
336 (trustedValidations_ *
339 }
340
341public:
343 Rules const& rules,
344 TrustedVotes const& trustedVotes,
345 std::lock_guard<std::mutex> const& lock)
346 : rules_(rules)
347 {
348 // process validations for ledger before flag ledger.
349 auto [trustedCount, newVotes] = trustedVotes.getVotes(rules, lock);
350
351 trustedValidations_ = trustedCount;
352 votes_.swap(newVotes);
353
354 computeThreshold(trustedValidations_, rules);
355 }
356
357 bool
358 passes(uint256 const& amendment) const
359 {
360 auto const& it = votes_.find(amendment);
361
362 if (it == votes_.end())
363 return false;
364
365 // Before this fix, it was possible for an amendment to activate with a
366 // percentage slightly less than 80% because we compared for "greater
367 // than or equal to" instead of strictly "greater than".
368 // One validator is an exception, otherwise it is not possible
369 // to gain majority.
370 if (!rules_.enabled(fixAmendmentMajorityCalc) ||
371 trustedValidations_ == 1)
372 return it->second >= threshold_;
373
374 return it->second > threshold_;
375 }
376
377 int
378 votes(uint256 const& amendment) const
379 {
380 auto const& it = votes_.find(amendment);
381
382 if (it == votes_.end())
383 return 0;
384
385 return it->second;
386 }
387
388 int
390 {
391 return trustedValidations_;
392 }
393
394 int
395 threshold() const
396 {
397 return threshold_;
398 }
399};
400
401//------------------------------------------------------------------------------
402
410{
411private:
413
416
417 // Record of the last votes seen from trusted validators.
419
420 // Time that an amendment must hold a majority for
422
423 // The results of the last voting round - may be empty if
424 // we haven't participated in one yet.
426
427 // True if an unsupported amendment is enabled
429
430 // Unset if no unsupported amendments reach majority,
431 // else set to the earliest time an unsupported amendment
432 // will be enabled.
434
436
437 // Database which persists veto/unveto vote
439
440 // Finds or creates state. Must be called with mutex_ locked.
442 add(uint256 const& amendment, std::lock_guard<std::mutex> const& lock);
443
444 // Finds existing state. Must be called with mutex_ locked.
446 get(uint256 const& amendment, std::lock_guard<std::mutex> const& lock);
447
448 AmendmentState const*
449 get(uint256 const& amendment,
450 std::lock_guard<std::mutex> const& lock) const;
451
452 // Injects amendment json into v. Must be called with mutex_ locked.
453 void
454 injectJson(
455 Json::Value& v,
456 uint256 const& amendment,
457 AmendmentState const& state,
458 bool isAdmin,
459 std::lock_guard<std::mutex> const& lock) const;
460
461 void
462 persistVote(
463 uint256 const& amendment,
464 std::string const& name,
465 AmendmentVote vote) const;
466
467public:
469 Application& app,
470 std::chrono::seconds majorityTime,
471 std::vector<FeatureInfo> const& supported,
472 Section const& enabled,
473 Section const& vetoed,
474 beast::Journal journal);
475
476 uint256
477 find(std::string const& name) const override;
478
479 bool
480 veto(uint256 const& amendment) override;
481 bool
482 unVeto(uint256 const& amendment) override;
483
484 bool
485 enable(uint256 const& amendment) override;
486
487 bool
488 isEnabled(uint256 const& amendment) const override;
489 bool
490 isSupported(uint256 const& amendment) const override;
491
492 bool
493 hasUnsupportedEnabled() const override;
494
496 firstUnsupportedExpected() const override;
497
499 getJson(bool isAdmin) const override;
501 getJson(uint256 const&, bool isAdmin) const override;
502
503 bool
504 needValidatedLedger(LedgerIndex seq) const override;
505
506 void
507 doValidatedLedger(
508 LedgerIndex seq,
509 std::set<uint256> const& enabled,
510 majorityAmendments_t const& majority) override;
511
512 void
513 trustChanged(hash_set<PublicKey> const& allTrusted) override;
514
516 doValidation(std::set<uint256> const& enabledAmendments) const override;
517
519 getDesired() const override;
520
522 doVoting(
523 Rules const& rules,
524 NetClock::time_point closeTime,
525 std::set<uint256> const& enabledAmendments,
526 majorityAmendments_t const& majorityAmendments,
527 std::vector<std::shared_ptr<STValidation>> const& validations) override;
528};
529
530//------------------------------------------------------------------------------
531
532AmendmentTableImpl::AmendmentTableImpl(
533 Application& app,
534 std::chrono::seconds majorityTime,
535 std::vector<FeatureInfo> const& supported,
536 Section const& enabled,
537 Section const& vetoed,
538 beast::Journal journal)
539 : lastUpdateSeq_(0)
540 , majorityTime_(majorityTime)
541 , unsupportedEnabled_(false)
542 , j_(journal)
543 , db_(app.getWalletDB())
544{
546
547 // Find out if the FeatureVotes table exists in WalletDB
548 bool const featureVotesExist = [this]() {
549 auto db = db_.checkoutDb();
550 return createFeatureVotes(*db);
551 }();
552
553 // Parse supported amendments
554 for (auto const& [name, amendment, votebehavior] : supported)
555 {
556 AmendmentState& s = add(amendment, lock);
557
558 s.name = name;
559 s.supported = true;
560 switch (votebehavior)
561 {
564 break;
565
568 break;
569
572 break;
573 }
574
575 JLOG(j_.debug()) << "Amendment " << amendment << " (" << s.name
576 << ") is supported and will be "
577 << (s.vote == AmendmentVote::up ? "up" : "down")
578 << " voted by default if not enabled on the ledger.";
579 }
580
581 hash_set<uint256> detect_conflict;
582 // Parse enabled amendments from config
583 for (std::pair<uint256, std::string> const& a : parseSection(enabled))
584 {
585 if (featureVotesExist)
586 { // If the table existed, warn about duplicate config info
587 JLOG(j_.warn()) << "[amendments] section in config file ignored"
588 " in favor of data in db/wallet.db.";
589 break;
590 }
591 else
592 { // Otherwise transfer config data into the table
593 detect_conflict.insert(a.first);
594 persistVote(a.first, a.second, AmendmentVote::up);
595 }
596 }
597
598 // Parse vetoed amendments from config
599 for (auto const& a : parseSection(vetoed))
600 {
601 if (featureVotesExist)
602 { // If the table existed, warn about duplicate config info
603 JLOG(j_.warn())
604 << "[veto_amendments] section in config file ignored"
605 " in favor of data in db/wallet.db.";
606 break;
607 }
608 else
609 { // Otherwise transfer config data into the table
610 if (detect_conflict.count(a.first) == 0)
611 {
612 persistVote(a.first, a.second, AmendmentVote::down);
613 }
614 else
615 {
616 JLOG(j_.warn())
617 << "[veto_amendments] section in config has amendment "
618 << '(' << a.first << ", " << a.second
619 << ") both [veto_amendments] and [amendments].";
620 }
621 }
622 }
623
624 // Read amendment votes from wallet.db
625 auto db = db_.checkoutDb();
627 *db,
628 [&](boost::optional<std::string> amendment_hash,
629 boost::optional<std::string> amendment_name,
630 boost::optional<AmendmentVote> vote) {
631 uint256 amend_hash;
632 if (!amendment_hash || !amendment_name || !vote)
633 {
634 // These fields should never have nulls, but check
635 Throw<std::runtime_error>(
636 "Invalid FeatureVotes row in wallet.db");
637 }
638 if (!amend_hash.parseHex(*amendment_hash))
639 {
640 Throw<std::runtime_error>(
641 "Invalid amendment ID '" + *amendment_hash +
642 " in wallet.db");
643 }
644 if (*vote == AmendmentVote::down)
645 {
646 // Unknown amendments are effectively vetoed already
647 if (auto s = get(amend_hash, lock))
648 {
649 JLOG(j_.info()) << "Amendment {" << *amendment_name << ", "
650 << amend_hash << "} is downvoted.";
651 if (!amendment_name->empty())
652 s->name = *amendment_name;
653 // An obsolete amendment's vote can never be changed
654 if (s->vote != AmendmentVote::obsolete)
655 s->vote = *vote;
656 }
657 }
658 else // up-vote
659 {
660 AmendmentState& s = add(amend_hash, lock);
661
662 JLOG(j_.debug()) << "Amendment {" << *amendment_name << ", "
663 << amend_hash << "} is upvoted.";
664 if (!amendment_name->empty())
665 s.name = *amendment_name;
666 // An obsolete amendment's vote can never be changed
668 s.vote = *vote;
669 }
670 });
671}
672
675 uint256 const& amendmentHash,
677{
678 // call with the mutex held
679 return amendmentMap_[amendmentHash];
680}
681
684 uint256 const& amendmentHash,
685 std::lock_guard<std::mutex> const& lock)
686{
687 // Forward to the const version of get.
688 return const_cast<AmendmentState*>(
689 std::as_const(*this).get(amendmentHash, lock));
690}
691
692AmendmentState const*
694 uint256 const& amendmentHash,
695 std::lock_guard<std::mutex> const&) const
696{
697 // call with the mutex held
698 auto ret = amendmentMap_.find(amendmentHash);
699
700 if (ret == amendmentMap_.end())
701 return nullptr;
702
703 return &ret->second;
704}
705
708{
710
711 for (auto const& e : amendmentMap_)
712 {
713 if (name == e.second.name)
714 return e.first;
715 }
716
717 return {};
718}
719
720void
722 uint256 const& amendment,
723 std::string const& name,
724 AmendmentVote vote) const
725{
726 XRPL_ASSERT(
728 "ripple::AmendmentTableImpl::persistVote : valid vote input");
729 auto db = db_.checkoutDb();
730 voteAmendment(*db, amendment, name, vote);
731}
732
733bool
735{
737 AmendmentState& s = add(amendment, lock);
738
739 if (s.vote != AmendmentVote::up)
740 return false;
742 persistVote(amendment, s.name, s.vote);
743 return true;
744}
745
746bool
748{
750 AmendmentState* const s = get(amendment, lock);
751
752 if (!s || s->vote != AmendmentVote::down)
753 return false;
755 persistVote(amendment, s->name, s->vote);
756 return true;
757}
758
759bool
761{
763 AmendmentState& s = add(amendment, lock);
764
765 if (s.enabled)
766 return false;
767
768 s.enabled = true;
769
770 if (!s.supported)
771 {
772 JLOG(j_.error()) << "Unsupported amendment " << amendment
773 << " activated.";
774 unsupportedEnabled_ = true;
775 }
776
777 return true;
778}
779
780bool
782{
784 AmendmentState const* s = get(amendment, lock);
785 return s && s->enabled;
786}
787
788bool
790{
792 AmendmentState const* s = get(amendment, lock);
793 return s && s->supported;
794}
795
796bool
798{
800 return unsupportedEnabled_;
801}
802
805{
808}
809
812{
813 // Get the list of amendments we support and do not
814 // veto, but that are not already enabled
815 std::vector<uint256> amendments;
816
817 {
819 amendments.reserve(amendmentMap_.size());
820 for (auto const& e : amendmentMap_)
821 {
822 if (e.second.supported && e.second.vote == AmendmentVote::up &&
823 (enabled.count(e.first) == 0))
824 {
825 amendments.push_back(e.first);
826 JLOG(j_.info()) << "Voting for amendment " << e.second.name;
827 }
828 }
829 }
830
831 if (!amendments.empty())
832 std::sort(amendments.begin(), amendments.end());
833
834 return amendments;
835}
836
839{
840 // Get the list of amendments we support and do not veto
841 return doValidation({});
842}
843
846 Rules const& rules,
847 NetClock::time_point closeTime,
848 std::set<uint256> const& enabledAmendments,
849 majorityAmendments_t const& majorityAmendments,
851{
852 JLOG(j_.trace()) << "voting at " << closeTime.time_since_epoch().count()
853 << ": " << enabledAmendments.size() << ", "
854 << majorityAmendments.size() << ", " << valSet.size();
855
857
858 // Keep a record of the votes we received.
859 previousTrustedVotes_.recordVotes(rules, valSet, closeTime, j_, lock);
860
861 // Tally the most recent votes.
862 auto vote =
863 std::make_unique<AmendmentSet>(rules, previousTrustedVotes_, lock);
864 JLOG(j_.debug()) << "Counted votes from " << vote->trustedValidations()
865 << " valid trusted validations, threshold is: "
866 << vote->threshold();
867
868 // Map of amendments to the action to be taken for each one. The action is
869 // the value of the flags in the pseudo-transaction
871
872 // process all amendments we know of
873 for (auto const& entry : amendmentMap_)
874 {
875 if (enabledAmendments.contains(entry.first))
876 {
877 JLOG(j_.trace()) << entry.first << ": amendment already enabled";
878
879 continue;
880 }
881
882 bool const hasValMajority = vote->passes(entry.first);
883
884 auto const majorityTime = [&]() -> std::optional<NetClock::time_point> {
885 auto const it = majorityAmendments.find(entry.first);
886 if (it != majorityAmendments.end())
887 return it->second;
888 return std::nullopt;
889 }();
890
891 bool const hasLedgerMajority = majorityTime.has_value();
892
893 auto const logStr = [&entry, &vote]() {
895 ss << entry.first << " (" << entry.second.name << ") has "
896 << vote->votes(entry.first) << " votes";
897 return ss.str();
898 }();
899
900 if (hasValMajority && !hasLedgerMajority &&
901 entry.second.vote == AmendmentVote::up)
902 {
903 // Ledger says no majority, validators say yes, and voting yes
904 // locally
905 JLOG(j_.debug()) << logStr << ": amendment got majority";
906 actions[entry.first] = tfGotMajority;
907 }
908 else if (!hasValMajority && hasLedgerMajority)
909 {
910 // Ledger says majority, validators say no
911 JLOG(j_.debug()) << logStr << ": amendment lost majority";
912 actions[entry.first] = tfLostMajority;
913 }
914 else if (
915 hasLedgerMajority &&
916 ((*majorityTime + majorityTime_) <= closeTime) &&
917 entry.second.vote == AmendmentVote::up)
918 {
919 // Ledger says majority held
920 JLOG(j_.debug()) << logStr << ": amendment majority held";
921 actions[entry.first] = 0;
922 }
923 // Logging only below this point
924 else if (hasValMajority && hasLedgerMajority)
925 {
926 JLOG(j_.debug())
927 << logStr
928 << ": amendment holding majority, waiting to be enabled";
929 }
930 else if (!hasValMajority)
931 {
932 JLOG(j_.debug()) << logStr << ": amendment does not have majority";
933 }
934 }
935
936 // Stash for reporting
937 lastVote_ = std::move(vote);
938 return actions;
939}
940
941bool
943{
945
946 // Is there a ledger in which an amendment could have been enabled
947 // between these two ledger sequences?
948
949 return ((ledgerSeq - 1) / 256) != ((lastUpdateSeq_ - 1) / 256);
950}
951
952void
954 LedgerIndex ledgerSeq,
955 std::set<uint256> const& enabled,
956 majorityAmendments_t const& majority)
957{
958 for (auto& e : enabled)
959 enable(e);
960
962
963 // Remember the ledger sequence of this update.
964 lastUpdateSeq_ = ledgerSeq;
965
966 // Since we have the whole list in `majority`, reset the time flag, even
967 // if it's currently set. If it's not set when the loop is done, then any
968 // prior unknown amendments have lost majority.
970 for (auto const& [hash, time] : majority)
971 {
972 AmendmentState& s = add(hash, lock);
973
974 if (s.enabled)
975 continue;
976
977 if (!s.supported)
978 {
979 JLOG(j_.info()) << "Unsupported amendment " << hash
980 << " reached majority at " << to_string(time);
983 }
984 }
987}
988
989void
991{
993 previousTrustedVotes_.trustChanged(allTrusted, lock);
994}
995
996void
998 Json::Value& v,
999 const uint256& id,
1000 const AmendmentState& fs,
1001 bool isAdmin,
1002 std::lock_guard<std::mutex> const&) const
1003{
1004 if (!fs.name.empty())
1005 v[jss::name] = fs.name;
1006
1007 v[jss::supported] = fs.supported;
1008 if (!fs.enabled && isAdmin)
1009 {
1010 if (fs.vote == AmendmentVote::obsolete)
1011 v[jss::vetoed] = "Obsolete";
1012 else
1013 v[jss::vetoed] = fs.vote == AmendmentVote::down;
1014 }
1015 v[jss::enabled] = fs.enabled;
1016
1017 if (!fs.enabled && lastVote_ && isAdmin)
1018 {
1019 auto const votesTotal = lastVote_->trustedValidations();
1020 auto const votesNeeded = lastVote_->threshold();
1021 auto const votesFor = lastVote_->votes(id);
1022
1023 v[jss::count] = votesFor;
1024 v[jss::validations] = votesTotal;
1025
1026 if (votesNeeded)
1027 v[jss::threshold] = votesNeeded;
1028 }
1029}
1030
1033{
1035 {
1036 std::lock_guard lock(mutex_);
1037 for (auto const& e : amendmentMap_)
1038 {
1039 injectJson(
1040 ret[to_string(e.first)] = Json::objectValue,
1041 e.first,
1042 e.second,
1043 isAdmin,
1044 lock);
1045 }
1046 }
1047 return ret;
1048}
1049
1051AmendmentTableImpl::getJson(uint256 const& amendmentID, bool isAdmin) const
1052{
1054
1055 {
1056 std::lock_guard lock(mutex_);
1057 AmendmentState const* a = get(amendmentID, lock);
1058 if (a)
1059 {
1060 Json::Value& jAmendment =
1061 (ret[to_string(amendmentID)] = Json::objectValue);
1062 injectJson(jAmendment, amendmentID, *a, isAdmin, lock);
1063 }
1064 }
1065
1066 return ret;
1067}
1068
1071 Application& app,
1072 std::chrono::seconds majorityTime,
1074 Section const& enabled,
1075 Section const& vetoed,
1076 beast::Journal journal)
1077{
1078 return std::make_unique<AmendmentTableImpl>(
1079 app, majorityTime, supported, enabled, vetoed, journal);
1080}
1081
1082} // 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)