1#include <test/jtx/Env.h>
2#include <test/unit_test/SuiteJournal.h>
4#include <xrpld/app/misc/AmendmentTable.h>
5#include <xrpld/core/ConfigSections.h>
7#include <xrpl/basics/BasicConfig.h>
8#include <xrpl/basics/Log.h>
9#include <xrpl/basics/chrono.h>
10#include <xrpl/beast/unit_test.h>
11#include <xrpl/protocol/Feature.h>
12#include <xrpl/protocol/PublicKey.h>
13#include <xrpl/protocol/STValidation.h>
14#include <xrpl/protocol/SecretKey.h>
15#include <xrpl/protocol/TxFlags.h>
16#include <xrpl/protocol/digest.h>
17#include <xrpl/protocol/jss.h>
40 for (
auto const& a : amendments)
72 result.
reserve(amendments.size());
73 for (
auto const& a : amendments)
105 template <
class Arg,
class... Args>
109 if constexpr (
sizeof...(args) > 0)
114 template <
class Arg,
class... Args>
120 if constexpr (
sizeof...(args) > 0)
124 template <
class Arg,
class... Args>
133 left.reserve(
totalsize(left, right, args...));
181 return makeTable(env.
app(), majorityTime, supported, enabled, vetoed);
209 for (
auto const& a :
yes_)
236 for (
auto const& a :
yes_)
245 BEAST_EXPECT(!table->find(a));
247 BEAST_EXPECT(!table->find(a));
254 BEAST_EXPECT(unsupp.
size() == 0);
258 table->veto(unsupportedID);
261 BEAST_EXPECT(unsupp[jss::vetoed].asBool());
282 fail(
"Accepted only amendment ID");
286 BEAST_EXPECT(e.
what() ==
"Invalid entry '" +
id +
"' in [Test]");
292 test.
append(
id +
" Test Name");
298 fail(
"Accepted extra arguments");
302 BEAST_EXPECT(e.
what() ==
"Invalid entry '" +
id +
" Test Name' in [Test]");
308 sid.resize(sid.length() - 1);
311 test.
append(sid +
" Name");
317 fail(
"Accepted short amendment ID");
321 BEAST_EXPECT(e.
what() ==
"Invalid entry '" + sid +
" Name' in [Test]");
327 sid.resize(sid.length() + 1,
'0');
330 test.
append(sid +
" Name");
336 fail(
"Accepted long amendment ID");
340 BEAST_EXPECT(e.
what() ==
"Invalid entry '" + sid +
" Name' in [Test]");
346 sid.resize(sid.length() - 1);
350 test.
append(sid +
" Name");
356 fail(
"Accepted non-hex amendment ID");
360 BEAST_EXPECT(e.
what() ==
"Invalid entry '" + sid +
" Name' in [Test]");
378 for (
uint256 const& a : allEnabled)
379 BEAST_EXPECT(table->enable(a));
382 BEAST_EXPECT(!table->hasUnsupportedEnabled());
388 bool const enabled = table->isEnabled(supportedID);
389 bool const found = allEnabled.
find(supportedID) != allEnabled.
end();
391 enabled == found, a + (enabled ?
" enabled " :
" disabled ") + (found ?
" found" :
" not found"));
401 for (
uint256 const& a : desired)
402 BEAST_EXPECT(vetoed.
count(a) == 0);
408 BEAST_EXPECT(desired == table->getDesired());
414 BEAST_EXPECT(table->unVeto(unvetoedID));
425 BEAST_EXPECT(table->getDesired().empty());
429 BEAST_EXPECT(!table->hasUnsupportedEnabled());
431 BEAST_EXPECT(table->hasUnsupportedEnabled());
443 trustedValidators.
reserve(num);
444 for (
int i = 0; i < num; ++i)
447 trustedValidators.
insert(back.first);
449 table->trustChanged(trustedValidators);
482 auto const roundTime =
hourTime(hour);
486 validations.
reserve(validators.size());
489 for (
auto const& [pub, sec] : validators)
494 for (
auto const& [hash, nVotes] : votes)
499 field.push_back(hash);
506 v.setFieldV256(sfAmendments,
STVector256(sfAmendments, field));
507 v.setFieldU32(sfLedgerSequence, 6180339);
515 auto actions = table.
doVoting(rules, roundTime, enabled, majority, validations);
516 for (
auto const& [hash, action] : actions)
524 if (enabled.
find(hash) != enabled.
end())
525 Throw<std::runtime_error>(
"enabling already enabled");
526 if (majority.
find(hash) == majority.
end())
527 Throw<std::runtime_error>(
"enabling without majority");
529 majority.
erase(hash);
533 if (majority.
find(hash) != majority.
end())
534 Throw<std::runtime_error>(
"got majority while having majority");
535 majority[hash] = roundTime;
539 if (majority.
find(hash) == majority.
end())
540 Throw<std::runtime_error>(
"lost majority without majority");
541 majority.
erase(hash);
545 Throw<std::runtime_error>(
"unknown action");
556 auto const testAmendment =
amendmentId(
"TestAmendment");
568 doRound(env.current()->rules(), *table,
weeks{1}, validators, votes, ourVotes, enabled, majority);
569 BEAST_EXPECT(ourVotes.
empty());
570 BEAST_EXPECT(enabled.
empty());
571 BEAST_EXPECT(majority.
empty());
576 BEAST_EXPECT(unsupp.
size() == 0);
579 table->veto(unsupportedID);
582 BEAST_EXPECT(!unsupp[jss::vetoed].asBool());
589 doRound(env.current()->rules(), *table,
weeks{2}, validators, votes, ourVotes, enabled, majority);
590 BEAST_EXPECT(ourVotes.
empty());
591 BEAST_EXPECT(enabled.
empty());
597 doRound(env.current()->rules(), *table,
weeks{5}, validators, votes, ourVotes, enabled, majority);
598 BEAST_EXPECT(ourVotes.
empty());
599 BEAST_EXPECT(enabled.
empty());
608 auto const testAmendment =
amendmentId(
"vetoedAmendment");
620 doRound(env.current()->rules(), *table,
weeks{1}, validators, votes, ourVotes, enabled, majority);
621 BEAST_EXPECT(ourVotes.
empty());
622 BEAST_EXPECT(enabled.
empty());
623 BEAST_EXPECT(majority.
empty());
627 doRound(env.current()->rules(), *table,
weeks{2}, validators, votes, ourVotes, enabled, majority);
628 BEAST_EXPECT(ourVotes.
empty());
629 BEAST_EXPECT(enabled.
empty());
633 doRound(env.current()->rules(), *table,
weeks{5}, validators, votes, ourVotes, enabled, majority);
634 BEAST_EXPECT(ourVotes.
empty());
635 BEAST_EXPECT(enabled.
empty());
655 doRound(env.current()->rules(), *table,
weeks{1}, validators, votes, ourVotes, enabled, majority);
657 BEAST_EXPECT(enabled.
empty());
658 for (
auto const& i :
yes_)
662 for (
auto const& i :
yes_)
666 doRound(env.current()->rules(), *table,
weeks{2}, validators, votes, ourVotes, enabled, majority);
668 BEAST_EXPECT(enabled.
empty());
670 for (
auto const& i :
yes_)
674 doRound(env.current()->rules(), *table,
weeks{5}, validators, votes, ourVotes, enabled, majority);
678 doRound(env.current()->rules(), *table,
weeks{6}, validators, votes, ourVotes, enabled, majority);
680 BEAST_EXPECT(ourVotes.
empty());
681 for (
auto const& i :
yes_)
691 auto const testAmendment =
amendmentId(
"detectMajority");
700 for (
int i = 0; i <= 17; ++i)
705 if ((i > 0) && (i < 17))
708 doRound(env.current()->rules(), *table,
weeks{i}, validators, votes, ourVotes, enabled, majority);
713 BEAST_EXPECT(!ourVotes.
empty());
714 BEAST_EXPECT(enabled.
empty());
715 BEAST_EXPECT(majority.
empty());
720 BEAST_EXPECT(!ourVotes.
empty());
721 BEAST_EXPECT(!majority.
empty());
722 BEAST_EXPECT(enabled.
empty());
727 BEAST_EXPECT(!ourVotes.
empty());
728 BEAST_EXPECT(majority.
empty());
729 BEAST_EXPECT(!enabled.
empty());
734 BEAST_EXPECT(ourVotes.
empty());
735 BEAST_EXPECT(majority.
empty());
736 BEAST_EXPECT(!enabled.
empty());
747 auto const testAmendment =
amendmentId(
"lostMajority");
764 doRound(env.current()->rules(), *table,
weeks{1}, validators, votes, ourVotes, enabled, majority);
766 BEAST_EXPECT(enabled.
empty());
767 BEAST_EXPECT(!majority.
empty());
770 for (
int i = 1; i < 8; ++i)
776 votes.
emplace_back(testAmendment, validators.size() - i);
778 doRound(env.current()->rules(), *table,
weeks{i + 1}, validators, votes, ourVotes, enabled, majority);
783 BEAST_EXPECT(!ourVotes.
empty());
784 BEAST_EXPECT(enabled.
empty());
785 BEAST_EXPECT(!majority.
empty());
790 BEAST_EXPECT(!ourVotes.
empty());
791 BEAST_EXPECT(majority.
empty());
792 BEAST_EXPECT(enabled.
empty());
803 auto const testAmendment =
amendmentId(
"changedUNL");
819 doRound(env.current()->rules(), *table,
weeks{1}, validators, votes, ourVotes, enabled, majority);
821 BEAST_EXPECT(enabled.
empty());
822 BEAST_EXPECT(majority.
empty());
836 trustedValidators.insert(val.first);
840 table->trustChanged(trustedValidators);
844 callTrustChanged(validators, table);
853 doRound(env.current()->rules(), *table,
weeks{2}, validators, votes, ourVotes, enabled, majority);
855 BEAST_EXPECT(enabled.
empty());
856 BEAST_EXPECT(!majority.
empty());
869 doRound(env.current()->rules(), *table,
weeks{3}, validators, votes, ourVotes, enabled, majority);
871 BEAST_EXPECT(enabled.
empty());
872 BEAST_EXPECT(majority.
empty());
876 validators.
insert(validators.
begin(), savedValidator);
878 votes.
front().second = validators.
size() - 2;
880 doRound(env.current()->rules(), *table,
weeks{4}, validators, votes, ourVotes, enabled, majority);
882 BEAST_EXPECT(enabled.
empty());
883 BEAST_EXPECT(!majority.
empty());
890 callTrustChanged(validators, table);
892 votes.
front().second = validators.
size() - 2;
894 doRound(env.current()->rules(), *table,
weeks{5}, validators, votes, ourVotes, enabled, majority);
896 BEAST_EXPECT(enabled.
empty());
897 BEAST_EXPECT(majority.
empty());
915 for (
int flapRateHours : {23, 25})
918 auto const testAmendment =
amendmentId(
"validatorFlapping");
924 decltype(allValidators)
const mostValidators(allValidators.begin() + 1, allValidators.end());
925 BEAST_EXPECT(allValidators.size() == mostValidators.size() + 1);
933 votes.
emplace_back(testAmendment, allValidators.size() - 2);
935 int delay = flapRateHours;
937 for (
int hour = 1; hour < (24 * 8); ++hour)
939 decltype(allValidators)
const& thisHoursValidators =
940 (delay < flapRateHours) ? mostValidators : allValidators;
941 delay = delay == flapRateHours ? 0 : delay + 1;
943 votes.
front().second = thisHoursValidators.size() - 2;
947 env.current()->rules(),
956 if (hour <= (24 * 7) || flapRateHours > 24)
960 BEAST_EXPECT(enabled.
empty());
966 bool const expectMajority = (delay <= 24) ?
true : &thisHoursValidators == &allValidators;
967 BEAST_EXPECT(majority.
empty() != expectMajority);
975 BEAST_EXPECT(!enabled.
empty());
976 BEAST_EXPECT(majority.
empty());
987 using namespace std::chrono_literals;
988 weeks constexpr w(1);
991 BEAST_EXPECT(!table->hasUnsupportedEnabled());
992 BEAST_EXPECT(!table->firstUnsupportedExpected());
993 BEAST_EXPECT(table->needValidatedLedger(1));
1000 table->doValidatedLedger(1, enabled, majority);
1001 BEAST_EXPECT(table->hasUnsupportedEnabled());
1002 BEAST_EXPECT(!table->firstUnsupportedExpected());
1006 majority[amendmentId(s)] = NetClock::time_point{--t};
1009 table->doValidatedLedger(1, enabled, majority);
1010 BEAST_EXPECT(table->hasUnsupportedEnabled());
1012 table->firstUnsupportedExpected() && *table->firstUnsupportedExpected() ==
NetClock::time_point{t} + w);
1015 BEAST_EXPECT(!table->needValidatedLedger(256));
1016 BEAST_EXPECT(table->needValidatedLedger(257));
1022 testNoOnUnknown(feat);
1023 testNoOnVetoed(feat);
1024 testVoteEnable(feat);
1025 testDetectMajority(feat);
1026 testLostMajority(feat);
1027 testChangedUNL(feat);
1028 testValidatorFlapping(feat);
1040 testHasUnsupported();
T back_inserter(T... args)
UInt size() const
Number of values in array or object.
testcase_t testcase
Memberspace for declaring test cases.
void fail(String const &reason, char const *file, int line)
Record a failure.
static Section makeSection(std::string const &name, std::vector< std::string > const &amendments)
void run() override
Runs the suite.
static NetClock::time_point hourTime(std::chrono::hours h)
void testLostMajority(FeatureBitset const &feat)
test::SuiteJournal journal_
static std::vector< AmendmentTable::FeatureInfo > makeFeatureInfo(std::vector< std::string > const &amendments, VoteBehavior voteBehavior)
std::unique_ptr< AmendmentTable > makeTable(test::jtx::Env &env, std::chrono::seconds majorityTime, std::vector< AmendmentTable::FeatureInfo > const &supported, Section const &enabled, Section const &vetoed)
static std::vector< AmendmentTable::FeatureInfo > makeDefaultYes(std::vector< std::string > const &amendments)
void testDetectMajority(FeatureBitset const &feat)
void testHasUnsupported()
void testValidatorFlapping(FeatureBitset const &feat)
std::vector< AmendmentTable::FeatureInfo > const emptyYes_
std::vector< std::pair< PublicKey, SecretKey > > makeValidators(int num, std::unique_ptr< AmendmentTable > const &table)
void testChangedUNL(FeatureBitset const &feat)
static Section makeSection(std::vector< std::string > const &amendments)
std::vector< std::string > const unsupportedMajority_
void testNoOnVetoed(FeatureBitset const &feat)
std::unique_ptr< AmendmentTable > makeTable(Application &app, std::chrono::seconds majorityTime, std::vector< AmendmentTable::FeatureInfo > const &supported, Section const &enabled, Section const &vetoed)
static Section makeSection(uint256 const &amendment)
static void combine_arg(std::vector< Arg > &dest, std::vector< Arg > const &src, Args const &... args)
static std::vector< AmendmentTable::FeatureInfo > makeObsolete(std::vector< std::string > const &amendments)
static std::vector< Arg > combine(std::vector< Arg > left, std::vector< Arg > const &right, Args const &... args)
std::vector< std::string > const vetoed_
std::unique_ptr< AmendmentTable > makeTable(test::jtx::Env &env, std::chrono::seconds majorityTime)
static std::vector< AmendmentTable::FeatureInfo > makeDefaultYes(uint256 const amendment)
std::vector< std::string > const enabled_
static uint256 amendmentId(std::string in)
static size_t totalsize(std::vector< Arg > const &src, Args const &... args)
std::vector< std::string > const allSupported_
std::vector< std::string > const unsupported_
std::vector< std::string > const obsolete_
void testVoteEnable(FeatureBitset const &feat)
Section const emptySection_
static std::vector< AmendmentTable::FeatureInfo > makeDefaultNo(std::vector< std::string > const &amendments)
std::unique_ptr< Config > makeConfig()
void doRound(Rules const &rules, AmendmentTable &table, std::chrono::hours hour, std::vector< std::pair< PublicKey, SecretKey > > const &validators, std::vector< std::pair< uint256, int > > const &votes, std::vector< uint256 > &ourVotes, std::set< uint256 > &enabled, majorityAmendments_t &majority)
std::vector< std::string > const yes_
void testFeature(FeatureBitset const &feat)
void testNoOnUnknown(FeatureBitset const &feat)
The amendment table stores the list of enabled and potential amendments.
virtual std::vector< uint256 > doValidation(std::set< uint256 > const &enabled) const =0
virtual 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 &valSet)=0
Rules controlling protocol behavior.
Holds a collection of configuration values.
void append(std::vector< std::string > const &lines)
Append a set of lines to this section.
static constexpr std::size_t size()
A transaction testing environment.
T emplace_back(T... args)
std::enable_if_t< is_contiguously_hashable< T, Hasher >::value > hash_append(Hasher &h, T const &t) noexcept
Logically concatenate input data to a Hasher.
FeatureBitset testable_amendments()
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
std::pair< PublicKey, SecretKey > randomKeyPair(KeyType type)
Create a key pair using secure random numbers.
std::string to_string(base_uint< Bits, Tag > const &a)
std::chrono::duration< int, std::ratio_multiply< days::period, std::ratio< 7 > > > weeks
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
constexpr std::uint32_t tfGotMajority
NodeID calcNodeID(PublicKey const &)
Calculate the 160-bit node ID from a node public key.
void hash_append(Hasher &h, Slice const &v)