fuzz: create and send transactions in cmpctblock harness
What changed, and why it matters
This commit only changes a fuzz test file (src/test/fuzz/cmpctblock.cpp). It makes the compact-block fuzzing harness create and send fake transactions during testing, and resets internal test state if the mempool changes. There is no change to production Bitcoin Core code, network rules, or wallet behavior. It does not fix or introduce a real security vulnerability.
No action required. This is a test-only change improving fuzz coverage for compact block handling. Reviewers may optionally verify that the fuzz harness reset logic does not hide real bugs, but the commit itself is not a security patch.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The diff expands the cmpctblock fuzz target to generate synthetic transactions from mature coinbase UTXOs or existing mempool entries, feeding them into the fuzzed net message loop. It also adds a deterministic reset of the random number generator, mempool, and chainman when the mempool sequence counter changes. All modifications are confined to test/fuzz infrastructure; no consensus, net_processing, mempool policy, or wallet logic is altered.
Changed components
src/test/fuzz/cmpctblock.cppInspect captured patch +80 / −1
diff --git a/src/test/fuzz/cmpctblock.cpp b/src/test/fuzz/cmpctblock.cpp
index 31a9d72f..a4ad037d 100644
--- a/src/test/fuzz/cmpctblock.cpp
+++ b/src/test/fuzz/cmpctblock.cpp
@@ -5,11 +5,14 @@
#include <addrman.h>
#include <chain.h>
#include <chainparams.h>
+#include <coins.h>
+#include <consensus/amount.h>
#include <consensus/consensus.h>
#include <net.h>
#include <net_processing.h>
#include <netmessagemaker.h>
#include <node/miner.h>
+#include <policy/truc_policy.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
#include <protocol.h>
@@ -28,14 +31,20 @@
#include <test/util/txmempool.h>
#include <test/util/validation.h>
#include <txmempool.h>
+#include <uint256.h>
#include <util/check.h>
#include <util/time.h>
#include <util/translation.h>
#include <validation.h>
#include <validationinterface.h>
+#include <boost/multi_index/detail/hash_index_iterator.hpp>
+#include <boost/operators.hpp>
+
+#include <cstddef>
#include <cstdint>
#include <functional>
+#include <iterator>
#include <memory>
#include <optional>
#include <string>
@@ -47,6 +56,11 @@ namespace {
TestingSetup* g_setup;
+//! Fee each created tx will pay.
+const CAmount AMOUNT_FEE{1000};
+//! Cached coinbases that each iteration can copy and use.
+std::vector<std::pair<COutPoint, CAmount>> g_mature_coinbase;
+
void ResetChainmanAndMempool(TestingSetup& setup)
{
SetMockTime(Params().GenesisBlock().Time());
@@ -64,13 +78,22 @@ void ResetChainmanAndMempool(TestingSetup& setup)
options.coinbase_output_script = P2WSH_OP_TRUE;
options.include_dummy_extranonce = true;
+ g_mature_coinbase.clear();
+
for (int i = 0; i < 2 * COINBASE_MATURITY; ++i) {
- MineBlock(setup.m_node, options);
+ COutPoint prevout{MineBlock(setup.m_node, options)};
+ if (i < COINBASE_MATURITY) {
+ LOCK(cs_main);
+ CAmount subsidy{setup.m_node.chainman->ActiveChainstate().CoinsTip().GetCoin(prevout)->out.nValue};
+ g_mature_coinbase.emplace_back(prevout, subsidy);
+ }
}
}
} // namespace
+extern void MakeRandDeterministicDANGEROUS(const uint256& seed) noexcept;
+
void initialize_cmpctblock()
{
static const auto testing_setup = MakeNoLogFileContext<TestingSetup>();
@@ -114,6 +137,50 @@ FUZZ_TARGET(cmpctblock, .init = initialize_cmpctblock)
connman.AddTestNode(p2p_node);
}
+ // Coinbase UTXOs for this iteration.
+ std::vector<std::pair<COutPoint, CAmount>> mature_coinbase = g_mature_coinbase;
+
+ const uint64_t initial_sequence{WITH_LOCK(mempool.cs, return mempool.GetSequence())};
+
+ auto create_tx = [&]() -> CTransactionRef {
+ CMutableTransaction tx_mut;
+ tx_mut.version = fuzzed_data_provider.ConsumeBool() ? CTransaction::CURRENT_VERSION : TRUC_VERSION;
+ tx_mut.nLockTime = fuzzed_data_provider.ConsumeBool() ? 0 : fuzzed_data_provider.ConsumeIntegral<uint32_t>();
+
+ // If the mempool is non-empty, sometimes choose a mempool outpoint. Otherwise, choose a coinbase.
+ CAmount amount_in;
+ COutPoint outpoint;
+ unsigned long mempool_size = mempool.size();
+ if (mempool_size != 0 && fuzzed_data_provider.ConsumeBool()) {
+ size_t random_idx = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, mempool_size - 1);
+ CTransactionRef tx = WITH_LOCK(mempool.cs, return mempool.txns_randomized[random_idx].second->GetSharedTx(););
+ outpoint = COutPoint(tx->GetHash(), 0);
+ amount_in = tx->vout[0].nValue;
+ } else {
+ auto coinbase_it = mature_coinbase.begin();
+ std::advance(coinbase_it, fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, mature_coinbase.size() - 1));
+ outpoint = coinbase_it->first;
+ amount_in = coinbase_it->second;
+ }
+
+ const auto sequence = ConsumeSequence(fuzzed_data_provider);
+ const auto script_sig = CScript{};
+ const auto script_wit_stack = std::vector<std::vector<uint8_t>>{WITNESS_STACK_ELEM_OP_TRUE};
+
+ CTxIn in;
+ in.prevout = outpoint;
+ in.nSequence = sequence;
+ in.scriptSig = script_sig;
+ in.scriptWitness.stack = script_wit_stack;
+ tx_mut.vin.push_back(in);
+
+ const CAmount amount_out = amount_in - AMOUNT_FEE;
+ tx_mut.vout.emplace_back(amount_out, P2WSH_OP_TRUE);
+
+ auto tx = MakeTransactionRef(tx_mut);
+ return tx;
+ };
+
LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 1000)
{
CSerializedNetMsg net_msg;
@@ -133,6 +200,11 @@ FUZZ_TARGET(cmpctblock, .init = initialize_cmpctblock)
sent_sendcmpct = true;
valid_sendcmpct = version == CMPCTBLOCKS_VERSION;
},
+ [&]() {
+ // Send a transaction.
+ CTransactionRef tx = create_tx();
+ net_msg = NetMsg::Make(NetMsgType::TX, TX_WITH_WITNESS(*tx));
+ },
[&]() {
// Set mock time randomly or to tip's time.
if (fuzzed_data_provider.ConsumeBool()) {
@@ -174,4 +246,11 @@ FUZZ_TARGET(cmpctblock, .init = initialize_cmpctblock)
setup->m_node.validation_signals->SyncWithValidationInterfaceQueue();
setup->m_node.validation_signals->UnregisterAllValidationInterfaces();
connman.StopNodes();
+
+ const uint64_t end_sequence{WITH_LOCK(mempool.cs, return mempool.GetSequence())};
+
+ if (initial_sequence != end_sequence) {
+ MakeRandDeterministicDANGEROUS(uint256::ZERO);
+ ResetChainmanAndMempool(*g_setup);
+ }
}
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.