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
25#include <xrpl/protocol/Feature.h>
26#include <xrpl/protocol/STValidation.h>
27#include <xrpl/protocol/TxFlags.h>
28#include <xrpl/protocol/jss.h>
29
30#include <boost/algorithm/string.hpp>
31#include <boost/format.hpp>
32#include <boost/range/adaptor/transformed.hpp>
33#include <boost/regex.hpp>
34
35#include <algorithm>
36#include <mutex>
37
38namespace ripple {
39
41parseSection(Section const& section)
42{
43 static boost::regex const re1(
44 "^" // start of line
45 "(?:\\s*)" // whitespace (optional)
46 "([abcdefABCDEF0-9]{64})" // <hexadecimal amendment ID>
47 "(?:\\s+)" // whitespace
48 "(\\S+)" // <description>
49 ,
50 boost::regex_constants::optimize);
51
53
54 for (auto const& line : section.lines())
55 {
56 boost::smatch match;
57
58 if (!boost::regex_match(line, match, re1))
59 Throw<std::runtime_error>(
60 "Invalid entry '" + line + "' in [" + section.name() + "]");
61
62 uint256 id;
63
64 if (!id.parseHex(match[1]))
65 Throw<std::runtime_error>(
66 "Invalid amendment ID '" + match[1] + "' in [" +
67 section.name() + "]");
68
69 names.push_back(std::make_pair(id, match[2]));
70 }
71
72 return names;
73}
74
94{
95private:
96 // Associates each trusted validator with the last votes we saw from them
97 // and an expiration for that record.
109
110public:
111 TrustedVotes() = default;
112 TrustedVotes(TrustedVotes const& rhs) = delete;
114 operator=(TrustedVotes const& rhs) = delete;
115
116 // Called when the list of trusted validators changes.
117 //
118 // Call with AmendmentTable::mutex_ locked.
119 void
121 hash_set<PublicKey> const& allTrusted,
122 std::lock_guard<std::mutex> const& lock)
123 {
124 decltype(recordedVotes_) newRecordedVotes;
125 newRecordedVotes.reserve(allTrusted.size());
126
127 // Make sure every PublicKey in allTrusted is represented in
128 // recordedVotes_. Also make sure recordedVotes_ contains
129 // no additional PublicKeys.
130 for (auto& trusted : allTrusted)
131 {
132 if (recordedVotes_.contains(trusted))
133 {
134 // Preserve this validator's previously saved voting state.
135 newRecordedVotes.insert(recordedVotes_.extract(trusted));
136 }
137 else
138 {
139 // New validators have a starting position of no on everything.
140 // Add the entry with an empty vector and unseated timeout.
141 newRecordedVotes[trusted];
142 }
143 }
144 // The votes of any no-longer-trusted validators will be destroyed
145 // when changedTrustedVotes goes out of scope.
146 recordedVotes_.swap(newRecordedVotes);
147 }
148
149 // Called when we receive the latest votes.
150 //
151 // Call with AmendmentTable::mutex_ locked.
152 void
154 Rules const& rules,
156 NetClock::time_point const closeTime,
158 std::lock_guard<std::mutex> const& lock)
159 {
160 // When we get an STValidation we save the upVotes it contains, but
161 // we also set an expiration for those upVotes. The following constant
162 // controls the timeout.
163 //
164 // There really is no "best" timeout to choose for when we finally
165 // lose confidence that we know how a validator is voting. But part
166 // of the point of recording validator votes is to avoid flapping of
167 // amendment votes. A 24h timeout says that we will change the local
168 // record of a validator's vote to "no" 24h after the last vote seen
169 // from that validator. So flapping due to that validator being off
170 // line will happen less frequently than every 24 hours.
171 using namespace std::chrono_literals;
172 static constexpr NetClock::duration expiresAfter = 24h;
173
174 auto const newTimeout = closeTime + expiresAfter;
175
176 // Walk all validations and replace previous votes from trusted
177 // validators with these newest votes.
178 for (auto const& val : valSet)
179 {
180 auto const pkHuman =
181 toBase58(TokenType::NodePublic, val->getSignerPublic());
182 // If this validation comes from one of our trusted validators...
183 if (auto const iter = recordedVotes_.find(val->getSignerPublic());
184 iter != recordedVotes_.end())
185 {
186 iter->second.timeout = newTimeout;
187 if (val->isFieldPresent(sfAmendments))
188 {
189 auto const& choices = val->getFieldV256(sfAmendments);
190 iter->second.upVotes.assign(choices.begin(), choices.end());
191 JLOG(j.debug())
192 << "recordVotes: Validation from trusted " << pkHuman
193 << " has " << choices.size() << " amendment votes: "
194 << boost::algorithm::join(
195 iter->second.upVotes |
196 boost::adaptors::transformed(
197 to_string<256, void>),
198 ", ");
199 // TODO: Maybe transform using to_short_string once #5126 is
200 // merged
201 //
202 // iter->second.upVotes |
203 // boost::adaptors::transformed(to_short_string<256, void>)
204 }
205 else
206 {
207 // This validator does not upVote any amendments right now.
208 iter->second.upVotes.clear();
209 JLOG(j.debug()) << "recordVotes: Validation from trusted "
210 << pkHuman << " has no amendment votes.";
211 }
212 }
213 else
214 {
215 JLOG(j.debug())
216 << "recordVotes: Ignoring validation from untrusted "
217 << pkHuman;
218 }
219 }
220
221 // Now remove any expired records from recordedVotes_.
223 recordedVotes_.begin(),
224 recordedVotes_.end(),
225 [&closeTime, newTimeout, &j](
226 decltype(recordedVotes_)::value_type& votes) {
227 auto const pkHuman =
228 toBase58(TokenType::NodePublic, votes.first);
229 if (!votes.second.timeout)
230 {
231 XRPL_ASSERT(
232 votes.second.upVotes.empty(),
233 "ripple::TrustedVotes::recordVotes : received no "
234 "upvotes");
235 JLOG(j.debug())
236 << "recordVotes: Have not received any "
237 "amendment votes from "
238 << pkHuman << " since last timeout or startup";
239 }
240 else if (closeTime > votes.second.timeout)
241 {
242 JLOG(j.debug())
243 << "recordVotes: Timeout: Clearing votes from "
244 << pkHuman;
245 votes.second.timeout.reset();
246 votes.second.upVotes.clear();
247 }
248 else if (votes.second.timeout != newTimeout)
249 {
250 XRPL_ASSERT(
251 votes.second.timeout < newTimeout,
252 "ripple::TrustedVotes::recordVotes : votes not "
253 "expired");
254 using namespace std::chrono;
255 auto const age = duration_cast<minutes>(
256 newTimeout - *votes.second.timeout);
257 JLOG(j.debug()) << "recordVotes: Using " << age.count()
258 << "min old cached votes from " << pkHuman;
259 }
260 });
261 }
262
263 // Return the information needed by AmendmentSet to determine votes.
264 //
265 // Call with AmendmentTable::mutex_ locked.
267 getVotes(Rules const& rules, std::lock_guard<std::mutex> const& lock) const
268 {
270 int available = 0;
271 for (auto& validatorVotes : recordedVotes_)
272 {
273 XRPL_ASSERT(
274 validatorVotes.second.timeout ||
275 validatorVotes.second.upVotes.empty(),
276 "ripple::TrustedVotes::getVotes : valid votes");
277 if (validatorVotes.second.timeout)
278 ++available;
279 for (uint256 const& amendment : validatorVotes.second.upVotes)
280 {
281 ret[amendment] += 1;
282 }
283 }
284 return {available, ret};
285 }
286};
287
293{
295 AmendmentVote vote = AmendmentVote::down;
296
302 bool enabled = false;
303
305 bool supported = false;
306
309
310 explicit AmendmentState() = default;
311};
312
315{
316private:
317 // How many yes votes each amendment received
319 Rules const& rules_;
320 // number of trusted validations
321 int trustedValidations_ = 0;
322 // number of votes needed
323 int threshold_ = 0;
324
325 void
326 computeThreshold(int trustedValidations, Rules const& rules)
327 {
328 threshold_ = !rules_.enabled(fixAmendmentMajorityCalc)
329 ? std::max(
330 1L,
331 static_cast<long>(
332 (trustedValidations_ *
335 : std::max(
336 1L,
337 static_cast<long>(
338 (trustedValidations_ *
341 }
342
343public:
345 Rules const& rules,
346 TrustedVotes const& trustedVotes,
347 std::lock_guard<std::mutex> const& lock)
348 : rules_(rules)
349 {
350 // process validations for ledger before flag ledger.
351 auto [trustedCount, newVotes] = trustedVotes.getVotes(rules, lock);
352
353 trustedValidations_ = trustedCount;
354 votes_.swap(newVotes);
355
356 computeThreshold(trustedValidations_, rules);
357 }
358
359 bool
360 passes(uint256 const& amendment) const
361 {
362 auto const& it = votes_.find(amendment);
363
364 if (it == votes_.end())
365 return false;
366
367 // Before this fix, it was possible for an amendment to activate with a
368 // percentage slightly less than 80% because we compared for "greater
369 // than or equal to" instead of strictly "greater than".
370 // One validator is an exception, otherwise it is not possible
371 // to gain majority.
372 if (!rules_.enabled(fixAmendmentMajorityCalc) ||
373 trustedValidations_ == 1)
374 return it->second >= threshold_;
375
376 return it->second > threshold_;
377 }
378
379 int
380 votes(uint256 const& amendment) const
381 {
382 auto const& it = votes_.find(amendment);
383
384 if (it == votes_.end())
385 return 0;
386
387 return it->second;
388 }
389
390 int
392 {
393 return trustedValidations_;
394 }
395
396 int
397 threshold() const
398 {
399 return threshold_;
400 }
401};
402
403//------------------------------------------------------------------------------
404
412{
413private:
415
418
419 // Record of the last votes seen from trusted validators.
421
422 // Time that an amendment must hold a majority for
424
425 // The results of the last voting round - may be empty if
426 // we haven't participated in one yet.
428
429 // True if an unsupported amendment is enabled
431
432 // Unset if no unsupported amendments reach majority,
433 // else set to the earliest time an unsupported amendment
434 // will be enabled.
436
438
439 // Database which persists veto/unveto vote
441
442 // Finds or creates state. Must be called with mutex_ locked.
444 add(uint256 const& amendment, std::lock_guard<std::mutex> const& lock);
445
446 // Finds existing state. Must be called with mutex_ locked.
448 get(uint256 const& amendment, std::lock_guard<std::mutex> const& lock);
449
450 AmendmentState const*
451 get(uint256 const& amendment,
452 std::lock_guard<std::mutex> const& lock) const;
453
454 // Injects amendment json into v. Must be called with mutex_ locked.
455 void
456 injectJson(
457 Json::Value& v,
458 uint256 const& amendment,
459 AmendmentState const& state,
460 bool isAdmin,
461 std::lock_guard<std::mutex> const& lock) const;
462
463 void
464 persistVote(
465 uint256 const& amendment,
466 std::string const& name,
467 AmendmentVote vote) const;
468
469public:
471 Application& app,
472 std::chrono::seconds majorityTime,
473 std::vector<FeatureInfo> const& supported,
474 Section const& enabled,
475 Section const& vetoed,
476 beast::Journal journal);
477
478 uint256
479 find(std::string const& name) const override;
480
481 bool
482 veto(uint256 const& amendment) override;
483 bool
484 unVeto(uint256 const& amendment) override;
485
486 bool
487 enable(uint256 const& amendment) override;
488
489 bool
490 isEnabled(uint256 const& amendment) const override;
491 bool
492 isSupported(uint256 const& amendment) const override;
493
494 bool
495 hasUnsupportedEnabled() const override;
496
498 firstUnsupportedExpected() const override;
499
501 getJson(bool isAdmin) const override;
503 getJson(uint256 const&, bool isAdmin) const override;
504
505 bool
506 needValidatedLedger(LedgerIndex seq) const override;
507
508 void
509 doValidatedLedger(
510 LedgerIndex seq,
511 std::set<uint256> const& enabled,
512 majorityAmendments_t const& majority) override;
513
514 void
515 trustChanged(hash_set<PublicKey> const& allTrusted) override;
516
518 doValidation(std::set<uint256> const& enabledAmendments) const override;
519
521 getDesired() const override;
522
524 doVoting(
525 Rules const& rules,
526 NetClock::time_point closeTime,
527 std::set<uint256> const& enabledAmendments,
528 majorityAmendments_t const& majorityAmendments,
529 std::vector<std::shared_ptr<STValidation>> const& validations) override;
530};
531
532//------------------------------------------------------------------------------
533
534AmendmentTableImpl::AmendmentTableImpl(
535 Application& app,
536 std::chrono::seconds majorityTime,
537 std::vector<FeatureInfo> const& supported,
538 Section const& enabled,
539 Section const& vetoed,
540 beast::Journal journal)
541 : lastUpdateSeq_(0)
542 , majorityTime_(majorityTime)
543 , unsupportedEnabled_(false)
544 , j_(journal)
545 , db_(app.getWalletDB())
546{
548
549 // Find out if the FeatureVotes table exists in WalletDB
550 bool const featureVotesExist = [this]() {
551 auto db = db_.checkoutDb();
552 return createFeatureVotes(*db);
553 }();
554
555 // Parse supported amendments
556 for (auto const& [name, amendment, votebehavior] : supported)
557 {
558 AmendmentState& s = add(amendment, lock);
559
560 s.name = name;
561 s.supported = true;
562 switch (votebehavior)
563 {
566 break;
567
570 break;
571
574 break;
575 }
576
577 JLOG(j_.debug()) << "Amendment " << amendment << " (" << s.name
578 << ") is supported and will be "
579 << (s.vote == AmendmentVote::up ? "up" : "down")
580 << " voted by default if not enabled on the ledger.";
581 }
582
583 hash_set<uint256> detect_conflict;
584 // Parse enabled amendments from config
585 for (std::pair<uint256, std::string> const& a : parseSection(enabled))
586 {
587 if (featureVotesExist)
588 { // If the table existed, warn about duplicate config info
589 JLOG(j_.warn()) << "[amendments] section in config file ignored"
590 " in favor of data in db/wallet.db.";
591 break;
592 }
593 else
594 { // Otherwise transfer config data into the table
595 detect_conflict.insert(a.first);
596 persistVote(a.first, a.second, AmendmentVote::up);
597 }
598 }
599
600 // Parse vetoed amendments from config
601 for (auto const& a : parseSection(vetoed))
602 {
603 if (featureVotesExist)
604 { // If the table existed, warn about duplicate config info
605 JLOG(j_.warn())
606 << "[veto_amendments] section in config file ignored"
607 " in favor of data in db/wallet.db.";
608 break;
609 }
610 else
611 { // Otherwise transfer config data into the table
612 if (detect_conflict.count(a.first) == 0)
613 {
614 persistVote(a.first, a.second, AmendmentVote::down);
615 }
616 else
617 {
618 JLOG(j_.warn())
619 << "[veto_amendments] section in config has amendment "
620 << '(' << a.first << ", " << a.second
621 << ") both [veto_amendments] and [amendments].";
622 }
623 }
624 }
625
626 // Read amendment votes from wallet.db
627 auto db = db_.checkoutDb();
629 *db,
630 [&](boost::optional<std::string> amendment_hash,
631 boost::optional<std::string> amendment_name,
632 boost::optional<AmendmentVote> vote) {
633 uint256 amend_hash;
634 if (!amendment_hash || !amendment_name || !vote)
635 {
636 // These fields should never have nulls, but check
637 Throw<std::runtime_error>(
638 "Invalid FeatureVotes row in wallet.db");
639 }
640 if (!amend_hash.parseHex(*amendment_hash))
641 {
642 Throw<std::runtime_error>(
643 "Invalid amendment ID '" + *amendment_hash +
644 " in wallet.db");
645 }
646 if (*vote == AmendmentVote::down)
647 {
648 // Unknown amendments are effectively vetoed already
649 if (auto s = get(amend_hash, lock))
650 {
651 JLOG(j_.info()) << "Amendment {" << *amendment_name << ", "
652 << amend_hash << "} is downvoted.";
653 if (!amendment_name->empty())
654 s->name = *amendment_name;
655 // An obsolete amendment's vote can never be changed
656 if (s->vote != AmendmentVote::obsolete)
657 s->vote = *vote;
658 }
659 }
660 else // up-vote
661 {
662 AmendmentState& s = add(amend_hash, lock);
663
664 JLOG(j_.debug()) << "Amendment {" << *amendment_name << ", "
665 << amend_hash << "} is upvoted.";
666 if (!amendment_name->empty())
667 s.name = *amendment_name;
668 // An obsolete amendment's vote can never be changed
670 s.vote = *vote;
671 }
672 });
673}
674
677 uint256 const& amendmentHash,
679{
680 // call with the mutex held
681 return amendmentMap_[amendmentHash];
682}
683
686 uint256 const& amendmentHash,
687 std::lock_guard<std::mutex> const& lock)
688{
689 // Forward to the const version of get.
690 return const_cast<AmendmentState*>(
691 std::as_const(*this).get(amendmentHash, lock));
692}
693
694AmendmentState const*
696 uint256 const& amendmentHash,
697 std::lock_guard<std::mutex> const&) const
698{
699 // call with the mutex held
700 auto ret = amendmentMap_.find(amendmentHash);
701
702 if (ret == amendmentMap_.end())
703 return nullptr;
704
705 return &ret->second;
706}
707
710{
712
713 for (auto const& e : amendmentMap_)
714 {
715 if (name == e.second.name)
716 return e.first;
717 }
718
719 return {};
720}
721
722void
724 uint256 const& amendment,
725 std::string const& name,
726 AmendmentVote vote) const
727{
728 XRPL_ASSERT(
730 "ripple::AmendmentTableImpl::persistVote : valid vote input");
731 auto db = db_.checkoutDb();
732 voteAmendment(*db, amendment, name, vote);
733}
734
735bool
737{
739 AmendmentState& s = add(amendment, lock);
740
741 if (s.vote != AmendmentVote::up)
742 return false;
744 persistVote(amendment, s.name, s.vote);
745 return true;
746}
747
748bool
750{
752 AmendmentState* const s = get(amendment, lock);
753
754 if (!s || s->vote != AmendmentVote::down)
755 return false;
757 persistVote(amendment, s->name, s->vote);
758 return true;
759}
760
761bool
763{
765 AmendmentState& s = add(amendment, lock);
766
767 if (s.enabled)
768 return false;
769
770 s.enabled = true;
771
772 if (!s.supported)
773 {
774 JLOG(j_.error()) << "Unsupported amendment " << amendment
775 << " activated.";
776 unsupportedEnabled_ = true;
777 }
778
779 return true;
780}
781
782bool
784{
786 AmendmentState const* s = get(amendment, lock);
787 return s && s->enabled;
788}
789
790bool
792{
794 AmendmentState const* s = get(amendment, lock);
795 return s && s->supported;
796}
797
798bool
804
811
814{
815 // Get the list of amendments we support and do not
816 // veto, but that are not already enabled
817 std::vector<uint256> amendments;
818
819 {
821 amendments.reserve(amendmentMap_.size());
822 for (auto const& e : amendmentMap_)
823 {
824 if (e.second.supported && e.second.vote == AmendmentVote::up &&
825 (enabled.count(e.first) == 0))
826 {
827 amendments.push_back(e.first);
828 JLOG(j_.info()) << "Voting for amendment " << e.second.name;
829 }
830 }
831 }
832
833 if (!amendments.empty())
834 std::sort(amendments.begin(), amendments.end());
835
836 return amendments;
837}
838
841{
842 // Get the list of amendments we support and do not veto
843 return doValidation({});
844}
845
848 Rules const& rules,
849 NetClock::time_point closeTime,
850 std::set<uint256> const& enabledAmendments,
851 majorityAmendments_t const& majorityAmendments,
853{
854 JLOG(j_.trace()) << "voting at " << closeTime.time_since_epoch().count()
855 << ": " << enabledAmendments.size() << ", "
856 << majorityAmendments.size() << ", " << valSet.size();
857
859
860 // Keep a record of the votes we received.
861 previousTrustedVotes_.recordVotes(rules, valSet, closeTime, j_, lock);
862
863 // Tally the most recent votes.
864 auto vote =
866 JLOG(j_.debug()) << "Counted votes from " << vote->trustedValidations()
867 << " valid trusted validations, threshold is: "
868 << vote->threshold();
869
870 // Map of amendments to the action to be taken for each one. The action is
871 // the value of the flags in the pseudo-transaction
873
874 // process all amendments we know of
875 for (auto const& entry : amendmentMap_)
876 {
877 if (enabledAmendments.contains(entry.first))
878 {
879 JLOG(j_.trace()) << entry.first << ": amendment already enabled";
880
881 continue;
882 }
883
884 bool const hasValMajority = vote->passes(entry.first);
885
886 auto const majorityTime = [&]() -> std::optional<NetClock::time_point> {
887 auto const it = majorityAmendments.find(entry.first);
888 if (it != majorityAmendments.end())
889 return it->second;
890 return std::nullopt;
891 }();
892
893 bool const hasLedgerMajority = majorityTime.has_value();
894
895 auto const logStr = [&entry, &vote]() {
897 ss << entry.first << " (" << entry.second.name << ") has "
898 << vote->votes(entry.first) << " votes";
899 return ss.str();
900 }();
901
902 if (hasValMajority && !hasLedgerMajority &&
903 entry.second.vote == AmendmentVote::up)
904 {
905 // Ledger says no majority, validators say yes, and voting yes
906 // locally
907 JLOG(j_.debug()) << logStr << ": amendment got majority";
908 actions[entry.first] = tfGotMajority;
909 }
910 else if (!hasValMajority && hasLedgerMajority)
911 {
912 // Ledger says majority, validators say no
913 JLOG(j_.debug()) << logStr << ": amendment lost majority";
914 actions[entry.first] = tfLostMajority;
915 }
916 else if (
917 hasLedgerMajority &&
918 ((*majorityTime + majorityTime_) <= closeTime) &&
919 entry.second.vote == AmendmentVote::up)
920 {
921 // Ledger says majority held
922 JLOG(j_.debug()) << logStr << ": amendment majority held";
923 actions[entry.first] = 0;
924 }
925 // Logging only below this point
926 else if (hasValMajority && hasLedgerMajority)
927 {
928 JLOG(j_.debug())
929 << logStr
930 << ": amendment holding majority, waiting to be enabled";
931 }
932 else if (!hasValMajority)
933 {
934 JLOG(j_.debug()) << logStr << ": amendment does not have majority";
935 }
936 }
937
938 // Stash for reporting
939 lastVote_ = std::move(vote);
940 return actions;
941}
942
943bool
945{
947
948 // Is there a ledger in which an amendment could have been enabled
949 // between these two ledger sequences?
950
951 return ((ledgerSeq - 1) / 256) != ((lastUpdateSeq_ - 1) / 256);
952}
953
954void
956 LedgerIndex ledgerSeq,
957 std::set<uint256> const& enabled,
958 majorityAmendments_t const& majority)
959{
960 for (auto& e : enabled)
961 enable(e);
962
964
965 // Remember the ledger sequence of this update.
966 lastUpdateSeq_ = ledgerSeq;
967
968 // Since we have the whole list in `majority`, reset the time flag, even
969 // if it's currently set. If it's not set when the loop is done, then any
970 // prior unknown amendments have lost majority.
972 for (auto const& [hash, time] : majority)
973 {
974 AmendmentState& s = add(hash, lock);
975
976 if (s.enabled)
977 continue;
978
979 if (!s.supported)
980 {
981 JLOG(j_.info()) << "Unsupported amendment " << hash
982 << " reached majority at " << to_string(time);
985 }
986 }
989}
990
991void
997
998void
1000 Json::Value& v,
1001 uint256 const& id,
1002 AmendmentState const& fs,
1003 bool isAdmin,
1004 std::lock_guard<std::mutex> const&) const
1005{
1006 if (!fs.name.empty())
1007 v[jss::name] = fs.name;
1008
1009 v[jss::supported] = fs.supported;
1010 if (!fs.enabled && isAdmin)
1011 {
1012 if (fs.vote == AmendmentVote::obsolete)
1013 v[jss::vetoed] = "Obsolete";
1014 else
1015 v[jss::vetoed] = fs.vote == AmendmentVote::down;
1016 }
1017 v[jss::enabled] = fs.enabled;
1018
1019 if (!fs.enabled && lastVote_ && isAdmin)
1020 {
1021 auto const votesTotal = lastVote_->trustedValidations();
1022 auto const votesNeeded = lastVote_->threshold();
1023 auto const votesFor = lastVote_->votes(id);
1024
1025 v[jss::count] = votesFor;
1026 v[jss::validations] = votesTotal;
1027
1028 if (votesNeeded)
1029 v[jss::threshold] = votesNeeded;
1030 }
1031}
1032
1035{
1037 {
1038 std::lock_guard lock(mutex_);
1039 for (auto const& e : amendmentMap_)
1040 {
1041 injectJson(
1042 ret[to_string(e.first)] = Json::objectValue,
1043 e.first,
1044 e.second,
1045 isAdmin,
1046 lock);
1047 }
1048 }
1049 return ret;
1050}
1051
1053AmendmentTableImpl::getJson(uint256 const& amendmentID, bool isAdmin) const
1054{
1056
1057 {
1058 std::lock_guard lock(mutex_);
1059 AmendmentState const* a = get(amendmentID, lock);
1060 if (a)
1061 {
1062 Json::Value& jAmendment =
1063 (ret[to_string(amendmentID)] = Json::objectValue);
1064 injectJson(jAmendment, amendmentID, *a, isAdmin, lock);
1065 }
1066 }
1067
1068 return ret;
1069}
1070
1073 Application& app,
1074 std::chrono::seconds majorityTime,
1076 Section const& enabled,
1077 Section const& vetoed,
1078 beast::Journal journal)
1079{
1081 app, majorityTime, supported, enabled, vetoed, journal);
1082}
1083
1084} // namespace ripple
T as_const(T... args)
Represents a JSON value.
Definition json_value.h:149
A generic endpoint for log messages.
Definition Journal.h:60
Stream error() const
Definition Journal.h:346
Stream debug() const
Definition Journal.h:328
Stream info() const
Definition Journal.h:334
Stream trace() const
Severity stream access functions.
Definition Journal.h:322
Stream warn() const
Definition Journal.h:340
The status of all amendments requested in a given window.
int votes(uint256 const &amendment) 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
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()
Rules controlling protocol behavior.
Definition Rules.h:38
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition Rules.cpp:130
Holds a collection of configuration values.
Definition BasicConfig.h:45
std::string const & name() const
Returns the name of this section.
Definition BasicConfig.h:61
std::vector< std::string > const & lines() const
Returns all the lines in the section.
Definition BasicConfig.h:70
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:503
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 is_same_v
T make_pair(T... args)
T max(T... args)
@ objectValue
object value (collection of name/value pairs).
Definition json_value.h:45
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
static std::vector< std::pair< uint256, std::string > > parseSection(Section const &section)
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
AmendmentVote
Definition Wallet.h:150
constexpr std::uint32_t tfGotMajority
Definition TxFlags.h:128
bool isAdmin(Port const &port, Json::Value const &params, beast::IP::Address const &remoteIp)
Definition Role.cpp:85
bool createFeatureVotes(soci::session &session)
createFeatureVotes Creates the FeatureVote table if it does not exist.
Definition Wallet.cpp:229
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:253
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:630
T get(Section const &section, std::string const &name, T const &defaultValue=T{})
Retrieve a key/value pair from a section.
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:129
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:289
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)