#include #include #include #include #include #include #include namespace ripple { namespace 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(rand_int(eng_)); return make_shamapitem(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 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::tnACCOUNT_STATE, 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 namespace beast::severities; test::SuiteJournal journal("SHAMapSync_test", *this); TestNodeFamily f(journal), f2(journal); SHAMap source(SHAMapType::FREE, f); SHAMap destination(SHAMapType::FREE, f2); int items = 10000; for (int i = 0; i < items; ++i) { source.addItem(SHAMapNodeType::tnACCOUNT_STATE, 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()); std::vector nodeIDs, gotNodeIDs; std::vector gotNodes; std::vector hashes; destination.setSynching(); { std::vector> a; BEAST_EXPECT(source.getNodeFat( SHAMapNodeID(), a, rand_bool(eng_), rand_int(eng_, 2))); unexpected(a.size() < 1, "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, rand_bool(eng_), rand_int(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, ripple); } // namespace tests } // namespace ripple