diff --git a/src/test/fuzz/mini_miner.cpp b/src/test/fuzz/mini_miner.cpp index baa28affcec..8185fa3ab67 100644 --- a/src/test/fuzz/mini_miner.cpp +++ b/src/test/fuzz/mini_miner.cpp @@ -129,14 +129,34 @@ 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; + // 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()); @@ -158,10 +178,17 @@ 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) { + // 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)); @@ -184,6 +211,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;