#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 (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