fuzz: create and send transactions in cmpctblock harness

If the mempool is modified at all (determined by a change in the sequence
counter), reset the rng, mempool, and the chainman for the next iteration.
This commit is contained in:
Eugene Siegel
2026-02-03 16:42:19 -05:00
parent 8c9a3fd0e8
commit 651622432d

View File

@@ -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);
}
}