Bitcoin Core 32.99.0
P2P Digital Currency
Loading...
Searching...
No Matches
cmpctblock.cpp
Go to the documentation of this file.
1// Copyright (c) 2026 The Bitcoin Core developers
2// Distributed under the MIT software license, see the accompanying
3// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5#include <addrman.h>
6#include <blockencodings.h>
7#include <chain.h>
8#include <chainparams.h>
9#include <coins.h>
10#include <consensus/amount.h>
11#include <consensus/consensus.h>
12#include <consensus/merkle.h>
13#include <net.h>
14#include <net_processing.h>
15#include <netmessagemaker.h>
16#include <node/blockstorage.h>
17#include <policy/truc_policy.h>
18#include <primitives/block.h>
20#include <protocol.h>
21#include <script/script.h>
22#include <serialize.h>
23#include <sync.h>
25#include <test/fuzz/fuzz.h>
26#include <test/fuzz/util.h>
27#include <test/fuzz/util/net.h>
29#include <test/util/net.h>
30#include <test/util/random.h>
31#include <test/util/script.h>
33#include <test/util/time.h>
35#include <txmempool.h>
36#include <uint256.h>
37#include <util/check.h>
38#include <util/time.h>
39#include <validation.h>
40#include <validationinterface.h>
41
42#include <boost/multi_index/detail/hash_index_iterator.hpp>
43
44#include <cstddef>
45#include <cstdint>
46#include <functional>
47#include <iterator>
48#include <memory>
49#include <optional>
50#include <string>
51#include <utility>
52#include <vector>
53
54namespace {
55
57constexpr ReachabilityGoal CMPCTBLOCK_CHANGES_MEMPOOL{"cmpctblock changes the mempool"};
58constexpr ReachabilityGoal CMPCTBLOCK_CHANGES_BLOCK_INDEX{"cmpctblock changes the block index"};
59
61const CAmount AMOUNT_FEE{1000};
63std::vector<std::pair<COutPoint, CAmount>> g_mature_coinbase;
67struct BlockInfo {
68 std::shared_ptr<CBlock> block;
69 uint256 hash;
70 uint32_t height;
71};
73class FuzzedCBlockHeaderAndShortTxIDs : public CBlockHeaderAndShortTxIDs
74{
76
77public:
79 {
80 prefilledtxn.push_back(std::move(prefilledtx));
81 }
82
84 {
85 prefilledtxn.erase(prefilledtxn.begin());
86 }
87
89 {
90 shorttxids.insert(shorttxids.begin(), shorttxid);
91 }
92
93 void EraseShortTxIDs(size_t index)
94 {
95 shorttxids.erase(shorttxids.begin() + index);
96 }
97
98 size_t PrefilledTxCount() {
99 return prefilledtxn.size();
100 }
101
102 size_t ShortTxIDCount() {
103 return shorttxids.size();
104 }
105};
106
107
108} // namespace
109
110extern void MakeRandDeterministicDANGEROUS(const uint256& seed) noexcept;
111
113{
116 FakeNodeClock init_clock{}; // Uses the existing mock time
117 static const auto testing_setup = MakeNoLogFileContext<TestingSetup>();
118 g_setup = testing_setup.get();
120 // Replace validation_signals before creating chainman and mempool so they use it.
121 testing_setup->m_node.validation_signals = std::make_unique<ValidationSignals>(std::make_unique<ImmediateBackgroundTaskRunner>());
123}
124
126{
128 FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
129
130 FakeNodeClock node_clock{1610000000s}; // 2021-01-07, arbitrary
131 FakeSteadyClock steady_clock;
132
133 auto setup = g_setup;
134 auto& connman = *static_cast<ConnmanTestMsg*>(setup->m_node.connman.get());
135 connman.Reset();
136 auto& mempool = *setup->m_node.mempool;
137 auto& chainman = static_cast<TestChainstateManager&>(*setup->m_node.chainman);
138 chainman.ResetIbd();
139 chainman.DisableNextWrite();
140 const size_t initial_index_size{WITH_LOCK(chainman.GetMutex(), return chainman.BlockIndex().size())};
141
142 AddrMan addrman{*setup->m_node.netgroupman, /*deterministic=*/true, /*consistency_check_ratio=*/0};
143 auto peerman = PeerManager::make(connman, addrman,
144 /*banman=*/nullptr, chainman,
145 mempool, *setup->m_node.warnings,
147 .deterministic_rng = true,
148 });
149 connman.SetMsgProc(peerman.get());
150
151 setup->m_node.validation_signals->RegisterValidationInterface(peerman.get());
152 setup->m_node.validation_signals->SyncWithValidationInterfaceQueue();
153
155
156 std::vector<CNode*> peers;
157 for (int i = 0; i < 4; ++i) {
158 peers.push_back(ConsumeNodeAsUniquePtr(fuzzed_data_provider, steady_clock, i).release());
159 CNode& p2p_node = *peers.back();
161 connman.AddTestNode(p2p_node);
162 }
163
164 // Stores blocks generated this iteration.
165 std::vector<BlockInfo> info;
166
167 // Coinbase UTXOs for this iteration.
168 std::vector<std::pair<COutPoint, CAmount>> mature_coinbase = g_mature_coinbase;
169
170 const uint64_t initial_sequence{WITH_LOCK(mempool.cs, return mempool.GetSequence())};
171
172 auto create_tx = [&]() -> CTransactionRef {
176
177 // Choose an outpoint from the mempool, created blocks, or coinbases.
179 COutPoint outpoint;
180 unsigned long mempool_size = mempool.size();
181 if (mempool_size != 0 && fuzzed_data_provider.ConsumeBool()) {
182 size_t random_idx = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, mempool_size - 1);
183 CTransactionRef tx = WITH_LOCK(mempool.cs, return mempool.txns_randomized[random_idx].second->GetSharedTx(););
184 outpoint = COutPoint(tx->GetHash(), 0);
185 amount_in = tx->vout[0].nValue;
186 } else if (info.size() != 0 && fuzzed_data_provider.ConsumeBool()) {
187 // These blocks (and txs) may be invalid, use a spent output, or not be in the main chain.
188 auto info_it = info.begin();
189 std::advance(info_it, fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, info.size() - 1));
190 auto tx_it = info_it->block->vtx.begin();
191 std::advance(tx_it, fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, info_it->block->vtx.size() - 1));
192 outpoint = COutPoint(tx_it->get()->GetHash(), 0);
193 amount_in = tx_it->get()->vout[0].nValue;
194 } else {
195 auto coinbase_it = mature_coinbase.begin();
196 std::advance(coinbase_it, fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, mature_coinbase.size() - 1));
197 outpoint = coinbase_it->first;
198 amount_in = coinbase_it->second;
199 }
200
202 const auto script_sig = CScript{};
203 const auto script_wit_stack = std::vector<std::vector<uint8_t>>{WITNESS_STACK_ELEM_OP_TRUE};
204
205 CTxIn in;
206 in.prevout = outpoint;
207 in.nSequence = sequence;
210 tx_mut.vin.push_back(in);
211
213 tx_mut.vout.emplace_back(amount_out, P2WSH_OP_TRUE);
214
215 auto tx = MakeTransactionRef(tx_mut);
216 return tx;
217 };
218
219 auto create_block = [&]() {
221 uint32_t height;
222
223 if (info.size() == 0 || fuzzed_data_provider.ConsumeBool()) {
224 LOCK(cs_main);
225 prev = chainman.ActiveChain().Tip()->GetBlockHash();
226 height = chainman.ActiveChain().Height() + 1;
227 } else {
228 size_t index = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, info.size() - 1);
229 prev = info[index].hash;
230 height = info[index].height + 1;
231 }
232
233 const auto new_time = WITH_LOCK(::cs_main, return chainman.ActiveChain().Tip()->GetMedianTimePast() + 1);
234
235 CBlockHeader header;
236 header.nNonce = 0;
237 header.hashPrevBlock = prev;
238 header.nBits = g_nBits;
239 header.nTime = new_time;
241
242 std::shared_ptr<CBlock> block = std::make_shared<CBlock>();
243 *block = header;
244
246 coinbase_tx.vin.resize(1);
247 coinbase_tx.vin[0].prevout.SetNull();
248 coinbase_tx.vin[0].scriptSig = CScript() << height << OP_0;
249 coinbase_tx.vout.resize(1);
250 coinbase_tx.vout[0].scriptPubKey = CScript() << OP_TRUE;
251 coinbase_tx.vout[0].nValue = COIN;
253
254 const auto mempool_size = mempool.size();
255 if (fuzzed_data_provider.ConsumeBool() && mempool_size != 0) {
256 // Add txns from the mempool. Since we do not include parents, it may be an invalid block.
257 size_t num_txns = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(1, mempool_size);
258 size_t random_idx = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, mempool_size - 1);
259
260 LOCK(mempool.cs);
261 for (size_t i = random_idx; i < random_idx + num_txns; ++i) {
262 CTransactionRef mempool_tx = mempool.txns_randomized[i % mempool_size].second->GetSharedTx();
263 block->vtx.push_back(mempool_tx);
264 }
265 }
266
267 // Create and add (possibly invalid) txns that are not in the mempool.
270 for (size_t i = 0; i < new_txns; ++i) {
272 block->vtx.push_back(non_mempool_tx);
273 }
274 }
275
276 CBlockIndex* pindexPrev{WITH_LOCK(::cs_main, return chainman.m_blockman.LookupBlockIndex(prev))};
277 chainman.GenerateCoinbaseCommitment(*block, pindexPrev);
278
279 bool mutated;
280 block->hashMerkleRoot = BlockMerkleRoot(*block, &mutated);
281 FinalizeHeader(*block, chainman);
282
283 BlockInfo block_info;
284 block_info.block = block;
285 block_info.hash = block->GetHash();
286 block_info.height = height;
287
288 return block_info;
289 };
290
293 bool sent_net_msg = true;
294 bool requested_hb = false;
295 bool sent_sendcmpct = false;
296 bool valid_sendcmpct = false;
297
298 CallOneOf(
300 [&]() {
301 // Send a compact block.
302 std::shared_ptr<CBlock> cblock;
303
304 // Pick an existing block or create a new block.
305 if (fuzzed_data_provider.ConsumeBool() && info.size() != 0) {
306 size_t index = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, info.size() - 1);
307 cblock = info[index].block;
308 } else {
309 BlockInfo block_info = create_block();
310 cblock = block_info.block;
311 info.push_back(block_info);
312 }
313
315 FuzzedCBlockHeaderAndShortTxIDs cmpctblock(*cblock, nonce);
316
320 return;
321 }
322
323 int prev_idx = 0;
324 size_t num_erased = 1;
325 size_t num_txs = cblock->vtx.size();
326
327 for (size_t i = 0; i < num_txs; ++i) {
328 if (i == 0) {
329 // Handle the coinbase specially. We either keep it prefilled or remove it.
330 if (fuzzed_data_provider.ConsumeBool()) continue;
331
332 // Remove the prefilled coinbase.
333 num_erased = 0;
334 uint64_t coinbase_shortid = cmpctblock.GetShortID(cblock->vtx[0]->GetWitnessHash());
335 cmpctblock.RemoveCoinbasePrefill();
336 cmpctblock.InsertCoinbaseShortTxID(coinbase_shortid);
337 continue;
338 }
339
340 if (fuzzed_data_provider.ConsumeBool()) continue;
341
342 uint16_t prefill_idx = num_erased == 0 ? i : i - prev_idx - 1;
343 prev_idx = i;
344 CTransactionRef txref = cblock->vtx[i];
346 cmpctblock.AddPrefilledTx(std::move(prefilledtx));
347
348 // Remove from shorttxids since we've prefilled. Subtract however many txs have been prefilled.
349 cmpctblock.EraseShortTxIDs(i - num_erased);
350 ++num_erased;
351 }
352
353 assert(cmpctblock.PrefilledTxCount() + cmpctblock.ShortTxIDCount() == num_txs);
354
357 },
358 [&]() {
359 // Send a blocktxn message for an existing block (if one exists).
360 size_t num_blocks = info.size();
361 if (num_blocks == 0) {
362 sent_net_msg = false;
363 return;
364 }
365
366 // Fetch an existing block and randomly choose transactions to send over.
367 size_t index = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, num_blocks - 1);
368 const BlockInfo& block_info = info[index];
371 std::shared_ptr<CBlock> cblock = block_info.block;
372
373 for (size_t i = 0; i < cblock->vtx.size(); i++) {
374 if (fuzzed_data_provider.ConsumeBool()) continue;
375
376 block_txn.txn.push_back(cblock->vtx[i]);
377 }
378
380 },
381 [&]() {
382 // Send a headers message for an existing block (if one exists).
383 size_t num_blocks = info.size();
384 if (num_blocks == 0) {
385 sent_net_msg = false;
386 return;
387 }
388
389 // Choose an existing block and send a HEADERS message for it.
390 size_t index = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, num_blocks - 1);
391 CBlock block = *info[index].block;
392 block.vtx.clear(); // No tx in HEADERS.
393 std::vector<CBlock> headers;
394 headers.emplace_back(block);
395
397 },
398 [&]() {
399 // Send a sendcmpct message, optionally setting hb mode.
402 net_msg = NetMsg::Make(NetMsgType::SENDCMPCT, /*high_bandwidth=*/hb, /*version=*/version);
404 sent_sendcmpct = true;
406 },
407 [&]() {
408 // Mine a block, but don't send it.
409 BlockInfo block_info = create_block();
410 info.push_back(block_info);
411 sent_net_msg = false;
412 },
413 [&]() {
414 // Send a transaction.
417 },
418 [&]() {
419 // Set mock time randomly or to tip's time.
422 } else {
423 const NodeSeconds tip_time = WITH_LOCK(::cs_main, return chainman.ActiveChain().Tip()->Time());
424 node_clock.set(tip_time);
425 }
426
427 sent_net_msg = false;
428 });
429
430 if (!sent_net_msg) {
431 continue;
432 }
433
435 connman.FlushSendBuffer(random_node);
436 (void)connman.ReceiveMsgFrom(random_node, std::move(net_msg));
437
438 bool more_work{true};
439 while (more_work) {
440 random_node.fPauseSend = false;
441
442 more_work = connman.ProcessMessagesOnce(random_node);
443 peerman->SendMessages(random_node);
444 }
445
446 std::vector<CNodeStats> stats;
447 connman.GetNodeStats(stats);
448
449 // We should have at maximum 3 HB peers.
450 int num_hb = 0;
451 for (const CNodeStats& stat : stats) {
452 if (stat.m_bip152_highbandwidth_to) {
453 // HB peers cannot be feelers or other "special" connections (besides addr-fetch).
454 CNode* hb_peer = peers[stat.nodeid];
455 if (!hb_peer->fDisconnect) num_hb += 1;
456 assert(hb_peer->IsInboundConn() || hb_peer->IsOutboundOrBlockRelayConn() || hb_peer->IsManualConn() || hb_peer->IsAddrFetchConn());
457 }
458 }
459 assert(num_hb <= 3);
460
461 if (sent_sendcmpct && !random_node.fDisconnect) {
462 // If the fuzzer sent SENDCMPCT with proper version, check the node's state matches what it sent.
463 const CNodeStats& random_node_stats = stats[random_node.GetId()];
464 if (valid_sendcmpct) assert(random_node_stats.m_bip152_highbandwidth_from == requested_hb);
465 }
466 }
467
468 setup->m_node.validation_signals->SyncWithValidationInterfaceQueue();
469 setup->m_node.validation_signals->UnregisterAllValidationInterfaces();
470 connman.StopNodes();
471
472 const size_t end_index_size{WITH_LOCK(chainman.GetMutex(), return chainman.BlockIndex().size())};
473 const uint64_t end_sequence{WITH_LOCK(mempool.cs, return mempool.GetSequence())};
474
480 }
481}
int64_t CAmount
Amount in satoshis (Can be negative)
Definition amount.h:12
constexpr CAmount COIN
The amount of satoshis in one BTC.
Definition amount.h:15
const TestingSetup * g_setup
const CChainParams & Params()
Return the currently selected parameters.
Stochastic address manager.
Definition addrman.h:110
CBlockHeaderAndShortTxIDs()=default
Dummy for deserialization.
Nodes collect new transactions into a block, hash them into a hash tree, and scan through nonce value...
Definition block.h:27
uint32_t nNonce
Definition block.h:35
uint32_t nBits
Definition block.h:34
uint32_t nTime
Definition block.h:33
int32_t nVersion
Definition block.h:30
uint256 hashPrevBlock
Definition block.h:31
Definition block.h:74
std::vector< CTransactionRef > vtx
Definition block.h:77
The block chain is a tree shaped structure starting with the genesis block at the root,...
Definition chain.h:94
uint256 hashMerkleRoot
Definition chain.h:141
const CBlock & GenesisBlock() const
Definition chainparams.h:94
Information about a peer.
Definition net.h:686
An outpoint - a combination of a transaction hash and an index n into its vout.
Definition transaction.h:30
Serialized script, used inside transaction inputs and outputs.
Definition script.h:406
static constexpr uint32_t CURRENT_VERSION
An input of a transaction.
Definition transaction.h:63
uint32_t nSequence
Definition transaction.h:67
CScript scriptSig
Definition transaction.h:66
CScriptWitness scriptWitness
Only serialized through CTransaction.
Definition transaction.h:68
COutPoint prevout
Definition transaction.h:65
Helper to initialize the global NodeClock, let a duration elapse, and reset it after use in a test.
Definition time.h:54
Helper to initialize the global MockableSteadyClock, let a duration elapse, and reset it after use in...
Definition time.h:29
T ConsumeIntegralInRange(T min, T max)
static Mutex g_msgproc_mutex
Mutex for anything that is only accessed via the msg processing thread.
Definition net.h:1049
static std::unique_ptr< PeerManager > make(CConnman &connman, AddrMan &addrman, BanMan *banman, ChainstateManager &chainman, CTxMemPool &pool, node::Warnings &warnings, Options opts)
void resize(size_type new_size)
Definition prevector.h:276
void push_back(const T &value)
Definition prevector.h:392
256-bit opaque blob.
Definition uint256.h:196
static const uint256 ZERO
Definition uint256.h:204
void MakeRandDeterministicDANGEROUS(const uint256 &seed) noexcept
Internal function to set g_determinstic_rng.
Definition random.cpp:595
void initialize_cmpctblock()
uint256 BlockMerkleRoot(const CBlock &block, bool *mutated)
Definition merkle.cpp:76
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate.
Definition cs_main.cpp:8
#define FUZZ_TARGET(...)
Definition fuzz.h:35
#define LIMITED_WHILE(condition, limit)
Can be used to limit a theoretically unbounded loop.
Definition fuzz.h:22
HTTPHeaders headers
uint64_t sequence
unsigned int nonce
CSerializedNetMsg Make(std::string msg_type, Args &&... args)
constexpr const char * HEADERS
The headers message sends one or more block headers to a node which previously requested certain head...
Definition protocol.h:123
constexpr const char * CMPCTBLOCK
Contains a CBlockHeaderAndShortTxIDs object - providing a header and list of "short txids".
Definition protocol.h:206
constexpr const char * BLOCKTXN
Contains a BlockTransactions.
Definition protocol.h:218
constexpr const char * SENDCMPCT
Contains a 1-byte bool and 8-byte LE version number.
Definition protocol.h:200
constexpr const char * TX
The tx message transmits a single transaction.
Definition protocol.h:117
Definition basic.cpp:11
constexpr uint64_t CMPCTBLOCKS_VERSION
The compactblocks version we support.
constexpr TransactionSerParams TX_WITH_WITNESS
static CTransactionRef MakeTransactionRef(Tx &&txIn)
std::shared_ptr< const CTransaction > CTransactionRef
void ObserveReachabilityGoal(bool reached, const ReachabilityGoal &goal)
Record whether a registered goal is satisfied by at least one fuzz input.
void RegisterReachabilityGoal(const ReachabilityGoal &goal)
Register a goal before executing any fuzz inputs.
@ OP_TRUE
Definition script.h:85
@ OP_0
Definition script.h:77
A mutable version of CTransaction.
std::vector< CTxIn > vin
std::vector< std::vector< unsigned char > > stack
Definition script.h:581
void Reset()
Reset the internal state.
Definition net.cpp:83
A reachability condition explicitly registered by a fuzz target.
void ResetIbd()
Reset the ibd cache to its initial state.
Testing setup that configures a complete environment.
#define LOCK(cs)
Definition sync.h:268
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Definition sync.h:299
SeedRandomStateForTest(SeedRand::ZEROS)
void FillNode(FuzzedDataProvider &fuzzed_data_provider, ConnmanTestMsg &connman, CNode &node) noexcept
Definition net.cpp:450
std::unique_ptr< CNode > ConsumeNodeAsUniquePtr(FuzzedDataProvider &fdp, FakeSteadyClock &clock, const std::optional< NodeId > &node_id_in=std::nullopt)
Definition net.h:310
NodeSeconds ConsumeTime(FuzzedDataProvider &fuzzed_data_provider, const std::optional< int64_t > &min, const std::optional< int64_t > &max) noexcept
Definition util.cpp:34
uint32_t ConsumeSequence(FuzzedDataProvider &fuzzed_data_provider) noexcept
Definition util.cpp:155
auto & PickValue(FuzzedDataProvider &fuzzed_data_provider, Collection &col)
Definition util.h:57
size_t CallOneOf(FuzzedDataProvider &fuzzed_data_provider, Callables... callables)
Definition util.h:37
void FinalizeHeader(CBlockHeader &header, const ChainstateManager &chainman)
Definition util.h:367
@ ZEROS
Seed with a compile time constant of zeros.
static const std::vector< uint8_t > WITNESS_STACK_ELEM_OP_TRUE
Definition script.h:12
static const CScript P2WSH_OP_TRUE
Definition script.h:13
std::vector< std::pair< COutPoint, CAmount > > ResetChainmanAndMempool(TestingSetup &setup, FakeNodeClock &node_clock, const ResetOptions &opts)
static int setup(void)
Definition tests.c:8289
constexpr decltype(CTransaction::version) TRUC_VERSION
Definition truc_policy.h:20
std::chrono::time_point< NodeClock, std::chrono::seconds > NodeSeconds
Definition time.h:35
constexpr auto Ticks(Dur2 d)
Helper to count the seconds of a duration/time_point.
Definition time.h:82
assert(!tx.IsCoinBase())
FuzzedDataProvider & fuzzed_data_provider
Definition fees.cpp:46