diff --git a/.github/scripts/levelization/results/ordering.txt b/.github/scripts/levelization/results/ordering.txt index 547c1b35390..89f6f5b6c3e 100644 --- a/.github/scripts/levelization/results/ordering.txt +++ b/.github/scripts/levelization/results/ordering.txt @@ -163,9 +163,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 @@ -189,12 +186,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.toplevel > test.csf test.toplevel > xrpl.json test.unit_test > xrpl.basics @@ -208,6 +199,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 diff --git a/src/test/resource/Logic_test.cpp b/src/test/resource/Logic_test.cpp deleted file mode 100644 index de4575cb165..00000000000 --- a/src/test/resource/Logic_test.cpp +++ /dev/null @@ -1,294 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include - -namespace xrpl::Resource { - -class ResourceManager_test : public beast::unit_test::Suite -{ -public: - class TestLogic : private boost::base_from_member, public Logic - - { - private: - using clock_type = boost::base_from_member; - - 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(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 const ep = limited - ? std::bind(&TestLogic::newInboundEndpoint, &logic, std::placeholders::_1) - : std::bind(&TestLogic::newUnlimitedEndpoint, &logic, std::placeholders::_1); - - { - 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 (int i = 0; i < 5; ++i) - createGossip(g[i]); - - 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 diff --git a/src/test/shamap/FetchPack_test.cpp b/src/test/shamap/FetchPack_test.cpp deleted file mode 100644 index b2fc185b316..00000000000 --- a/src/test/shamap/FetchPack_test.cpp +++ /dev/null @@ -1,169 +0,0 @@ -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include - -namespace xrpl::tests { - -class FetchPack_test : public beast::unit_test::Suite -{ -public: - static constexpr auto kTableItems = 100; - static constexpr auto kTableItemsExtra = 20; - - using Map = hash_map; - using Table = SHAMap; - using Item = SHAMapItem; - - struct Handler - { - void - operator()(std::uint32_t refNum) const - { - Throw("missing node"); - } - }; - - struct TestFilter : SHAMapSyncFilter - { - TestFilter(Map& map, beast::Journal journal) : map(map), journal(journal) - { - } - - void - gotNode( - bool fromFilter, - SHAMapHash const& nodeHash, - std::uint32_t ledgerSeq, - Blob&& nodeData, // NOLINT(cppcoreguidelines-rvalue-reference-param-not-moved) - SHAMapNodeType type) const override - { - } - - [[nodiscard]] std::optional - getNode(SHAMapHash const& nodeHash) const override - { - Map::iterator const it = map.find(nodeHash); - if (it == map.end()) - { - JLOG(journal.fatal()) << "Test filter missing node"; - return std::nullopt; - } - return it->second; - } - - Map& map; - beast::Journal journal; - }; - - static boost::intrusive_ptr - makeRandomItemMember(beast::xor_shift_engine& r) - { - Serializer s; - for (int d = 0; d < 3; ++d) - s.add32(xrpl::randInt(r)); - return makeShamapitem(s.getSHA512Half(), s.slice()); - } - - static void - addRandomItems(std::size_t n, Table& t, beast::xor_shift_engine& r) - { - while ((n--) != 0u) - { - auto const result(t.addItem(SHAMapNodeType::TnAccountState, makeRandomItemMember(r))); - assert(result); - (void)result; - } - } - - void - onFetch(Map& map, SHAMapHash const& hash, Blob const& blob) - { - BEAST_EXPECT(sha512Half(makeSlice(blob)) == hash.asUInt256()); - map.emplace(hash, blob); - } - - void - run() override - { - using beast::Severity; - test::SuiteJournal journal("FetchPack_test", *this); - - TestNodeFamily f(journal); - std::shared_ptr const t1(std::make_shared
(SHAMapType::FREE, f)); - - pass(); - - // beast::Random r; - // add_random_items_ (tableItems, *t1, r); - // std::shared_ptr
t2 (t1->snapShot (true)); - // - // add_random_items_ (tableItemsExtra, *t1, r); - // add_random_items_ (tableItemsExtra, *t2, r); - - // turn t1 into t2 - // Map map; - // t2->getFetchPack (t1.get(), true, 1000000, std::bind ( - // &FetchPack_test::on_fetch, this, std::ref (map), - // std::placeholders::_1, std::placeholders::_2)); - // t1->getFetchPack (nullptr, true, 1000000, std::bind ( - // &FetchPack_test::on_fetch, this, std::ref (map), - // std::placeholders::_1, std::placeholders::_2)); - - // try to rebuild t2 from the fetch pack - // std::shared_ptr
t3; - // try - // { - // TestFilter filter (map, beast::Journal()); - // - // t3 = std::make_shared
(SHAMapType::FREE, - // t2->getHash (), - // fullBelowCache); - // - // BEAST_EXPECT(t3->fetchRoot (t2->getHash (), &filter), - // "unable to get root"); - // - // // everything should be in the pack, no hashes should be - // needed std::vector hashes = - // t3->getNeededHashes(1, &filter); - // BEAST_EXPECT(hashes.empty(), "missing hashes"); - // - // BEAST_EXPECT(t3->getHash () == t2->getHash (), "root - // hashes do not match"); BEAST_EXPECT(t3->deepCompare - // (*t2), "failed compare"); - // } - // catch (std::exception const&) - // { - // fail ("unhandled exception"); - // } - } -}; - -BEAST_DEFINE_TESTSUITE(FetchPack, shamap, xrpl); - -} // namespace xrpl::tests diff --git a/src/test/shamap/SHAMapSync_test.cpp b/src/test/shamap/SHAMapSync_test.cpp deleted file mode 100644 index 9e9a4909775..00000000000 --- a/src/test/shamap/SHAMapSync_test.cpp +++ /dev/null @@ -1,178 +0,0 @@ -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -namespace xrpl::tests { - -class SHAMapSync_test : public beast::unit_test::Suite -{ -public: - beast::xor_shift_engine eng; - - boost::intrusive_ptr - makeRandomAS() - { - Serializer s; - - for (int d = 0; d < 3; ++d) - s.add32(randInt(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 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 missingNodes; - source.walkMap(missingNodes, 2048); - BEAST_EXPECT(missingNodes.empty()); - - destination.setSynching(); - - { - std::vector> 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> 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 (std::size_t i = 0; i < b.size(); ++i) - { - // 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(b[i].first, makeSlice(b[i].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 diff --git a/src/test/shamap/SHAMap_test.cpp b/src/test/shamap/SHAMap_test.cpp deleted file mode 100644 index ab7e01e3afb..00000000000 --- a/src/test/shamap/SHAMap_test.cpp +++ /dev/null @@ -1,427 +0,0 @@ -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -namespace xrpl::tests { - -#ifndef __INTELLISENSE__ -static_assert(std::is_nothrow_destructible{}, ""); -static_assert(!std::is_default_constructible{}, ""); -static_assert(!std::is_copy_constructible{}, ""); -static_assert(!std::is_copy_assignable{}, ""); -static_assert(!std::is_move_constructible{}, ""); -static_assert(!std::is_move_assignable{}, ""); - -static_assert(std::is_nothrow_destructible{}, ""); -static_assert(std::is_copy_constructible{}, ""); -static_assert(std::is_copy_assignable{}, ""); -static_assert(std::is_move_constructible{}, ""); -static_assert(std::is_move_assignable{}, ""); - -static_assert(std::is_nothrow_destructible{}, ""); -static_assert(!std::is_default_constructible{}, ""); -static_assert(!std::is_copy_constructible{}, ""); - -static_assert(std::is_nothrow_destructible{}, ""); -static_assert(std::is_default_constructible{}, ""); -static_assert(std::is_copy_constructible{}, ""); -static_assert(std::is_copy_assignable{}, ""); -static_assert(std::is_move_constructible{}, ""); -static_assert(std::is_move_assignable{}, ""); - -static_assert(std::is_nothrow_destructible{}, ""); -static_assert(std::is_default_constructible{}, ""); -static_assert(std::is_copy_constructible{}, ""); -static_assert(std::is_copy_assignable{}, ""); -static_assert(std::is_move_constructible{}, ""); -static_assert(std::is_move_assignable{}, ""); - -static_assert(std::is_nothrow_destructible{}, ""); -static_assert(!std::is_default_constructible{}, ""); -static_assert(!std::is_copy_constructible{}, ""); -static_assert(!std::is_copy_assignable{}, ""); -static_assert(!std::is_move_constructible{}, ""); -static_assert(!std::is_move_assignable{}, ""); - -static_assert(std::is_nothrow_destructible{}, ""); -static_assert(!std::is_default_constructible{}, ""); -static_assert(!std::is_copy_constructible{}, ""); -static_assert(!std::is_copy_assignable{}, ""); -static_assert(!std::is_move_constructible{}, ""); -static_assert(!std::is_move_assignable{}, ""); - -static_assert(std::is_nothrow_destructible{}, ""); -static_assert(!std::is_default_constructible{}, ""); -static_assert(!std::is_copy_constructible{}, ""); -static_assert(!std::is_copy_assignable{}, ""); -static_assert(!std::is_move_constructible{}, ""); -static_assert(!std::is_move_assignable{}, ""); -#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(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 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 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 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 diff --git a/src/tests/libxrpl/CMakeLists.txt b/src/tests/libxrpl/CMakeLists.txt index 2dae6fccb98..fefd217245b 100644 --- a/src/tests/libxrpl/CMakeLists.txt +++ b/src/tests/libxrpl/CMakeLists.txt @@ -27,6 +27,8 @@ set(test_modules basics crypto json + resource + shamap tx protocol_autogen ) diff --git a/src/tests/libxrpl/resource/Logic.cpp b/src/tests/libxrpl/resource/Logic.cpp new file mode 100644 index 00000000000..6ad266e88f8 --- /dev/null +++ b/src/tests/libxrpl/resource/Logic.cpp @@ -0,0 +1,232 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include + +namespace xrpl::Resource { + +class ResourceManagerTest : public ::testing::Test +{ +protected: + beast::Journal const j_{TestSink::instance()}; + + class TestLogic : private boost::base_from_member, public Logic + { + private: + using clock_type = boost::base_from_member; + + 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(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 (int i = 0; i < 5; ++i) + createGossip(g[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 diff --git a/src/tests/libxrpl/shamap/FetchPack.cpp b/src/tests/libxrpl/shamap/FetchPack.cpp new file mode 100644 index 00000000000..2a7a083d83f --- /dev/null +++ b/src/tests/libxrpl/shamap/FetchPack.cpp @@ -0,0 +1,22 @@ +#include +#include +#include + +#include +#include +#include + +#include + +namespace xrpl::tests { + +TEST(FetchPackTest, construct_table) +{ + beast::Journal const j{TestSink::instance()}; + TestNodeFamily f(j); + std::shared_ptr const t1(std::make_shared(SHAMapType::FREE, f)); + + EXPECT_NE(t1, nullptr); +} + +} // namespace xrpl::tests diff --git a/src/tests/libxrpl/shamap/SHAMap.cpp b/src/tests/libxrpl/shamap/SHAMap.cpp new file mode 100644 index 00000000000..3b98bfcfbe3 --- /dev/null +++ b/src/tests/libxrpl/shamap/SHAMap.cpp @@ -0,0 +1,393 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace xrpl::tests { + +#ifndef __INTELLISENSE__ +static_assert(std::is_nothrow_destructible{}, ""); +static_assert(!std::is_default_constructible{}, ""); +static_assert(!std::is_copy_constructible{}, ""); +static_assert(!std::is_copy_assignable{}, ""); +static_assert(!std::is_move_constructible{}, ""); +static_assert(!std::is_move_assignable{}, ""); + +static_assert(std::is_nothrow_destructible{}, ""); +static_assert(std::is_copy_constructible{}, ""); +static_assert(std::is_copy_assignable{}, ""); +static_assert(std::is_move_constructible{}, ""); +static_assert(std::is_move_assignable{}, ""); + +static_assert(std::is_nothrow_destructible{}, ""); +static_assert(!std::is_default_constructible{}, ""); +static_assert(!std::is_copy_constructible{}, ""); + +static_assert(std::is_nothrow_destructible{}, ""); +static_assert(std::is_default_constructible{}, ""); +static_assert(std::is_copy_constructible{}, ""); +static_assert(std::is_copy_assignable{}, ""); +static_assert(std::is_move_constructible{}, ""); +static_assert(std::is_move_assignable{}, ""); + +static_assert(std::is_nothrow_destructible{}, ""); +static_assert(std::is_default_constructible{}, ""); +static_assert(std::is_copy_constructible{}, ""); +static_assert(std::is_copy_assignable{}, ""); +static_assert(std::is_move_constructible{}, ""); +static_assert(std::is_move_assignable{}, ""); + +static_assert(std::is_nothrow_destructible{}, ""); +static_assert(!std::is_default_constructible{}, ""); +static_assert(!std::is_copy_constructible{}, ""); +static_assert(!std::is_copy_assignable{}, ""); +static_assert(!std::is_move_constructible{}, ""); +static_assert(!std::is_move_assignable{}, ""); + +static_assert(std::is_nothrow_destructible{}, ""); +static_assert(!std::is_default_constructible{}, ""); +static_assert(!std::is_copy_constructible{}, ""); +static_assert(!std::is_copy_assignable{}, ""); +static_assert(!std::is_move_constructible{}, ""); +static_assert(!std::is_move_assignable{}, ""); + +static_assert(std::is_nothrow_destructible{}, ""); +static_assert(!std::is_default_constructible{}, ""); +static_assert(!std::is_copy_constructible{}, ""); +static_assert(!std::is_copy_assignable{}, ""); +static_assert(!std::is_move_constructible{}, ""); +static_assert(!std::is_move_assignable{}, ""); +#endif + +inline bool +operator!=(SHAMapItem const& a, SHAMapItem const& b) +{ + return a.key() != b.key(); +} + +struct SHAMapBackingMode +{ + bool backed; + char const* testName; +}; + +constexpr SHAMapBackingMode kBackedMode{.backed = true, .testName = "backed"}; +constexpr SHAMapBackingMode kUnbackedMode{.backed = false, .testName = "unbacked"}; + +std::string +shamapBackingModeName(::testing::TestParamInfo const& info) +{ + return std::string{info.param.testName}; +} + +class SHAMapTest : public ::testing::TestWithParam +{ +protected: + beast::Journal const j_{TestSink::instance()}; + + static Buffer + intToVuc(int v) + { + Buffer vuc(32); + std::fill_n(vuc.data(), vuc.size(), static_cast(v)); + return vuc; + } +}; + +TEST_P(SHAMapTest, add_traverse_snapshot_build_tear_and_iterate) +{ + auto const testMode = GetParam(); + tests::TestNodeFamily f(j_); + + // 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"); + + 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 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 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")}; + + 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 (!testMode.backed) + map.setUnbacked(); + + EXPECT_EQ(map.getHash(), beast::kZero); + for (std::size_t k = 0; k < kEYS.size(); ++k) + { + EXPECT_TRUE(map.addItem( + SHAMapNodeType::TnTransactionNm, + makeShamapitem(kEYS[k], intToVuc(static_cast(k))))); + EXPECT_EQ(map.getHash().asUInt256(), kHASHES[k]); + map.invariants(); + } + for (std::size_t k = kEYS.size(); k-- > 0;) + { + EXPECT_EQ(map.getHash().asUInt256(), kHASHES[k]); + EXPECT_TRUE(map.delItem(kEYS[k])); + map.invariants(); + } + EXPECT_EQ(map.getHash(), beast::kZero); + } + + { + 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{j_}; + SHAMap map{SHAMapType::FREE, tf}; + if (!testMode.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) + { + EXPECT_EQ(k.key(), kEYS[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 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 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 diff --git a/src/tests/libxrpl/shamap/SHAMapSync.cpp b/src/tests/libxrpl/shamap/SHAMapSync.cpp new file mode 100644 index 00000000000..6aada4b5604 --- /dev/null +++ b/src/tests/libxrpl/shamap/SHAMapSync.cpp @@ -0,0 +1,171 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace xrpl::tests { + +class SHAMapSyncTest : public ::testing::Test +{ +protected: + beast::Journal const j_{TestSink::instance()}; + beast::xor_shift_engine eng_; + + boost::intrusive_ptr + makeRandomAS() + { + Serializer s; + + for (int d = 0; d < 3; ++d) + s.add32(randInt(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 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 missingNodes; + source.walkMap(missingNodes, 2048); + EXPECT_TRUE(missingNodes.empty()); + + destination.setSynching(); + + { + std::vector> 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> 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 (std::size_t i = 0; i < b.size(); ++i) + { + // 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(b[i].first, makeSlice(b[i].second), nullptr).isUseful()) + FAIL() << "Known node was not useful"; + } + } while (true); + + destination.clearSynching(); + + EXPECT_TRUE(source.deepCompare(destination)); + + destination.invariants(); +} + +} // namespace xrpl::tests diff --git a/src/test/shamap/common.h b/src/tests/libxrpl/shamap/common.h similarity index 83% rename from src/test/shamap/common.h rename to src/tests/libxrpl/shamap/common.h index 0475acdf6d9..457b0e89f98 100644 --- a/src/test/shamap/common.h +++ b/src/tests/libxrpl/shamap/common.h @@ -1,12 +1,19 @@ #pragma once #include +#include +#include #include #include #include #include #include +#include +#include +#include +#include + namespace xrpl::tests { class TestNodeFamily : public Family @@ -79,13 +86,17 @@ class TestNodeFamily : public Family } void - missingNodeAcquireBySeq(std::uint32_t refNum, uint256 const& nodeHash) override + missingNodeAcquireBySeq( + [[maybe_unused]] std::uint32_t refNum, + [[maybe_unused]] uint256 const& nodeHash) override { Throw("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("missing node"); }