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 // number of trusted validations
320 int trustedValidations_ = 0;
321 // number of votes needed
322 int threshold_ = 0;
323
324public:
326 Rules const& rules,
327 TrustedVotes const& trustedVotes,
328 std::lock_guard<std::mutex> const& lock)
329 {
330 // process validations for ledger before flag ledger.
331 auto [trustedCount, newVotes] = trustedVotes.getVotes(rules, lock);
332
333 trustedValidations_ = trustedCount;
334 votes_.swap(newVotes);
335
336 threshold_ = std::max(
337 1L,
338 static_cast<long>(
339 (trustedValidations_ * amendmentMajorityCalcThreshold.num) /
341 }
342
343 bool
344 passes(uint256 const& amendment) const
345 {
346 auto const& it = votes_.find(amendment);
347
348 if (it == votes_.end())
349 return false;
350
351 // One validator is an exception, otherwise it is not possible
352 // to gain majority.
353 if (trustedValidations_ == 1)
354 return it->second >= threshold_;
355
356 return it->second > threshold_;
357 }
358
359 int
360 votes(uint256 const& amendment) const
361 {
362 auto const& it = votes_.find(amendment);
363
364 if (it == votes_.end())
365 return 0;
366
367 return it->second;
368 }
369
370 int
372 {
373 return trustedValidations_;
374 }
375
376 int
377 threshold() const
378 {
379 return threshold_;
380 }
381};
382
383//------------------------------------------------------------------------------
384
392{
393private:
395
398
399 // Record of the last votes seen from trusted validators.
401
402 // Time that an amendment must hold a majority for
404
405 // The results of the last voting round - may be empty if
406 // we haven't participated in one yet.
408
409 // True if an unsupported amendment is enabled
411
412 // Unset if no unsupported amendments reach majority,
413 // else set to the earliest time an unsupported amendment
414 // will be enabled.
416
418
419 // Database which persists veto/unveto vote
421
422 // Finds or creates state. Must be called with mutex_ locked.
424 add(uint256 const& amendment, std::lock_guard<std::mutex> const& lock);
425
426 // Finds existing state. Must be called with mutex_ locked.
428 get(uint256 const& amendment, std::lock_guard<std::mutex> const& lock);
429
430 AmendmentState const*
431 get(uint256 const& amendment,
432 std::lock_guard<std::mutex> const& lock) const;
433
434 // Injects amendment json into v. Must be called with mutex_ locked.
435 void
436 injectJson(
437 Json::Value& v,
438 uint256 const& amendment,
439 AmendmentState const& state,
440 bool isAdmin,
441 std::lock_guard<std::mutex> const& lock) const;
442
443 void
444 persistVote(
445 uint256 const& amendment,
446 std::string const& name,
447 AmendmentVote vote) const;
448
449public:
451 Application& app,
452 std::chrono::seconds majorityTime,
453 std::vector<FeatureInfo> const& supported,
454 Section const& enabled,
455 Section const& vetoed,
456 beast::Journal journal);
457
458 uint256
459 find(std::string const& name) const override;
460
461 bool
462 veto(uint256 const& amendment) override;
463 bool
464 unVeto(uint256 const& amendment) override;
465
466 bool
467 enable(uint256 const& amendment) override;
468
469 bool
470 isEnabled(uint256 const& amendment) const override;
471 bool
472 isSupported(uint256 const& amendment) const override;
473
474 bool
475 hasUnsupportedEnabled() const override;
476
478 firstUnsupportedExpected() const override;
479
481 getJson(bool isAdmin) const override;
483 getJson(uint256 const&, bool isAdmin) const override;
484
485 bool
486 needValidatedLedger(LedgerIndex seq) const override;
487
488 void
489 doValidatedLedger(
490 LedgerIndex seq,
491 std::set<uint256> const& enabled,
492 majorityAmendments_t const& majority) override;
493
494 void
495 trustChanged(hash_set<PublicKey> const& allTrusted) override;
496
498 doValidation(std::set<uint256> const& enabledAmendments) const override;
499
501 getDesired() const override;
502
504 doVoting(
505 Rules const& rules,
506 NetClock::time_point closeTime,
507 std::set<uint256> const& enabledAmendments,
508 majorityAmendments_t const& majorityAmendments,
509 std::vector<std::shared_ptr<STValidation>> const& validations) override;
510};
511
512//------------------------------------------------------------------------------
513
514AmendmentTableImpl::AmendmentTableImpl(
515 Application& app,
516 std::chrono::seconds majorityTime,
517 std::vector<FeatureInfo> const& supported,
518 Section const& enabled,
519 Section const& vetoed,
520 beast::Journal journal)
521 : lastUpdateSeq_(0)
522 , majorityTime_(majorityTime)
523 , unsupportedEnabled_(false)
524 , j_(journal)
525 , db_(app.getWalletDB())
526{
528
529 // Find out if the FeatureVotes table exists in WalletDB
530 bool const featureVotesExist = [this]() {
531 auto db = db_.checkoutDb();
532 return createFeatureVotes(*db);
533 }();
534
535 // Parse supported amendments
536 for (auto const& [name, amendment, votebehavior] : supported)
537 {
538 AmendmentState& s = add(amendment, lock);
539
540 s.name = name;
541 s.supported = true;
542 switch (votebehavior)
543 {
546 break;
547
550 break;
551
554 break;
555 }
556
557 JLOG(j_.debug()) << "Amendment " << amendment << " (" << s.name
558 << ") is supported and will be "
559 << (s.vote == AmendmentVote::up ? "up" : "down")
560 << " voted by default if not enabled on the ledger.";
561 }
562
563 hash_set<uint256> detect_conflict;
564 // Parse enabled amendments from config
565 for (std::pair<uint256, std::string> const& a : parseSection(enabled))
566 {
567 if (featureVotesExist)
568 { // If the table existed, warn about duplicate config info
569 JLOG(j_.warn()) << "[amendments] section in config file ignored"
570 " in favor of data in db/wallet.db.";
571 break;
572 }
573 else
574 { // Otherwise transfer config data into the table
575 detect_conflict.insert(a.first);
576 persistVote(a.first, a.second, AmendmentVote::up);
577 }
578 }
579
580 // Parse vetoed amendments from config
581 for (auto const& a : parseSection(vetoed))
582 {
583 if (featureVotesExist)
584 { // If the table existed, warn about duplicate config info
585 JLOG(j_.warn())
586 << "[veto_amendments] section in config file ignored"
587 " in favor of data in db/wallet.db.";
588 break;
589 }
590 else
591 { // Otherwise transfer config data into the table
592 if (detect_conflict.count(a.first) == 0)
593 {
594 persistVote(a.first, a.second, AmendmentVote::down);
595 }
596 else
597 {
598 JLOG(j_.warn())
599 << "[veto_amendments] section in config has amendment "
600 << '(' << a.first << ", " << a.second
601 << ") both [veto_amendments] and [amendments].";
602 }
603 }
604 }
605
606 // Read amendment votes from wallet.db
607 auto db = db_.checkoutDb();
609 *db,
610 [&](boost::optional<std::string> amendment_hash,
611 boost::optional<std::string> amendment_name,
612 boost::optional<AmendmentVote> vote) {
613 uint256 amend_hash;
614 if (!amendment_hash || !amendment_name || !vote)
615 {
616 // These fields should never have nulls, but check
617 Throw<std::runtime_error>(
618 "Invalid FeatureVotes row in wallet.db");
619 }
620 if (!amend_hash.parseHex(*amendment_hash))
621 {
622 Throw<std::runtime_error>(
623 "Invalid amendment ID '" + *amendment_hash +
624 " in wallet.db");
625 }
626 if (*vote == AmendmentVote::down)
627 {
628 // Unknown amendments are effectively vetoed already
629 if (auto s = get(amend_hash, lock))
630 {
631 JLOG(j_.info()) << "Amendment {" << *amendment_name << ", "
632 << amend_hash << "} is downvoted.";
633 if (!amendment_name->empty())
634 s->name = *amendment_name;
635 // An obsolete amendment's vote can never be changed
636 if (s->vote != AmendmentVote::obsolete)
637 s->vote = *vote;
638 }
639 }
640 else // up-vote
641 {
642 AmendmentState& s = add(amend_hash, lock);
643
644 JLOG(j_.debug()) << "Amendment {" << *amendment_name << ", "
645 << amend_hash << "} is upvoted.";
646 if (!amendment_name->empty())
647 s.name = *amendment_name;
648 // An obsolete amendment's vote can never be changed
650 s.vote = *vote;
651 }
652 });
653}
654
657 uint256 const& amendmentHash,
659{
660 // call with the mutex held
661 return amendmentMap_[amendmentHash];
662}
663
666 uint256 const& amendmentHash,
667 std::lock_guard<std::mutex> const& lock)
668{
669 // Forward to the const version of get.
670 return const_cast<AmendmentState*>(
671 std::as_const(*this).get(amendmentHash, lock));
672}
673
674AmendmentState const*
676 uint256 const& amendmentHash,
677 std::lock_guard<std::mutex> const&) const
678{
679 // call with the mutex held
680 auto ret = amendmentMap_.find(amendmentHash);
681
682 if (ret == amendmentMap_.end())
683 return nullptr;
684
685 return &ret->second;
686}
687
690{
692
693 for (auto const& e : amendmentMap_)
694 {
695 if (name == e.second.name)
696 return e.first;
697 }
698
699 return {};
700}
701
702void
704 uint256 const& amendment,
705 std::string const& name,
706 AmendmentVote vote) const
707{
708 XRPL_ASSERT(
710 "ripple::AmendmentTableImpl::persistVote : valid vote input");
711 auto db = db_.checkoutDb();
712 voteAmendment(*db, amendment, name, vote);
713}
714
715bool
717{
719 AmendmentState& s = add(amendment, lock);
720
721 if (s.vote != AmendmentVote::up)
722 return false;
724 persistVote(amendment, s.name, s.vote);
725 return true;
726}
727
728bool
730{
732 AmendmentState* const s = get(amendment, lock);
733
734 if (!s || s->vote != AmendmentVote::down)
735 return false;
737 persistVote(amendment, s->name, s->vote);
738 return true;
739}
740
741bool
743{
745 AmendmentState& s = add(amendment, lock);
746
747 if (s.enabled)
748 return false;
749
750 s.enabled = true;
751
752 if (!s.supported)
753 {
754 JLOG(j_.error()) << "Unsupported amendment " << amendment
755 << " activated.";
756 unsupportedEnabled_ = true;
757 }
758
759 return true;
760}
761
762bool
764{
766 AmendmentState const* s = get(amendment, lock);
767 return s && s->enabled;
768}
769
770bool
772{
774 AmendmentState const* s = get(amendment, lock);
775 return s && s->supported;
776}
777
778bool
784
791
794{
795 // Get the list of amendments we support and do not
796 // veto, but that are not already enabled
797 std::vector<uint256> amendments;
798
799 {
801 amendments.reserve(amendmentMap_.size());
802 for (auto const& e : amendmentMap_)
803 {
804 if (e.second.supported && e.second.vote == AmendmentVote::up &&
805 (enabled.count(e.first) == 0))
806 {
807 amendments.push_back(e.first);
808 JLOG(j_.info()) << "Voting for amendment " << e.second.name;
809 }
810 }
811 }
812
813 if (!amendments.empty())
814 std::sort(amendments.begin(), amendments.end());
815
816 return amendments;
817}
818
821{
822 // Get the list of amendments we support and do not veto
823 return doValidation({});
824}
825
828 Rules const& rules,
829 NetClock::time_point closeTime,
830 std::set<uint256> const& enabledAmendments,
831 majorityAmendments_t const& majorityAmendments,
833{
834 JLOG(j_.trace()) << "voting at " << closeTime.time_since_epoch().count()
835 << ": " << enabledAmendments.size() << ", "
836 << majorityAmendments.size() << ", " << valSet.size();
837
839
840 // Keep a record of the votes we received.
841 previousTrustedVotes_.recordVotes(rules, valSet, closeTime, j_, lock);
842
843 // Tally the most recent votes.
844 auto vote =
846 JLOG(j_.debug()) << "Counted votes from " << vote->trustedValidations()
847 << " valid trusted validations, threshold is: "
848 << vote->threshold();
849
850 // Map of amendments to the action to be taken for each one. The action is
851 // the value of the flags in the pseudo-transaction
853
854 // process all amendments we know of
855 for (auto const& entry : amendmentMap_)
856 {
857 if (enabledAmendments.contains(entry.first))
858 {
859 JLOG(j_.trace()) << entry.first << ": amendment already enabled";
860
861 continue;
862 }
863
864 bool const hasValMajority = vote->passes(entry.first);
865
866 auto const majorityTime = [&]() -> std::optional<NetClock::time_point> {
867 auto const it = majorityAmendments.find(entry.first);
868 if (it != majorityAmendments.end())
869 return it->second;
870 return std::nullopt;
871 }();
872
873 bool const hasLedgerMajority = majorityTime.has_value();
874
875 auto const logStr = [&entry, &vote]() {
877 ss << entry.first << " (" << entry.second.name << ") has "
878 << vote->votes(entry.first) << " votes";
879 return ss.str();
880 }();
881
882 if (hasValMajority && !hasLedgerMajority &&
883 entry.second.vote == AmendmentVote::up)
884 {
885 // Ledger says no majority, validators say yes, and voting yes
886 // locally
887 JLOG(j_.debug()) << logStr << ": amendment got majority";
888 actions[entry.first] = tfGotMajority;
889 }
890 else if (!hasValMajority && hasLedgerMajority)
891 {
892 // Ledger says majority, validators say no
893 JLOG(j_.debug()) << logStr << ": amendment lost majority";
894 actions[entry.first] = tfLostMajority;
895 }
896 else if (
897 hasLedgerMajority &&
898 ((*majorityTime + majorityTime_) <= closeTime) &&
899 entry.second.vote == AmendmentVote::up)
900 {
901 // Ledger says majority held
902 JLOG(j_.debug()) << logStr << ": amendment majority held";
903 actions[entry.first] = 0;
904 }
905 // Logging only below this point
906 else if (hasValMajority && hasLedgerMajority)
907 {
908 JLOG(j_.debug())
909 << logStr
910 << ": amendment holding majority, waiting to be enabled";
911 }
912 else if (!hasValMajority)
913 {
914 JLOG(j_.debug()) << logStr << ": amendment does not have majority";
915 }
916 }
917
918 // Stash for reporting
919 lastVote_ = std::move(vote);
920 return actions;
921}
922
923bool
925{
927
928 // Is there a ledger in which an amendment could have been enabled
929 // between these two ledger sequences?
930
931 return ((ledgerSeq - 1) / 256) != ((lastUpdateSeq_ - 1) / 256);
932}
933
934void
936 LedgerIndex ledgerSeq,
937 std::set<uint256> const& enabled,
938 majorityAmendments_t const& majority)
939{
940 for (auto& e : enabled)
941 enable(e);
942
944
945 // Remember the ledger sequence of this update.
946 lastUpdateSeq_ = ledgerSeq;
947
948 // Since we have the whole list in `majority`, reset the time flag, even
949 // if it's currently set. If it's not set when the loop is done, then any
950 // prior unknown amendments have lost majority.
952 for (auto const& [hash, time] : majority)
953 {
954 AmendmentState& s = add(hash, lock);
955
956 if (s.enabled)
957 continue;
958
959 if (!s.supported)
960 {
961 JLOG(j_.info()) << "Unsupported amendment " << hash
962 << " reached majority at " << to_string(time);
965 }
966 }
969}
970
971void
977
978void
980 Json::Value& v,
981 uint256 const& id,
982 AmendmentState const& fs,
983 bool isAdmin,
984 std::lock_guard<std::mutex> const&) const
985{
986 if (!fs.name.empty())
987 v[jss::name] = fs.name;
988
989 v[jss::supported] = fs.supported;
990 if (!fs.enabled && isAdmin)
991 {
993 v[jss::vetoed] = "Obsolete";
994 else
995 v[jss::vetoed] = fs.vote == AmendmentVote::down;
996 }
997 v[jss::enabled] = fs.enabled;
998
999 if (!fs.enabled && lastVote_ && isAdmin)
1000 {
1001 auto const votesTotal = lastVote_->trustedValidations();
1002 auto const votesNeeded = lastVote_->threshold();
1003 auto const votesFor = lastVote_->votes(id);
1004
1005 v[jss::count] = votesFor;
1006 v[jss::validations] = votesTotal;
1007
1008 if (votesNeeded)
1009 v[jss::threshold] = votesNeeded;
1010 }
1011}
1012
1015{
1017 {
1018 std::lock_guard lock(mutex_);
1019 for (auto const& e : amendmentMap_)
1020 {
1021 injectJson(
1022 ret[to_string(e.first)] = Json::objectValue,
1023 e.first,
1024 e.second,
1025 isAdmin,
1026 lock);
1027 }
1028 }
1029 return ret;
1030}
1031
1033AmendmentTableImpl::getJson(uint256 const& amendmentID, bool isAdmin) const
1034{
1036
1037 {
1038 std::lock_guard lock(mutex_);
1039 AmendmentState const* a = get(amendmentID, lock);
1040 if (a)
1041 {
1042 Json::Value& jAmendment =
1043 (ret[to_string(amendmentID)] = Json::objectValue);
1044 injectJson(jAmendment, amendmentID, *a, isAdmin, lock);
1045 }
1046 }
1047
1048 return ret;
1049}
1050
1053 Application& app,
1054 std::chrono::seconds majorityTime,
1056 Section const& enabled,
1057 Section const& vetoed,
1058 beast::Journal journal)
1059{
1061 app, majorityTime, supported, enabled, vetoed, journal);
1062}
1063
1064} // 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
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
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
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::ratio< 80, 100 > amendmentMajorityCalcThreshold
The minimum amount of support an amendment should have.
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)