mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Move tests to app/tests
This commit is contained in:
committed by
Nik Bougalis
parent
c3da2e1f03
commit
8aafebbb75
@@ -1,251 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
#include <ripple/core/DatabaseCon.h>
|
||||
#include <ripple/app/misc/impl/AccountTxPaging.h>
|
||||
#include <ripple/protocol/types.h>
|
||||
#include <beast/cxx14/memory.h> // <memory>
|
||||
#include <beast/unit_test/suite.h>
|
||||
#include <cstdlib>
|
||||
#include <vector>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
struct AccountTxPaging_test : beast::unit_test::suite
|
||||
{
|
||||
std::unique_ptr<DatabaseCon> db_;
|
||||
NetworkOPs::AccountTxs txs_;
|
||||
AccountID account_;
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
std::string data_path;
|
||||
|
||||
if (auto const fixtures = std::getenv("TEST_FIXTURES"))
|
||||
data_path = fixtures;
|
||||
|
||||
if (data_path.empty ())
|
||||
{
|
||||
fail("The 'TEST_FIXTURES' environment variable is empty.");
|
||||
return;
|
||||
}
|
||||
|
||||
DatabaseCon::Setup dbConf;
|
||||
dbConf.dataDir = data_path + "/";
|
||||
|
||||
db_ = std::make_unique <DatabaseCon> (
|
||||
dbConf, "account-tx-transactions.db", nullptr, 0);
|
||||
|
||||
account_ = *parseBase58<AccountID>(
|
||||
"rfu6L5p3azwPzQZsbTafuVk884N9YoKvVG");
|
||||
|
||||
testAccountTxPaging();
|
||||
}
|
||||
|
||||
void
|
||||
checkToken (Json::Value const& token, int ledger, int sequence)
|
||||
{
|
||||
expect (token.isMember ("ledger"));
|
||||
expect (token["ledger"].asInt() == ledger);
|
||||
expect (token.isMember ("seq"));
|
||||
expect (token["seq"].asInt () == sequence);
|
||||
}
|
||||
|
||||
void
|
||||
checkTransaction (NetworkOPs::AccountTx const& tx, int ledger, int index)
|
||||
{
|
||||
expect (tx.second->getLgrSeq () == ledger);
|
||||
expect (tx.second->getIndex () == index);
|
||||
}
|
||||
|
||||
std::size_t
|
||||
next (
|
||||
int limit,
|
||||
bool forward,
|
||||
Json::Value& token,
|
||||
std::int32_t minLedger,
|
||||
std::int32_t maxLedger)
|
||||
{
|
||||
txs_.clear();
|
||||
|
||||
std::int32_t const page_length = 200;
|
||||
bool const admin = true;
|
||||
|
||||
auto& txs = txs_;
|
||||
|
||||
auto bound = [&txs](
|
||||
std::uint32_t ledger_index,
|
||||
std::string const& status,
|
||||
Blob const& rawTxn,
|
||||
Blob const& rawMeta)
|
||||
{
|
||||
convertBlobsToTxResult (txs, ledger_index, status, rawTxn, rawMeta);
|
||||
};
|
||||
|
||||
accountTxPage(*db_, [](std::uint32_t){}, bound, account_, minLedger,
|
||||
maxLedger, forward, token, limit, admin, page_length);
|
||||
|
||||
return txs_.size();
|
||||
}
|
||||
|
||||
void
|
||||
testAccountTxPaging ()
|
||||
{
|
||||
using namespace std::placeholders;
|
||||
|
||||
bool const forward = true;
|
||||
|
||||
std::int32_t min_ledger;
|
||||
std::int32_t max_ledger;
|
||||
Json::Value token;
|
||||
int limit;
|
||||
|
||||
// the supplied account-tx-transactions.db contains contains
|
||||
// transactions with the following ledger/sequence pairs.
|
||||
// 3|5
|
||||
// 4|4
|
||||
// 4|10
|
||||
// 5|4
|
||||
// 5|7
|
||||
// 6|1
|
||||
// 6|5
|
||||
// 6|6
|
||||
// 6|7
|
||||
// 6|8
|
||||
// 6|9
|
||||
// 6|10
|
||||
// 6|11
|
||||
|
||||
min_ledger = 2;
|
||||
max_ledger = 5;
|
||||
|
||||
{
|
||||
limit = 2;
|
||||
|
||||
expect (next(limit, forward, token, min_ledger, max_ledger) == 2);
|
||||
checkTransaction (txs_[0], 3, 5);
|
||||
checkTransaction (txs_[1], 4, 4);
|
||||
checkToken (token, 4, 10);
|
||||
|
||||
expect (next(limit, forward, token, min_ledger, max_ledger) == 2);
|
||||
checkTransaction (txs_[0], 4, 10);
|
||||
checkTransaction (txs_[1], 5, 4);
|
||||
checkToken (token, 5, 7);
|
||||
|
||||
expect (next(limit, forward, token, min_ledger, max_ledger) == 1);
|
||||
checkTransaction (txs_[0], 5, 7);
|
||||
|
||||
expect(! token["ledger"]);
|
||||
expect(! token["seq"]);
|
||||
}
|
||||
|
||||
token = Json::nullValue;
|
||||
|
||||
min_ledger = 3;
|
||||
max_ledger = 9;
|
||||
|
||||
{
|
||||
limit = 1;
|
||||
|
||||
expect (next(limit, forward, token, min_ledger, max_ledger) == 1);
|
||||
checkTransaction (txs_[0], 3, 5);
|
||||
checkToken (token, 4, 4);
|
||||
|
||||
expect(next(limit, forward, token, min_ledger, max_ledger) == 1);
|
||||
checkTransaction (txs_[0], 4, 4);
|
||||
checkToken (token, 4, 10);
|
||||
|
||||
expect (next(limit, forward, token, min_ledger, max_ledger) == 1);
|
||||
checkTransaction (txs_[0], 4, 10);
|
||||
checkToken (token, 5, 4);
|
||||
}
|
||||
|
||||
{
|
||||
limit = 3;
|
||||
|
||||
expect (next(limit, forward, token, min_ledger, max_ledger) == 3);
|
||||
checkTransaction (txs_[0], 5, 4);
|
||||
checkTransaction (txs_[1], 5, 7);
|
||||
checkTransaction (txs_[2], 6, 1);
|
||||
checkToken (token, 6, 5);
|
||||
|
||||
expect (next(limit, forward, token, min_ledger, max_ledger) == 3);
|
||||
checkTransaction (txs_[0], 6, 5);
|
||||
checkTransaction (txs_[1], 6, 6);
|
||||
checkTransaction (txs_[2], 6, 7);
|
||||
checkToken (token, 6, 8);
|
||||
|
||||
expect (next(limit, forward, token, min_ledger, max_ledger) == 3);
|
||||
checkTransaction (txs_[0], 6, 8);
|
||||
checkTransaction (txs_[1], 6, 9);
|
||||
checkTransaction (txs_[2], 6, 10);
|
||||
checkToken (token, 6, 11);
|
||||
|
||||
expect(next(limit, forward, token, min_ledger, max_ledger) == 1);
|
||||
checkTransaction (txs_[0], 6, 11);
|
||||
|
||||
expect(! token["ledger"]);
|
||||
expect(! token["seq"]);
|
||||
}
|
||||
|
||||
token = Json::nullValue;
|
||||
|
||||
{
|
||||
limit = 2;
|
||||
|
||||
expect (next(limit, ! forward, token, min_ledger, max_ledger) == 2);
|
||||
checkTransaction (txs_[0], 6, 11);
|
||||
checkTransaction (txs_[1], 6, 10);
|
||||
checkToken (token, 6, 9);
|
||||
|
||||
expect(next(limit, ! forward, token, min_ledger, max_ledger) == 2);
|
||||
checkTransaction (txs_[0], 6, 9);
|
||||
checkTransaction (txs_[1], 6, 8);
|
||||
checkToken (token, 6, 7);
|
||||
}
|
||||
|
||||
{
|
||||
limit = 3;
|
||||
|
||||
expect (next(limit, ! forward, token, min_ledger, max_ledger) == 3);
|
||||
checkTransaction (txs_[0], 6, 7);
|
||||
checkTransaction (txs_[1], 6, 6);
|
||||
checkTransaction (txs_[2], 6, 5);
|
||||
checkToken (token, 6, 1);
|
||||
|
||||
expect (next(limit, ! forward, token, min_ledger, max_ledger) == 3);
|
||||
checkTransaction (txs_[0], 6, 1);
|
||||
checkTransaction (txs_[1], 5, 7);
|
||||
checkTransaction (txs_[2], 5, 4);
|
||||
checkToken (token, 4, 10);
|
||||
|
||||
expect (next(limit, ! forward, token, min_ledger, max_ledger) == 3);
|
||||
checkTransaction (txs_[0], 4, 10);
|
||||
checkTransaction (txs_[1], 4, 4);
|
||||
checkTransaction (txs_[2], 3, 5);
|
||||
}
|
||||
|
||||
expect (! token["ledger"]);
|
||||
expect (! token["seq"]);
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE_MANUAL(AccountTxPaging,app,ripple);
|
||||
|
||||
}
|
||||
@@ -1,793 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <BeastConfig.h>
|
||||
|
||||
#include <ripple/app/misc/AmendmentTable.h>
|
||||
#include <ripple/basics/BasicConfig.h>
|
||||
#include <ripple/basics/chrono.h>
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <ripple/core/ConfigSections.h>
|
||||
#include <ripple/protocol/TxFlags.h>
|
||||
#include <beast/unit_test/suite.h>
|
||||
|
||||
namespace ripple
|
||||
{
|
||||
|
||||
class AmendmentTable_test final : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
using StringPairVec = std::vector<std::pair<std::string, std::string>>;
|
||||
|
||||
private:
|
||||
enum class TablePopulationAlgo
|
||||
{
|
||||
addInitial,
|
||||
addKnown
|
||||
};
|
||||
|
||||
// 204/256 about 80% (we round down because the implementation rounds up)
|
||||
static int const majorityFraction{204};
|
||||
|
||||
static void populateTable (AmendmentTable& table,
|
||||
std::vector<std::string> const& configLines)
|
||||
{
|
||||
Section section (SECTION_AMENDMENTS);
|
||||
section.append (configLines);
|
||||
table.addInitial (section);
|
||||
}
|
||||
|
||||
static std::vector<AmendmentName> getAmendmentNames (
|
||||
StringPairVec const& amendmentPairs)
|
||||
{
|
||||
std::vector<AmendmentName> amendmentNames;
|
||||
amendmentNames.reserve (amendmentPairs.size ());
|
||||
for (auto const& i : amendmentPairs)
|
||||
{
|
||||
amendmentNames.emplace_back (i.first, i.second);
|
||||
}
|
||||
return amendmentNames;
|
||||
}
|
||||
|
||||
std::vector<AmendmentName> populateTable (
|
||||
AmendmentTable& table,
|
||||
StringPairVec const& amendmentPairs,
|
||||
TablePopulationAlgo populationAlgo = TablePopulationAlgo::addKnown)
|
||||
{
|
||||
std::vector<AmendmentName> const amendmentNames (
|
||||
getAmendmentNames (amendmentPairs));
|
||||
switch (populationAlgo)
|
||||
{
|
||||
case TablePopulationAlgo::addKnown:
|
||||
for (auto const& i : amendmentNames)
|
||||
{
|
||||
table.addKnown (i);
|
||||
}
|
||||
break;
|
||||
case TablePopulationAlgo::addInitial:
|
||||
{
|
||||
std::vector<std::string> configLines;
|
||||
configLines.reserve (amendmentPairs.size ());
|
||||
for (auto const& i : amendmentPairs)
|
||||
{
|
||||
configLines.emplace_back (i.first + " " + i.second);
|
||||
}
|
||||
populateTable (table, configLines);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
fail ("Error in test case logic");
|
||||
}
|
||||
|
||||
return amendmentNames;
|
||||
}
|
||||
|
||||
static std::unique_ptr< AmendmentTable >
|
||||
makeTable (int w)
|
||||
{
|
||||
return make_AmendmentTable (
|
||||
weeks (w),
|
||||
majorityFraction,
|
||||
deprecatedLogs().journal("TestAmendmentTable"));
|
||||
};
|
||||
|
||||
// Create the amendments by string pairs instead of AmendmentNames
|
||||
// as this helps test the AmendmentNames class
|
||||
StringPairVec const m_knownAmendmentPairs;
|
||||
StringPairVec const m_unknownAmendmentPairs;
|
||||
|
||||
public:
|
||||
AmendmentTable_test ()
|
||||
: m_knownAmendmentPairs (
|
||||
{{"a49f90e7cddbcadfed8fc89ec4d02011", "Known1"},
|
||||
{"ca956ccabf25151a16d773171c485423", "Known2"},
|
||||
{"60dcd528f057711c5d26b57be28e23df", "Known3"},
|
||||
{"da956ccabf25151a16d773171c485423", "Known4"},
|
||||
{"70dcd528f057711c5d26b57be28e23df", "Known5"},
|
||||
{"70dcd528f057711c5d26b57be28e23d0", "Known6"}})
|
||||
, m_unknownAmendmentPairs (
|
||||
{{"a9f90e7cddbcadfed8fc89ec4d02011c", "Unknown1"},
|
||||
{"c956ccabf25151a16d773171c485423b", "Unknown2"},
|
||||
{"6dcd528f057711c5d26b57be28e23dfa", "Unknown3"}})
|
||||
{
|
||||
}
|
||||
|
||||
void testGet ()
|
||||
{
|
||||
testcase ("get");
|
||||
auto table (makeTable (2));
|
||||
std::vector<AmendmentName> const amendmentNames (
|
||||
populateTable (*table, m_knownAmendmentPairs));
|
||||
std::vector<AmendmentName> const unknownAmendmentNames (
|
||||
getAmendmentNames (m_unknownAmendmentPairs));
|
||||
for (auto const& i : amendmentNames)
|
||||
{
|
||||
expect (table->get (i.friendlyName ()) == i.id ());
|
||||
}
|
||||
|
||||
for (auto const& i : unknownAmendmentNames)
|
||||
{
|
||||
expect (table->get (i.friendlyName ()) == uint256 ());
|
||||
}
|
||||
}
|
||||
|
||||
void testAddInitialAddKnown ()
|
||||
{
|
||||
testcase ("addInitialAddKnown");
|
||||
|
||||
for (auto tablePopulationAlgo :
|
||||
{TablePopulationAlgo::addInitial, TablePopulationAlgo::addKnown})
|
||||
{
|
||||
{
|
||||
// test that the amendments we add are enabled and amendments we
|
||||
// didn't add are not enabled
|
||||
|
||||
auto table (makeTable (2));
|
||||
std::vector<AmendmentName> const amendmentNames (populateTable (
|
||||
*table, m_knownAmendmentPairs, tablePopulationAlgo));
|
||||
std::vector<AmendmentName> const unknownAmendmentNames (
|
||||
getAmendmentNames (m_unknownAmendmentPairs));
|
||||
|
||||
for (auto const& i : amendmentNames)
|
||||
{
|
||||
expect (table->isSupported (i.id ()));
|
||||
if (tablePopulationAlgo == TablePopulationAlgo::addInitial)
|
||||
expect (table->isEnabled (i.id ()));
|
||||
}
|
||||
|
||||
for (auto const& i : unknownAmendmentNames)
|
||||
{
|
||||
expect (!table->isSupported (i.id ()));
|
||||
expect (!table->isEnabled (i.id ()));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// check that we throw an exception on bad hex pairs
|
||||
StringPairVec const badHexPairs (
|
||||
{{"a9f90e7cddbcadfedm8fc89ec4d02011c", "BadHex1"},
|
||||
{"c956ccabf25151a16d77T3171c485423b", "BadHex2"},
|
||||
{"6dcd528f057711c5d2Z6b57be28e23dfa", "BadHex3"}});
|
||||
|
||||
// make sure each element throws
|
||||
for (auto const& i : badHexPairs)
|
||||
{
|
||||
StringPairVec v ({i});
|
||||
auto table (makeTable (2));
|
||||
try
|
||||
{
|
||||
populateTable (*table, v, tablePopulationAlgo);
|
||||
// line above should throw
|
||||
fail ("didn't throw");
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
pass ();
|
||||
}
|
||||
try
|
||||
{
|
||||
populateTable (
|
||||
*table, badHexPairs, tablePopulationAlgo);
|
||||
// line above should throw
|
||||
fail ("didn't throw");
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
pass ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// check that we thow on bad num tokens
|
||||
std::vector<std::string> const badNumTokensConfigLines (
|
||||
{"19f6d",
|
||||
"19fd6 bad friendly name"
|
||||
"9876 one two"});
|
||||
|
||||
// make sure each element throws
|
||||
for (auto const& i : badNumTokensConfigLines)
|
||||
{
|
||||
std::vector<std::string> v ({i});
|
||||
auto table (makeTable (2));
|
||||
try
|
||||
{
|
||||
populateTable (*table, v);
|
||||
// line above should throw
|
||||
fail ("didn't throw");
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
pass ();
|
||||
}
|
||||
try
|
||||
{
|
||||
populateTable (*table, badNumTokensConfigLines);
|
||||
// line above should throw
|
||||
fail ("didn't throw");
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
pass ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void testEnable ()
|
||||
{
|
||||
testcase ("enable");
|
||||
auto table (makeTable (2));
|
||||
std::vector<AmendmentName> const amendmentNames (
|
||||
populateTable (*table, m_knownAmendmentPairs));
|
||||
{
|
||||
// enable/disable tests
|
||||
for (auto const& i : amendmentNames)
|
||||
{
|
||||
auto id (i.id ());
|
||||
table->enable (id);
|
||||
expect (table->isEnabled (id));
|
||||
table->disable (id);
|
||||
expect (!table->isEnabled (id));
|
||||
table->enable (id);
|
||||
expect (table->isEnabled (id));
|
||||
}
|
||||
|
||||
std::vector<uint256> toEnable;
|
||||
for (auto const& i : amendmentNames)
|
||||
{
|
||||
auto id (i.id ());
|
||||
toEnable.emplace_back (id);
|
||||
table->disable (id);
|
||||
expect (!table->isEnabled (id));
|
||||
}
|
||||
table->setEnabled (toEnable);
|
||||
for (auto const& i : toEnable)
|
||||
{
|
||||
expect (table->isEnabled (i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
using ATSetter =
|
||||
void (AmendmentTable::*)(const std::vector<uint256>& amendments);
|
||||
using ATGetter = bool (AmendmentTable::*)(uint256 const& amendment);
|
||||
void testVectorSetUnset (ATSetter setter, ATGetter getter)
|
||||
{
|
||||
auto table (makeTable (2));
|
||||
// make pointer to ref syntax a little nicer
|
||||
auto& tableRef (*table);
|
||||
std::vector<AmendmentName> const amendmentNames (
|
||||
populateTable (tableRef, m_knownAmendmentPairs));
|
||||
|
||||
// they should all be set
|
||||
for (auto const& i : amendmentNames)
|
||||
{
|
||||
expect ((tableRef.*getter)(i.id ())); // i.e. "isSupported"
|
||||
}
|
||||
|
||||
{
|
||||
// only set every other amendment
|
||||
std::vector<uint256> toSet;
|
||||
toSet.reserve (amendmentNames.size ());
|
||||
for (int i = 0; i < amendmentNames.size (); ++i)
|
||||
{
|
||||
if (i % 2)
|
||||
{
|
||||
toSet.emplace_back (amendmentNames[i].id ());
|
||||
}
|
||||
}
|
||||
(tableRef.*setter)(toSet);
|
||||
for (int i = 0; i < amendmentNames.size (); ++i)
|
||||
{
|
||||
bool const shouldBeSet = i % 2;
|
||||
expect (shouldBeSet ==
|
||||
(tableRef.*getter)(
|
||||
amendmentNames[i].id ())); // i.e. "isSupported"
|
||||
}
|
||||
}
|
||||
}
|
||||
void testSupported ()
|
||||
{
|
||||
testcase ("supported");
|
||||
testVectorSetUnset (&AmendmentTable::setSupported,
|
||||
&AmendmentTable::isSupported);
|
||||
}
|
||||
void testEnabled ()
|
||||
{
|
||||
testcase ("enabled");
|
||||
testVectorSetUnset (&AmendmentTable::setEnabled,
|
||||
&AmendmentTable::isEnabled);
|
||||
}
|
||||
void testSupportedEnabled ()
|
||||
{
|
||||
// Check that supported/enabled aren't the same thing
|
||||
testcase ("supportedEnabled");
|
||||
auto table (makeTable (2));
|
||||
|
||||
std::vector<AmendmentName> const amendmentNames (
|
||||
populateTable (*table, m_knownAmendmentPairs));
|
||||
|
||||
{
|
||||
// support every even amendment
|
||||
// enable every odd amendment
|
||||
std::vector<uint256> toSupport;
|
||||
toSupport.reserve (amendmentNames.size ());
|
||||
std::vector<uint256> toEnable;
|
||||
toEnable.reserve (amendmentNames.size ());
|
||||
for (int i = 0; i < amendmentNames.size (); ++i)
|
||||
{
|
||||
if (i % 2)
|
||||
{
|
||||
toSupport.emplace_back (amendmentNames[i].id ());
|
||||
}
|
||||
else
|
||||
{
|
||||
toEnable.emplace_back (amendmentNames[i].id ());
|
||||
}
|
||||
}
|
||||
table->setEnabled (toEnable);
|
||||
table->setSupported (toSupport);
|
||||
for (int i = 0; i < amendmentNames.size (); ++i)
|
||||
{
|
||||
bool const shouldBeSupported = i % 2;
|
||||
bool const shouldBeEnabled = !(i % 2);
|
||||
expect (shouldBeEnabled ==
|
||||
(table->isEnabled (amendmentNames[i].id ())));
|
||||
expect (shouldBeSupported ==
|
||||
(table->isSupported (amendmentNames[i].id ())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector <RippleAddress> makeValidators (int num)
|
||||
{
|
||||
std::vector <RippleAddress> ret;
|
||||
ret.reserve (num);
|
||||
for (int i = 0; i < num; ++i)
|
||||
ret.push_back (RippleAddress::createNodePublic (
|
||||
RippleAddress::createSeedRandom ()));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static std::uint32_t weekTime (int w)
|
||||
{
|
||||
return w * (7*24*60*60);
|
||||
}
|
||||
|
||||
// Execute a pretend consensus round for a flag ledger
|
||||
void doRound
|
||||
( AmendmentTable& table
|
||||
, int week
|
||||
, std::vector <RippleAddress> const& validators
|
||||
, std::vector <std::pair <uint256, int> > const& votes
|
||||
, std::vector <uint256>& ourVotes
|
||||
, enabledAmendments_t& enabled
|
||||
, majorityAmendments_t& majority)
|
||||
{
|
||||
// Do a round at the specified time
|
||||
// Returns the amendments we voted for
|
||||
|
||||
// Parameters:
|
||||
// table: Our table of known and vetoed amendments
|
||||
// validators: The addreses of validators we trust
|
||||
// votes: Amendments and the number of validators who vote for them
|
||||
// ourVotes: The amendments we vote for in our validation
|
||||
// enabled: In/out enabled amendments
|
||||
// majority: In/our majority amendments (and when they got a majority)
|
||||
|
||||
std::uint32_t const roundTime = weekTime (week);
|
||||
|
||||
// Build validations
|
||||
ValidationSet validations;
|
||||
validations.reserve (validators.size ());
|
||||
|
||||
int i = 0;
|
||||
for (auto const& val : validators)
|
||||
{
|
||||
STValidation::pointer v =
|
||||
std::make_shared <STValidation>
|
||||
(uint256(), roundTime, val, true);
|
||||
|
||||
++i;
|
||||
STVector256 field (sfAmendments);
|
||||
|
||||
for (auto const& amendment : votes)
|
||||
{
|
||||
if ((256 * i) < (validators.size() * amendment.second))
|
||||
{
|
||||
// We vote yes on this amendment
|
||||
field.push_back (amendment.first);
|
||||
}
|
||||
}
|
||||
if (!field.empty ())
|
||||
v->setFieldV256 (sfAmendments, field);
|
||||
|
||||
v->setTrusted();
|
||||
validations [val.getNodeID()] = v;
|
||||
}
|
||||
|
||||
ourVotes = table.doValidation (enabled);
|
||||
|
||||
auto actions = table.doVoting (roundTime, enabled, majority, validations);
|
||||
for (auto const& action : actions)
|
||||
{
|
||||
// This code assumes other validators do as we do
|
||||
|
||||
auto const& hash = action.first;
|
||||
switch (action.second)
|
||||
{
|
||||
case 0:
|
||||
// amendment goes from majority to enabled
|
||||
if (enabled.find (hash) != enabled.end ())
|
||||
throw std::runtime_error ("enabling already enabled");
|
||||
if (majority.find (hash) == majority.end ())
|
||||
throw std::runtime_error ("enabling without majority");
|
||||
enabled.insert (hash);
|
||||
majority.erase (hash);
|
||||
break;
|
||||
|
||||
case tfGotMajority:
|
||||
if (majority.find (hash) != majority.end ())
|
||||
throw std::runtime_error ("got majority while having majority");
|
||||
majority[hash] = roundTime;
|
||||
break;
|
||||
|
||||
case tfLostMajority:
|
||||
if (majority.find (hash) == majority.end ())
|
||||
throw std::runtime_error ("lost majority without majority");
|
||||
majority.erase (hash);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert (false);
|
||||
throw std::runtime_error ("unknown action");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No vote on unknown amendment
|
||||
void testNoUnknown ()
|
||||
{
|
||||
testcase ("voteNoUnknown");
|
||||
|
||||
auto table (makeTable (2));
|
||||
|
||||
auto const validators = makeValidators (10);
|
||||
|
||||
uint256 testAmendment;
|
||||
testAmendment.SetHex("6dcd528f057711c5d26b57be28e23dfa");
|
||||
|
||||
std::vector <std::pair <uint256, int>> votes;
|
||||
std::vector <uint256> ourVotes;
|
||||
enabledAmendments_t enabled;
|
||||
majorityAmendments_t majority;
|
||||
|
||||
doRound (*table, 1,
|
||||
validators,
|
||||
votes,
|
||||
ourVotes,
|
||||
enabled,
|
||||
majority);
|
||||
expect (ourVotes.empty(), "Voted with nothing to vote on");
|
||||
expect (enabled.empty(), "Enabled amendment for no reason");
|
||||
expect (majority.empty(), "Majority found for no reason");
|
||||
|
||||
votes.emplace_back (testAmendment, 256);
|
||||
|
||||
doRound (*table, 2,
|
||||
validators,
|
||||
votes,
|
||||
ourVotes,
|
||||
enabled,
|
||||
majority);
|
||||
expect (ourVotes.empty(), "Voted on unknown because others did");
|
||||
expect (enabled.empty(), "Enabled amendment for no reason");
|
||||
|
||||
majority[testAmendment] = weekTime(1);
|
||||
|
||||
// Note that the simulation code assumes others behave as we do,
|
||||
// so the amendment won't get enabled
|
||||
doRound (*table, 5,
|
||||
validators,
|
||||
votes,
|
||||
ourVotes,
|
||||
enabled,
|
||||
majority);
|
||||
expect (ourVotes.empty(), "Voted on unknown because it had majority");
|
||||
expect (enabled.empty(), "Pseudo-transaction from nowhere");
|
||||
}
|
||||
|
||||
// No vote on vetoed amendment
|
||||
void testNoVetoed ()
|
||||
{
|
||||
testcase ("voteNoVetoed");
|
||||
|
||||
auto table (makeTable (2));
|
||||
|
||||
auto const validators = makeValidators (10);
|
||||
|
||||
uint256 testAmendment;
|
||||
testAmendment.SetHex("6dcd528f057711c5d26b57be28e23dfa");
|
||||
table->veto(testAmendment);
|
||||
|
||||
std::vector <std::pair <uint256, int>> votes;
|
||||
std::vector <uint256> ourVotes;
|
||||
enabledAmendments_t enabled;
|
||||
majorityAmendments_t majority;
|
||||
|
||||
doRound (*table, 1,
|
||||
validators,
|
||||
votes,
|
||||
ourVotes,
|
||||
enabled,
|
||||
majority);
|
||||
expect (ourVotes.empty(), "Voted with nothing to vote on");
|
||||
expect (enabled.empty(), "Enabled amendment for no reason");
|
||||
expect (majority.empty(), "Majority found for no reason");
|
||||
|
||||
votes.emplace_back (testAmendment, 256);
|
||||
|
||||
doRound (*table, 2,
|
||||
validators,
|
||||
votes,
|
||||
ourVotes,
|
||||
enabled,
|
||||
majority);
|
||||
expect (ourVotes.empty(), "Voted on vetoed amendment because others did");
|
||||
expect (enabled.empty(), "Enabled amendment for no reason");
|
||||
|
||||
majority[testAmendment] = weekTime(1);
|
||||
|
||||
doRound (*table, 5,
|
||||
validators,
|
||||
votes,
|
||||
ourVotes,
|
||||
enabled,
|
||||
majority);
|
||||
expect (ourVotes.empty(), "Voted on vetoed because it had majority");
|
||||
expect (enabled.empty(), "Enabled amendment for no reason");
|
||||
}
|
||||
|
||||
// Vote on and enable known, not-enabled amendment
|
||||
void testVoteEnable ()
|
||||
{
|
||||
testcase ("voteEnable");
|
||||
|
||||
auto table (makeTable (2));
|
||||
auto const amendmentNames (
|
||||
populateTable (*table, m_knownAmendmentPairs));
|
||||
|
||||
auto const validators = makeValidators (10);
|
||||
|
||||
std::vector <std::pair <uint256, int>> votes;
|
||||
std::vector <uint256> ourVotes;
|
||||
enabledAmendments_t enabled;
|
||||
majorityAmendments_t majority;
|
||||
|
||||
// Week 1: We should vote for all known amendments not enabled
|
||||
doRound (*table, 1,
|
||||
validators,
|
||||
votes,
|
||||
ourVotes,
|
||||
enabled,
|
||||
majority);
|
||||
expect (ourVotes.size() == amendmentNames.size(), "Did not vote");
|
||||
expect (enabled.empty(), "Enabled amendment for no reason");
|
||||
for (auto const& i : amendmentNames)
|
||||
expect(majority.find(i.id()) == majority.end(), "majority detected for no reaosn");
|
||||
|
||||
// Now, everyone votes for this feature
|
||||
for (auto const& i : amendmentNames)
|
||||
votes.emplace_back (i.id(), 256);
|
||||
|
||||
// Week 2: We should recognize a majority
|
||||
doRound (*table, 2,
|
||||
validators,
|
||||
votes,
|
||||
ourVotes,
|
||||
enabled,
|
||||
majority);
|
||||
expect (ourVotes.size() == amendmentNames.size(), "Did not vote");
|
||||
expect (enabled.empty(), "Enabled amendment for no reason");
|
||||
for (auto const& i : amendmentNames)
|
||||
expect (majority[i.id()] == weekTime(2), "majority not detected");
|
||||
|
||||
// Week 5: We should enable the amendment
|
||||
doRound (*table, 5,
|
||||
validators,
|
||||
votes,
|
||||
ourVotes,
|
||||
enabled,
|
||||
majority);
|
||||
expect (enabled.size() == amendmentNames.size(), "Did not enable");
|
||||
|
||||
// Week 6: We should remove it from our votes and from having a majority
|
||||
doRound (*table, 6,
|
||||
validators,
|
||||
votes,
|
||||
ourVotes,
|
||||
enabled,
|
||||
majority);
|
||||
expect (enabled.size() == amendmentNames.size(), "Disabled");
|
||||
expect (ourVotes.empty(), "Voted after enabling");
|
||||
for (auto const& i : amendmentNames)
|
||||
expect(majority.find(i.id()) == majority.end(), "majority not removed");
|
||||
}
|
||||
|
||||
// Detect majority at 80%, enable later
|
||||
void testDetectMajority ()
|
||||
{
|
||||
testcase ("detectMajority");
|
||||
auto table (makeTable (2));
|
||||
|
||||
uint256 testAmendment;
|
||||
testAmendment.SetHex("6dcd528f057711c5d26b57be28e23dfa");
|
||||
table->addKnown({testAmendment, "testAmendment"});
|
||||
|
||||
auto const validators = makeValidators (16);
|
||||
|
||||
enabledAmendments_t enabled;
|
||||
majorityAmendments_t majority;
|
||||
|
||||
for (int i = 0; i <= 17; ++i)
|
||||
{
|
||||
std::vector <std::pair <uint256, int>> votes;
|
||||
std::vector <uint256> ourVotes;
|
||||
|
||||
if ((i > 0) && (i < 17))
|
||||
votes.emplace_back (testAmendment, i * 16);
|
||||
|
||||
doRound (*table, i,
|
||||
validators, votes, ourVotes, enabled, majority);
|
||||
|
||||
if (i < 14)
|
||||
{
|
||||
// rounds 0-13
|
||||
// We are voting yes, not enabled, no majority
|
||||
expect (!ourVotes.empty(), "We aren't voting");
|
||||
expect (enabled.empty(), "Enabled too early");
|
||||
expect (majority.empty(), "Majority too early");
|
||||
}
|
||||
else if (i < 16)
|
||||
{
|
||||
// rounds 14 and 15
|
||||
// We have a majority, not enabled, keep voting
|
||||
expect (!ourVotes.empty(), "We stopped voting");
|
||||
expect (!majority.empty(), "Failed to detect majority");
|
||||
expect (enabled.empty(), "Enabled too early");
|
||||
}
|
||||
else if (i == 16) // round 16
|
||||
{
|
||||
// round 16
|
||||
// enable, keep voting, remove from majority
|
||||
expect (!ourVotes.empty(), "We stopped voting");
|
||||
expect (majority.empty(), "Failed to remove from majority");
|
||||
expect (!enabled.empty(), "Did not enable");
|
||||
}
|
||||
else
|
||||
{
|
||||
// round 17
|
||||
// Done, we should be enabled and not voting
|
||||
expect (ourVotes.empty(), "We did not stop voting");
|
||||
expect (majority.empty(), "Failed to revove from majority");
|
||||
expect (!enabled.empty(), "Did not enable");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Detect loss of majority
|
||||
void testLostMajority ()
|
||||
{
|
||||
testcase ("lostMajority");
|
||||
|
||||
auto table (makeTable (8));
|
||||
|
||||
uint256 testAmendment;
|
||||
testAmendment.SetHex("6dcd528f057711c5d26b57be28e23dfa");
|
||||
table->addKnown({testAmendment, "testAmendment"});
|
||||
|
||||
auto const validators = makeValidators (16);
|
||||
|
||||
enabledAmendments_t enabled;
|
||||
majorityAmendments_t majority;
|
||||
|
||||
{
|
||||
// establish majority
|
||||
std::vector <std::pair <uint256, int>> votes;
|
||||
std::vector <uint256> ourVotes;
|
||||
|
||||
votes.emplace_back (testAmendment, 250);
|
||||
|
||||
doRound (*table, 1,
|
||||
validators, votes, ourVotes, enabled, majority);
|
||||
|
||||
expect (enabled.empty(), "Enabled for no reason");
|
||||
expect (!majority.empty(), "Failed to detect majority");
|
||||
}
|
||||
|
||||
for (int i = 1; i < 16; ++i)
|
||||
{
|
||||
std::vector <std::pair <uint256, int>> votes;
|
||||
std::vector <uint256> ourVotes;
|
||||
|
||||
// Gradually reduce support
|
||||
votes.emplace_back (testAmendment, 256 - i * 8);
|
||||
|
||||
doRound (*table, i + 1,
|
||||
validators, votes, ourVotes, enabled, majority);
|
||||
|
||||
if (i < 6)
|
||||
{
|
||||
// rounds 1 to 5
|
||||
// We are voting yes, not enabled, majority
|
||||
expect (!ourVotes.empty(), "We aren't voting");
|
||||
expect (enabled.empty(), "Enabled for no reason");
|
||||
expect (!majority.empty(), "Lost majority too early");
|
||||
}
|
||||
else
|
||||
{
|
||||
// rounds 6 to 15
|
||||
// No majority, not enabled, keep voting
|
||||
expect (!ourVotes.empty(), "We stopped voting");
|
||||
expect (majority.empty(), "Failed to detect loss of majority");
|
||||
expect (enabled.empty(), "Enabled errneously");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void run ()
|
||||
{
|
||||
testGet ();
|
||||
testAddInitialAddKnown ();
|
||||
testEnable ();
|
||||
testSupported ();
|
||||
testSupportedEnabled ();
|
||||
testNoUnknown ();
|
||||
testNoVetoed ();
|
||||
testVoteEnable ();
|
||||
testDetectMajority ();
|
||||
testLostMajority ();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE (AmendmentTable, app, ripple);
|
||||
|
||||
} // ripple
|
||||
Reference in New Issue
Block a user