/root/bitcoin/src/test/fuzz/merkle.cpp
Line | Count | Source |
1 | | // Copyright (c) 2025 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()) { |
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) { |
25 | 0 | if (position % 2 == 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)}; |
42 | 0 | if (!block){ |
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) { |
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(); |
55 | 0 | const uint256 merkle_root = ComputeMerkleRoot(tx_hashes, &mutated); |
56 | | |
57 | | // Basic sanity checks for ComputeMerkleRoot |
58 | 0 | if (tx_hashes.size() == 1) { |
59 | 0 | assert(merkle_root == tx_hashes[0]); |
60 | 0 | } |
61 | | |
62 | | |
63 | 0 | const uint256 block_merkle_root = BlockMerkleRoot(*block, &mutated); |
64 | 0 | if (tx_hashes.size() == 1) { |
65 | 0 | assert(block_merkle_root == tx_hashes[0]); |
66 | 0 | } |
67 | | |
68 | 0 | if (!block->vtx.empty()){ |
69 | 0 | const uint256 block_witness_merkle_root = BlockWitnessMerkleRoot(*block, &mutated); |
70 | 0 | if (tx_hashes.size() == 1) { |
71 | 0 | assert(block_witness_merkle_root == uint256()); |
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); |
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) { |
81 | 0 | uint256 merkle_root_from_merkle_path = ComputeMerkleRootFromPath(*block, position, merkle_path); |
82 | 0 | assert(merkle_root_from_merkle_path == merkle_root); |
83 | 0 | assert(merkle_root_from_merkle_path == block_merkle_root); |
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 |
88 | 0 | if (num_txs == 1 || num_txs == 0) { |
89 | 0 | assert(merkle_path.empty()); // Single transaction has no path |
90 | 0 | } |
91 | |
|
92 | 0 | } |