9 #include <boost/test/unit_test.hpp> 14 BOOST_AUTO_TEST_SUITE(txgraph_tests)
20 constexpr uint64_t HIGH_ACCEPTABLE_COST = 100
'000'000;
37 static constexpr
int MAX_CLUSTER_COUNT = 50;
39 static constexpr
int NUM_BOTTOM_TX = 49;
43 static constexpr
int NUM_TOP_TX = 50;
45 static constexpr
int NUM_TOTAL_TX = NUM_BOTTOM_TX + NUM_TOP_TX;
46 static_assert(NUM_TOTAL_TX > MAX_CLUSTER_COUNT);
48 static constexpr int32_t MAX_CLUSTER_SIZE = 100
'000 * 100; 50 // Create a new graph for the test. 51 auto graph = MakeTxGraph(MAX_CLUSTER_COUNT, MAX_CLUSTER_SIZE, HIGH_ACCEPTABLE_COST, PointerComparator); 53 // Add all transactions and store their Refs. 54 std::vector<TxGraph::Ref> refs; 55 refs.reserve(NUM_TOTAL_TX); 56 // First all bottom transactions: the i'th bottom transaction is at position i.
57 for (
unsigned int i = 0; i < NUM_BOTTOM_TX; ++i) {
58 graph->AddTransaction(refs.emplace_back(),
FeePerWeight{200 - i, 100});
61 for (
unsigned int i = 0; i < NUM_TOP_TX; ++i) {
62 graph->AddTransaction(refs.emplace_back(),
FeePerWeight{100 - i, 100});
68 for (
unsigned int i = 0; i < NUM_BOTTOM_TX; ++i) {
69 graph->AddDependency(refs[NUM_BOTTOM_TX + i], refs[i]);
70 graph->AddDependency(refs[NUM_BOTTOM_TX + i + 1], refs[i]);
80 auto removed_refs = graph->Trim();
87 for (
unsigned int i = 0; i < refs.size(); ++i) {
104 static constexpr
int MAX_CLUSTER_COUNT = 50;
108 static constexpr
int NUM_TOP_TX = MAX_CLUSTER_COUNT * 2;
110 static constexpr
int NUM_TOTAL_TX = NUM_TOP_TX + 1;
112 static constexpr int32_t MAX_CLUSTER_SIZE = 100
'000 * 100; 114 auto graph = MakeTxGraph(MAX_CLUSTER_COUNT, MAX_CLUSTER_SIZE, HIGH_ACCEPTABLE_COST, PointerComparator); 116 // Add all transactions and store their Refs. 117 std::vector<TxGraph::Ref> refs; 118 refs.reserve(NUM_TOTAL_TX); 120 // Add all transactions. They are in individual clusters. 121 graph->AddTransaction(refs.emplace_back(), {1, 100}); 122 for (unsigned int i = 0; i < NUM_TOP_TX; ++i) { 123 graph->AddTransaction(refs.emplace_back(), FeePerWeight{500 + i, 100}); 125 graph->SanityCheck(); 127 // The 0th transaction spends all the top transactions. 128 for (unsigned int i = 1; i < NUM_TOTAL_TX; ++i) { 129 graph->AddDependency(/*parent=*/refs[i], /*child=*/refs[0]); 131 graph->SanityCheck(); 133 // Check that the graph is now oversized. This also forces the graph to 134 // group clusters and compute the oversized status. 135 BOOST_CHECK(graph->IsOversized(TxGraph::Level::TOP)); 137 // Call Trim() to remove transactions and bring the cluster back within limits. 138 auto removed_refs = graph->Trim(); 139 graph->SanityCheck(); 140 BOOST_CHECK(!graph->IsOversized(TxGraph::Level::TOP)); 142 // Since only the bottom transaction connects these clusters, we only need to remove it. 143 BOOST_CHECK_EQUAL(removed_refs.size(), 1); 144 BOOST_CHECK_EQUAL(graph->GetTransactionCount(TxGraph::Level::TOP), MAX_CLUSTER_COUNT * 2); 145 BOOST_CHECK(!graph->Exists(refs[0], TxGraph::Level::TOP)); 146 for (unsigned int i = 1; i < refs.size(); ++i) { 147 BOOST_CHECK(graph->Exists(refs[i], TxGraph::Level::TOP)); 151 BOOST_AUTO_TEST_CASE(txgraph_trim_huge) 153 // The from-block transactions consist of 1000 fully linear clusters, each with 64 154 // transactions. The mempool contains 11 transactions that together merge all of these into 157 // (1000 chains of 64 transactions, 64000 T's total)
176 static constexpr
int MAX_CLUSTER_COUNT = 64;
178 static constexpr
int NUM_TOP_CHAINS = 1000;
180 static constexpr
int NUM_TX_PER_TOP_CHAIN = MAX_CLUSTER_COUNT;
182 static constexpr
int NUM_DEPS_PER_BOTTOM_TX = 100;
184 static constexpr
int NUM_BOTTOM_TX = (NUM_TOP_CHAINS - 1 + (NUM_DEPS_PER_BOTTOM_TX - 2)) / (NUM_DEPS_PER_BOTTOM_TX - 1);
186 static constexpr
int NUM_TOTAL_TX = NUM_TOP_CHAINS * NUM_TX_PER_TOP_CHAIN + NUM_BOTTOM_TX;
188 static constexpr int32_t MAX_CLUSTER_SIZE = 100
'000 * 100; 191 std::vector<TxGraph::Ref> top_refs; 193 std::vector<TxGraph::Ref> bottom_refs; 197 std::vector<size_t> top_components; 199 FastRandomContext rng; 200 auto graph = MakeTxGraph(MAX_CLUSTER_COUNT, MAX_CLUSTER_SIZE, HIGH_ACCEPTABLE_COST, PointerComparator); 202 // Construct the top chains. 203 for (int chain = 0; chain < NUM_TOP_CHAINS; ++chain) { 204 for (int chaintx = 0; chaintx < NUM_TX_PER_TOP_CHAIN; ++chaintx) { 205 // Use random fees, size 1. 206 int64_t fee = rng.randbits<27>() + 100; 207 FeePerWeight feerate{fee, 1}; 208 graph->AddTransaction(top_refs.emplace_back(), feerate); 209 // Add internal dependencies linking the chain transactions together. 211 graph->AddDependency(*(top_refs.rbegin()), *(top_refs.rbegin() + 1)); 214 // Remember the last transaction in each chain, to attach the bottom transactions to. 215 top_components.push_back(top_refs.size() - 1); 217 graph->SanityCheck(); 219 // Not oversized so far (just 1000 clusters of 64). 220 BOOST_CHECK(!graph->IsOversized(TxGraph::Level::TOP)); 222 // Construct the bottom transactions, and dependencies to the top chains. 223 while (top_components.size() > 1) { 224 // Construct the transaction. 225 int64_t fee = rng.randbits<27>() + 100; 226 FeePerWeight feerate{fee, 1}; 227 TxGraph::Ref bottom_tx; 228 graph->AddTransaction(bottom_tx, feerate); 229 // Determine the number of dependencies this transaction will have. 230 int deps = std::min<int>(NUM_DEPS_PER_BOTTOM_TX, top_components.size()); 231 for (int dep = 0; dep < deps; ++dep) { 232 // Pick an transaction in top_components to attach to. 233 auto idx = rng.randrange(top_components.size()); 235 graph->AddDependency(/*parent=*/top_refs[top_components[idx]], /*child=*/bottom_tx); 236 // Unless this is the last dependency being added, remove from top_components, as 237 // the component will be merged with that one. 238 if (dep < deps - 1) { 239 // Move entry top the back. 240 if (idx != top_components.size() - 1) std::swap(top_components.back(), top_components[idx]); 242 top_components.pop_back(); 245 bottom_refs.push_back(std::move(bottom_tx)); 247 graph->SanityCheck(); 249 // Now we are oversized (one cluster of 64011). 250 BOOST_CHECK(graph->IsOversized(TxGraph::Level::TOP)); 251 const auto total_tx_count = graph->GetTransactionCount(TxGraph::Level::TOP); 252 BOOST_CHECK(total_tx_count == top_refs.size() + bottom_refs.size()); 253 BOOST_CHECK(total_tx_count == NUM_TOTAL_TX); 255 // Call Trim() to remove transactions and bring the cluster back within limits. 256 auto removed_refs = graph->Trim(); 257 BOOST_CHECK(!graph->IsOversized(TxGraph::Level::TOP)); 258 BOOST_CHECK(removed_refs.size() == total_tx_count - graph->GetTransactionCount(TxGraph::Level::TOP)); 259 graph->SanityCheck(); 261 // At least 99% of chains must survive. 262 BOOST_CHECK(graph->GetTransactionCount(TxGraph::Level::TOP) >= (NUM_TOP_CHAINS * NUM_TX_PER_TOP_CHAIN * 99) / 100); 265 BOOST_AUTO_TEST_CASE(txgraph_trim_big_singletons) 267 // Mempool consists of 100 singleton clusters; there are no dependencies. Some are oversized. Trim() should remove all of the oversized ones. 268 static constexpr int MAX_CLUSTER_COUNT = 64; 269 static constexpr int32_t MAX_CLUSTER_SIZE = 100'000;
270 static constexpr
int NUM_TOTAL_TX = 100;
273 auto graph =
MakeTxGraph(MAX_CLUSTER_COUNT, MAX_CLUSTER_SIZE, HIGH_ACCEPTABLE_COST, PointerComparator);
276 std::vector<TxGraph::Ref> refs;
277 refs.reserve(NUM_TOTAL_TX);
280 for (
unsigned int i = 0; i < NUM_TOTAL_TX; ++i) {
283 const FeePerWeight feerate{500 + i, (i == 88 || i % 20 == 0) ? MAX_CLUSTER_SIZE + 1 : 100};
284 graph->AddTransaction(refs.emplace_back(), feerate);
286 graph->SanityCheck();
293 auto removed_refs = graph->Trim();
294 graph->SanityCheck();
299 for (
unsigned int i = 0; i < refs.size(); ++i) {
307 auto graph =
MakeTxGraph(50, 1000, HIGH_ACCEPTABLE_COST, PointerComparator);
309 auto block_builder_checker = [&graph](std::vector<std::vector<TxGraph::Ref*>> expected_chunks) {
310 std::vector<std::vector<TxGraph::Ref*>> chunks;
311 auto builder = graph->GetBlockBuilder();
313 while (
auto chunk = builder->GetCurrentChunk()) {
318 BOOST_CHECK(graph->GetMainChunkFeerate(*ref) == chunk->second);
320 sum += graph->GetIndividualFeerate(*ref);
323 chunks.push_back(std::move(chunk->first));
324 last_chunk_feerate = chunk->second;
329 auto& last_chunk = chunks.back();
331 std::reverse(last_chunk.begin(), last_chunk.end());
332 auto [worst_chunk, worst_chunk_feerate] = graph->GetWorstMainChunk();
334 BOOST_CHECK(last_chunk_feerate == worst_chunk_feerate);
337 std::vector<TxGraph::Ref> refs;
347 graph->AddTransaction(refs.emplace_back(), feerateA);
349 block_builder_checker({{&refs[0]}});
351 graph->AddTransaction(refs.emplace_back(), feerateB);
352 graph->AddDependency(refs[0], refs[1]);
354 block_builder_checker({{&refs[0]}, {&refs[1]}});
357 graph->AddTransaction(refs.emplace_back(), feerateC);
358 graph->AddDependency(refs[1], refs[2]);
360 block_builder_checker({{&refs[0]}, {&refs[1], &refs[2]}});
363 graph->AddTransaction(refs.emplace_back(), feerateD);
364 graph->AddDependency(refs[2], refs[3]);
366 block_builder_checker({{&refs[0], &refs[1], &refs[2], &refs[3]}});
368 graph->SanityCheck();
371 graph->RemoveTransaction(refs[1]);
375 graph->RemoveTransaction(refs[2]);
376 graph->RemoveTransaction(refs[3]);
378 block_builder_checker({{&refs[0]}});
386 auto graph =
MakeTxGraph(10, 1000, HIGH_ACCEPTABLE_COST, PointerComparator);
388 std::vector<TxGraph::Ref> refs;
396 graph->AddTransaction(refs.emplace_back(), feerateA);
400 graph->StartStaging();
405 graph->AddTransaction(refs.emplace_back(), feerateB);
411 graph->AddDependency(refs[0], refs[1]);
415 graph->CommitStaging();
420 graph->StartStaging();
423 graph->RemoveTransaction(refs[1]);
427 graph->CommitStaging();
431 graph->SanityCheck();
Always refers to the main graph, whether staging is present or not.
Tagged wrapper around FeeFrac to avoid unit confusion.
BOOST_AUTO_TEST_SUITE_END()
#define BOOST_CHECK_EQUAL(v1, v2)
std::unique_ptr< TxGraph > MakeTxGraph(unsigned max_cluster_count, uint64_t max_cluster_size, uint64_t acceptable_cost, const std::function< std::strong_ordering(const TxGraph::Ref &, const TxGraph::Ref &)> &fallback_order) noexcept
Construct a new TxGraph with the specified limit on the number of transactions within a cluster...
Refers to staging if it exists, main otherwise.
BOOST_AUTO_TEST_CASE(txgraph_trim_zigzag)
#define BOOST_CHECK(expr)