From 4a1ac31e97c252e5977ddd85109978307d427807 Mon Sep 17 00:00:00 2001 From: Hao Xu Date: Fri, 12 Dec 2025 20:52:46 +0800 Subject: [PATCH 1/3] test: add a chunk test for txgraph Add a test for GetWorstMainChunk(), which picks the worst chunk from txgraph. Co-developed-by: rkrux --- src/test/txgraph_tests.cpp | 59 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/src/test/txgraph_tests.cpp b/src/test/txgraph_tests.cpp index 3fb20d8dcc..3472f69924 100644 --- a/src/test/txgraph_tests.cpp +++ b/src/test/txgraph_tests.cpp @@ -291,4 +291,63 @@ BOOST_AUTO_TEST_CASE(txgraph_trim_big_singletons) } } +BOOST_AUTO_TEST_CASE(txgraph_get_worst_chunk_chain) +{ + // Create a new graph for the test. + auto graph = MakeTxGraph(50, 1000, NUM_ACCEPTABLE_ITERS); + + auto chunk_check_helper = [&graph](const std::vector& expected_chunk_txs_feerates, FeePerWeight expected_chunk_feerate) { + auto chunk = graph->GetWorstMainChunk(); + BOOST_CHECK_EQUAL(chunk.first.size(), expected_chunk_txs_feerates.size()); + for (size_t i = 0; i < expected_chunk_txs_feerates.size(); i++) + BOOST_CHECK(graph->GetIndividualFeerate(*chunk.first[i]) == expected_chunk_txs_feerates[i]); + + BOOST_CHECK_EQUAL(chunk.second.fee, expected_chunk_feerate.fee); + BOOST_CHECK_EQUAL(chunk.second.size, expected_chunk_feerate.size); + }; + + std::vector refs; + refs.reserve(4); + + FeePerWeight feerateA{2, 10}; + FeePerWeight feerateB{1, 10}; + FeePerWeight feerateC{2, 10}; + FeePerWeight feerateD{4, 10}; + + // everytime adding a transaction, test the chunk status + // [A] + refs.push_back(graph->AddTransaction(feerateA)); + BOOST_CHECK_EQUAL(graph->GetTransactionCount(TxGraph::Level::TOP), 1); + chunk_check_helper({feerateA}, feerateA); + // [A, B] + refs.push_back(graph->AddTransaction(feerateB)); + graph->AddDependency(/*parent=*/refs[0], /*child=*/refs[1]); + BOOST_CHECK_EQUAL(graph->GetTransactionCount(TxGraph::Level::TOP), 2); + chunk_check_helper({feerateB}, feerateB); + + // [A, BC] + refs.push_back(graph->AddTransaction(feerateC)); + graph->AddDependency(/*parent=*/refs[1], /*child=*/refs[2]); + BOOST_CHECK_EQUAL(graph->GetTransactionCount(TxGraph::Level::TOP), 3); + chunk_check_helper({feerateC, feerateB}, FeePerWeight{3, 20}); + + // [ABCD] + refs.push_back(graph->AddTransaction(feerateD)); + graph->AddDependency(/*parent=*/refs[2], /*child=*/refs[3]); + BOOST_CHECK_EQUAL(graph->GetTransactionCount(TxGraph::Level::TOP), 4); + chunk_check_helper({feerateD, feerateC, feerateB, feerateA}, FeePerWeight{9, 40}); + + graph->SanityCheck(); + + // D->C->A + graph->RemoveTransaction(refs[1]); + // txgraph is not responsible for removing the descendants or ancestors + BOOST_CHECK_EQUAL(graph->GetTransactionCount(TxGraph::Level::TOP), 3); + // only A remains there + graph->RemoveTransaction(refs[2]); + graph->RemoveTransaction(refs[3]); + BOOST_CHECK_EQUAL(graph->GetTransactionCount(TxGraph::Level::TOP), 1); + chunk_check_helper({feerateA}, feerateA); +} + BOOST_AUTO_TEST_SUITE_END() From ef253a9d3d16f62fe39fbeee336b100554ceaff7 Mon Sep 17 00:00:00 2001 From: Hao Xu Date: Thu, 25 Dec 2025 22:07:07 +0800 Subject: [PATCH 2/3] test: add block builder tests for txgraph Add block builder tests to make sure chunks for a cluster are all right. --- src/test/txgraph_tests.cpp | 44 +++++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/src/test/txgraph_tests.cpp b/src/test/txgraph_tests.cpp index 3472f69924..e87c1c5eda 100644 --- a/src/test/txgraph_tests.cpp +++ b/src/test/txgraph_tests.cpp @@ -291,19 +291,37 @@ BOOST_AUTO_TEST_CASE(txgraph_trim_big_singletons) } } -BOOST_AUTO_TEST_CASE(txgraph_get_worst_chunk_chain) +BOOST_AUTO_TEST_CASE(txgraph_chunk_chain) { // Create a new graph for the test. auto graph = MakeTxGraph(50, 1000, NUM_ACCEPTABLE_ITERS); - auto chunk_check_helper = [&graph](const std::vector& expected_chunk_txs_feerates, FeePerWeight expected_chunk_feerate) { - auto chunk = graph->GetWorstMainChunk(); - BOOST_CHECK_EQUAL(chunk.first.size(), expected_chunk_txs_feerates.size()); - for (size_t i = 0; i < expected_chunk_txs_feerates.size(); i++) - BOOST_CHECK(graph->GetIndividualFeerate(*chunk.first[i]) == expected_chunk_txs_feerates[i]); + auto block_builder_checker = [&graph](std::vector> expected_chunks) { + std::vector> chunks; + auto builder = graph->GetBlockBuilder(); + FeePerWeight last_chunk_feerate; + while (auto chunk = builder->GetCurrentChunk()) { + FeePerWeight sum; + for (TxGraph::Ref* ref : chunk->first) { + // The reported chunk feerate must match the chunk feerate obtained by asking + // it for each of the chunk's transactions individually. + BOOST_CHECK(graph->GetMainChunkFeerate(*ref) == chunk->second); + // Verify the chunk feerate matches the sum of the reported individual feerates. + sum += graph->GetIndividualFeerate(*ref); + } + BOOST_CHECK(sum == chunk->second); + chunks.push_back(std::move(chunk->first)); + last_chunk_feerate = chunk->second; + builder->Include(); + } - BOOST_CHECK_EQUAL(chunk.second.fee, expected_chunk_feerate.fee); - BOOST_CHECK_EQUAL(chunk.second.size, expected_chunk_feerate.size); + BOOST_CHECK(chunks == expected_chunks); + auto& last_chunk = chunks.back(); + // The last chunk returned by the BlockBuilder must match GetWorstMainChunk, in reverse. + std::reverse(last_chunk.begin(), last_chunk.end()); + auto [worst_chunk, worst_chunk_feerate] = graph->GetWorstMainChunk(); + BOOST_CHECK(last_chunk == worst_chunk); + BOOST_CHECK(last_chunk_feerate == worst_chunk_feerate); }; std::vector refs; @@ -318,24 +336,24 @@ BOOST_AUTO_TEST_CASE(txgraph_get_worst_chunk_chain) // [A] refs.push_back(graph->AddTransaction(feerateA)); BOOST_CHECK_EQUAL(graph->GetTransactionCount(TxGraph::Level::TOP), 1); - chunk_check_helper({feerateA}, feerateA); + block_builder_checker({{&refs[0]}}); // [A, B] refs.push_back(graph->AddTransaction(feerateB)); graph->AddDependency(/*parent=*/refs[0], /*child=*/refs[1]); BOOST_CHECK_EQUAL(graph->GetTransactionCount(TxGraph::Level::TOP), 2); - chunk_check_helper({feerateB}, feerateB); + block_builder_checker({{&refs[0]}, {&refs[1]}}); // [A, BC] refs.push_back(graph->AddTransaction(feerateC)); graph->AddDependency(/*parent=*/refs[1], /*child=*/refs[2]); BOOST_CHECK_EQUAL(graph->GetTransactionCount(TxGraph::Level::TOP), 3); - chunk_check_helper({feerateC, feerateB}, FeePerWeight{3, 20}); + block_builder_checker({{&refs[0]}, {&refs[1], &refs[2]}}); // [ABCD] refs.push_back(graph->AddTransaction(feerateD)); graph->AddDependency(/*parent=*/refs[2], /*child=*/refs[3]); BOOST_CHECK_EQUAL(graph->GetTransactionCount(TxGraph::Level::TOP), 4); - chunk_check_helper({feerateD, feerateC, feerateB, feerateA}, FeePerWeight{9, 40}); + block_builder_checker({{&refs[0], &refs[1], &refs[2], &refs[3]}}); graph->SanityCheck(); @@ -347,7 +365,7 @@ BOOST_AUTO_TEST_CASE(txgraph_get_worst_chunk_chain) graph->RemoveTransaction(refs[2]); graph->RemoveTransaction(refs[3]); BOOST_CHECK_EQUAL(graph->GetTransactionCount(TxGraph::Level::TOP), 1); - chunk_check_helper({feerateA}, feerateA); + block_builder_checker({{&refs[0]}}); } BOOST_AUTO_TEST_SUITE_END() From fe0b1513a7c53b8490b81165acf1c7d42297a2ed Mon Sep 17 00:00:00 2001 From: Hao Xu Date: Tue, 30 Dec 2025 17:53:52 +0800 Subject: [PATCH 3/3] test: add a test for txgraph staging staging is a batching mechanism for txgraph, add a test for this feature. --- src/test/txgraph_tests.cpp | 53 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/test/txgraph_tests.cpp b/src/test/txgraph_tests.cpp index e87c1c5eda..26cf2b3a97 100644 --- a/src/test/txgraph_tests.cpp +++ b/src/test/txgraph_tests.cpp @@ -368,4 +368,57 @@ BOOST_AUTO_TEST_CASE(txgraph_chunk_chain) block_builder_checker({{&refs[0]}}); } +BOOST_AUTO_TEST_CASE(txgraph_staging) +{ + /* Create a new graph for the test. + * The parameters are max_cluster_count, max_cluster_size, acceptable_iters + */ + auto graph = MakeTxGraph(10, 1000, NUM_ACCEPTABLE_ITERS); + + std::vector refs; + refs.reserve(2); + + FeePerWeight feerateA{2, 10}; + FeePerWeight feerateB{1, 10}; + + // everytime adding a transaction, test the chunk status + // [A] + refs.push_back(graph->AddTransaction(feerateA)); + BOOST_CHECK_EQUAL(graph->HaveStaging(), false); + BOOST_CHECK_EQUAL(graph->GetTransactionCount(TxGraph::Level::TOP), 1); + + graph->StartStaging(); + BOOST_CHECK_EQUAL(graph->HaveStaging(), true); + BOOST_CHECK_EQUAL(graph->GetTransactionCount(TxGraph::Level::TOP), 1); + + // [A, B] + refs.push_back(graph->AddTransaction(feerateB)); + BOOST_CHECK_EQUAL(graph->GetTransactionCount(TxGraph::Level::MAIN), 1); + BOOST_CHECK_EQUAL(graph->GetTransactionCount(TxGraph::Level::TOP), 2); + BOOST_CHECK_EQUAL(graph->Exists(refs[0], TxGraph::Level::TOP), true); + BOOST_CHECK_EQUAL(graph->Exists(refs[1], TxGraph::Level::TOP), true); + + graph->AddDependency(/*parent=*/refs[0], /*child=*/refs[1]); + BOOST_CHECK_EQUAL(graph->GetTransactionCount(TxGraph::Level::MAIN), 1); + BOOST_CHECK_EQUAL(graph->GetTransactionCount(TxGraph::Level::TOP), 2); + + graph->CommitStaging(); + BOOST_CHECK_EQUAL(graph->HaveStaging(), false); + + BOOST_CHECK_EQUAL(graph->GetTransactionCount(TxGraph::Level::MAIN), 2); + + graph->StartStaging(); + + // [A] + graph->RemoveTransaction(refs[1]); + BOOST_CHECK_EQUAL(graph->GetTransactionCount(TxGraph::Level::MAIN), 2); + BOOST_CHECK_EQUAL(graph->GetTransactionCount(TxGraph::Level::TOP), 1); + + graph->CommitStaging(); + + BOOST_CHECK_EQUAL(graph->GetTransactionCount(TxGraph::Level::MAIN), 1); + + graph->SanityCheck(); +} + BOOST_AUTO_TEST_SUITE_END()