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

Code before:
       root.
*/


uint256 ComputeMerkleRoot(std::vector<uint256> hashes, bool* mutated) {
    bool mutation = false;
    while (hashes.size() > 1) {
        if (mutated) {
            for (size_t pos = 0; pos + 1 < hashes.size(); pos += 2) {
                if (hashes[pos] == hashes[pos + 1]) mutation = true;
            }
        }
        if (hashes.size() & 1) {
            hashes.push_back(hashes.back());
        }
        SHA256D64(hashes[0].begin(), hashes[0].begin(), hashes.size() / 2);
        hashes.resize(hashes.size() / 2);
    }
    if (mutated) *mutated = mutation;
    if (hashes.size() == 0) return uint256();
    return hashes[0];
}


uint256 BlockMerkleRoot(const CBlock& block, bool* mutated)
{
    std::vector<uint256> leaves;
    leaves.resize(block.vtx.size());
    for (size_t s = 0; s < block.vtx.size(); s++) {
        leaves[s] = block.vtx[s]->GetHash().ToUint256();
    }
    return ComputeMerkleRoot(std::move(leaves), mutated);
}

uint256 BlockWitnessMerkleRoot(const CBlock& block)
{
    std::vector<uint256> leaves;
    leaves.resize(block.vtx.size());
    leaves[0].SetNull(); // The witness hash of the coinbase is 0.
    for (size_t s = 1; s < block.vtx.size(); s++) {
        leaves[s] = block.vtx[s]->GetWitnessHash().ToUint256();
    }
    return ComputeMerkleRoot(std::move(leaves));
}

/* This implements a constant-space merkle path calculator, limited to 2^32 leaves. */
static void MerkleComputation(const std::vector<uint256>& leaves, uint32_t leaf_pos, std::vector<uint256>& path)
{
    path.clear();
    Assume(leaves.size() <= UINT32_MAX);
    if (leaves.size() == 0) {

Review comment (performance):
If we keep the lambda, `_` is more the Python way of declaring unused variables, while C/C++ usually omits the identifier entirely:
```suggestion
    auto leaves{ToMerkleLeaves(block.vtx, [](bool, const auto& tx) {
```
or
```suggestion
    auto leaves{ToMerkleLeaves(block.vtx, [](auto /*first*/, const auto& tx) {
```
Notice I also removed the capture-`&` when it is not needed, which makes the lambda more pure (less cognitive load). (Also prefer `bool` over `auto` since it doesn't cost a single char, but that's more subjective).

<details><summary>Full diff</summary>

```diff
diff --git a/src/bench/merkle_root.cpp b/src/bench/merkle_root.cpp
index bd8524c805..55baed6e02 100644
--- a/src/bench/merkle_root.cpp
+++ b/src/bench/merkle_root.cpp
@@ -22,7 +22,7 @@ static void MerkleRoot(benchmark::Bench& bench)
 
     constexpr uint256 expected_root{"d8d4dfd014a533bc3941b8663fa6e7f3a8707af124f713164d75b0c3179ecb08"};
     bench.batch(hashes.size()).unit("leaf").run([&] {
-        auto leaves{ToMerkleLeaves(hashes, [&](bool, const auto& h) { return h; })};
+        auto leaves{ToMerkleLeaves(hashes, [](bool, const auto& h) { return h; })};
         const uint256 root{ComputeMerkleRoot(std::move(leaves))};
         assert(root == expected_root);
     });
diff --git a/src/consensus/merkle.cpp b/src/consensus/merkle.cpp
index 4f637110dd..41eb1f6e5a 100644
--- a/src/consensus/merkle.cpp
+++ b/src/consensus/merkle.cpp
@@ -64,7 +64,7 @@ uint256 ComputeMerkleRoot(std::vector<uint256> hashes, bool* mutated) {
 
 uint256 BlockMerkleRoot(const CBlock& block, bool* mutated)
 {
-    auto leaves{ToMerkleLeaves(block.vtx, [&](auto _, const auto& tx) {
+    auto leaves{ToMerkleLeaves(block.vtx, [](bool, const auto& tx) {
         return tx->GetHash().ToUint256();
     })};
     return ComputeMerkleRoot(std::move(leaves), mutated);
@@ -72,7 +72,7 @@ uint256 BlockMerkleRoot(const CBlock& block, bool* mutated)
 
 uint256 BlockWitnessMerkleRoot(const CBlock& block, bool* mutated)
 {
-    auto leaves{ToMerkleLeaves(block.vtx, [&](auto first, const auto& tx) {
+    auto leaves{ToMerkleLeaves(block.vtx, [](bool first, const auto& tx) {
         return first ? uint256{} : tx->GetWitnessHash().ToUint256();
     })};
     return ComputeMerkleRoot(std::move(leaves), mutated);
diff --git a/src/signet.cpp b/src/signet.cpp
index 9c564d66d7..ed42b554f3 100644
--- a/src/signet.cpp
+++ b/src/signet.cpp
@@ -57,7 +57,7 @@ static bool FetchAndClearCommitmentSection(const std::span<const uint8_t> header
 
 static uint256 ComputeModifiedMerkleRoot(const CMutableTransaction& cb, const CBlock& block)
 {
-    auto leaves{ToMerkleLeaves(block.vtx, [&](auto first, const auto& tx) {
+    auto leaves{ToMerkleLeaves(block.vtx, [&](bool first, const auto& tx) {
         return (first ? cb.GetHash() : tx->GetHash()).ToUint256();
     })};
     return ComputeMerkleRoot(std::move(leaves));
diff --git a/src/test/fuzz/merkle.cpp b/src/test/fuzz/merkle.cpp
index 4ac83ffb81..b4136008e2 100644
--- a/src/test/fuzz/merkle.cpp
+++ b/src/test/fuzz/merkle.cpp
@@ -44,7 +44,7 @@ FUZZ_TARGET(merkle)
     }
 
     const std::size_t num_txs{block->vtx.size()};
-    auto tx_hashes{ToMerkleLeaves(block->vtx, [&](bool, const auto& tx) {
+    auto tx_hashes{ToMerkleLeaves(block->vtx, [](bool, const auto& tx) {
         return tx->GetHash().ToUint256();
     })};
 
diff --git a/src/test/merkle_tests.cpp b/src/test/merkle_tests.cpp
index c31368c9ec..c8f8079d22 100644
--- a/src/test/merkle_tests.cpp
+++ b/src/test/merkle_tests.cpp
@@ -236,7 +236,7 @@ BOOST_AUTO_TEST_CASE(merkle_test_BlockWitness)
 
     uint256 blockWitness = BlockWitnessMerkleRoot(block);
 
-    auto leaves{ToMerkleLeaves(block.vtx, [&](bool first, const auto& tx) {
+    auto leaves{ToMerkleLeaves(block.vtx, [](bool first, const auto& tx) {
         return first ? uint256{} : tx->GetWitnessHash().ToUint256();
     })};
     uint256 merkleRootofHashes{ComputeMerkleRoot(std::move(leaves))};
```

</details>

Code after:
       root.
*/


uint256 ComputeMerkleRoot(std::vector<uint256> hashes, bool* mutated) {
    bool mutation = false;
    while (hashes.size() > 1) {
        if (mutated) {
            for (size_t pos = 0; pos + 1 < hashes.size(); pos += 2) {
                if (hashes[pos] == hashes[pos + 1]) mutation = true;
            }
        }
        if (hashes.size() & 1) {
            hashes.push_back(hashes.back());
        }
        SHA256D64(hashes[0].begin(), hashes[0].begin(), hashes.size() / 2);
        hashes.resize(hashes.size() / 2);
    }
    if (mutated) *mutated = mutation;
    if (hashes.size() == 0) return uint256();
    return hashes[0];
}


uint256 BlockMerkleRoot(const CBlock& block, bool* mutated)
{
    std::vector<uint256> leaves;
    leaves.reserve((block.vtx.size() + 1) & ~1ULL); // capacity rounded up to even
    for (size_t s = 0; s < block.vtx.size(); s++) {
        leaves.push_back(block.vtx[s]->GetHash().ToUint256());
    }
    return ComputeMerkleRoot(std::move(leaves), mutated);
}

uint256 BlockWitnessMerkleRoot(const CBlock& block)
{
    std::vector<uint256> leaves;
    leaves.reserve((block.vtx.size() + 1) & ~1ULL); // capacity rounded up to even
    leaves.emplace_back(); // The witness hash of the coinbase is 0.
    for (size_t s = 1; s < block.vtx.size(); s++) {
        leaves.push_back(block.vtx[s]->GetWitnessHash().ToUint256());
    }
    return ComputeMerkleRoot(std::move(leaves));
}

/* This implements a constant-space merkle path calculator, limited to 2^32 leaves. */
static void MerkleComputation(const std::vector<uint256>& leaves, uint32_t leaf_pos, std::vector<uint256>& path)
{
    path.clear();
    Assume(leaves.size() <= UINT32_MAX);
    if (leaves.size() == 0) {