Repository: bitcoin/bitcoin
File: src/test/fuzz/merkle.cpp (C++)

Code before:
        }
        position = position / 2;
    }

    return current_hash;
}

FUZZ_TARGET(merkle)
{
    FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());

    const bool with_witness = fuzzed_data_provider.ConsumeBool();
    std::optional<CBlock> block {ConsumeDeserializable<CBlock>(fuzzed_data_provider, with_witness ? TX_WITH_WITNESS : TX_NO_WITNESS)};
    if (!block){
        return;
    }
    const size_t num_txs = block->vtx.size();
    std::vector<uint256> tx_hashes;
    tx_hashes.reserve(num_txs);

    for (size_t i = 0; i < num_txs; ++i) {
        tx_hashes.push_back(block->vtx[i]->GetHash().ToUint256());
    }

    // Test ComputeMerkleRoot
    bool mutated = fuzzed_data_provider.ConsumeBool();
    const uint256 merkle_root = ComputeMerkleRoot(tx_hashes, &mutated);

    // Basic sanity checks for ComputeMerkleRoot
    if (tx_hashes.size() == 1) {
        assert(merkle_root == tx_hashes[0]);
    }


    const uint256 block_merkle_root = BlockMerkleRoot(*block, &mutated);
    if (tx_hashes.size() == 1) {
        assert(block_merkle_root == tx_hashes[0]);
    }

    if (!block->vtx.empty()){
        const uint256 block_witness_merkle_root = BlockWitnessMerkleRoot(*block, &mutated);
        if (tx_hashes.size() == 1) {
            assert(block_witness_merkle_root == uint256());
        }
    }

    // Test TransactionMerklePath
    const uint32_t position = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(0, num_txs > 0 ? num_txs - 1 : 0);
    std::vector<uint256> merkle_path = TransactionMerklePath(*block, position);

    // Check that the root we compute from TransactionMerklePath equals the same merkle_root and block_merkle_root

Review comment (bug):
Do you think there would be value in also passing in nullptr:
```diff
diff --git a/src/test/fuzz/merkle.cpp b/src/test/fuzz/merkle.cpp
index 4bb91faf0b..5b91a97b75 100644
--- a/src/test/fuzz/merkle.cpp
+++ b/src/test/fuzz/merkle.cpp
@@ -55 +55,2 @@ FUZZ_TARGET(merkle)
-    const uint256 merkle_root = ComputeMerkleRoot(tx_hashes, &mutated);
+    bool* pmutated = fuzzed_data_provider.PickValueInArray({&mutated, static_cast<bool*>(nullptr)});
+    const uint256 merkle_root = ComputeMerkleRoot(tx_hashes, pmutated);
```

Code after:
        }
        position = position / 2;
    }

    return current_hash;
}

FUZZ_TARGET(merkle)
{
    FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());

    const bool with_witness = fuzzed_data_provider.ConsumeBool();
    std::optional<CBlock> block {ConsumeDeserializable<CBlock>(fuzzed_data_provider, with_witness ? TX_WITH_WITNESS : TX_NO_WITNESS)};
    if (!block){
        return;
    }
    const size_t num_txs = block->vtx.size();
    std::vector<uint256> tx_hashes;
    tx_hashes.reserve(num_txs);

    for (size_t i = 0; i < num_txs; ++i) {
        tx_hashes.push_back(block->vtx[i]->GetHash().ToUint256());
    }

    // Test ComputeMerkleRoot
    bool mutated = fuzzed_data_provider.ConsumeBool(); // output param, initial value shouldn't matter
    const uint256 merkle_root = ComputeMerkleRoot(tx_hashes, &mutated);

    // Basic sanity checks for ComputeMerkleRoot
    if (tx_hashes.size() == 1) {
        assert(merkle_root == tx_hashes[0]);
    }


    const uint256 block_merkle_root = BlockMerkleRoot(*block, &mutated);
    if (tx_hashes.size() == 1) {
        assert(block_merkle_root == tx_hashes[0]);
    }

    if (!block->vtx.empty()){
        const uint256 block_witness_merkle_root = BlockWitnessMerkleRoot(*block);
        if (tx_hashes.size() == 1) {
            assert(block_witness_merkle_root == uint256());
        }
    }

    // Test TransactionMerklePath
    const uint32_t position = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(0, num_txs > 0 ? num_txs - 1 : 0);
    std::vector<uint256> merkle_path = TransactionMerklePath(*block, position);

    // Check that the root we compute from TransactionMerklePath equals the same merkle_root and block_merkle_root