20#include <test/jtx/Env.h>
21#include <test/unit_test/SuiteJournal.h>
23#include <xrpld/app/misc/AmendmentTable.h>
24#include <xrpld/core/ConfigSections.h>
26#include <xrpl/basics/BasicConfig.h>
27#include <xrpl/basics/Log.h>
28#include <xrpl/basics/chrono.h>
29#include <xrpl/beast/unit_test.h>
30#include <xrpl/protocol/Feature.h>
31#include <xrpl/protocol/PublicKey.h>
32#include <xrpl/protocol/STValidation.h>
33#include <xrpl/protocol/SecretKey.h>
34#include <xrpl/protocol/TxFlags.h>
35#include <xrpl/protocol/digest.h>
36#include <xrpl/protocol/jss.h>
61 for (
auto const& a : amendments)
84 cfg->section(SECTION_AMENDMENTS) =
86 cfg->section(SECTION_VETO_AMENDMENTS) =
97 result.
reserve(amendments.size());
98 for (
auto const& a : amendments)
131 template <
class Arg,
class... Args>
135 if constexpr (
sizeof...(args) > 0)
140 template <
class Arg,
class... Args>
149 if constexpr (
sizeof...(args) > 0)
153 template <
class Arg,
class... Args>
162 left.reserve(
totalsize(left, right, args...));
174 yes_{
"g",
"i",
"k",
"m",
"o",
"q",
"r",
"s",
"t",
"u"};
203 app, majorityTime, supported, enabled, vetoed,
journal_);
214 return makeTable(env.
app(), majorityTime, supported, enabled, vetoed);
248 for (
auto const& a :
yes_)
275 for (
auto const& a :
yes_)
284 BEAST_EXPECT(!table->find(a));
286 BEAST_EXPECT(!table->find(a));
293 table->getJson(unsupportedID,
true)[
to_string(unsupportedID)];
294 BEAST_EXPECT(unsupp.
size() == 0);
298 table->veto(unsupportedID);
301 table->getJson(unsupportedID,
true)[
to_string(unsupportedID)];
302 BEAST_EXPECT(unsupp[jss::vetoed].asBool());
323 fail(
"Accepted only amendment ID");
328 e.
what() ==
"Invalid entry '" +
id +
"' in [Test]");
334 test.
append(
id +
" Test Name");
340 fail(
"Accepted extra arguments");
346 "Invalid entry '" +
id +
" Test Name' in [Test]");
352 sid.resize(sid.length() - 1);
355 test.
append(sid +
" Name");
361 fail(
"Accepted short amendment ID");
366 e.
what() ==
"Invalid entry '" + sid +
" Name' in [Test]");
372 sid.resize(sid.length() + 1,
'0');
375 test.
append(sid +
" Name");
381 fail(
"Accepted long amendment ID");
386 e.
what() ==
"Invalid entry '" + sid +
" Name' in [Test]");
392 sid.resize(sid.length() - 1);
396 test.
append(sid +
" Name");
402 fail(
"Accepted non-hex amendment ID");
407 e.
what() ==
"Invalid entry '" + sid +
" Name' in [Test]");
425 for (
uint256 const& a : allEnabled)
426 BEAST_EXPECT(table->enable(a));
429 BEAST_EXPECT(!table->hasUnsupportedEnabled());
435 bool const enabled = table->isEnabled(supportedID);
436 bool const found = allEnabled.
find(supportedID) != allEnabled.
end();
439 a + (enabled ?
" enabled " :
" disabled ") +
440 (found ?
" found" :
" not found"));
450 for (
uint256 const& a : desired)
451 BEAST_EXPECT(vetoed.
count(a) == 0);
457 BEAST_EXPECT(desired == table->getDesired());
463 BEAST_EXPECT(table->unVeto(unvetoedID));
476 BEAST_EXPECT(table->getDesired().empty());
480 BEAST_EXPECT(!table->hasUnsupportedEnabled());
482 BEAST_EXPECT(table->hasUnsupportedEnabled());
494 trustedValidators.
reserve(num);
495 for (
int i = 0; i < num; ++i)
499 trustedValidators.
insert(back.first);
501 table->trustChanged(trustedValidators);
534 auto const roundTime =
hourTime(hour);
538 validations.
reserve(validators.size());
541 for (
auto const& [pub, sec] : validators)
546 for (
auto const& [hash, nVotes] : votes)
548 if (rules.
enabled(fixAmendmentMajorityCalc) ? nVotes >= i
552 field.push_back(hash);
565 v.setFieldU32(sfLedgerSequence, 6180339);
574 table.
doVoting(rules, roundTime, enabled, majority, validations);
575 for (
auto const& [hash, action] : actions)
583 if (enabled.
find(hash) != enabled.
end())
584 Throw<std::runtime_error>(
"enabling already enabled");
585 if (majority.
find(hash) == majority.
end())
586 Throw<std::runtime_error>(
"enabling without majority");
588 majority.
erase(hash);
592 if (majority.
find(hash) != majority.
end())
593 Throw<std::runtime_error>(
594 "got majority while having majority");
595 majority[hash] = roundTime;
599 if (majority.
find(hash) == majority.
end())
600 Throw<std::runtime_error>(
601 "lost majority without majority");
602 majority.
erase(hash);
606 Throw<std::runtime_error>(
"unknown action");
617 auto const testAmendment =
amendmentId(
"TestAmendment");
631 env.current()->rules(),
639 BEAST_EXPECT(ourVotes.
empty());
640 BEAST_EXPECT(enabled.
empty());
641 BEAST_EXPECT(majority.
empty());
646 table->getJson(unsupportedID,
false)[
to_string(unsupportedID)];
647 BEAST_EXPECT(unsupp.
size() == 0);
650 table->veto(unsupportedID);
653 table->getJson(unsupportedID,
false)[
to_string(unsupportedID)];
654 BEAST_EXPECT(!unsupp[jss::vetoed].asBool());
662 env.current()->rules(),
670 BEAST_EXPECT(ourVotes.
empty());
671 BEAST_EXPECT(enabled.
empty());
678 env.current()->rules(),
686 BEAST_EXPECT(ourVotes.
empty());
687 BEAST_EXPECT(enabled.
empty());
696 auto const testAmendment =
amendmentId(
"vetoedAmendment");
714 env.current()->rules(),
722 BEAST_EXPECT(ourVotes.
empty());
723 BEAST_EXPECT(enabled.
empty());
724 BEAST_EXPECT(majority.
empty());
729 env.current()->rules(),
737 BEAST_EXPECT(ourVotes.
empty());
738 BEAST_EXPECT(enabled.
empty());
743 env.current()->rules(),
751 BEAST_EXPECT(ourVotes.
empty());
752 BEAST_EXPECT(enabled.
empty());
774 env.current()->rules(),
783 BEAST_EXPECT(enabled.
empty());
784 for (
auto const& i :
yes_)
788 for (
auto const& i :
yes_)
793 env.current()->rules(),
802 BEAST_EXPECT(enabled.
empty());
804 for (
auto const& i :
yes_)
809 env.current()->rules(),
821 env.current()->rules(),
830 BEAST_EXPECT(ourVotes.
empty());
831 for (
auto const& i :
yes_)
841 auto const testAmendment =
amendmentId(
"detectMajority");
855 for (
int i = 0; i <= 17; ++i)
860 if ((i > 0) && (i < 17))
864 env.current()->rules(),
876 BEAST_EXPECT(!ourVotes.
empty());
877 BEAST_EXPECT(enabled.
empty());
878 BEAST_EXPECT(majority.
empty());
883 BEAST_EXPECT(!ourVotes.
empty());
884 BEAST_EXPECT(!majority.
empty());
885 BEAST_EXPECT(enabled.
empty());
890 BEAST_EXPECT(!ourVotes.
empty());
891 BEAST_EXPECT(majority.
empty());
892 BEAST_EXPECT(!enabled.
empty());
897 BEAST_EXPECT(ourVotes.
empty());
898 BEAST_EXPECT(majority.
empty());
899 BEAST_EXPECT(!enabled.
empty());
910 auto const testAmendment =
amendmentId(
"lostMajority");
933 env.current()->rules(),
942 BEAST_EXPECT(enabled.
empty());
943 BEAST_EXPECT(!majority.
empty());
946 for (
int i = 1; i < 8; ++i)
952 votes.
emplace_back(testAmendment, validators.size() - i);
955 env.current()->rules(),
967 BEAST_EXPECT(!ourVotes.
empty());
968 BEAST_EXPECT(enabled.
empty());
969 BEAST_EXPECT(!majority.
empty());
974 BEAST_EXPECT(!ourVotes.
empty());
975 BEAST_EXPECT(majority.
empty());
976 BEAST_EXPECT(enabled.
empty());
986 if (!feat[fixAmendmentMajorityCalc])
991 auto const testAmendment =
amendmentId(
"changedUNL");
1014 env.current()->rules(),
1023 BEAST_EXPECT(enabled.
empty());
1024 BEAST_EXPECT(majority.
empty());
1032 auto callTrustChanged =
1041 [&trustedValidators](
auto const& val) {
1042 trustedValidators.insert(val.first);
1046 table->trustChanged(trustedValidators);
1050 callTrustChanged(validators, table);
1060 env.current()->rules(),
1069 BEAST_EXPECT(enabled.
empty());
1070 BEAST_EXPECT(!majority.
empty());
1085 env.current()->rules(),
1094 BEAST_EXPECT(enabled.
empty());
1095 BEAST_EXPECT(majority.
empty());
1099 validators.
insert(validators.
begin(), savedValidator);
1101 votes.
front().second = validators.
size() - 2;
1104 env.current()->rules(),
1113 BEAST_EXPECT(enabled.
empty());
1114 BEAST_EXPECT(!majority.
empty());
1121 callTrustChanged(validators, table);
1123 votes.
front().second = validators.
size() - 2;
1126 env.current()->rules(),
1135 BEAST_EXPECT(enabled.
empty());
1136 BEAST_EXPECT(majority.
empty());
1147 if (!feat[fixAmendmentMajorityCalc])
1158 for (
int flapRateHours : {23, 25})
1161 auto const testAmendment =
amendmentId(
"validatorFlapping");
1172 decltype(allValidators)
const mostValidators(
1173 allValidators.begin() + 1, allValidators.end());
1174 BEAST_EXPECT(allValidators.size() == mostValidators.size() + 1);
1182 votes.
emplace_back(testAmendment, allValidators.size() - 2);
1184 int delay = flapRateHours;
1186 for (
int hour = 1; hour < (24 * 8); ++hour)
1188 decltype(allValidators)
const& thisHoursValidators =
1189 (delay < flapRateHours) ? mostValidators : allValidators;
1190 delay = delay == flapRateHours ? 0 : delay + 1;
1192 votes.
front().second = thisHoursValidators.size() - 2;
1196 env.current()->rules(),
1199 thisHoursValidators,
1205 if (hour <= (24 * 7) || flapRateHours > 24)
1209 BEAST_EXPECT(enabled.
empty());
1215 bool const expectMajority = (delay <= 24)
1217 : &thisHoursValidators == &allValidators;
1218 BEAST_EXPECT(majority.
empty() != expectMajority);
1226 BEAST_EXPECT(!enabled.
empty());
1227 BEAST_EXPECT(majority.
empty());
1238 using namespace std::chrono_literals;
1239 weeks constexpr w(1);
1242 BEAST_EXPECT(!table->hasUnsupportedEnabled());
1243 BEAST_EXPECT(!table->firstUnsupportedExpected());
1244 BEAST_EXPECT(table->needValidatedLedger(1));
1250 [&enabled](
auto const& s) { enabled.insert(amendmentId(s)); });
1253 table->doValidatedLedger(1, enabled, majority);
1254 BEAST_EXPECT(table->hasUnsupportedEnabled());
1255 BEAST_EXPECT(!table->firstUnsupportedExpected());
1261 [&majority, &t](
auto const& s) {
1262 majority[amendmentId(s)] = NetClock::time_point{--t};
1265 table->doValidatedLedger(1, enabled, majority);
1266 BEAST_EXPECT(table->hasUnsupportedEnabled());
1268 table->firstUnsupportedExpected() &&
1272 BEAST_EXPECT(!table->needValidatedLedger(256));
1273 BEAST_EXPECT(table->needValidatedLedger(257));
1279 testNoOnUnknown(feat);
1280 testNoOnVetoed(feat);
1281 testVoteEnable(feat);
1282 testDetectMajority(feat);
1283 testLostMajority(feat);
1284 testChangedUNL(feat);
1285 testValidatorFlapping(feat);
1292 FeatureBitset const fixMajorityCalc{fixAmendmentMajorityCalc};
1298 testHasUnsupported();
1299 testFeature(
all - fixMajorityCalc);
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.
void testNoOnVetoed(FeatureBitset const &feat)
static std::vector< AmendmentTable::FeatureInfo > makeObsolete(std::vector< std::string > const &amendments)
static Section makeSection(std::vector< std::string > const &amendments)
static uint256 amendmentId(std::string in)
void testDetectMajority(FeatureBitset const &feat)
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)
std::vector< std::string > const obsolete_
std::vector< std::string > const unsupportedMajority_
static void combine_arg(std::vector< Arg > &dest, std::vector< Arg > const &src, Args const &... args)
void testChangedUNL(FeatureBitset const &feat)
std::unique_ptr< AmendmentTable > makeTable(test::jtx::Env &env, std::chrono::seconds majorityTime)
std::vector< std::string > const enabled_
test::SuiteJournal journal_
void testValidatorFlapping(FeatureBitset const &feat)
std::unique_ptr< Config > makeConfig()
std::vector< std::string > const vetoed_
void testFeature(FeatureBitset const &feat)
std::vector< std::string > const unsupported_
std::unique_ptr< AmendmentTable > makeTable(Application &app, std::chrono::seconds majorityTime, std::vector< AmendmentTable::FeatureInfo > const &supported, Section const &enabled, Section const &vetoed)
static NetClock::time_point hourTime(std::chrono::hours h)
static Section makeSection(std::string const &name, std::vector< std::string > const &amendments)
static std::vector< Arg > combine(std::vector< Arg > left, std::vector< Arg > const &right, Args const &... args)
void testNoOnUnknown(FeatureBitset const &feat)
Section const emptySection_
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)
static size_t totalsize(std::vector< Arg > const &src, Args const &... args)
static std::vector< AmendmentTable::FeatureInfo > makeFeatureInfo(std::vector< std::string > const &amendments, VoteBehavior voteBehavior)
std::vector< std::string > const allSupported_
static std::vector< AmendmentTable::FeatureInfo > makeDefaultNo(std::vector< std::string > const &amendments)
static std::vector< AmendmentTable::FeatureInfo > makeDefaultYes(uint256 const amendment)
static Section makeSection(uint256 const &amendment)
std::vector< std::string > const yes_
void testLostMajority(FeatureBitset const &feat)
std::vector< std::pair< PublicKey, SecretKey > > makeValidators(int num, std::unique_ptr< AmendmentTable > const &table)
std::vector< AmendmentTable::FeatureInfo > const emptyYes_
void testHasUnsupported()
void run() override
Runs the suite.
void testVoteEnable(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.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
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.
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
FeatureBitset testable_amendments()
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
void hash_append(Hasher &h, Slice const &v)
constexpr std::uint32_t tfGotMajority
std::chrono::duration< int, std::ratio_multiply< days::period, std::ratio< 7 > > > weeks
NodeID calcNodeID(PublicKey const &)
Calculate the 160-bit node ID from a node public key.
std::string to_string(base_uint< Bits, Tag > const &a)
std::pair< PublicKey, SecretKey > randomKeyPair(KeyType type)
Create a key pair using secure random numbers.
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