merkle: remove unused `mutated` arg from `BlockWitnessMerkleRoot`
What changed, and why it matters
This is a small code cleanup change. It removes an unused optional parameter from a helper function that calculates the witness Merkle root for a Bitcoin block. All callers were already passing a special 'not used' value (nullptr), so the behavior of the program does not change. There is no security bug being fixed here.
No security action required. Treat as normal refactoring/cleanup.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit refactors BlockWitnessMerkleRoot() in src/consensus/merkle.cpp/.h to drop the bool* mutated output parameter, because every call site passed nullptr. The underlying ComputeMerkleRoot() still supports mutation detection; only the witness-root wrapper no longer exposes it. Call sites and a fuzz test are updated accordingly. The existing comment in validation.cpp explains why mutation detection is unnecessary for the witness tree: transaction malleability is already disallowed by the transaction tree, making witness-tree malleation impossible.
Changed components
src/consensus/merkle.cppsrc/consensus/merkle.hsrc/validation.cppsrc/test/fuzz/merkle.cppInspect captured patch +7 / −8
diff --git a/src/consensus/merkle.cpp b/src/consensus/merkle.cpp
index d7f1435f..703a824e 100644
--- a/src/consensus/merkle.cpp
+++ b/src/consensus/merkle.cpp
@@ -73,7 +73,7 @@ uint256 BlockMerkleRoot(const CBlock& block, bool* mutated)
return ComputeMerkleRoot(std::move(leaves), mutated);
}
-uint256 BlockWitnessMerkleRoot(const CBlock& block, bool* mutated)
+uint256 BlockWitnessMerkleRoot(const CBlock& block)
{
std::vector<uint256> leaves;
leaves.resize(block.vtx.size());
@@ -81,7 +81,7 @@ uint256 BlockWitnessMerkleRoot(const CBlock& block, bool* mutated)
for (size_t s = 1; s < block.vtx.size(); s++) {
leaves[s] = block.vtx[s]->GetWitnessHash().ToUint256();
}
- return ComputeMerkleRoot(std::move(leaves), mutated);
+ return ComputeMerkleRoot(std::move(leaves));
}
/* This implements a constant-space merkle path calculator, limited to 2^32 leaves. */
diff --git a/src/consensus/merkle.h b/src/consensus/merkle.h
index c722cbe4..29282d21 100644
--- a/src/consensus/merkle.h
+++ b/src/consensus/merkle.h
@@ -20,9 +20,8 @@ uint256 BlockMerkleRoot(const CBlock& block, bool* mutated = nullptr);
/*
* Compute the Merkle root of the witness transactions in a block.
- * *mutated is set to true if a duplicated subtree was found.
*/
-uint256 BlockWitnessMerkleRoot(const CBlock& block, bool* mutated = nullptr);
+uint256 BlockWitnessMerkleRoot(const CBlock& block);
/**
* Compute merkle path to the specified transaction
diff --git a/src/test/fuzz/merkle.cpp b/src/test/fuzz/merkle.cpp
index 4bb91faf..9ba461bb 100644
--- a/src/test/fuzz/merkle.cpp
+++ b/src/test/fuzz/merkle.cpp
@@ -51,7 +51,7 @@ FUZZ_TARGET(merkle)
}
// Test ComputeMerkleRoot
- bool mutated = fuzzed_data_provider.ConsumeBool();
+ 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
@@ -66,7 +66,7 @@ FUZZ_TARGET(merkle)
}
if (!block->vtx.empty()){
- const uint256 block_witness_merkle_root = BlockWitnessMerkleRoot(*block, &mutated);
+ const uint256 block_witness_merkle_root = BlockWitnessMerkleRoot(*block);
if (tx_hashes.size() == 1) {
assert(block_witness_merkle_root == uint256());
}
diff --git a/src/validation.cpp b/src/validation.cpp
index af523b06..aaf80b33 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -3996,7 +3996,7 @@ static bool CheckWitnessMalleation(const CBlock& block, bool expect_witness_comm
// The malleation check is ignored; as the transaction tree itself
// already does not permit it, it is impossible to trigger in the
// witness tree.
- uint256 hash_witness = BlockWitnessMerkleRoot(block, /*mutated=*/nullptr);
+ uint256 hash_witness = BlockWitnessMerkleRoot(block);
CHash256().Write(hash_witness).Write(witness_stack[0]).Finalize(hash_witness);
if (memcmp(hash_witness.begin(), &block.vtx[0]->vout[commitpos].scriptPubKey[6], 32)) {
@@ -4109,7 +4109,7 @@ std::vector<unsigned char> ChainstateManager::GenerateCoinbaseCommitment(CBlock&
int commitpos = GetWitnessCommitmentIndex(block);
std::vector<unsigned char> ret(32, 0x00);
if (commitpos == NO_WITNESS_COMMITMENT) {
- uint256 witnessroot = BlockWitnessMerkleRoot(block, nullptr);
+ uint256 witnessroot = BlockWitnessMerkleRoot(block);
CHash256().Write(witnessroot).Write(ret).Finalize(witnessroot);
CTxOut out;
out.nValue = 0;
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.