fuzz: try to add more code coverage for mempool fuzzing
What changed, and why it matters
This commit is a test-only change. It expands Bitcoin Core's fuzz testing for the transaction memory pool (mempool) to cover more behaviors like eviction, expiry, reorg handling, and block template creation. It also adds a sanity check inside the mempool's own consistency verification code. There is no indication this fixes a security vulnerability or changes production network behavior.
No security action required. Treat as normal test/QA improvement. If reviewing, confirm the new fuzz harness compiles and runs without triggering the added SanityCheck assertion.
Security signals we found
No security-relevant signals detected in the diff or commit message.
Change is confined to fuzz tests and an internal debug sanity check.
Commit title and message describe coverage improvement, not a bug fix.
Evidence from the diff
The diff modifies src/test/fuzz/tx_pool.cpp to broaden mempool fuzz coverage: it replaces ancestor/descendant limit arguments with cluster limits, simulates a block being mined and then reorged back into the mempool, exercises TrimToSize eviction and Expire logic, and adds a branch that creates outputs with signature operations to test sigops counting. In src/txmempool.cpp it adds m_txgraph->SanityCheck() inside CTxMemPool::check(), which is an internal consistency assertion used only in debug/fuzz builds. No consensus, P2P, or wallet code is changed.
Changed components
src/test/fuzz/tx_pool.cppsrc/txmempool.cppInspect captured patch +43 / −10
diff --git a/src/test/fuzz/tx_pool.cpp b/src/test/fuzz/tx_pool.cpp
index c728485d..4dcfd30a 100644
--- a/src/test/fuzz/tx_pool.cpp
+++ b/src/test/fuzz/tx_pool.cpp
@@ -80,14 +80,10 @@ struct TransactionsDelta final : public CValidationInterface {
void SetMempoolConstraints(ArgsManager& args, FuzzedDataProvider& fuzzed_data_provider)
{
- args.ForceSetArg("-limitancestorcount",
- ToString(fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 50)));
- args.ForceSetArg("-limitancestorsize",
- ToString(fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 202)));
- args.ForceSetArg("-limitdescendantcount",
- ToString(fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 50)));
- args.ForceSetArg("-limitdescendantsize",
- ToString(fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 202)));
+ args.ForceSetArg("-limitclustercount",
+ ToString(fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(1, 64)));
+ args.ForceSetArg("-limitclustersize",
+ ToString(fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(1, 250)));
args.ForceSetArg("-maxmempool",
ToString(fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 200)));
args.ForceSetArg("-mempoolexpiry",
@@ -104,14 +100,41 @@ void Finish(FuzzedDataProvider& fuzzed_data_provider, MockedTxPool& tx_pool, Cha
auto assembler = BlockAssembler{chainstate, &tx_pool, options};
auto block_template = assembler.CreateNewBlock();
Assert(block_template->block.vtx.size() >= 1);
+
+ // Try updating the mempool for this block, as though it were mined.
+ LOCK2(::cs_main, tx_pool.cs);
+ tx_pool.removeForBlock(block_template->block.vtx, chainstate.m_chain.Height() + 1);
+
+ // Now try to add those transactions back, as though a reorg happened.
+ std::vector<Txid> hashes_to_update;
+ for (const auto& tx : block_template->block.vtx) {
+ const auto res = AcceptToMemoryPool(chainstate, tx, GetTime(), true, /*test_accept=*/false);
+ if (res.m_result_type == MempoolAcceptResult::ResultType::VALID) {
+ hashes_to_update.push_back(tx->GetHash());
+ } else {
+ tx_pool.removeRecursive(*tx, MemPoolRemovalReason::REORG);
+ }
+ }
+ tx_pool.UpdateTransactionsFromBlock(hashes_to_update);
}
const auto info_all = tx_pool.infoAll();
if (!info_all.empty()) {
const auto& tx_to_remove = *PickValue(fuzzed_data_provider, info_all).tx;
WITH_LOCK(tx_pool.cs, tx_pool.removeRecursive(tx_to_remove, MemPoolRemovalReason::BLOCK /* dummy */));
assert(tx_pool.size() < info_all.size());
- WITH_LOCK(::cs_main, tx_pool.check(chainstate.CoinsTip(), chainstate.m_chain.Height() + 1));
}
+
+ if (fuzzed_data_provider.ConsumeBool()) {
+ // Try eviction
+ LOCK2(::cs_main, tx_pool.cs);
+ tx_pool.TrimToSize(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0U, tx_pool.DynamicMemoryUsage() * 2));
+ }
+ if (fuzzed_data_provider.ConsumeBool()) {
+ // Try expiry
+ LOCK2(::cs_main, tx_pool.cs);
+ tx_pool.Expire(GetMockTime() - std::chrono::seconds(fuzzed_data_provider.ConsumeIntegral<uint32_t>()));
+ }
+ WITH_LOCK(::cs_main, tx_pool.check(chainstate.CoinsTip(), chainstate.m_chain.Height() + 1));
g_setup->m_node.validation_signals->SyncWithValidationInterfaceQueue();
}
@@ -264,11 +287,20 @@ FUZZ_TARGET(tx_pool_standard, .init = initialize_tx_pool)
tx_mut.vin.push_back(in);
}
+
+ // Check sigops in mempool + block template creation
+ bool add_sigops{fuzzed_data_provider.ConsumeBool()};
+
const auto amount_fee = fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(-1000, amount_in);
const auto amount_out = (amount_in - amount_fee) / num_out;
for (int i = 0; i < num_out; ++i) {
- tx_mut.vout.emplace_back(amount_out, P2WSH_OP_TRUE);
+ if (i == 0 && add_sigops) {
+ tx_mut.vout.emplace_back(amount_out, CScript() << std::vector<unsigned char>(33, 0x02) << OP_CHECKSIG);
+ } else {
+ tx_mut.vout.emplace_back(amount_out, P2WSH_OP_TRUE);
+ }
}
+
auto tx = MakeTransactionRef(tx_mut);
// Restore previously removed outpoints
for (const auto& in : tx->vin) {
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 64acf952..d7303cc6 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -418,6 +418,7 @@ void CTxMemPool::check(const CCoinsViewCache& active_coins_tip, int64_t spendhei
uint64_t innerUsage = 0;
assert(!m_txgraph->IsOversized(TxGraph::Level::MAIN));
+ m_txgraph->SanityCheck();
CCoinsViewCache mempoolDuplicate(const_cast<CCoinsViewCache*>(&active_coins_tip));
Why this scored 12/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.