/root/bitcoin/src/test/fuzz/connect_block.cpp
Line | Count | Source |
1 | | // Copyright (c) 2026-present 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 <addresstype.h> |
6 | | #include <chain.h> |
7 | | #include <consensus/amount.h> |
8 | | #include <consensus/merkle.h> |
9 | | #include <node/kernel_notifications.h> |
10 | | #include <node/mining_types.h> |
11 | | #include <primitives/block.h> |
12 | | #include <primitives/transaction.h> |
13 | | #include <pubkey.h> |
14 | | #include <script/interpreter.h> |
15 | | #include <script/script.h> |
16 | | #include <sync.h> |
17 | | #include <test/fuzz/FuzzedDataProvider.h> |
18 | | #include <test/fuzz/fuzz.h> |
19 | | #include <test/fuzz/util.h> |
20 | | #include <test/util/mining.h> |
21 | | #include <test/util/script.h> |
22 | | #include <test/util/setup_common.h> |
23 | | #include <test/util/time.h> |
24 | | #include <txmempool.h> |
25 | | #include <uint256.h> |
26 | | #include <validation.h> |
27 | | #include <validationinterface.h> |
28 | | |
29 | | #include <algorithm> |
30 | | #include <cstdint> |
31 | | #include <memory> |
32 | | #include <set> |
33 | | #include <utility> |
34 | | #include <vector> |
35 | | |
36 | | |
37 | | namespace { |
38 | | |
39 | | TestingSetup* g_setup; |
40 | | |
41 | | /** Vector of blocks to keep references to blocks (to enable fuzzing input to pick one to build upon) */ |
42 | | static std::vector<std::shared_ptr<CBlock>> g_blocks; |
43 | | /** Set of block hashes in g_blocks */ |
44 | | static std::set<uint256> g_existing_block_hashes; |
45 | | /** CTxIns for spending outputs (excluding OP_RETURN), which can be unspent, already spent, or an immature coinbase. */ |
46 | | static std::vector<CTxIn> g_spend_candidate_txins; |
47 | | /** Static P2SH_OP_TRUE script */ |
48 | | static const CScript P2SH_OP_TRUE = CScript() << OP_HASH160 << ToByteVector(ScriptHash(CScript() << OP_TRUE)) << OP_EQUAL; |
49 | | /** Static P2SH_OP_TRUE unlock script */ |
50 | | static const CScript P2SH_OP_TRUE_UNLOCK = CScript() << MakeUCharSpan(CScript() << OP_TRUE); |
51 | | /** Static TAPROOT_OP_TRUE script and its witness */ |
52 | | static CScript TAPROOT_OP_TRUE; |
53 | | static std::vector<std::vector<uint8_t>> TAPROOT_OP_TRUE_WITNESS; |
54 | | |
55 | | /** |
56 | | * Initialize TAPROOT_OP_TRUE and TAPROOT_OP_TRUE_WITNESS static variables. |
57 | | */ |
58 | | static void InitTaprootScript() |
59 | 0 | { |
60 | 0 | uint256 merkle_tree_hash = ComputeTapleafHash(TAPROOT_LEAF_TAPSCRIPT, MakeUCharSpan(CScript() << OP_TRUE)); |
61 | 0 | uint256 internal_key{std::vector<uint8_t>(32, 1)}; |
62 | 0 | auto res = XOnlyPubKey(internal_key).CreateTapTweak(&merkle_tree_hash); |
63 | 0 | Assert(res.has_value()); |
64 | 0 | auto control = ToByteVector(internal_key); |
65 | 0 | control.insert(control.begin(), TAPROOT_LEAF_TAPSCRIPT | (res->second ? 1 : 0)); Branch (65:63): [True: 0, False: 0]
|
66 | |
|
67 | 0 | TAPROOT_OP_TRUE = CScript() << OP_1 << ToByteVector(res->first); |
68 | 0 | TAPROOT_OP_TRUE_WITNESS.clear(); |
69 | 0 | TAPROOT_OP_TRUE_WITNESS.emplace_back(ToByteVector(CScript() << OP_TRUE)); |
70 | 0 | TAPROOT_OP_TRUE_WITNESS.emplace_back(std::move(control)); |
71 | 0 | } |
72 | | |
73 | | /** |
74 | | * Given a transaction and an output index, create a CTxIn that can be used to |
75 | | * spend it (if possible). |
76 | | */ |
77 | | static CTxIn GetSpendingScript(const CTransaction& tx, uint32_t vout_index) |
78 | 14.1k | { |
79 | 14.1k | Assert(vout_index < tx.vout.size()); |
80 | 14.1k | const CTxOut& output = tx.vout[vout_index]; |
81 | | |
82 | 14.1k | CTxIn res{COutPoint(tx.GetHash(), vout_index)}; |
83 | 14.1k | if (output.scriptPubKey.size() >= 1 && output.scriptPubKey[0] == OP_RETURN) Branch (83:9): [True: 11.2k, False: 2.85k]
Branch (83:44): [True: 57, False: 11.2k]
|
84 | 57 | return res; |
85 | | |
86 | 14.0k | if (output.scriptPubKey == P2WSH_OP_TRUE) { Branch (86:9): [True: 5.66k, False: 8.40k]
|
87 | 5.66k | res.scriptSig = CScript(); |
88 | 5.66k | res.scriptWitness.stack.push_back(WITNESS_STACK_ELEM_OP_TRUE); |
89 | 8.40k | } else if (output.scriptPubKey == P2SH_OP_TRUE) { Branch (89:16): [True: 1.80k, False: 6.60k]
|
90 | 1.80k | res.scriptSig = P2SH_OP_TRUE_UNLOCK; |
91 | 6.60k | } else if (output.scriptPubKey == CScript()) { Branch (91:16): [True: 2.85k, False: 3.75k]
|
92 | 2.85k | res.scriptSig = CScript() << OP_TRUE; |
93 | 3.75k | } else if (output.scriptPubKey == TAPROOT_OP_TRUE) { Branch (93:16): [True: 1.88k, False: 1.86k]
|
94 | 1.88k | res.scriptSig = CScript(); |
95 | 1.88k | res.scriptWitness.stack = TAPROOT_OP_TRUE_WITNESS; |
96 | 1.88k | } |
97 | | |
98 | 14.0k | return res; |
99 | 14.1k | } |
100 | | |
101 | | |
102 | | /** |
103 | | * Read the block from the BlockManager and add it to g_blocks and g_existing_block_hashes. |
104 | | */ |
105 | | static void LoadCurrentBlock(Chainstate& chainstate, CBlockIndex* current_block) |
106 | 0 | { |
107 | | // Read the block from the BlockManager. |
108 | 0 | Assert(current_block->nHeight >= 0); |
109 | | // Resize g_blocks if needed. |
110 | 0 | if (g_blocks.size() <= (size_t)current_block->nHeight) { Branch (110:9): [True: 0, False: 0]
|
111 | 0 | g_blocks.resize(current_block->nHeight + 1); |
112 | 0 | } |
113 | |
|
114 | 0 | g_blocks[current_block->nHeight] = std::make_shared<CBlock>(); |
115 | 0 | Assert(chainstate.m_blockman.ReadBlock(*g_blocks[current_block->nHeight], *current_block)); |
116 | | // Update hash set. |
117 | 0 | g_existing_block_hashes.insert(g_blocks[current_block->nHeight]->GetHash()); |
118 | | |
119 | | // Iterate all transaction outputs. |
120 | 0 | for (const auto& tx : g_blocks[current_block->nHeight]->vtx) { Branch (120:25): [True: 0, False: 0]
|
121 | 0 | for (uint32_t vout_index{0}; vout_index < tx->vout.size(); ++vout_index) { Branch (121:38): [True: 0, False: 0]
|
122 | 0 | auto& vout = tx->vout[vout_index]; |
123 | | // Do not keep OP_RETURN outputs as they are not spendable. |
124 | 0 | if (vout.scriptPubKey.size() >= 1 && vout.scriptPubKey[0] == OP_RETURN) continue; Branch (124:17): [True: 0, False: 0]
Branch (124:50): [True: 0, False: 0]
|
125 | | // Create the CTxIn that can be used to spend this output. |
126 | 0 | g_spend_candidate_txins.push_back(GetSpendingScript(*tx, vout_index)); |
127 | 0 | } |
128 | 0 | } |
129 | 0 | } |
130 | | |
131 | | /** |
132 | | * Read the ChainState object into g_blocks. |
133 | | * Then fill g_spend_candidate_txins with inputs that can be tried by the target. |
134 | | */ |
135 | | static void LoadCurrentChain() |
136 | 0 | { |
137 | | // Clear existing data. |
138 | 0 | g_blocks.clear(); |
139 | 0 | g_existing_block_hashes.clear(); |
140 | 0 | g_spend_candidate_txins.clear(); |
141 | |
|
142 | 0 | { |
143 | 0 | LOCK(::cs_main); |
144 | | // Retrieve the current chainstate. |
145 | 0 | auto& chainstate = Assert(g_setup->m_node.chainman)->ActiveChainstate(); |
146 | | // Make sure it contains a valid mempool. |
147 | 0 | Assert(chainstate.GetMempool()); |
148 | | |
149 | | // Traverse the chain from tip to genesis. |
150 | 0 | auto current_block = chainstate.m_chain.Tip(); |
151 | |
|
152 | 0 | while (current_block != nullptr) { Branch (152:16): [True: 0, False: 0]
|
153 | 0 | LoadCurrentBlock(chainstate, current_block); |
154 | | // Move to previous block. |
155 | 0 | current_block = current_block->pprev; |
156 | 0 | } |
157 | 0 | } |
158 | | |
159 | | // Reverse the order of g_spend_candidate_txins to have them in ascending order of |
160 | | // block height. |
161 | 0 | std::reverse(g_spend_candidate_txins.begin(), g_spend_candidate_txins.end()); |
162 | 0 | } |
163 | | |
164 | | |
165 | | /** |
166 | | * Reset the chainman in the testing setup object. |
167 | | * Mine 2*COINBASE_MATURITY blocks to have spendable UTXOs. |
168 | | * It is called once in the initialization function. |
169 | | */ |
170 | | void ResetChainman(TestingSetup& setup) |
171 | 0 | { |
172 | 0 | SetMockTime(setup.m_node.chainman->GetParams().GenesisBlock().Time()); |
173 | 0 | setup.m_node.chainman.reset(); |
174 | 0 | setup.m_node.notifications->m_shutdown_on_fatal_error = false; |
175 | 0 | setup.m_make_chainman(); |
176 | 0 | setup.LoadVerifyActivateChainstate(); |
177 | |
|
178 | 0 | for (int i = 0; i < 2 * COINBASE_MATURITY; i++) { Branch (178:21): [True: 0, False: 0]
|
179 | 0 | node::BlockCreateOptions options; |
180 | 0 | options.coinbase_output_script = P2WSH_OP_TRUE; |
181 | 0 | MineBlock(setup.m_node, options); |
182 | 0 | } |
183 | 0 | setup.m_node.validation_signals->SyncWithValidationInterfaceQueue(); |
184 | 0 | } |
185 | | |
186 | | /** Create additional transactions in the mempool that spend |
187 | | * coins from mature blocks. Otherwise the mined chain only contains |
188 | | * coinbase transactions. |
189 | | */ |
190 | | void AddExtraTxsToMempool(TestingSetup& setup) |
191 | 0 | { |
192 | 0 | Assert(Assert(Assert(setup.m_node.chainman)->ActiveChainstate().GetMempool())->size() == 0); |
193 | 0 | for (size_t i = 1; i <= 10; i++) { Branch (193:24): [True: 0, False: 0]
|
194 | 0 | CMutableTransaction ctx; |
195 | 0 | ctx.version = CTransaction::CURRENT_VERSION; |
196 | 0 | ctx.vin.resize(1); |
197 | | // CTxIn is spendable as g_spend_candidate_txins comes from early blocks whose |
198 | | // coinbases are mature. |
199 | 0 | ctx.vin[0] = g_spend_candidate_txins[i]; |
200 | 0 | ctx.vout.resize(4); |
201 | | // Arbitrarily create various outputs of different kinds in the same tx. |
202 | | // P2WSH |
203 | 0 | ctx.vout[0].nValue = CAmount(15 * COIN); |
204 | 0 | ctx.vout[0].scriptPubKey = P2WSH_OP_TRUE; |
205 | | // P2SH |
206 | 0 | ctx.vout[1].nValue = CAmount(15 * COIN); |
207 | 0 | ctx.vout[1].scriptPubKey = P2SH_OP_TRUE; |
208 | | // Taproot |
209 | 0 | ctx.vout[2].nValue = CAmount(10 * COIN); |
210 | 0 | ctx.vout[2].scriptPubKey = TAPROOT_OP_TRUE; |
211 | | // Empty script |
212 | 0 | ctx.vout[3].nValue = CAmount(10 * COIN); |
213 | 0 | ctx.vout[3].scriptPubKey = CScript(); |
214 | |
|
215 | 0 | LOCK(::cs_main); |
216 | | // Add transaction to the mempool. |
217 | 0 | const MempoolAcceptResult ctx_result = setup.m_node.chainman->ProcessTransaction(MakeTransactionRef(ctx)); |
218 | 0 | Assert(ctx_result.m_result_type == MempoolAcceptResult::ResultType::VALID); |
219 | |
|
220 | 0 | Assert(setup.m_node.chainman->ActiveChainstate().GetMempool()->size() == i); |
221 | | // Force the mempool to select this transaction even though its fee is zero. |
222 | 0 | setup.m_node.chainman->ActiveChainstate().GetMempool()->PrioritiseTransaction(ctx.GetHash(), COIN); |
223 | 0 | } |
224 | 0 | } |
225 | | |
226 | | /** Initialize the chain for this target. */ |
227 | | static void initialize_connect_block() |
228 | 0 | { |
229 | | // Instantiate REGTEST chain. |
230 | 0 | static auto testing_setup = MakeNoLogFileContext<TestingSetup>( |
231 | 0 | /*chain_type=*/ChainType::REGTEST, TestOpts{ |
232 | 0 | .extra_args = { |
233 | 0 | "-minrelaytxfee=0", |
234 | 0 | "-acceptnonstdtxn", |
235 | 0 | }, |
236 | 0 | }); |
237 | 0 | g_setup = testing_setup.get(); |
238 | | |
239 | | // Reset the chainman in the testing setup object. |
240 | 0 | ResetChainman(*g_setup); |
241 | | |
242 | | // Initialize Taproot script declared as static variables. |
243 | 0 | InitTaprootScript(); |
244 | | |
245 | | // Load the chain mined in ResetChainman in global variables g_blocks and |
246 | | // g_spend_candidate_txins, to make them available to pick by the target. |
247 | 0 | LoadCurrentChain(); |
248 | | |
249 | | // Prepare multiple transactions for block 201. They spend coins |
250 | | // from various coinbases that are now mature enough. |
251 | 0 | AddExtraTxsToMempool(*g_setup); |
252 | | // Mine block 201, which contains the transactions added to the mempool. |
253 | 0 | node::BlockCreateOptions options; |
254 | 0 | options.coinbase_output_script = P2WSH_OP_TRUE; |
255 | 0 | MineBlock(g_setup->m_node, options); |
256 | 0 | Assert(g_setup->m_node.chainman->ActiveChainstate().GetMempool()->size() == 0); |
257 | | |
258 | | // Load the 201th block into g_blocks. |
259 | 0 | LOCK(::cs_main); |
260 | 0 | auto& chainstate = Assert(g_setup->m_node.chainman)->ActiveChainstate(); |
261 | 0 | auto current_block = chainstate.m_chain.Tip(); |
262 | 0 | LoadCurrentBlock(chainstate, current_block); |
263 | 0 | } |
264 | | |
265 | | /** |
266 | | * Read one transaction from the fuzzing input through the FuzzedDataProvider. |
267 | | * It is intended to leave more space to craft complex transactions, especially |
268 | | * with various script types (P2SH, P2WSH, TAPROOT, NOSCRIPT). |
269 | | * It is exclusively used by ConsumeBlock to read transactions inside a block. |
270 | | */ |
271 | | CTransactionRef ConsumeTransaction(FuzzedDataProvider& fuzzed_data_provider, |
272 | | std::vector<CTxIn>& additional_txins, |
273 | | bool coinbase = false, |
274 | | int target_height = 0) |
275 | 10.0k | { |
276 | 10.0k | CMutableTransaction tx; |
277 | 10.0k | tx.version = fuzzed_data_provider.ConsumeBool() ? Branch (277:18): [True: 6.61k, False: 3.41k]
|
278 | 6.61k | CTransaction::CURRENT_VERSION : |
279 | 10.0k | fuzzed_data_provider.ConsumeIntegral<uint32_t>(); |
280 | 10.0k | tx.nLockTime = fuzzed_data_provider.ConsumeBool() ? Branch (280:20): [True: 5.92k, False: 4.09k]
|
281 | 5.92k | 0 : |
282 | 10.0k | fuzzed_data_provider.ConsumeIntegral<uint32_t>(); |
283 | | |
284 | | // Some harnesses want to explicitly read coinbase transactions from input. |
285 | 10.0k | if (coinbase) { Branch (285:9): [True: 3.86k, False: 6.15k]
|
286 | | // vin size is hardcoded. |
287 | 3.86k | tx.vin.resize(1); |
288 | 3.86k | tx.vin[0].prevout.SetNull(); |
289 | 3.86k | tx.vin[0].nSequence = CTxIn::MAX_SEQUENCE_NONFINAL; |
290 | 3.86k | if (fuzzed_data_provider.ConsumeBool()) { Branch (290:13): [True: 3.54k, False: 321]
|
291 | | // 1/2 probability of a valid vin. |
292 | 3.54k | tx.vin[0].scriptSig = CScript() << target_height; |
293 | 3.54k | } else { |
294 | | // Read arbitrary data from input as scriptSig. |
295 | 321 | auto script_sig = ConsumeRandomLengthByteVector<unsigned char>(fuzzed_data_provider, 100); |
296 | 321 | tx.vin[0].scriptSig.assign(script_sig.begin(), script_sig.end()); |
297 | 321 | } |
298 | 6.15k | } else { |
299 | | // Read a normal transaction, with up to 10 inputs. |
300 | 6.15k | int num_inputs = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 10); |
301 | 6.15k | tx.vin.resize(num_inputs); |
302 | 18.9k | for (int i = 0; i < num_inputs; i++) { Branch (302:25): [True: 12.8k, False: 6.15k]
|
303 | | // Read an integer to choose a CTxIn or reuse one generated by the |
304 | | // input. The content of the CTxIn is not read from the input per se. |
305 | 12.8k | uint32_t input_index = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(0, g_spend_candidate_txins.size() + additional_txins.size() - 1); |
306 | 12.8k | if (input_index < g_spend_candidate_txins.size()) { Branch (306:17): [True: 12.1k, False: 646]
|
307 | | // Pick it from the spend candidates. |
308 | 12.1k | tx.vin[i] = g_spend_candidate_txins[input_index]; |
309 | 12.1k | } else { |
310 | | // Pick it in the additional_txins set. |
311 | 646 | Assert((input_index - g_spend_candidate_txins.size()) < additional_txins.size()); |
312 | 646 | tx.vin[i] = additional_txins[input_index - g_spend_candidate_txins.size()]; |
313 | 646 | } |
314 | | |
315 | | // Enable the fuzzer to mutate every CTxIn field after it is taken |
316 | | // from the spend candidates. |
317 | 12.8k | if (fuzzed_data_provider.ConsumeBool()) { Branch (317:17): [True: 3.94k, False: 8.88k]
|
318 | 3.94k | tx.vin[i].nSequence = ConsumeSequence(fuzzed_data_provider); |
319 | 3.94k | } |
320 | 12.8k | if (fuzzed_data_provider.ConsumeBool()) { Branch (320:17): [True: 2.87k, False: 9.95k]
|
321 | 2.87k | tx.vin[i].prevout.n = fuzzed_data_provider.ConsumeIntegral<uint32_t>(); |
322 | 2.87k | } |
323 | 12.8k | if (fuzzed_data_provider.ConsumeBool()) { Branch (323:17): [True: 2.28k, False: 10.5k]
|
324 | 2.28k | tx.vin[i].prevout.hash = Txid::FromUint256(ConsumeUInt256(fuzzed_data_provider)); |
325 | 2.28k | } |
326 | 12.8k | if (fuzzed_data_provider.ConsumeBool()) { Branch (326:17): [True: 4.77k, False: 8.06k]
|
327 | 4.77k | tx.vin[i].scriptSig = ConsumeScript(fuzzed_data_provider); |
328 | 4.77k | } |
329 | 12.8k | if (fuzzed_data_provider.ConsumeBool()) { Branch (329:17): [True: 1.64k, False: 11.1k]
|
330 | 1.64k | tx.vin[i].scriptWitness.stack.clear(); |
331 | 1.64k | int num_wit = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 10); |
332 | 7.20k | for (int j = 0; j < num_wit; j++) { Branch (332:33): [True: 5.56k, False: 1.64k]
|
333 | 5.56k | tx.vin[i].scriptWitness.stack.push_back(ConsumeRandomLengthByteVector<unsigned char>(fuzzed_data_provider, 100)); |
334 | 5.56k | } |
335 | 1.64k | } |
336 | 12.8k | } |
337 | 6.15k | } |
338 | | |
339 | | // Read outputs. |
340 | 10.0k | int num_outputs = fuzzed_data_provider.ConsumeIntegralInRange<int>(1, 10); |
341 | 10.0k | tx.vout.resize(num_outputs); |
342 | 34.0k | for (int i = 0; i < num_outputs; i++) { Branch (342:21): [True: 24.0k, False: 10.0k]
|
343 | | // Read CAmount to spend. |
344 | 24.0k | tx.vout[i].nValue = fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(-10, 50 * COIN + 10); |
345 | | |
346 | | // Read scriptPubKey type into one of the valid types. |
347 | 24.0k | switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 4)) { |
348 | 9.26k | case 0: Branch (348:9): [True: 9.26k, False: 14.7k]
|
349 | | // P2WSH |
350 | 9.26k | tx.vout[i].scriptPubKey = P2WSH_OP_TRUE; |
351 | 9.26k | break; |
352 | 3.73k | case 1: Branch (352:9): [True: 3.73k, False: 20.3k]
|
353 | | // P2SH |
354 | 3.73k | tx.vout[i].scriptPubKey = P2SH_OP_TRUE; |
355 | 3.73k | break; |
356 | 2.86k | case 2: Branch (356:9): [True: 2.86k, False: 21.1k]
|
357 | | // Taproot |
358 | 2.86k | tx.vout[i].scriptPubKey = TAPROOT_OP_TRUE; |
359 | 2.86k | break; |
360 | 2.22k | case 3: Branch (360:9): [True: 2.22k, False: 21.8k]
|
361 | | // Empty script |
362 | 2.22k | tx.vout[i].scriptPubKey = CScript(); |
363 | 2.22k | break; |
364 | 5.96k | default: { Branch (364:9): [True: 5.96k, False: 18.0k]
|
365 | | // Read arbitrary scriptPubKey. |
366 | 5.96k | tx.vout[i].scriptPubKey = ConsumeScript(fuzzed_data_provider); |
367 | 5.96k | break; |
368 | 0 | } |
369 | 24.0k | } |
370 | 24.0k | } |
371 | | |
372 | | // Create the shared pointer to the CTransaction object. |
373 | 10.0k | auto res = MakeTransactionRef(tx); |
374 | | |
375 | 10.0k | if (!coinbase) { Branch (375:9): [True: 6.15k, False: 3.86k]
|
376 | | // Create spending scripts for all CTxOuts so they can be spent in later |
377 | | // transactions. Do it here as the transaction hash is definitive. |
378 | 20.2k | for (int i = 0; i < num_outputs; i++) { Branch (378:25): [True: 14.1k, False: 6.15k]
|
379 | 14.1k | additional_txins.emplace_back(GetSpendingScript(*res, i)); |
380 | 14.1k | } |
381 | 6.15k | } |
382 | | |
383 | 10.0k | return res; |
384 | 10.0k | } |
385 | | |
386 | | /** |
387 | | * Consume a block from the fuzzing input. |
388 | | * It builds a block on top of the given prev_block. |
389 | | */ |
390 | | CBlock ConsumeBlock(FuzzedDataProvider& fuzzed_data_provider, const CBlock& prev_block, int target_height, |
391 | | std::vector<CTxIn>& additional_txins) |
392 | 3.86k | { |
393 | 3.86k | CBlock block; |
394 | | |
395 | | // Initialize header fields. |
396 | 3.86k | block.nVersion = g_blocks.back()->nVersion; |
397 | 3.86k | block.hashPrevBlock = prev_block.GetHash(); |
398 | 3.86k | block.nTime = g_blocks.back()->nTime + 2; |
399 | 3.86k | block.nBits = g_blocks.back()->nBits; |
400 | | |
401 | | // Give the fuzzer input the ability to mutate block header fields. |
402 | 3.86k | if (fuzzed_data_provider.ConsumeBool()) { Branch (402:9): [True: 275, False: 3.59k]
|
403 | 275 | block.nVersion = fuzzed_data_provider.ConsumeIntegral<int32_t>(); |
404 | 275 | } |
405 | 3.86k | if (fuzzed_data_provider.ConsumeBool()) { Branch (405:9): [True: 386, False: 3.48k]
|
406 | 386 | block.hashPrevBlock = ConsumeUInt256(fuzzed_data_provider); |
407 | 386 | } |
408 | | |
409 | 3.86k | if (fuzzed_data_provider.ConsumeBool()) { Branch (409:9): [True: 311, False: 3.55k]
|
410 | 311 | block.nTime = fuzzed_data_provider.ConsumeIntegral<uint32_t>(); |
411 | 311 | } |
412 | 3.86k | if (fuzzed_data_provider.ConsumeBool()) { Branch (412:9): [True: 433, False: 3.43k]
|
413 | 433 | block.nBits = fuzzed_data_provider.ConsumeIntegral<uint32_t>(); |
414 | 433 | } |
415 | | |
416 | | // Read the coinbase transaction from the input. |
417 | 3.86k | block.vtx.push_back(ConsumeTransaction(fuzzed_data_provider, additional_txins, true, target_height)); |
418 | | |
419 | | // Read up to num_tx transactions from the input. |
420 | 3.86k | int num_tx = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 5); |
421 | 10.0k | for (int i = 0; i < num_tx; i++) { Branch (421:21): [True: 6.15k, False: 3.86k]
|
422 | 6.15k | block.vtx.push_back(ConsumeTransaction(fuzzed_data_provider, additional_txins)); |
423 | 6.15k | } |
424 | | |
425 | | // Commit witness. |
426 | 3.86k | if (num_tx > 0) { Branch (426:9): [True: 3.28k, False: 587]
|
427 | 3.28k | g_setup->m_node.chainman->GenerateCoinbaseCommitment(block, nullptr); |
428 | 3.28k | } |
429 | | |
430 | | // Set hashMerkleRoot to expected value. |
431 | 3.86k | block.hashMerkleRoot = BlockMerkleRoot(block); |
432 | | // Let the fuzzer mutate hashMerkleRoot. |
433 | 3.86k | if (fuzzed_data_provider.ConsumeBool()) { Branch (433:9): [True: 841, False: 3.02k]
|
434 | 841 | block.hashMerkleRoot = ConsumeUInt256(fuzzed_data_provider); |
435 | 841 | } |
436 | | |
437 | | // Read the nonce from the input and avoid reusing a setup block hash. |
438 | 3.86k | block.nNonce = fuzzed_data_provider.ConsumeIntegral<uint32_t>(); |
439 | 3.86k | while (g_existing_block_hashes.contains(block.GetHash())) { Branch (439:12): [True: 0, False: 3.86k]
|
440 | 0 | ++block.nNonce; |
441 | 0 | } |
442 | | |
443 | 3.86k | return block; |
444 | 3.86k | } |
445 | | |
446 | | |
447 | | FUZZ_TARGET(connect_block, .init = initialize_connect_block) |
448 | 3.86k | { |
449 | 3.86k | SeedRandomStateForTest(SeedRand::ZEROS); |
450 | 3.86k | FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); |
451 | 3.86k | FakeNodeClock clock{g_blocks.back()->Time() + 2s}; |
452 | | |
453 | 3.86k | LOCK(::cs_main); |
454 | 3.86k | Chainstate& active_chainstate = g_setup->m_node.chainman->ActiveChainstate(); |
455 | 3.86k | CBlockIndex* active_tip = active_chainstate.m_chain.Tip(); |
456 | 3.86k | Assert(active_tip->GetBlockHash() == g_blocks.back()->GetHash()); |
457 | 3.86k | CCoinsViewCache active_coins(&active_chainstate.CoinsTip()); |
458 | | |
459 | | // Read a new block from the data provider. |
460 | 3.86k | std::vector<CTxIn> additional_txins; |
461 | 3.86k | CBlock block = ConsumeBlock(fuzzed_data_provider, *g_blocks.back(), active_tip->nHeight + 1, additional_txins); |
462 | | |
463 | | // Compute new CBlockIndex object. |
464 | 3.86k | uint256 current_hash = block.GetHash(); |
465 | 3.86k | CBlockIndex new_index(block); |
466 | 3.86k | new_index.pprev = active_tip; |
467 | 3.86k | new_index.nHeight = active_tip->nHeight + 1; |
468 | 3.86k | new_index.phashBlock = ¤t_hash; |
469 | | |
470 | | // Try to connect the block. |
471 | 3.86k | BlockValidationState state; |
472 | 3.86k | bool connected = active_chainstate.ConnectBlock(block, |
473 | 3.86k | state, |
474 | 3.86k | &new_index, |
475 | 3.86k | active_coins, |
476 | 3.86k | /*fJustCheck=*/true); |
477 | 3.86k | Assert(connected == state.IsValid()); |
478 | 3.86k | } |
479 | | |
480 | | } // namespace |