From 22b023b09da3e2fe00467c77b105a61c1961081f Mon Sep 17 00:00:00 2001 From: glozow Date: Fri, 3 Jan 2025 10:44:36 -0500 Subject: [PATCH] [unit test] multiple orphan announcers --- src/test/orphanage_tests.cpp | 126 +++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) diff --git a/src/test/orphanage_tests.cpp b/src/test/orphanage_tests.cpp index 49ddd10863e..a33db09ad77 100644 --- a/src/test/orphanage_tests.cpp +++ b/src/test/orphanage_tests.cpp @@ -446,4 +446,130 @@ BOOST_AUTO_TEST_CASE(process_block) BOOST_CHECK_EQUAL(orphanage.Size(), 1); BOOST_CHECK(orphanage.HaveTx(control_tx->GetWitnessHash())); } + +BOOST_AUTO_TEST_CASE(multiple_announcers) +{ + const NodeId node0{0}; + const NodeId node1{1}; + const NodeId node2{2}; + size_t expected_total_count{0}; + FastRandomContext det_rand{true}; + TxOrphanageTest orphanage{det_rand}; + + // Check accounting per peer. + // Check that EraseForPeer works with multiple announcers. + { + auto ptx = MakeTransactionSpending({}, det_rand); + const auto& wtxid = ptx->GetWitnessHash(); + BOOST_CHECK(orphanage.AddTx(ptx, node0)); + BOOST_CHECK(orphanage.HaveTx(wtxid)); + expected_total_count += 1; + BOOST_CHECK_EQUAL(orphanage.Size(), expected_total_count); + + // Adding again should do nothing. + BOOST_CHECK(!orphanage.AddTx(ptx, node0)); + BOOST_CHECK_EQUAL(orphanage.Size(), expected_total_count); + + // We can add another tx with the same txid but different witness. + auto ptx_mutated{MakeMutation(ptx)}; + BOOST_CHECK(orphanage.AddTx(ptx_mutated, node0)); + BOOST_CHECK(orphanage.HaveTx(ptx_mutated->GetWitnessHash())); + expected_total_count += 1; + + BOOST_CHECK(!orphanage.AddTx(ptx, node0)); + + // Adding a new announcer should not change overall accounting. + BOOST_CHECK(orphanage.AddAnnouncer(ptx->GetWitnessHash(), node2)); + BOOST_CHECK_EQUAL(orphanage.Size(), expected_total_count); + + // If we already have this announcer, AddAnnouncer returns false. + BOOST_CHECK(orphanage.HaveTxFromPeer(ptx->GetWitnessHash(), node2)); + BOOST_CHECK(!orphanage.AddAnnouncer(ptx->GetWitnessHash(), node2)); + + // Same with using AddTx for an existing tx, which is equivalent to using AddAnnouncer + BOOST_CHECK(!orphanage.AddTx(ptx, node1)); + BOOST_CHECK_EQUAL(orphanage.Size(), expected_total_count); + + // if EraseForPeer is called for an orphan with multiple announcers, the orphanage should only + // erase that peer from the announcers set. + orphanage.EraseForPeer(node0); + BOOST_CHECK(orphanage.HaveTx(ptx->GetWitnessHash())); + BOOST_CHECK(!orphanage.HaveTxFromPeer(ptx->GetWitnessHash(), node0)); + // node0 is the only one that announced ptx_mutated + BOOST_CHECK(!orphanage.HaveTx(ptx_mutated->GetWitnessHash())); + expected_total_count -= 1; + BOOST_CHECK_EQUAL(orphanage.Size(), expected_total_count); + + // EraseForPeer should delete the orphan if it's the only announcer left. + orphanage.EraseForPeer(node1); + BOOST_CHECK_EQUAL(orphanage.Size(), expected_total_count); + BOOST_CHECK(orphanage.HaveTx(ptx->GetWitnessHash())); + orphanage.EraseForPeer(node2); + expected_total_count -= 1; + BOOST_CHECK_EQUAL(orphanage.Size(), expected_total_count); + BOOST_CHECK(!orphanage.HaveTx(ptx->GetWitnessHash())); + } + + // Check that erasure for blocks removes for all peers. + { + CBlock block; + auto tx_block = MakeTransactionSpending({}, det_rand); + block.vtx.emplace_back(tx_block); + BOOST_CHECK(orphanage.AddTx(tx_block, node0)); + BOOST_CHECK(!orphanage.AddTx(tx_block, node1)); + + expected_total_count += 1; + + BOOST_CHECK_EQUAL(orphanage.Size(), expected_total_count); + + orphanage.EraseForBlock(block); + + expected_total_count -= 1; + + BOOST_CHECK_EQUAL(orphanage.Size(), expected_total_count); + } +} +BOOST_AUTO_TEST_CASE(peer_worksets) +{ + const NodeId node0{0}; + const NodeId node1{1}; + const NodeId node2{2}; + FastRandomContext det_rand{true}; + TxOrphanageTest orphanage{det_rand}; + // AddChildrenToWorkSet should pick an announcer randomly + { + auto tx_missing_parent = MakeTransactionSpending({}, det_rand); + auto tx_orphan = MakeTransactionSpending({COutPoint{tx_missing_parent->GetHash(), 0}}, det_rand); + const auto& orphan_wtxid = tx_orphan->GetWitnessHash(); + + // All 3 peers are announcers. + BOOST_CHECK(orphanage.AddTx(tx_orphan, node0)); + BOOST_CHECK(!orphanage.AddTx(tx_orphan, node1)); + BOOST_CHECK(orphanage.AddAnnouncer(orphan_wtxid, node2)); + for (NodeId node = node0; node <= node2; ++node) { + BOOST_CHECK(orphanage.HaveTxFromPeer(orphan_wtxid, node)); + } + + // Parent accepted: add child to all 3 worksets. + orphanage.AddChildrenToWorkSet(*tx_missing_parent); + BOOST_CHECK_EQUAL(orphanage.GetTxToReconsider(node0), tx_orphan); + BOOST_CHECK_EQUAL(orphanage.GetTxToReconsider(node1), tx_orphan); + // Don't call GetTxToReconsider(node2) yet because it mutates the workset. + + // EraseForPeer also removes that tx from the workset. + orphanage.EraseForPeer(node0); + BOOST_CHECK_EQUAL(orphanage.GetTxToReconsider(node0), nullptr); + + // However, the other peers' worksets are not touched. + BOOST_CHECK_EQUAL(orphanage.GetTxToReconsider(node2), tx_orphan); + + // Delete this tx, clearing the orphanage. + BOOST_CHECK_EQUAL(orphanage.EraseTx(orphan_wtxid), 1); + BOOST_CHECK_EQUAL(orphanage.Size(), 0); + for (NodeId node = node0; node <= node2; ++node) { + BOOST_CHECK_EQUAL(orphanage.GetTxToReconsider(node), nullptr); + BOOST_CHECK(!orphanage.HaveTxFromPeer(orphan_wtxid, node)); + } + } +} BOOST_AUTO_TEST_SUITE_END()