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