Summary
This adds a new fuzz target “merkle” which adds fuzz coverage to consensus/merkle.cpp
I can also add this to an existing fuzz target if that is preferable
Before:
After:
This adds a new fuzz target “merkle” which adds fuzz coverage to consensus/merkle.cpp
I can also add this to an existing fuzz target if that is preferable
Before:
After:
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.
For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/32243.
See the guideline for information on the review process.
Type | Reviewers |
---|---|
ACK | marcofleon |
If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.
🚧 At least one of the CI tasks failed. Debug: https://github.com/bitcoin/bitcoin/runs/40295372010
Try to run the tests locally, according to the documentation. However, a CI failure may still happen due to a number of reasons, for example:
Possibly due to a silent merge conflict (the changes in this pull request being incompatible with the current code in the target branch). If so, make sure to rebase on the latest commit of the target branch.
A sanitizer issue, which can only be found by compiling with the sanitizer and running the affected test.
An intermittent issue.
Leave a comment here, if you need help tracking down a confusing failure.
38+ prevout_hash_bytes.resize(32, 0); // Pad with zeros if needed
39+ }
40+ uint256 prevout_hash;
41+ memcpy(prevout_hash.begin(), prevout_hash_bytes.data(), 32);
42+ txin.prevout = COutPoint(Txid::FromUint256(prevout_hash), fuzzed_data_provider.ConsumeIntegral<uint32_t>());
43+ std::vector<unsigned char> script_sig_bytes = fuzzed_data_provider.ConsumeBytes<unsigned char>(40);
I’m new to the fuzzing test framework, but does this essentially build a fixed size 40 byte scriptSig?
It may be worth making this variable in size to test different weaknesses in the merkle tree.
Particularly if we can occasionally generate a 64 byte transaction, whose format you can read about here, you may be able to make some assertions on the mutated
field below.
Thanks for the review! I pushed cdd3ad0
Now I am using the MAX_SCRIPT_SIZE since that is what I see for the IsUnspendable function for CScript
BlockWitnessMerkleRoot
and BlockMerkleRoot
so we are fuzzing everything in src/consensus/merkle.h
78+ assert(block_witness_merkle_root == uint256());
79+ }
80+ }
81+
82+ // Test TransactionMerklePath
83+ const uint32_t position = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(0, num_txs);
num_txs - 1
no? Just based on the coverage it seems the fuzzer is choosing num_txs
which means matchh
won’t be set to true here:
https://github.com/bitcoin/bitcoin/blob/baa848b8d38187ce6b24a57cfadf1ea180209711/src/consensus/merkle.cpp#L111
Yes that makes sense to me!
updated in 2c8a2b4
56+ tx_hashes.push_back(block.vtx[i]->GetHash());
57+ }
58+
59+ // Test ComputeMerkleRoot
60+ bool mutated = fuzzed_data_provider.ConsumeBool();
61+ //bool mutated = false;
oops, thanks for the review!
updated in 2c8a2b4
Good stuff, nice to have new coverage.
I think it’d be worth it to add an oracle in the test where you reconstruct the merkle root with the path you get from TransactionMerklePath
and verify that it matches the root from the earlier ComputeMerkleRoot
call.
Good stuff, nice to have new coverage.
I think it’d be worth it to add an oracle in the test where you reconstruct the merkle root with the path you get from
TransactionMerklePath
and verify that it matches the root from the earlierComputeMerkleRoot
call.
Thank you for the review!
In 2c8a2b4 I added a helper function ComputeMerkleRootFromPath to help rebuild the root so then I could compare the responses from TransactionMerklePath
, BlockMerkleRoot
and also ComputeMerkleRoot
let me know if that looks good of if there is anything you’d like to change!
🚧 At least one of the CI tasks failed.
Task lint
: https://github.com/bitcoin/bitcoin/runs/41809311683
LLM reason (✨ experimental): The CI failure is caused by a lint error, specifically due to the use of quote syntax includes instead of bracket syntax.
Try to run the tests locally, according to the documentation. However, a CI failure may still happen due to a number of reasons, for example:
Possibly due to a silent merge conflict (the changes in this pull request being incompatible with the current code in the target branch). If so, make sure to rebase on the latest commit of the target branch.
A sanitizer issue, which can only be found by compiling with the sanitizer and running the affected test.
An intermittent issue.
Leave a comment here, if you need help tracking down a confusing failure.
98+ assert(block_witness_merkle_root == uint256());
99+ }
100+ }
101+
102+ // Test TransactionMerklePath
103+ const uint32_t position = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(0, num_txs - 1);
/ci_container_base/src/test/fuzz/merkle.cpp:103:96: runtime error: unsigned integer overflow: 0 - 1 cannot be represented in type 'size_t' (aka 'unsigned long')
. num_txs
can be zero, so it’s causing the overflow here.
num_txs
in 7c80dcd
59+ prevout_hash_bytes.resize(32, 0); // Pad with zeros if needed
60+ }
61+ uint256 prevout_hash;
62+ memcpy(prevout_hash.begin(), prevout_hash_bytes.data(), 32);
63+ txin.prevout = COutPoint(Txid::FromUint256(prevout_hash), fuzzed_data_provider.ConsumeIntegral<uint32_t>());
64+ std::vector<unsigned char> script_sig_bytes = fuzzed_data_provider.ConsumeBytes<unsigned char>(MAX_SCRIPT_SIZE);
I think it would be better to consume from a range here and below. MAX_SCRIPT_SIZE
is large (10000) so the fuzzer ends up consuming the remaining bytes in the data provider at this line.
edit: This is what was actually causing the lack of coverage for matchh
it seems, as position
is always 0 later in the test. I’m not sure how long it would take for the provider to have enough bytes… but still, I think ConsumeIntegralInRange
ConsumeRandomLengthByteVector
makes sense here in place of ConsumeBytes
.
Good catch! You’re right it doesnt make sense to always have the MAX_SCRIPT_SIZE
we should instead use in that range
updated in 0fc30f2
ConsumeScript
to create the scriptPubKey and scriptSig?
ConsumeTransaction
make sense and just pushing that transaction into the block?
I used ConsumeTransaction
in 44298f0
let me know if that looks good or if you would prefer to use ConsumeScript
ConsumeTransaction
is fine.
This adds a new fuzz target merkle which adds fuzz coverage to
consensus/merkle.cpp
ACK 44298f0cb9c3de42b1c0f464c0d7b2779aa4c3b7
Using ConsumeTransaction
works, and makes sense. Here’s the coverage. Everything lgtm!