fuzz: remove comparison between mini_miner block construction and miner
What changed, and why it matters
This commit removes a fuzz test that compared two internal Bitcoin components (MiniMiner and BlockAssembler) to ensure they built the same block. The test is being deleted because an upcoming code change ('cluster mempool') will make their outputs intentionally differ. This is a test-only cleanup, not a fix for a security bug.
No security action required. Reviewers may want to confirm that a replacement test or updated MiniMiner logic is planned, as noted in the commit message.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The diff deletes the FUZZ_TARGET(mini_miner_selection) fuzzing target from src/test/fuzz/mini_miner.cpp. That target used BlockAssembler as an oracle and asserted that MiniMiner produced an identical transaction set. The commit message states that after cluster mempool the two algorithms will diverge, so the comparison is no longer valid. No production code is changed.
Changed components
src/test/fuzz/mini_miner.cppInspect captured patch +0 / −91
diff --git a/src/test/fuzz/mini_miner.cpp b/src/test/fuzz/mini_miner.cpp
index a0c16c51..226c47d8 100644
--- a/src/test/fuzz/mini_miner.cpp
+++ b/src/test/fuzz/mini_miner.cpp
@@ -116,95 +116,4 @@ FUZZ_TARGET(mini_miner, .init = initialize_miner)
// Overlapping ancestry across multiple outpoints can only reduce the total bump fee.
assert (sum_fees >= *total_bumpfee);
}
-
-// Test that MiniMiner and BlockAssembler build the same block given the same transactions and constraints.
-FUZZ_TARGET(mini_miner_selection, .init = initialize_miner)
-{
- SeedRandomStateForTest(SeedRand::ZEROS);
- FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
- SetMockTime(ConsumeTime(fuzzed_data_provider));
- bilingual_str error;
- CTxMemPool pool{CTxMemPool::Options{}, error};
- Assert(error.empty());
- // Make a copy to preserve determinism.
- std::deque<COutPoint> available_coins = g_available_coins;
- std::vector<CTransactionRef> transactions;
-
- LOCK2(::cs_main, pool.cs);
- LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 100)
- {
- 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<size_t>(2, 5);
- for (size_t n{0}; n < num_inputs; ++n) {
- auto prevout = available_coins.at(0);
- mtx.vin.emplace_back(prevout, CScript());
- available_coins.pop_front();
- }
- for (uint32_t n{0}; n < num_outputs; ++n) {
- mtx.vout.emplace_back(100, P2WSH_OP_TRUE);
- }
- CTransactionRef tx = MakeTransactionRef(mtx);
-
- // First 2 outputs are available to spend. The rest are added to outpoints to calculate bumpfees.
- // There is no overlap between spendable coins and outpoints passed to MiniMiner because the
- // MiniMiner interprets spent coins as to-be-replaced and excludes them.
- for (uint32_t n{0}; n < num_outputs - 1; ++n) {
- if (fuzzed_data_provider.ConsumeBool()) {
- available_coins.emplace_front(tx->GetHash(), n);
- } else {
- available_coins.emplace_back(tx->GetHash(), n);
- }
- }
-
- 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;
- TestMemPoolEntryHelper entry;
- const CAmount fee{ConsumeMoney(fuzzed_data_provider, /*max=*/MAX_MONEY/100000)};
- assert(MoneyRange(fee));
- AddToMempool(pool, entry.Fee(fee).FromTx(tx));
- transactions.push_back(tx);
- }
- std::vector<COutPoint> outpoints;
- for (const auto& coin : g_available_coins) {
- if (!pool.GetConflictTx(coin)) outpoints.push_back(coin);
- }
- for (const auto& tx : transactions) {
- assert(pool.exists(tx->GetHash()));
- for (uint32_t n{0}; n < tx->vout.size(); ++n) {
- COutPoint coin{tx->GetHash(), n};
- if (!pool.GetConflictTx(coin)) outpoints.push_back(coin);
- }
- }
- const CFeeRate target_feerate{ConsumeMoney(fuzzed_data_provider, /*max=*/MAX_MONEY/100000)};
-
- node::BlockAssembler::Options miner_options;
- miner_options.blockMinFeeRate = target_feerate;
- miner_options.nBlockMaxWeight = MAX_BLOCK_WEIGHT;
- miner_options.test_block_validity = false;
- miner_options.coinbase_output_script = CScript() << OP_0;
-
- node::BlockAssembler miner{g_setup->m_node.chainman->ActiveChainstate(), &pool, miner_options};
- node::MiniMiner mini_miner{pool, outpoints};
- assert(mini_miner.IsReadyToCalculate());
-
- // Use BlockAssembler as oracle. BlockAssembler and MiniMiner should select the same
- // transactions, stopping once packages do not meet target_feerate.
- const auto blocktemplate{miner.CreateNewBlock()};
- mini_miner.BuildMockTemplate(target_feerate);
- assert(!mini_miner.IsReadyToCalculate());
- auto mock_template_txids = mini_miner.GetMockTemplateTxids();
- // MiniMiner doesn't add a coinbase tx.
- assert(mock_template_txids.count(blocktemplate->block.vtx[0]->GetHash()) == 0);
- auto [iter, new_entry] = mock_template_txids.emplace(blocktemplate->block.vtx[0]->GetHash());
- assert(new_entry);
-
- assert(mock_template_txids.size() == blocktemplate->block.vtx.size());
- for (const auto& tx : blocktemplate->block.vtx) {
- assert(mock_template_txids.count(tx->GetHash()));
- }
-}
} // namespace
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.