/root/bitcoin/src/test/fuzz/merkle.cpp
Line | Count | Source |
1 | | // Copyright (c) 2025-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 <consensus/merkle.h> |
6 | | #include <test/fuzz/fuzz.h> |
7 | | #include <test/fuzz/FuzzedDataProvider.h> |
8 | | #include <test/fuzz/util.h> |
9 | | #include <test/util/str.h> |
10 | | #include <util/strencodings.h> |
11 | | #include <hash.h> |
12 | | |
13 | | #include <cassert> |
14 | | #include <cstdint> |
15 | | #include <vector> |
16 | | |
17 | 0 | uint256 ComputeMerkleRootFromPath(const CBlock& block, uint32_t position, const std::vector<uint256>& merkle_path) { |
18 | 0 | if (position >= block.vtx.size()) { Branch (18:9): [True: 0, False: 0]
|
19 | 0 | throw std::out_of_range("Position out of range"); |
20 | 0 | } |
21 | | |
22 | 0 | uint256 current_hash = block.vtx[position]->GetHash().ToUint256(); |
23 | |
|
24 | 0 | for (const uint256& sibling : merkle_path) { Branch (24:33): [True: 0, False: 0]
|
25 | 0 | if (position % 2 == 0) { Branch (25:13): [True: 0, False: 0]
|
26 | 0 | current_hash = Hash(current_hash, sibling); |
27 | 0 | } else { |
28 | 0 | current_hash = Hash(sibling, current_hash); |
29 | 0 | } |
30 | 0 | position = position / 2; |
31 | 0 | } |
32 | |
|
33 | 0 | return current_hash; |
34 | 0 | } |
35 | | |
36 | | FUZZ_TARGET(merkle) |
37 | 0 | { |
38 | 0 | FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); |
39 | |
|
40 | 0 | const bool with_witness = fuzzed_data_provider.ConsumeBool(); |
41 | 0 | std::optional<CBlock> block {ConsumeDeserializable<CBlock>(fuzzed_data_provider, with_witness ? TX_WITH_WITNESS : TX_NO_WITNESS)}; Branch (41:86): [True: 0, False: 0]
|
42 | 0 | if (!block){ Branch (42:9): [True: 0, False: 0]
|
43 | 0 | return; |
44 | 0 | } |
45 | 0 | const size_t num_txs = block->vtx.size(); |
46 | 0 | std::vector<uint256> tx_hashes; |
47 | 0 | tx_hashes.reserve(num_txs); |
48 | |
|
49 | 0 | for (size_t i = 0; i < num_txs; ++i) { Branch (49:24): [True: 0, False: 0]
|
50 | 0 | tx_hashes.push_back(block->vtx[i]->GetHash().ToUint256()); |
51 | 0 | } |
52 | | |
53 | | // Test ComputeMerkleRoot |
54 | 0 | bool mutated = fuzzed_data_provider.ConsumeBool(); // output param, initial value shouldn't matter |
55 | 0 | const uint256 merkle_root = ComputeMerkleRoot(tx_hashes, fuzzed_data_provider.ConsumeBool() ? &mutated : nullptr); Branch (55:62): [True: 0, False: 0]
|
56 | | |
57 | | // Basic sanity checks for ComputeMerkleRoot |
58 | 0 | if (tx_hashes.size() == 1) { Branch (58:9): [True: 0, False: 0]
|
59 | 0 | assert(merkle_root == tx_hashes[0]); Branch (59:9): [True: 0, False: 0]
|
60 | 0 | } |
61 | | |
62 | | |
63 | 0 | const uint256 block_merkle_root = BlockMerkleRoot(*block, &mutated); |
64 | 0 | if (tx_hashes.size() == 1) { Branch (64:9): [True: 0, False: 0]
|
65 | 0 | assert(block_merkle_root == tx_hashes[0]); Branch (65:9): [True: 0, False: 0]
|
66 | 0 | } |
67 | | |
68 | 0 | if (!block->vtx.empty()){ Branch (68:9): [True: 0, False: 0]
|
69 | 0 | const uint256 block_witness_merkle_root = BlockWitnessMerkleRoot(*block); |
70 | 0 | if (tx_hashes.size() == 1) { Branch (70:13): [True: 0, False: 0]
|
71 | 0 | assert(block_witness_merkle_root == uint256()); Branch (71:13): [True: 0, False: 0]
|
72 | 0 | } |
73 | 0 | } |
74 | | |
75 | | // Test TransactionMerklePath |
76 | 0 | const uint32_t position = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(0, num_txs > 0 ? num_txs - 1 : 0); Branch (76:88): [True: 0, False: 0]
|
77 | 0 | std::vector<uint256> merkle_path = TransactionMerklePath(*block, position); |
78 | | |
79 | | // Check that the root we compute from TransactionMerklePath equals the same merkle_root and block_merkle_root |
80 | 0 | if (tx_hashes.size() > 1) { Branch (80:9): [True: 0, False: 0]
|
81 | 0 | uint256 merkle_root_from_merkle_path = ComputeMerkleRootFromPath(*block, position, merkle_path); |
82 | 0 | assert(merkle_root_from_merkle_path == merkle_root); Branch (82:9): [True: 0, False: 0]
|
83 | 0 | assert(merkle_root_from_merkle_path == block_merkle_root); Branch (83:9): [True: 0, False: 0]
|
84 | 0 | } |
85 | | |
86 | | // Basic sanity checks for TransactionMerklePath |
87 | 0 | assert(merkle_path.size() <= 32); // Maximum depth of a Merkle tree with 2^32 leaves Branch (87:5): [True: 0, False: 0]
|
88 | 0 | if (num_txs == 1 || num_txs == 0) { Branch (88:9): [True: 0, False: 0]
Branch (88:25): [True: 0, False: 0]
|
89 | 0 | assert(merkle_path.empty()); // Single transaction has no path Branch (89:9): [True: 0, False: 0]
|
90 | 0 | } |
91 | |
|
92 | 0 | } |