mirror of
https://github.com/XRPLF/rippled.git
synced 2026-07-27 09:00:32 +00:00
test: Migrate resource, shamap Beast tests to GTest (#7133)
Co-authored-by: Alex Kremer <akremer@ripple.com>
This commit is contained in:
@@ -162,9 +162,6 @@ test.protocol > test.unit_test
|
||||
test.protocol > xrpl.basics
|
||||
test.protocol > xrpl.json
|
||||
test.protocol > xrpl.protocol
|
||||
test.resource > test.unit_test
|
||||
test.resource > xrpl.basics
|
||||
test.resource > xrpl.resource
|
||||
test.rpc > test.jtx
|
||||
test.rpc > xrpl.basics
|
||||
test.rpc > xrpl.config
|
||||
@@ -188,12 +185,6 @@ test.server > xrpld.core
|
||||
test.server > xrpl.json
|
||||
test.server > xrpl.protocol
|
||||
test.server > xrpl.server
|
||||
test.shamap > test.unit_test
|
||||
test.shamap > xrpl.basics
|
||||
test.shamap > xrpl.config
|
||||
test.shamap > xrpl.nodestore
|
||||
test.shamap > xrpl.protocol
|
||||
test.shamap > xrpl.shamap
|
||||
test.unit_test > xrpl.basics
|
||||
test.unit_test > xrpl.protocol
|
||||
tests.libxrpl > xrpl.basics
|
||||
@@ -205,6 +196,7 @@ tests.libxrpl > xrpl.net
|
||||
tests.libxrpl > xrpl.nodestore
|
||||
tests.libxrpl > xrpl.protocol
|
||||
tests.libxrpl > xrpl.protocol_autogen
|
||||
tests.libxrpl > xrpl.resource
|
||||
tests.libxrpl > xrpl.server
|
||||
tests.libxrpl > xrpl.shamap
|
||||
tests.libxrpl > xrpl.tx
|
||||
|
||||
@@ -1,296 +0,0 @@
|
||||
#include <test/unit_test/SuiteJournal.h>
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/chrono.h>
|
||||
#include <xrpl/basics/random.h>
|
||||
#include <xrpl/beast/insight/NullCollector.h>
|
||||
#include <xrpl/beast/net/IPAddressV4.h>
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/resource/Charge.h>
|
||||
#include <xrpl/resource/Consumer.h>
|
||||
#include <xrpl/resource/Disposition.h>
|
||||
#include <xrpl/resource/Gossip.h>
|
||||
#include <xrpl/resource/detail/Logic.h>
|
||||
#include <xrpl/resource/detail/Tuning.h>
|
||||
|
||||
#include <boost/utility/base_from_member.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace xrpl::Resource {
|
||||
|
||||
class ResourceManager_test : public beast::unit_test::Suite
|
||||
{
|
||||
public:
|
||||
class TestLogic : private boost::base_from_member<TestStopwatch>, public Logic
|
||||
|
||||
{
|
||||
private:
|
||||
using clock_type = boost::base_from_member<TestStopwatch>;
|
||||
|
||||
public:
|
||||
explicit TestLogic(beast::Journal journal)
|
||||
: Logic(beast::insight::NullCollector::make(), member, journal)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
advance()
|
||||
{
|
||||
++member;
|
||||
}
|
||||
|
||||
TestStopwatch&
|
||||
clock()
|
||||
{
|
||||
return member;
|
||||
}
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
static void
|
||||
createGossip(Gossip& gossip)
|
||||
{
|
||||
std::uint8_t const v(10 + randInt(9));
|
||||
std::uint8_t const n(10 + randInt(9));
|
||||
gossip.items.reserve(n);
|
||||
for (std::uint8_t i = 0; i < n; ++i)
|
||||
{
|
||||
Gossip::Item item;
|
||||
item.balance = 100 + randInt(499);
|
||||
beast::IP::AddressV4::bytes_type const d = {
|
||||
{192, 0, 2, static_cast<std::uint8_t>(v + i)}};
|
||||
item.address = beast::IP::Endpoint{beast::IP::AddressV4{d}};
|
||||
gossip.items.push_back(item);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void
|
||||
testDrop(beast::Journal j, bool limited)
|
||||
{
|
||||
if (limited)
|
||||
{
|
||||
testcase("Limited warn/drop");
|
||||
}
|
||||
else
|
||||
{
|
||||
testcase("Unlimited warn/drop");
|
||||
}
|
||||
|
||||
TestLogic logic(j);
|
||||
|
||||
Charge const fee(kDropThreshold + 1);
|
||||
beast::IP::Endpoint const addr(beast::IP::Endpoint::fromString("192.0.2.2"));
|
||||
|
||||
std::function<Consumer(beast::IP::Endpoint)> const ep =
|
||||
[&logic, limited](beast::IP::Endpoint const& address) {
|
||||
return limited ? logic.newInboundEndpoint(address)
|
||||
: logic.newUnlimitedEndpoint(address);
|
||||
};
|
||||
|
||||
{
|
||||
Consumer c(ep(addr));
|
||||
|
||||
// Create load until we get a warning
|
||||
int n = 10000;
|
||||
|
||||
while (--n >= 0)
|
||||
{
|
||||
if (n == 0)
|
||||
{
|
||||
if (limited)
|
||||
{
|
||||
fail("Loop count exceeded without warning");
|
||||
}
|
||||
else
|
||||
{
|
||||
pass();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (c.charge(fee) == Disposition::Warn)
|
||||
{
|
||||
if (limited)
|
||||
{
|
||||
pass();
|
||||
}
|
||||
else
|
||||
{
|
||||
fail("Should loop forever with no warning");
|
||||
}
|
||||
break;
|
||||
}
|
||||
++logic.clock();
|
||||
}
|
||||
|
||||
// Create load until we get dropped
|
||||
while (--n >= 0)
|
||||
{
|
||||
if (n == 0)
|
||||
{
|
||||
if (limited)
|
||||
{
|
||||
fail("Loop count exceeded without dropping");
|
||||
}
|
||||
else
|
||||
{
|
||||
pass();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (c.charge(fee) == Disposition::Drop)
|
||||
{
|
||||
// Disconnect abusive Consumer
|
||||
BEAST_EXPECT(c.disconnect(j) == limited);
|
||||
break;
|
||||
}
|
||||
++logic.clock();
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the consumer is on the blacklist for a while.
|
||||
{
|
||||
Consumer const c(logic.newInboundEndpoint(addr));
|
||||
logic.periodicActivity();
|
||||
if (c.disposition() != Disposition::Drop)
|
||||
{
|
||||
if (limited)
|
||||
{
|
||||
fail("Dropped consumer not put on blacklist");
|
||||
}
|
||||
else
|
||||
{
|
||||
pass();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Makes sure the Consumer is eventually removed from blacklist
|
||||
bool readmitted = false;
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
// Give Consumer time to become readmitted. Should never
|
||||
// exceed expiration time.
|
||||
auto n = kSecondsUntilExpiration + 1s;
|
||||
while (--n > 0s)
|
||||
{
|
||||
++logic.clock();
|
||||
logic.periodicActivity();
|
||||
Consumer const c(logic.newInboundEndpoint(addr));
|
||||
if (c.disposition() != Disposition::Drop)
|
||||
{
|
||||
readmitted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!readmitted)
|
||||
{
|
||||
fail("Dropped Consumer left on blacklist too long");
|
||||
return;
|
||||
}
|
||||
pass();
|
||||
}
|
||||
|
||||
void
|
||||
testImports(beast::Journal j)
|
||||
{
|
||||
testcase("Imports");
|
||||
|
||||
TestLogic logic(j);
|
||||
|
||||
Gossip g[5];
|
||||
|
||||
for (auto& gossip : g)
|
||||
createGossip(gossip);
|
||||
|
||||
for (int i = 0; i < 5; ++i)
|
||||
logic.importConsumers(std::to_string(i), g[i]);
|
||||
|
||||
pass();
|
||||
}
|
||||
|
||||
void
|
||||
testImport(beast::Journal j)
|
||||
{
|
||||
testcase("Import");
|
||||
|
||||
TestLogic logic(j);
|
||||
|
||||
Gossip g;
|
||||
Gossip::Item item;
|
||||
item.balance = 100;
|
||||
beast::IP::AddressV4::bytes_type const d = {{192, 0, 2, 1}};
|
||||
item.address = beast::IP::Endpoint{beast::IP::AddressV4{d}};
|
||||
g.items.push_back(item);
|
||||
|
||||
logic.importConsumers("g", g);
|
||||
|
||||
pass();
|
||||
}
|
||||
|
||||
void
|
||||
testCharges(beast::Journal j)
|
||||
{
|
||||
testcase("Charge");
|
||||
|
||||
TestLogic logic(j);
|
||||
|
||||
{
|
||||
beast::IP::Endpoint const address(beast::IP::Endpoint::fromString("192.0.2.1"));
|
||||
Consumer c(logic.newInboundEndpoint(address));
|
||||
Charge const fee(1000);
|
||||
JLOG(j.info()) << "Charging " << c.toString() << " " << fee << " per second";
|
||||
c.charge(fee);
|
||||
for (int i = 0; i < 128; ++i)
|
||||
{
|
||||
JLOG(j.info()) << "Time= " << logic.clock().now().time_since_epoch().count()
|
||||
<< ", Balance = " << c.balance();
|
||||
logic.advance();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
beast::IP::Endpoint const address(beast::IP::Endpoint::fromString("192.0.2.2"));
|
||||
Consumer c(logic.newInboundEndpoint(address));
|
||||
Charge const fee(1000);
|
||||
JLOG(j.info()) << "Charging " << c.toString() << " " << fee << " per second";
|
||||
for (int i = 0; i < 128; ++i)
|
||||
{
|
||||
c.charge(fee);
|
||||
JLOG(j.info()) << "Time= " << logic.clock().now().time_since_epoch().count()
|
||||
<< ", Balance = " << c.balance();
|
||||
logic.advance();
|
||||
}
|
||||
}
|
||||
|
||||
pass();
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
using beast::Severity;
|
||||
test::SuiteJournal journal("ResourceManager_test", *this);
|
||||
|
||||
testDrop(journal, true);
|
||||
testDrop(journal, false);
|
||||
testCharges(journal);
|
||||
testImports(journal);
|
||||
testImport(journal);
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(ResourceManager, resource, xrpl);
|
||||
|
||||
} // namespace xrpl::Resource
|
||||
@@ -1,177 +0,0 @@
|
||||
#include <test/shamap/common.h>
|
||||
#include <test/unit_test/SuiteJournal.h>
|
||||
|
||||
#include <xrpl/basics/Blob.h>
|
||||
#include <xrpl/basics/SHAMapHash.h>
|
||||
#include <xrpl/basics/Slice.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/basics/random.h>
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/beast/xor_shift_engine.h>
|
||||
#include <xrpl/protocol/Serializer.h>
|
||||
#include <xrpl/shamap/SHAMap.h>
|
||||
#include <xrpl/shamap/SHAMapItem.h>
|
||||
#include <xrpl/shamap/SHAMapMissingNode.h>
|
||||
#include <xrpl/shamap/SHAMapTreeNode.h>
|
||||
|
||||
#include <boost/smart_ptr/intrusive_ptr.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <list>
|
||||
#include <ostream>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl::tests {
|
||||
|
||||
class SHAMapSync_test : public beast::unit_test::Suite
|
||||
{
|
||||
public:
|
||||
beast::xor_shift_engine eng;
|
||||
|
||||
boost::intrusive_ptr<SHAMapItem>
|
||||
makeRandomAS()
|
||||
{
|
||||
Serializer s;
|
||||
|
||||
for (int d = 0; d < 3; ++d)
|
||||
s.add32(randInt<std::uint32_t>(eng));
|
||||
return makeShamapitem(s.getSHA512Half(), s.slice());
|
||||
}
|
||||
|
||||
bool
|
||||
confuseMap(SHAMap& map, int count)
|
||||
{
|
||||
// add a bunch of random states to a map, then remove them
|
||||
// map should be the same
|
||||
SHAMapHash const beforeHash = map.getHash();
|
||||
|
||||
std::list<uint256> items;
|
||||
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
auto item = makeRandomAS();
|
||||
items.push_back(item->key());
|
||||
|
||||
if (!map.addItem(SHAMapNodeType::TnAccountState, item))
|
||||
{
|
||||
log << "Unable to add item to map\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto const& item : items)
|
||||
{
|
||||
if (!map.delItem(item))
|
||||
{
|
||||
log << "Unable to remove item from map\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (beforeHash != map.getHash())
|
||||
{
|
||||
log << "Hashes do not match " << beforeHash << " " << map.getHash() << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
using beast::Severity;
|
||||
test::SuiteJournal journal("SHAMapSync_test", *this);
|
||||
|
||||
TestNodeFamily f(journal), f2(journal);
|
||||
SHAMap source(SHAMapType::FREE, f);
|
||||
SHAMap destination(SHAMapType::FREE, f2);
|
||||
|
||||
int const items = 10000;
|
||||
for (int i = 0; i < items; ++i)
|
||||
{
|
||||
source.addItem(SHAMapNodeType::TnAccountState, makeRandomAS());
|
||||
if (i % 100 == 0)
|
||||
source.invariants();
|
||||
}
|
||||
|
||||
source.invariants();
|
||||
BEAST_EXPECT(confuseMap(source, 500));
|
||||
source.invariants();
|
||||
|
||||
source.setImmutable();
|
||||
|
||||
int count = 0;
|
||||
source.visitLeaves([&count](auto const& item) { ++count; });
|
||||
BEAST_EXPECT(count == items);
|
||||
|
||||
std::vector<SHAMapMissingNode> missingNodes;
|
||||
source.walkMap(missingNodes, 2048);
|
||||
BEAST_EXPECT(missingNodes.empty());
|
||||
|
||||
destination.setSynching();
|
||||
|
||||
{
|
||||
std::vector<std::pair<SHAMapNodeID, Blob>> a;
|
||||
|
||||
BEAST_EXPECT(source.getNodeFat(SHAMapNodeID(), a, randBool(eng), randInt(eng, 2)));
|
||||
|
||||
unexpected(a.empty(), "NodeSize");
|
||||
|
||||
BEAST_EXPECT(destination.addRootNode(source.getHash(), makeSlice(a[0].second), nullptr)
|
||||
.isGood());
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
f.clock().advance(std::chrono::seconds(1));
|
||||
|
||||
// get the list of nodes we know we need
|
||||
auto nodesMissing = destination.getMissingNodes(2048, nullptr);
|
||||
|
||||
if (nodesMissing.empty())
|
||||
break;
|
||||
|
||||
// get as many nodes as possible based on this information
|
||||
std::vector<std::pair<SHAMapNodeID, Blob>> b;
|
||||
|
||||
for (auto& it : nodesMissing)
|
||||
{
|
||||
// Don't use BEAST_EXPECT here b/c it will be called a
|
||||
// non-deterministic number of times and the number of tests run
|
||||
// should be deterministic
|
||||
if (!source.getNodeFat(it.first, b, randBool(eng), randInt(eng, 2)))
|
||||
fail("", __FILE__, __LINE__);
|
||||
}
|
||||
|
||||
// Don't use BEAST_EXPECT here b/c it will be called a
|
||||
// non-deterministic number of times and the number of tests run
|
||||
// should be deterministic
|
||||
if (b.empty())
|
||||
fail("", __FILE__, __LINE__);
|
||||
|
||||
for (auto& node : b)
|
||||
{
|
||||
// Don't use BEAST_EXPECT here b/c it will be called a
|
||||
// non-deterministic number of times and the number of tests run
|
||||
// should be deterministic
|
||||
if (!destination.addKnownNode(node.first, makeSlice(node.second), nullptr)
|
||||
.isUseful())
|
||||
fail("", __FILE__, __LINE__);
|
||||
}
|
||||
} while (true);
|
||||
|
||||
destination.clearSynching();
|
||||
|
||||
BEAST_EXPECT(source.deepCompare(destination));
|
||||
|
||||
destination.invariants();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(SHAMapSync, shamap, xrpl);
|
||||
|
||||
} // namespace xrpl::tests
|
||||
@@ -1,427 +0,0 @@
|
||||
#include <test/shamap/common.h>
|
||||
#include <test/unit_test/SuiteJournal.h>
|
||||
|
||||
#include <xrpl/basics/Blob.h>
|
||||
#include <xrpl/basics/Buffer.h>
|
||||
#include <xrpl/basics/SHAMapHash.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/shamap/SHAMap.h>
|
||||
#include <xrpl/shamap/SHAMapInnerNode.h>
|
||||
#include <xrpl/shamap/SHAMapItem.h>
|
||||
#include <xrpl/shamap/SHAMapLeafNode.h>
|
||||
#include <xrpl/shamap/SHAMapMissingNode.h>
|
||||
#include <xrpl/shamap/SHAMapTreeNode.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl::tests {
|
||||
|
||||
#ifndef __INTELLISENSE__
|
||||
static_assert(std::is_nothrow_destructible<SHAMap>{});
|
||||
static_assert(!std::is_default_constructible<SHAMap>{});
|
||||
static_assert(!std::is_copy_constructible<SHAMap>{});
|
||||
static_assert(!std::is_copy_assignable<SHAMap>{});
|
||||
static_assert(!std::is_move_constructible<SHAMap>{});
|
||||
static_assert(!std::is_move_assignable<SHAMap>{});
|
||||
|
||||
static_assert(std::is_nothrow_destructible<SHAMap::ConstIterator>{});
|
||||
static_assert(std::is_copy_constructible<SHAMap::ConstIterator>{});
|
||||
static_assert(std::is_copy_assignable<SHAMap::ConstIterator>{});
|
||||
static_assert(std::is_move_constructible<SHAMap::ConstIterator>{});
|
||||
static_assert(std::is_move_assignable<SHAMap::ConstIterator>{});
|
||||
|
||||
static_assert(std::is_nothrow_destructible<SHAMapItem>{});
|
||||
static_assert(!std::is_default_constructible<SHAMapItem>{});
|
||||
static_assert(!std::is_copy_constructible<SHAMapItem>{});
|
||||
|
||||
static_assert(std::is_nothrow_destructible<SHAMapNodeID>{});
|
||||
static_assert(std::is_default_constructible<SHAMapNodeID>{});
|
||||
static_assert(std::is_copy_constructible<SHAMapNodeID>{});
|
||||
static_assert(std::is_copy_assignable<SHAMapNodeID>{});
|
||||
static_assert(std::is_move_constructible<SHAMapNodeID>{});
|
||||
static_assert(std::is_move_assignable<SHAMapNodeID>{});
|
||||
|
||||
static_assert(std::is_nothrow_destructible<SHAMapHash>{});
|
||||
static_assert(std::is_default_constructible<SHAMapHash>{});
|
||||
static_assert(std::is_copy_constructible<SHAMapHash>{});
|
||||
static_assert(std::is_copy_assignable<SHAMapHash>{});
|
||||
static_assert(std::is_move_constructible<SHAMapHash>{});
|
||||
static_assert(std::is_move_assignable<SHAMapHash>{});
|
||||
|
||||
static_assert(std::is_nothrow_destructible<SHAMapTreeNode>{});
|
||||
static_assert(!std::is_default_constructible<SHAMapTreeNode>{});
|
||||
static_assert(!std::is_copy_constructible<SHAMapTreeNode>{});
|
||||
static_assert(!std::is_copy_assignable<SHAMapTreeNode>{});
|
||||
static_assert(!std::is_move_constructible<SHAMapTreeNode>{});
|
||||
static_assert(!std::is_move_assignable<SHAMapTreeNode>{});
|
||||
|
||||
static_assert(std::is_nothrow_destructible<SHAMapInnerNode>{});
|
||||
static_assert(!std::is_default_constructible<SHAMapInnerNode>{});
|
||||
static_assert(!std::is_copy_constructible<SHAMapInnerNode>{});
|
||||
static_assert(!std::is_copy_assignable<SHAMapInnerNode>{});
|
||||
static_assert(!std::is_move_constructible<SHAMapInnerNode>{});
|
||||
static_assert(!std::is_move_assignable<SHAMapInnerNode>{});
|
||||
|
||||
static_assert(std::is_nothrow_destructible<SHAMapLeafNode>{});
|
||||
static_assert(!std::is_default_constructible<SHAMapLeafNode>{});
|
||||
static_assert(!std::is_copy_constructible<SHAMapLeafNode>{});
|
||||
static_assert(!std::is_copy_assignable<SHAMapLeafNode>{});
|
||||
static_assert(!std::is_move_constructible<SHAMapLeafNode>{});
|
||||
static_assert(!std::is_move_assignable<SHAMapLeafNode>{});
|
||||
#endif
|
||||
|
||||
inline bool
|
||||
operator==(SHAMapItem const& a, SHAMapItem const& b)
|
||||
{
|
||||
return a.key() == b.key();
|
||||
}
|
||||
inline bool
|
||||
operator!=(SHAMapItem const& a, SHAMapItem const& b)
|
||||
{
|
||||
return a.key() != b.key();
|
||||
}
|
||||
inline bool
|
||||
operator==(SHAMapItem const& a, uint256 const& b)
|
||||
{
|
||||
return a.key() == b;
|
||||
}
|
||||
inline bool
|
||||
operator!=(SHAMapItem const& a, uint256 const& b)
|
||||
{
|
||||
return a.key() != b;
|
||||
}
|
||||
|
||||
class SHAMap_test : public beast::unit_test::Suite
|
||||
{
|
||||
public:
|
||||
static Buffer
|
||||
intToVuc(int v)
|
||||
{
|
||||
Buffer vuc(32);
|
||||
std::fill_n(vuc.data(), vuc.size(), static_cast<std::uint8_t>(v));
|
||||
return vuc;
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
using beast::Severity;
|
||||
test::SuiteJournal journal("SHAMap_test", *this);
|
||||
|
||||
run(true, journal);
|
||||
run(false, journal);
|
||||
}
|
||||
|
||||
void
|
||||
run(bool backed, beast::Journal const& journal)
|
||||
{
|
||||
if (backed)
|
||||
{
|
||||
testcase("add/traverse backed");
|
||||
}
|
||||
else
|
||||
{
|
||||
testcase("add/traverse unbacked");
|
||||
}
|
||||
|
||||
tests::TestNodeFamily f(journal);
|
||||
|
||||
// h3 and h4 differ only in the leaf, same terminal node (level 19)
|
||||
constexpr uint256 kH1("092891fe4ef6cee585fdc6fda0e09eb4d386363158ec3321b8123e5a772c6ca7");
|
||||
constexpr uint256 kH2("436ccbac3347baa1f1e53baeef1f43334da88f1f6d70d963b833afd6dfa289fe");
|
||||
constexpr uint256 kH3("b92891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8");
|
||||
constexpr uint256 kH4("b92891fe4ef6cee585fdc6fda2e09eb4d386363158ec3321b8123e5a772c6ca8");
|
||||
constexpr uint256 kH5("a92891fe4ef6cee585fdc6fda0e09eb4d386363158ec3321b8123e5a772c6ca7");
|
||||
|
||||
SHAMap sMap(SHAMapType::FREE, f);
|
||||
sMap.invariants();
|
||||
if (!backed)
|
||||
sMap.setUnbacked();
|
||||
|
||||
auto i1 = makeShamapitem(kH1, intToVuc(1));
|
||||
auto i2 = makeShamapitem(kH2, intToVuc(2));
|
||||
auto i3 = makeShamapitem(kH3, intToVuc(3));
|
||||
auto i4 = makeShamapitem(kH4, intToVuc(4));
|
||||
auto i5 = makeShamapitem(kH5, intToVuc(5));
|
||||
|
||||
unexpected(!sMap.addItem(SHAMapNodeType::TnTransactionNm, makeShamapitem(*i2)), "no add");
|
||||
sMap.invariants();
|
||||
unexpected(!sMap.addItem(SHAMapNodeType::TnTransactionNm, makeShamapitem(*i1)), "no add");
|
||||
sMap.invariants();
|
||||
|
||||
auto i = sMap.begin();
|
||||
auto e = sMap.end();
|
||||
unexpected(i == e || (*i != *i1), "bad traverse");
|
||||
++i;
|
||||
unexpected(i == e || (*i != *i2), "bad traverse");
|
||||
++i;
|
||||
unexpected(i != e, "bad traverse");
|
||||
sMap.addItem(SHAMapNodeType::TnTransactionNm, makeShamapitem(*i4));
|
||||
sMap.invariants();
|
||||
sMap.delItem(i2->key());
|
||||
sMap.invariants();
|
||||
sMap.addItem(SHAMapNodeType::TnTransactionNm, makeShamapitem(*i3));
|
||||
sMap.invariants();
|
||||
i = sMap.begin();
|
||||
e = sMap.end();
|
||||
unexpected(i == e || (*i != *i1), "bad traverse");
|
||||
++i;
|
||||
unexpected(i == e || (*i != *i3), "bad traverse");
|
||||
++i;
|
||||
unexpected(i == e || (*i != *i4), "bad traverse");
|
||||
++i;
|
||||
unexpected(i != e, "bad traverse");
|
||||
|
||||
if (backed)
|
||||
{
|
||||
testcase("snapshot backed");
|
||||
}
|
||||
else
|
||||
{
|
||||
testcase("snapshot unbacked");
|
||||
}
|
||||
|
||||
SHAMapHash const mapHash = sMap.getHash();
|
||||
std::shared_ptr<SHAMap> const map2 = sMap.snapShot(false);
|
||||
map2->invariants();
|
||||
unexpected(sMap.getHash() != mapHash, "bad snapshot");
|
||||
unexpected(map2->getHash() != mapHash, "bad snapshot");
|
||||
|
||||
SHAMap::Delta delta;
|
||||
BEAST_EXPECT(sMap.compare(*map2, delta, 100));
|
||||
BEAST_EXPECT(delta.empty());
|
||||
|
||||
unexpected(!sMap.delItem(sMap.begin()->key()), "bad mod");
|
||||
sMap.invariants();
|
||||
unexpected(sMap.getHash() == mapHash, "bad snapshot");
|
||||
unexpected(map2->getHash() != mapHash, "bad snapshot");
|
||||
|
||||
BEAST_EXPECT(sMap.compare(*map2, delta, 100));
|
||||
BEAST_EXPECT(delta.size() == 1);
|
||||
BEAST_EXPECT(delta.begin()->first == kH1);
|
||||
BEAST_EXPECT(delta.begin()->second.first == nullptr);
|
||||
BEAST_EXPECT(delta.begin()->second.second->key() == kH1);
|
||||
|
||||
sMap.dump();
|
||||
|
||||
if (backed)
|
||||
{
|
||||
testcase("build/tear backed");
|
||||
}
|
||||
else
|
||||
{
|
||||
testcase("build/tear unbacked");
|
||||
}
|
||||
{
|
||||
static constexpr std::array keys{
|
||||
uint256(
|
||||
"b92891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
|
||||
"5a772c6ca8"),
|
||||
uint256(
|
||||
"b92881fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
|
||||
"5a772c6ca8"),
|
||||
uint256(
|
||||
"b92691fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
|
||||
"5a772c6ca8"),
|
||||
uint256(
|
||||
"b92791fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
|
||||
"5a772c6ca8"),
|
||||
uint256(
|
||||
"b91891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
|
||||
"5a772c6ca8"),
|
||||
uint256(
|
||||
"b99891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
|
||||
"5a772c6ca8"),
|
||||
uint256(
|
||||
"f22891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
|
||||
"5a772c6ca8"),
|
||||
uint256(
|
||||
"292891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
|
||||
"5a772c6ca8")};
|
||||
|
||||
static constexpr std::array kHashes{
|
||||
uint256(
|
||||
"B7387CFEA0465759ADC718E8C42B52D2309D179B326E239EB5075C"
|
||||
"64B6281F7F"),
|
||||
uint256(
|
||||
"FBC195A9592A54AB44010274163CB6BA95F497EC5BA0A883184546"
|
||||
"7FB2ECE266"),
|
||||
uint256(
|
||||
"4E7D2684B65DFD48937FFB775E20175C43AF0C94066F7D5679F51A"
|
||||
"E756795B75"),
|
||||
uint256(
|
||||
"7A2F312EB203695FFD164E038E281839EEF06A1B99BFC263F3CECC"
|
||||
"6C74F93E07"),
|
||||
uint256(
|
||||
"395A6691A372387A703FB0F2C6D2C405DAF307D0817F8F0E207596"
|
||||
"462B0E3A3E"),
|
||||
uint256(
|
||||
"D044C0A696DE3169CC70AE216A1564D69DE96582865796142CE7D9"
|
||||
"8A84D9DDE4"),
|
||||
uint256(
|
||||
"76DCC77C4027309B5A91AD164083264D70B77B5E43E08AEDA5EBF9"
|
||||
"4361143615"),
|
||||
uint256(
|
||||
"DF4220E93ADC6F5569063A01B4DC79F8DB9553B6A3222ADE23DEA0"
|
||||
"2BBE7230E5")};
|
||||
|
||||
SHAMap map(SHAMapType::FREE, f);
|
||||
if (!backed)
|
||||
map.setUnbacked();
|
||||
|
||||
BEAST_EXPECT(map.getHash() == beast::kZero);
|
||||
for (int k = 0; k < keys.size(); ++k)
|
||||
{
|
||||
BEAST_EXPECT(map.addItem(
|
||||
SHAMapNodeType::TnTransactionNm, makeShamapitem(keys[k], intToVuc(k))));
|
||||
BEAST_EXPECT(map.getHash().asUInt256() == kHashes[k]);
|
||||
map.invariants();
|
||||
}
|
||||
for (int k = keys.size() - 1; k >= 0; --k)
|
||||
{
|
||||
BEAST_EXPECT(map.getHash().asUInt256() == kHashes[k]);
|
||||
BEAST_EXPECT(map.delItem(keys[k]));
|
||||
map.invariants();
|
||||
}
|
||||
BEAST_EXPECT(map.getHash() == beast::kZero);
|
||||
}
|
||||
|
||||
if (backed)
|
||||
{
|
||||
testcase("iterate backed");
|
||||
}
|
||||
else
|
||||
{
|
||||
testcase("iterate unbacked");
|
||||
}
|
||||
|
||||
{
|
||||
static constexpr std::array keys{
|
||||
uint256(
|
||||
"f22891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
|
||||
"5a772c6ca8"),
|
||||
uint256(
|
||||
"b99891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
|
||||
"5a772c6ca8"),
|
||||
uint256(
|
||||
"b92891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
|
||||
"5a772c6ca8"),
|
||||
uint256(
|
||||
"b92881fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
|
||||
"5a772c6ca8"),
|
||||
uint256(
|
||||
"b92791fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
|
||||
"5a772c6ca8"),
|
||||
uint256(
|
||||
"b92691fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
|
||||
"5a772c6ca8"),
|
||||
uint256(
|
||||
"b91891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
|
||||
"5a772c6ca8"),
|
||||
uint256(
|
||||
"292891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
|
||||
"5a772c6ca8")};
|
||||
|
||||
tests::TestNodeFamily tf{journal};
|
||||
SHAMap map{SHAMapType::FREE, tf};
|
||||
if (!backed)
|
||||
map.setUnbacked();
|
||||
for (auto const& k : keys)
|
||||
{
|
||||
map.addItem(SHAMapNodeType::TnTransactionNm, makeShamapitem(k, intToVuc(0)));
|
||||
map.invariants();
|
||||
}
|
||||
|
||||
int h = 7;
|
||||
for (auto const& k : map)
|
||||
{
|
||||
BEAST_EXPECT(k.key() == keys[h]);
|
||||
--h;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class SHAMapPathProof_test : public beast::unit_test::Suite
|
||||
{
|
||||
void
|
||||
run() override
|
||||
{
|
||||
test::SuiteJournal journal("SHAMapPathProof_test", *this);
|
||||
|
||||
tests::TestNodeFamily tf{journal};
|
||||
SHAMap map{SHAMapType::FREE, tf};
|
||||
map.setUnbacked();
|
||||
|
||||
uint256 key;
|
||||
uint256 rootHash;
|
||||
std::vector<Blob> goodPath;
|
||||
|
||||
for (unsigned char c = 1; c < 100; ++c)
|
||||
{
|
||||
uint256 k(c);
|
||||
map.addItem(
|
||||
SHAMapNodeType::TnAccountState, makeShamapitem(k, Slice{k.data(), k.size()}));
|
||||
map.invariants();
|
||||
|
||||
auto root = map.getHash().asUInt256();
|
||||
auto path = map.getProofPath(k);
|
||||
BEAST_EXPECT(path);
|
||||
if (!path)
|
||||
break;
|
||||
BEAST_EXPECT(map.verifyProofPath(root, k, *path));
|
||||
if (c == 1)
|
||||
{
|
||||
// extra node
|
||||
path->insert(path->begin(), path->front());
|
||||
BEAST_EXPECT(!map.verifyProofPath(root, k, *path));
|
||||
// wrong key
|
||||
uint256 const wrongKey(c + 1);
|
||||
BEAST_EXPECT(!map.getProofPath(wrongKey));
|
||||
}
|
||||
if (c == 99)
|
||||
{
|
||||
key = k;
|
||||
rootHash = root;
|
||||
goodPath = std::move(*path);
|
||||
}
|
||||
}
|
||||
|
||||
// still good
|
||||
BEAST_EXPECT(map.verifyProofPath(rootHash, key, goodPath));
|
||||
// empty path
|
||||
std::vector<Blob> badPath;
|
||||
BEAST_EXPECT(!map.verifyProofPath(rootHash, key, badPath));
|
||||
// too long
|
||||
badPath = goodPath;
|
||||
badPath.push_back(goodPath.back());
|
||||
BEAST_EXPECT(!map.verifyProofPath(rootHash, key, badPath));
|
||||
// bad node
|
||||
badPath.clear();
|
||||
badPath.emplace_back(100, 100);
|
||||
BEAST_EXPECT(!map.verifyProofPath(rootHash, key, badPath));
|
||||
// bad node type
|
||||
badPath.clear();
|
||||
badPath.push_back(goodPath.front());
|
||||
badPath.front().back()--; // change node type
|
||||
BEAST_EXPECT(!map.verifyProofPath(rootHash, key, badPath));
|
||||
// all inner
|
||||
badPath.clear();
|
||||
badPath = goodPath;
|
||||
badPath.erase(badPath.begin());
|
||||
BEAST_EXPECT(!map.verifyProofPath(rootHash, key, badPath));
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(SHAMap, shamap, xrpl);
|
||||
BEAST_DEFINE_TESTSUITE(SHAMapPathProof, shamap, xrpl);
|
||||
} // namespace xrpl::tests
|
||||
@@ -29,6 +29,8 @@ set(test_modules
|
||||
basics
|
||||
crypto
|
||||
json
|
||||
resource
|
||||
shamap
|
||||
tx
|
||||
protocol_autogen
|
||||
)
|
||||
|
||||
241
src/tests/libxrpl/resource/Logic.cpp
Normal file
241
src/tests/libxrpl/resource/Logic.cpp
Normal file
@@ -0,0 +1,241 @@
|
||||
#include <xrpl/resource/detail/Logic.h>
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/chrono.h>
|
||||
#include <xrpl/basics/random.h>
|
||||
#include <xrpl/beast/insight/NullCollector.h>
|
||||
#include <xrpl/beast/net/IPAddressV4.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/resource/Charge.h>
|
||||
#include <xrpl/resource/Consumer.h>
|
||||
#include <xrpl/resource/Disposition.h>
|
||||
#include <xrpl/resource/Gossip.h>
|
||||
#include <xrpl/resource/detail/Tuning.h>
|
||||
|
||||
#include <boost/utility/base_from_member.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <helpers/TestSink.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace xrpl::Resource {
|
||||
|
||||
class ResourceManagerTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
beast::Journal const j_{TestSink::instance()};
|
||||
|
||||
class TestLogic : private boost::base_from_member<TestStopwatch>, public Logic
|
||||
{
|
||||
private:
|
||||
using clock_type = boost::base_from_member<TestStopwatch>;
|
||||
|
||||
public:
|
||||
explicit TestLogic(beast::Journal journal)
|
||||
: Logic(beast::insight::NullCollector::make(), member, journal)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
advance()
|
||||
{
|
||||
++member;
|
||||
}
|
||||
|
||||
TestStopwatch&
|
||||
clock()
|
||||
{
|
||||
return member;
|
||||
}
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
static void
|
||||
populateGossip(Gossip& gossip)
|
||||
{
|
||||
std::uint8_t const v(10 + randInt(9));
|
||||
std::uint8_t const n(10 + randInt(9));
|
||||
gossip.items.reserve(n);
|
||||
for (std::uint8_t i = 0; i < n; ++i)
|
||||
{
|
||||
Gossip::Item item;
|
||||
item.balance = 100 + randInt(499);
|
||||
beast::IP::AddressV4::bytes_type const d = {{
|
||||
192,
|
||||
0,
|
||||
2,
|
||||
static_cast<std::uint8_t>(v + i),
|
||||
}};
|
||||
item.address = beast::IP::Endpoint{beast::IP::AddressV4{d}};
|
||||
gossip.items.push_back(item);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(ResourceManagerTest, limited_warn_drop)
|
||||
{
|
||||
TestLogic logic{j_};
|
||||
|
||||
Charge const fee{kDropThreshold + 1};
|
||||
beast::IP::Endpoint const addr{beast::IP::Endpoint::fromString("192.0.2.2")};
|
||||
|
||||
{
|
||||
Consumer c{logic.newInboundEndpoint(addr)};
|
||||
|
||||
// Create load until we get a warning
|
||||
int n = 10000;
|
||||
bool warned = false;
|
||||
|
||||
while (--n >= 0)
|
||||
{
|
||||
if (c.charge(fee) == Disposition::Warn)
|
||||
{
|
||||
warned = true;
|
||||
break;
|
||||
}
|
||||
++logic.clock();
|
||||
}
|
||||
|
||||
ASSERT_TRUE(warned) << "Loop count exceeded without warning";
|
||||
|
||||
// Create load until we get dropped
|
||||
bool dropped = false;
|
||||
while (--n >= 0)
|
||||
{
|
||||
if (c.charge(fee) == Disposition::Drop)
|
||||
{
|
||||
dropped = true;
|
||||
// Disconnect abusive Consumer
|
||||
EXPECT_TRUE(c.disconnect(j_));
|
||||
break;
|
||||
}
|
||||
++logic.clock();
|
||||
}
|
||||
|
||||
ASSERT_TRUE(dropped) << "Loop count exceeded without dropping";
|
||||
}
|
||||
|
||||
// Make sure the consumer is on the blacklist for a while.
|
||||
{
|
||||
Consumer const c{logic.newInboundEndpoint(addr)};
|
||||
logic.periodicActivity();
|
||||
EXPECT_EQ(c.disposition(), Disposition::Drop) << "Dropped consumer not put on blacklist";
|
||||
}
|
||||
|
||||
// Makes sure the Consumer is eventually removed from blacklist
|
||||
bool readmitted = false;
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
// Give Consumer time to become readmitted. Should never
|
||||
// exceed expiration time.
|
||||
auto n = kSecondsUntilExpiration + 1s;
|
||||
while (--n > 0s)
|
||||
{
|
||||
++logic.clock();
|
||||
logic.periodicActivity();
|
||||
Consumer const c{logic.newInboundEndpoint(addr)};
|
||||
if (c.disposition() != Disposition::Drop)
|
||||
{
|
||||
readmitted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
EXPECT_TRUE(readmitted) << "Dropped Consumer left on blacklist too long";
|
||||
}
|
||||
|
||||
TEST_F(ResourceManagerTest, unlimited_warn_drop)
|
||||
{
|
||||
TestLogic logic{j_};
|
||||
|
||||
Charge const fee{kDropThreshold + 1};
|
||||
beast::IP::Endpoint const addr{beast::IP::Endpoint::fromString("192.0.2.2")};
|
||||
Consumer c{logic.newUnlimitedEndpoint(addr)};
|
||||
|
||||
// Create load until we get a warning
|
||||
int n = 10000;
|
||||
bool warned = false;
|
||||
|
||||
while (--n >= 0)
|
||||
{
|
||||
if (c.charge(fee) == Disposition::Warn)
|
||||
{
|
||||
warned = true;
|
||||
break;
|
||||
}
|
||||
++logic.clock();
|
||||
}
|
||||
|
||||
EXPECT_FALSE(warned) << "Should loop forever with no warning";
|
||||
}
|
||||
|
||||
TEST_F(ResourceManagerTest, charges)
|
||||
{
|
||||
TestLogic logic{j_};
|
||||
|
||||
{
|
||||
beast::IP::Endpoint const address{beast::IP::Endpoint::fromString("192.0.2.1")};
|
||||
Consumer c{logic.newInboundEndpoint(address)};
|
||||
Charge const fee{1000};
|
||||
JLOG(j_.info()) << "Charging " << c.toString() << " " << fee << " per second";
|
||||
c.charge(fee);
|
||||
for (int i = 0; i < 128; ++i)
|
||||
{
|
||||
JLOG(j_.info()) << "Time= " << logic.clock().now().time_since_epoch().count()
|
||||
<< ", Balance = " << c.balance();
|
||||
logic.advance();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
beast::IP::Endpoint const address{beast::IP::Endpoint::fromString("192.0.2.2")};
|
||||
Consumer c{logic.newInboundEndpoint(address)};
|
||||
Charge const fee{1000};
|
||||
JLOG(j_.info()) << "Charging " << c.toString() << " " << fee << " per second";
|
||||
for (int i = 0; i < 128; ++i)
|
||||
{
|
||||
c.charge(fee);
|
||||
JLOG(j_.info()) << "Time= " << logic.clock().now().time_since_epoch().count()
|
||||
<< ", Balance = " << c.balance();
|
||||
logic.advance();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ResourceManagerTest, imports)
|
||||
{
|
||||
TestLogic logic{j_};
|
||||
|
||||
Gossip g[5];
|
||||
|
||||
for (auto& i : g)
|
||||
populateGossip(i);
|
||||
|
||||
for (int i = 0; i < 5; ++i)
|
||||
logic.importConsumers(std::to_string(i), g[i]);
|
||||
}
|
||||
|
||||
TEST_F(ResourceManagerTest, import)
|
||||
{
|
||||
TestLogic logic{j_};
|
||||
|
||||
Gossip g;
|
||||
Gossip::Item item;
|
||||
item.balance = 100;
|
||||
beast::IP::AddressV4::bytes_type const d = {{
|
||||
192,
|
||||
0,
|
||||
2,
|
||||
1,
|
||||
}};
|
||||
item.address = beast::IP::Endpoint{beast::IP::AddressV4{d}};
|
||||
g.items.push_back(item);
|
||||
|
||||
logic.importConsumers("g", g);
|
||||
}
|
||||
|
||||
} // namespace xrpl::Resource
|
||||
22
src/tests/libxrpl/shamap/FetchPack.cpp
Normal file
22
src/tests/libxrpl/shamap/FetchPack.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/shamap/SHAMap.h>
|
||||
#include <xrpl/shamap/SHAMapMissingNode.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <helpers/TestSink.h>
|
||||
#include <shamap/common.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace xrpl::tests {
|
||||
|
||||
TEST(FetchPackTest, construct_table)
|
||||
{
|
||||
beast::Journal const j{TestSink::instance()};
|
||||
TestNodeFamily f{j};
|
||||
std::shared_ptr<SHAMap> const t1{std::make_shared<SHAMap>(SHAMapType::FREE, f)};
|
||||
|
||||
EXPECT_NE(t1, nullptr);
|
||||
}
|
||||
|
||||
} // namespace xrpl::tests
|
||||
349
src/tests/libxrpl/shamap/SHAMap.cpp
Normal file
349
src/tests/libxrpl/shamap/SHAMap.cpp
Normal file
@@ -0,0 +1,349 @@
|
||||
#include <xrpl/shamap/SHAMap.h>
|
||||
|
||||
#include <xrpl/basics/Blob.h>
|
||||
#include <xrpl/basics/Buffer.h>
|
||||
#include <xrpl/basics/SHAMapHash.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/shamap/SHAMapInnerNode.h>
|
||||
#include <xrpl/shamap/SHAMapItem.h>
|
||||
#include <xrpl/shamap/SHAMapLeafNode.h>
|
||||
#include <xrpl/shamap/SHAMapMissingNode.h>
|
||||
#include <xrpl/shamap/SHAMapTreeNode.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <helpers/TestSink.h>
|
||||
#include <shamap/common.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl::tests {
|
||||
|
||||
#ifndef __INTELLISENSE__
|
||||
static_assert(std::is_nothrow_destructible<SHAMap>{});
|
||||
static_assert(!std::is_default_constructible<SHAMap>{});
|
||||
static_assert(!std::is_copy_constructible<SHAMap>{});
|
||||
static_assert(!std::is_copy_assignable<SHAMap>{});
|
||||
static_assert(!std::is_move_constructible<SHAMap>{});
|
||||
static_assert(!std::is_move_assignable<SHAMap>{});
|
||||
|
||||
static_assert(std::is_nothrow_destructible<SHAMap::ConstIterator>{});
|
||||
static_assert(std::is_copy_constructible<SHAMap::ConstIterator>{});
|
||||
static_assert(std::is_copy_assignable<SHAMap::ConstIterator>{});
|
||||
static_assert(std::is_move_constructible<SHAMap::ConstIterator>{});
|
||||
static_assert(std::is_move_assignable<SHAMap::ConstIterator>{});
|
||||
|
||||
static_assert(std::is_nothrow_destructible<SHAMapItem>{});
|
||||
static_assert(!std::is_default_constructible<SHAMapItem>{});
|
||||
static_assert(!std::is_copy_constructible<SHAMapItem>{});
|
||||
|
||||
static_assert(std::is_nothrow_destructible<SHAMapNodeID>{});
|
||||
static_assert(std::is_default_constructible<SHAMapNodeID>{});
|
||||
static_assert(std::is_copy_constructible<SHAMapNodeID>{});
|
||||
static_assert(std::is_copy_assignable<SHAMapNodeID>{});
|
||||
static_assert(std::is_move_constructible<SHAMapNodeID>{});
|
||||
static_assert(std::is_move_assignable<SHAMapNodeID>{});
|
||||
|
||||
static_assert(std::is_nothrow_destructible<SHAMapHash>{});
|
||||
static_assert(std::is_default_constructible<SHAMapHash>{});
|
||||
static_assert(std::is_copy_constructible<SHAMapHash>{});
|
||||
static_assert(std::is_copy_assignable<SHAMapHash>{});
|
||||
static_assert(std::is_move_constructible<SHAMapHash>{});
|
||||
static_assert(std::is_move_assignable<SHAMapHash>{});
|
||||
|
||||
static_assert(std::is_nothrow_destructible<SHAMapTreeNode>{});
|
||||
static_assert(!std::is_default_constructible<SHAMapTreeNode>{});
|
||||
static_assert(!std::is_copy_constructible<SHAMapTreeNode>{});
|
||||
static_assert(!std::is_copy_assignable<SHAMapTreeNode>{});
|
||||
static_assert(!std::is_move_constructible<SHAMapTreeNode>{});
|
||||
static_assert(!std::is_move_assignable<SHAMapTreeNode>{});
|
||||
|
||||
static_assert(std::is_nothrow_destructible<SHAMapInnerNode>{});
|
||||
static_assert(!std::is_default_constructible<SHAMapInnerNode>{});
|
||||
static_assert(!std::is_copy_constructible<SHAMapInnerNode>{});
|
||||
static_assert(!std::is_copy_assignable<SHAMapInnerNode>{});
|
||||
static_assert(!std::is_move_constructible<SHAMapInnerNode>{});
|
||||
static_assert(!std::is_move_assignable<SHAMapInnerNode>{});
|
||||
|
||||
static_assert(std::is_nothrow_destructible<SHAMapLeafNode>{});
|
||||
static_assert(!std::is_default_constructible<SHAMapLeafNode>{});
|
||||
static_assert(!std::is_copy_constructible<SHAMapLeafNode>{});
|
||||
static_assert(!std::is_copy_assignable<SHAMapLeafNode>{});
|
||||
static_assert(!std::is_move_constructible<SHAMapLeafNode>{});
|
||||
static_assert(!std::is_move_assignable<SHAMapLeafNode>{});
|
||||
#endif
|
||||
|
||||
inline bool
|
||||
operator!=(SHAMapItem const& a, SHAMapItem const& b)
|
||||
{
|
||||
return a.key() != b.key();
|
||||
}
|
||||
|
||||
struct SHAMapBackingMode
|
||||
{
|
||||
bool backed;
|
||||
std::string_view testName;
|
||||
};
|
||||
|
||||
constexpr SHAMapBackingMode kBackedMode{.backed = true, .testName = "backed"};
|
||||
constexpr SHAMapBackingMode kUnbackedMode{.backed = false, .testName = "unbacked"};
|
||||
|
||||
std::string
|
||||
shamapBackingModeName(::testing::TestParamInfo<SHAMapBackingMode> const& info)
|
||||
{
|
||||
return std::string{info.param.testName};
|
||||
}
|
||||
|
||||
class SHAMapTest : public ::testing::TestWithParam<SHAMapBackingMode>
|
||||
{
|
||||
protected:
|
||||
beast::Journal const j_{TestSink::instance()};
|
||||
|
||||
static Buffer
|
||||
intToVuc(std::uint8_t v)
|
||||
{
|
||||
Buffer vuc{32};
|
||||
std::fill_n(vuc.data(), vuc.size(), v);
|
||||
return vuc;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_P(SHAMapTest, add_traverse_snapshot_build_tear_and_iterate)
|
||||
{
|
||||
auto const testMode = GetParam();
|
||||
tests::TestNodeFamily f{j_};
|
||||
|
||||
// kH3 and kH4 differ only in the leaf, same terminal node (level 19)
|
||||
constexpr uint256 kH1("092891fe4ef6cee585fdc6fda0e09eb4d386363158ec3321b8123e5a772c6ca7");
|
||||
constexpr uint256 kH2("436ccbac3347baa1f1e53baeef1f43334da88f1f6d70d963b833afd6dfa289fe");
|
||||
constexpr uint256 kH3("b92891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8");
|
||||
constexpr uint256 kH4("b92891fe4ef6cee585fdc6fda2e09eb4d386363158ec3321b8123e5a772c6ca8");
|
||||
|
||||
SHAMap sMap{SHAMapType::FREE, f};
|
||||
sMap.invariants();
|
||||
if (!testMode.backed)
|
||||
sMap.setUnbacked();
|
||||
|
||||
auto i1 = makeShamapitem(kH1, intToVuc(1));
|
||||
auto i2 = makeShamapitem(kH2, intToVuc(2));
|
||||
auto i3 = makeShamapitem(kH3, intToVuc(3));
|
||||
auto i4 = makeShamapitem(kH4, intToVuc(4));
|
||||
|
||||
EXPECT_TRUE(sMap.addItem(SHAMapNodeType::TnTransactionNm, makeShamapitem(*i2))) << "no add";
|
||||
sMap.invariants();
|
||||
EXPECT_TRUE(sMap.addItem(SHAMapNodeType::TnTransactionNm, makeShamapitem(*i1))) << "no add";
|
||||
sMap.invariants();
|
||||
|
||||
auto i = sMap.begin();
|
||||
auto e = sMap.end();
|
||||
EXPECT_FALSE(i == e || (*i != *i1)) << "bad traverse";
|
||||
++i;
|
||||
EXPECT_FALSE(i == e || (*i != *i2)) << "bad traverse";
|
||||
++i;
|
||||
EXPECT_EQ(i, e) << "bad traverse";
|
||||
sMap.addItem(SHAMapNodeType::TnTransactionNm, makeShamapitem(*i4));
|
||||
sMap.invariants();
|
||||
sMap.delItem(i2->key());
|
||||
sMap.invariants();
|
||||
sMap.addItem(SHAMapNodeType::TnTransactionNm, makeShamapitem(*i3));
|
||||
sMap.invariants();
|
||||
i = sMap.begin();
|
||||
e = sMap.end();
|
||||
EXPECT_FALSE(i == e || (*i != *i1)) << "bad traverse";
|
||||
++i;
|
||||
EXPECT_FALSE(i == e || (*i != *i3)) << "bad traverse";
|
||||
++i;
|
||||
EXPECT_FALSE(i == e || (*i != *i4)) << "bad traverse";
|
||||
++i;
|
||||
EXPECT_EQ(i, e) << "bad traverse";
|
||||
|
||||
SHAMapHash const mapHash = sMap.getHash();
|
||||
std::shared_ptr<SHAMap> const map2 = sMap.snapShot(false);
|
||||
map2->invariants();
|
||||
EXPECT_EQ(sMap.getHash(), mapHash) << "bad snapshot";
|
||||
EXPECT_EQ(map2->getHash(), mapHash) << "bad snapshot";
|
||||
|
||||
SHAMap::Delta delta;
|
||||
ASSERT_TRUE(sMap.compare(*map2, delta, 100));
|
||||
EXPECT_TRUE(delta.empty());
|
||||
|
||||
EXPECT_TRUE(sMap.delItem(sMap.begin()->key())) << "bad mod";
|
||||
sMap.invariants();
|
||||
EXPECT_NE(sMap.getHash(), mapHash) << "bad snapshot";
|
||||
EXPECT_EQ(map2->getHash(), mapHash) << "bad snapshot";
|
||||
|
||||
ASSERT_TRUE(sMap.compare(*map2, delta, 100));
|
||||
ASSERT_EQ(delta.size(), 1);
|
||||
EXPECT_EQ(delta.begin()->first, kH1);
|
||||
EXPECT_EQ(delta.begin()->second.first, nullptr);
|
||||
ASSERT_NE(delta.begin()->second.second, nullptr);
|
||||
EXPECT_EQ(delta.begin()->second.second->key(), kH1);
|
||||
|
||||
sMap.dump();
|
||||
{
|
||||
constexpr std::array kKeys{
|
||||
uint256{"b92891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
|
||||
uint256{"b92881fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
|
||||
uint256{"b92691fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
|
||||
uint256{"b92791fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
|
||||
uint256{"b91891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
|
||||
uint256{"b99891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
|
||||
uint256{"f22891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
|
||||
uint256{"292891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
|
||||
};
|
||||
|
||||
constexpr std::array kHashes{
|
||||
uint256{"B7387CFEA0465759ADC718E8C42B52D2309D179B326E239EB5075C64B6281F7F"},
|
||||
uint256{"FBC195A9592A54AB44010274163CB6BA95F497EC5BA0A8831845467FB2ECE266"},
|
||||
uint256{"4E7D2684B65DFD48937FFB775E20175C43AF0C94066F7D5679F51AE756795B75"},
|
||||
uint256{"7A2F312EB203695FFD164E038E281839EEF06A1B99BFC263F3CECC6C74F93E07"},
|
||||
uint256{"395A6691A372387A703FB0F2C6D2C405DAF307D0817F8F0E207596462B0E3A3E"},
|
||||
uint256{"D044C0A696DE3169CC70AE216A1564D69DE96582865796142CE7D98A84D9DDE4"},
|
||||
uint256{"76DCC77C4027309B5A91AD164083264D70B77B5E43E08AEDA5EBF94361143615"},
|
||||
uint256{"DF4220E93ADC6F5569063A01B4DC79F8DB9553B6A3222ADE23DEA02BBE7230E5"},
|
||||
};
|
||||
|
||||
SHAMap map{SHAMapType::FREE, f};
|
||||
if (!testMode.backed)
|
||||
map.setUnbacked();
|
||||
|
||||
EXPECT_EQ(map.getHash(), beast::kZero);
|
||||
for (std::size_t k = 0; k < kKeys.size(); ++k)
|
||||
{
|
||||
EXPECT_TRUE(map.addItem(
|
||||
SHAMapNodeType::TnTransactionNm,
|
||||
makeShamapitem(kKeys[k], intToVuc(static_cast<std::uint8_t>(k)))));
|
||||
EXPECT_EQ(map.getHash().asUInt256(), kHashes[k]);
|
||||
map.invariants();
|
||||
}
|
||||
for (std::size_t k = kKeys.size(); k-- > 0;)
|
||||
{
|
||||
EXPECT_EQ(map.getHash().asUInt256(), kHashes[k]);
|
||||
EXPECT_TRUE(map.delItem(kKeys[k]));
|
||||
map.invariants();
|
||||
}
|
||||
EXPECT_EQ(map.getHash(), beast::kZero);
|
||||
}
|
||||
|
||||
{
|
||||
constexpr std::array kKeys{
|
||||
uint256{"f22891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
|
||||
uint256{"b99891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
|
||||
uint256{"b92891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
|
||||
uint256{"b92881fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
|
||||
uint256{"b92791fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
|
||||
uint256{"b92691fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
|
||||
uint256{"b91891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
|
||||
uint256{"292891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
|
||||
};
|
||||
|
||||
tests::TestNodeFamily tf{j_};
|
||||
SHAMap map{SHAMapType::FREE, tf};
|
||||
if (!testMode.backed)
|
||||
map.setUnbacked();
|
||||
for (auto const& k : kKeys)
|
||||
{
|
||||
map.addItem(SHAMapNodeType::TnTransactionNm, makeShamapitem(k, intToVuc(0)));
|
||||
map.invariants();
|
||||
}
|
||||
|
||||
int h = 7;
|
||||
for (auto const& k : map)
|
||||
{
|
||||
EXPECT_EQ(k.key(), kKeys[h]);
|
||||
--h;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
BackingMode,
|
||||
SHAMapTest,
|
||||
::testing::Values(kBackedMode, kUnbackedMode),
|
||||
shamapBackingModeName);
|
||||
|
||||
class SHAMapPathProof : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
beast::Journal const j_{TestSink::instance()};
|
||||
};
|
||||
|
||||
TEST_F(SHAMapPathProof, verify_proof_path)
|
||||
{
|
||||
tests::TestNodeFamily tf{j_};
|
||||
SHAMap map{SHAMapType::FREE, tf};
|
||||
map.setUnbacked();
|
||||
|
||||
uint256 key;
|
||||
uint256 rootHash;
|
||||
std::vector<Blob> goodPath;
|
||||
|
||||
for (unsigned char c = 1; c < 100; ++c)
|
||||
{
|
||||
uint256 k(c);
|
||||
map.addItem(SHAMapNodeType::TnAccountState, makeShamapitem(k, Slice{k.data(), k.size()}));
|
||||
map.invariants();
|
||||
|
||||
auto root = map.getHash().asUInt256();
|
||||
auto path = map.getProofPath(k);
|
||||
if (!path)
|
||||
{
|
||||
ADD_FAILURE() << "Missing proof path";
|
||||
return;
|
||||
}
|
||||
auto& proofPath = *path;
|
||||
|
||||
EXPECT_TRUE(map.verifyProofPath(root, k, proofPath));
|
||||
if (c == 1)
|
||||
{
|
||||
// extra node
|
||||
proofPath.insert(proofPath.begin(), proofPath.front());
|
||||
EXPECT_FALSE(map.verifyProofPath(root, k, proofPath));
|
||||
// wrong key
|
||||
uint256 const wrongKey(c + 1);
|
||||
EXPECT_FALSE(map.getProofPath(wrongKey));
|
||||
}
|
||||
if (c == 99)
|
||||
{
|
||||
key = k;
|
||||
rootHash = root;
|
||||
goodPath = std::move(proofPath);
|
||||
}
|
||||
}
|
||||
|
||||
// still good
|
||||
EXPECT_TRUE(map.verifyProofPath(rootHash, key, goodPath));
|
||||
// empty path
|
||||
std::vector<Blob> badPath;
|
||||
EXPECT_FALSE(map.verifyProofPath(rootHash, key, badPath));
|
||||
// too long
|
||||
badPath = goodPath;
|
||||
badPath.push_back(goodPath.back());
|
||||
EXPECT_FALSE(map.verifyProofPath(rootHash, key, badPath));
|
||||
// bad node
|
||||
badPath.clear();
|
||||
badPath.emplace_back(100, 100);
|
||||
EXPECT_FALSE(map.verifyProofPath(rootHash, key, badPath));
|
||||
// bad node type
|
||||
badPath.clear();
|
||||
badPath.push_back(goodPath.front());
|
||||
badPath.front().back()--; // change node type
|
||||
EXPECT_FALSE(map.verifyProofPath(rootHash, key, badPath));
|
||||
// all inner
|
||||
badPath.clear();
|
||||
badPath = goodPath;
|
||||
badPath.erase(badPath.begin());
|
||||
EXPECT_FALSE(map.verifyProofPath(rootHash, key, badPath));
|
||||
}
|
||||
|
||||
} // namespace xrpl::tests
|
||||
170
src/tests/libxrpl/shamap/SHAMapSync.cpp
Normal file
170
src/tests/libxrpl/shamap/SHAMapSync.cpp
Normal file
@@ -0,0 +1,170 @@
|
||||
#include <xrpl/basics/Blob.h>
|
||||
#include <xrpl/basics/SHAMapHash.h>
|
||||
#include <xrpl/basics/Slice.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/basics/random.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/beast/xor_shift_engine.h>
|
||||
#include <xrpl/protocol/Serializer.h>
|
||||
#include <xrpl/shamap/SHAMap.h>
|
||||
#include <xrpl/shamap/SHAMapItem.h>
|
||||
#include <xrpl/shamap/SHAMapMissingNode.h>
|
||||
#include <xrpl/shamap/SHAMapTreeNode.h>
|
||||
|
||||
#include <boost/smart_ptr/intrusive_ptr.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <helpers/TestSink.h>
|
||||
#include <shamap/common.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <list>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl::tests {
|
||||
|
||||
class SHAMapSyncTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
beast::Journal const j_{TestSink::instance()};
|
||||
beast::xor_shift_engine eng_;
|
||||
|
||||
boost::intrusive_ptr<SHAMapItem>
|
||||
makeRandomAS()
|
||||
{
|
||||
Serializer s;
|
||||
|
||||
for (int d = 0; d < 3; ++d)
|
||||
s.add32(randInt<std::uint32_t>(eng_));
|
||||
return makeShamapitem(s.getSHA512Half(), s.slice());
|
||||
}
|
||||
|
||||
bool
|
||||
confuseMap(SHAMap& map, int count)
|
||||
{
|
||||
// add a bunch of random states to a map, then remove them
|
||||
// map should be the same
|
||||
SHAMapHash const beforeHash = map.getHash();
|
||||
|
||||
std::list<uint256> items;
|
||||
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
auto item = makeRandomAS();
|
||||
items.push_back(item->key());
|
||||
|
||||
if (!map.addItem(SHAMapNodeType::TnAccountState, item))
|
||||
{
|
||||
ADD_FAILURE() << "Unable to add item to map";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto const& item : items)
|
||||
{
|
||||
if (!map.delItem(item))
|
||||
{
|
||||
ADD_FAILURE() << "Unable to remove item from map";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (beforeHash != map.getHash())
|
||||
{
|
||||
ADD_FAILURE() << "Hashes do not match " << beforeHash << " " << map.getHash();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(SHAMapSyncTest, sync)
|
||||
{
|
||||
TestNodeFamily f{j_}, f2{j_};
|
||||
SHAMap source{SHAMapType::FREE, f};
|
||||
SHAMap destination{SHAMapType::FREE, f2};
|
||||
|
||||
int const items = 10000;
|
||||
for (int i = 0; i < items; ++i)
|
||||
{
|
||||
source.addItem(SHAMapNodeType::TnAccountState, makeRandomAS());
|
||||
if (i % 100 == 0)
|
||||
source.invariants();
|
||||
}
|
||||
|
||||
source.invariants();
|
||||
ASSERT_TRUE(confuseMap(source, 500));
|
||||
source.invariants();
|
||||
|
||||
source.setImmutable();
|
||||
|
||||
int count = 0;
|
||||
source.visitLeaves([&count]([[maybe_unused]] auto const& item) { ++count; });
|
||||
EXPECT_EQ(count, items);
|
||||
|
||||
std::vector<SHAMapMissingNode> missingNodes;
|
||||
source.walkMap(missingNodes, 2048);
|
||||
EXPECT_TRUE(missingNodes.empty());
|
||||
|
||||
destination.setSynching();
|
||||
|
||||
{
|
||||
std::vector<std::pair<SHAMapNodeID, Blob>> a;
|
||||
|
||||
ASSERT_TRUE(source.getNodeFat(SHAMapNodeID(), a, randBool(eng_), randInt(eng_, 2)));
|
||||
|
||||
ASSERT_FALSE(a.empty()) << "NodeSize";
|
||||
|
||||
ASSERT_TRUE(
|
||||
destination.addRootNode(source.getHash(), makeSlice(a[0].second), nullptr).isGood());
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
f.clock().advance(std::chrono::seconds(1));
|
||||
|
||||
// get the list of nodes we know we need
|
||||
auto nodesMissing = destination.getMissingNodes(2048, nullptr);
|
||||
|
||||
if (nodesMissing.empty())
|
||||
break;
|
||||
|
||||
// get as many nodes as possible based on this information
|
||||
std::vector<std::pair<SHAMapNodeID, Blob>> b;
|
||||
|
||||
for (auto& it : nodesMissing)
|
||||
{
|
||||
// Keep failures fatal here because this loop is data-dependent.
|
||||
// non-deterministic number of times and the number of tests run
|
||||
// should be deterministic
|
||||
if (!source.getNodeFat(it.first, b, randBool(eng_), randInt(eng_, 2)))
|
||||
FAIL() << "Unable to fetch node";
|
||||
}
|
||||
|
||||
// Keep failures fatal here because this loop is data-dependent.
|
||||
// non-deterministic number of times and the number of tests run
|
||||
// should be deterministic
|
||||
if (b.empty())
|
||||
FAIL() << "No nodes returned";
|
||||
|
||||
for (auto const& i : b)
|
||||
{
|
||||
// Keep failures fatal here because this loop is data-dependent.
|
||||
// non-deterministic number of times and the number of tests run
|
||||
// should be deterministic
|
||||
if (!destination.addKnownNode(i.first, makeSlice(i.second), nullptr).isUseful())
|
||||
FAIL() << "Known node was not useful";
|
||||
}
|
||||
} while (true);
|
||||
|
||||
destination.clearSynching();
|
||||
|
||||
EXPECT_TRUE(source.deepCompare(destination));
|
||||
|
||||
destination.invariants();
|
||||
}
|
||||
|
||||
} // namespace xrpl::tests
|
||||
@@ -4,7 +4,6 @@
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/basics/chrono.h>
|
||||
#include <xrpl/basics/contract.h>
|
||||
#include <xrpl/beast/clock/manual_clock.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/config/BasicConfig.h>
|
||||
#include <xrpl/config/Constants.h>
|
||||
@@ -92,13 +91,17 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
missingNodeAcquireBySeq(std::uint32_t refNum, uint256 const& nodeHash) override
|
||||
missingNodeAcquireBySeq(
|
||||
[[maybe_unused]] std::uint32_t refNum,
|
||||
[[maybe_unused]] uint256 const& nodeHash) override
|
||||
{
|
||||
Throw<std::runtime_error>("missing node");
|
||||
}
|
||||
|
||||
void
|
||||
missingNodeAcquireByHash(uint256 const& refHash, std::uint32_t refNum) override
|
||||
missingNodeAcquireByHash(
|
||||
[[maybe_unused]] uint256 const& refHash,
|
||||
[[maybe_unused]] std::uint32_t refNum) override
|
||||
{
|
||||
Throw<std::runtime_error>("missing node");
|
||||
}
|
||||
@@ -110,7 +113,7 @@ public:
|
||||
(*tnCache_).reset();
|
||||
}
|
||||
|
||||
beast::ManualClock<std::chrono::steady_clock>
|
||||
TestStopwatch&
|
||||
clock()
|
||||
{
|
||||
return clock_;
|
||||
Reference in New Issue
Block a user