From 0da2ae35c82637e9c2830a2324c993756c94f15f Mon Sep 17 00:00:00 2001 From: Fabian Jahr Date: Thu, 27 Mar 2025 23:40:32 +0100 Subject: [PATCH 1/3] fuzz: Fix block size stop gap in mini_miner_selection The check was comparing bytes (left) to WU (right). The check was not necessary so far because the test never hit high enough numbers for it to be actually necessary. --- src/test/fuzz/mini_miner.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/fuzz/mini_miner.cpp b/src/test/fuzz/mini_miner.cpp index baa28affcec..36c734e9d30 100644 --- a/src/test/fuzz/mini_miner.cpp +++ b/src/test/fuzz/mini_miner.cpp @@ -161,7 +161,7 @@ FUZZ_TARGET(mini_miner_selection, .init = initialize_miner) const auto block_adjusted_max_weight = MAX_BLOCK_WEIGHT - DEFAULT_BLOCK_RESERVED_WEIGHT; // Stop if pool reaches block_adjusted_max_weight because BlockAssembler will stop when the // block template reaches that, but the MiniMiner will keep going. - if (pool.GetTotalTxSize() + GetVirtualTransactionSize(*tx) >= block_adjusted_max_weight) break; + if ((pool.GetTotalTxSize() + GetVirtualTransactionSize(*tx)) * 4 >= block_adjusted_max_weight) break; TestMemPoolEntryHelper entry; const CAmount fee{ConsumeMoney(fuzzed_data_provider, /*max=*/MAX_MONEY/100000)}; assert(MoneyRange(fee)); From fd68e0690805d7e8980b84c3601a06c2b4291476 Mon Sep 17 00:00:00 2001 From: Fabian Jahr Date: Wed, 5 Feb 2025 17:15:34 +0100 Subject: [PATCH 2/3] fuzz: Fuzz reserved weight option in BlockAssembler --- src/test/fuzz/mini_miner.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/test/fuzz/mini_miner.cpp b/src/test/fuzz/mini_miner.cpp index 36c734e9d30..2ce10f12a5c 100644 --- a/src/test/fuzz/mini_miner.cpp +++ b/src/test/fuzz/mini_miner.cpp @@ -129,6 +129,8 @@ FUZZ_TARGET(mini_miner_selection, .init = initialize_miner) // Make a copy to preserve determinism. std::deque available_coins = g_available_coins; std::vector transactions; + // The maximum block template size we expect to produce + const auto block_adjusted_max_weight = MAX_BLOCK_WEIGHT - MINIMUM_BLOCK_RESERVED_WEIGHT; LOCK2(::cs_main, pool.cs); LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 100) @@ -158,7 +160,6 @@ FUZZ_TARGET(mini_miner_selection, .init = initialize_miner) } } - const auto block_adjusted_max_weight = MAX_BLOCK_WEIGHT - DEFAULT_BLOCK_RESERVED_WEIGHT; // Stop if pool reaches block_adjusted_max_weight because BlockAssembler will stop when the // block template reaches that, but the MiniMiner will keep going. if ((pool.GetTotalTxSize() + GetVirtualTransactionSize(*tx)) * 4 >= block_adjusted_max_weight) break; @@ -184,6 +185,11 @@ FUZZ_TARGET(mini_miner_selection, .init = initialize_miner) node::BlockAssembler::Options miner_options; miner_options.blockMinFeeRate = target_feerate; miner_options.nBlockMaxWeight = MAX_BLOCK_WEIGHT; + // Only setting reserved weight when necessary based on the template size + const auto reserved_weight = MAX_BLOCK_WEIGHT - pool.GetTotalTxSize() * 4; + if (reserved_weight < DEFAULT_BLOCK_RESERVED_WEIGHT) { + miner_options.block_reserved_weight = reserved_weight - 1; + } miner_options.test_block_validity = false; miner_options.coinbase_output_script = CScript() << OP_0; From 5702c4410de5cd0c1ebc325a184e635e87752075 Mon Sep 17 00:00:00 2001 From: Fabian Jahr Date: Tue, 4 Mar 2025 15:25:38 +0100 Subject: [PATCH 3/3] fuzz: Increase number of transactions in block in mini_miner test At the current configuration there was zero chance to hit the size limit of a block. The new configuration uses larger transactions and conditionally adds the maximum possible number of transactions instead of using the previous limited while loop to ensure that with some regularity the test runs into full blocks. --- src/test/fuzz/mini_miner.cpp | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/src/test/fuzz/mini_miner.cpp b/src/test/fuzz/mini_miner.cpp index 2ce10f12a5c..8185fa3ab67 100644 --- a/src/test/fuzz/mini_miner.cpp +++ b/src/test/fuzz/mini_miner.cpp @@ -131,14 +131,32 @@ FUZZ_TARGET(mini_miner_selection, .init = initialize_miner) std::vector transactions; // The maximum block template size we expect to produce const auto block_adjusted_max_weight = MAX_BLOCK_WEIGHT - MINIMUM_BLOCK_RESERVED_WEIGHT; + // When this is set to true we try to fill up the rest of the block with + // a lot of small transactions so we can actually get close to actual + // maximum block size + std::optional min_size_tx{std::nullopt}; + // This decides if we target a larger, potentially full block or a smaller + // block that will complete the test much faster + bool use_limited_loop = fuzzed_data_provider.ConsumeBool(); + auto should_continue = [&]() -> bool { + if (use_limited_loop) { + return fuzzed_data_provider.ConsumeBool(); + } + return true; + }; LOCK2(::cs_main, pool.cs); - LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 100) + // Limited to 500 because of ClusterMempool DoS protection + LIMITED_WHILE(should_continue(), 500) { CMutableTransaction mtx = CMutableTransaction(); assert(!available_coins.empty()); - const size_t num_inputs = std::min(size_t{2}, available_coins.size()); - const size_t num_outputs = fuzzed_data_provider.ConsumeIntegralInRange(2, 5); + size_t num_inputs = std::min(size_t{2}, available_coins.size()); + size_t num_outputs = fuzzed_data_provider.ConsumeIntegralInRange(50, 500); + if (min_size_tx.has_value() && min_size_tx.value()) { + num_inputs = fuzzed_data_provider.ConsumeIntegralInRange(1, 5); + num_outputs = fuzzed_data_provider.ConsumeIntegralInRange(1, 5); + } for (size_t n{0}; n < num_inputs; ++n) { auto prevout = available_coins.at(0); mtx.vin.emplace_back(prevout, CScript()); @@ -162,7 +180,15 @@ FUZZ_TARGET(mini_miner_selection, .init = initialize_miner) // Stop if pool reaches block_adjusted_max_weight because BlockAssembler will stop when the // block template reaches that, but the MiniMiner will keep going. - if ((pool.GetTotalTxSize() + GetVirtualTransactionSize(*tx)) * 4 >= block_adjusted_max_weight) break; + if ((pool.GetTotalTxSize() + GetVirtualTransactionSize(*tx)) * 4 >= block_adjusted_max_weight) { + // Either stop here or fill up the rest of the block with very small + // transactions and break when the block is close to the possible max + if (!min_size_tx.has_value()) { + min_size_tx = fuzzed_data_provider.ConsumeBool(); + if (!min_size_tx.value()) break; + } + break; + } TestMemPoolEntryHelper entry; const CAmount fee{ConsumeMoney(fuzzed_data_provider, /*max=*/MAX_MONEY/100000)}; assert(MoneyRange(fee));