diff --git a/src/bench/merkle_root.cpp b/src/bench/merkle_root.cpp index b27bbd461d5..0f59ccf81c4 100644 --- a/src/bench/merkle_root.cpp +++ b/src/bench/merkle_root.cpp @@ -7,21 +7,33 @@ #include #include +#include #include static void MerkleRoot(benchmark::Bench& bench) { - FastRandomContext rng(true); - std::vector leaves; - leaves.resize(9001); - for (auto& item : leaves) { + FastRandomContext rng{/*fDeterministic=*/true}; + + std::vector hashes{}; + hashes.resize(9001); + for (auto& item : hashes) { item = rng.rand256(); } - bench.batch(leaves.size()).unit("leaf").run([&] { - bool mutation = false; - uint256 hash = ComputeMerkleRoot(std::vector(leaves), &mutation); - leaves[mutation] = hash; - }); + + constexpr uint256 expected_root{"d8d4dfd014a533bc3941b8663fa6e7f3a8707af124f713164d75b0c3179ecb08"}; + for (bool mutate : {false, true}) { + bench.name(mutate ? "MerkleRootWithMutation" : "MerkleRoot").batch(hashes.size()).unit("leaf").run([&] { + std::vector leaves; + leaves.reserve((hashes.size() + 1) & ~1ULL); // capacity rounded up to even + for (size_t s = 0; s < hashes.size(); s++) { + leaves.push_back(hashes[s]); + } + + bool mutated{false}; + const uint256 root{ComputeMerkleRoot(std::move(leaves), mutate ? &mutated : nullptr)}; + assert(root == expected_root); + }); + } } BENCHMARK(MerkleRoot); diff --git a/src/consensus/merkle.cpp b/src/consensus/merkle.cpp index 84ff352d1f1..c6ae81c6142 100644 --- a/src/consensus/merkle.cpp +++ b/src/consensus/merkle.cpp @@ -66,9 +66,9 @@ uint256 ComputeMerkleRoot(std::vector hashes, bool* mutated) { uint256 BlockMerkleRoot(const CBlock& block, bool* mutated) { std::vector leaves; - leaves.resize(block.vtx.size()); + leaves.reserve((block.vtx.size() + 1) & ~1ULL); // capacity rounded up to even for (size_t s = 0; s < block.vtx.size(); s++) { - leaves[s] = block.vtx[s]->GetHash().ToUint256(); + leaves.push_back(block.vtx[s]->GetHash().ToUint256()); } return ComputeMerkleRoot(std::move(leaves), mutated); } @@ -76,10 +76,10 @@ uint256 BlockMerkleRoot(const CBlock& block, bool* mutated) uint256 BlockWitnessMerkleRoot(const CBlock& block) { std::vector leaves; - leaves.resize(block.vtx.size()); - leaves[0].SetNull(); // The witness hash of the coinbase is 0. + 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[s] = block.vtx[s]->GetWitnessHash().ToUint256(); + leaves.push_back(block.vtx[s]->GetWitnessHash().ToUint256()); } return ComputeMerkleRoot(std::move(leaves)); } diff --git a/src/signet.cpp b/src/signet.cpp index 787f5ae42ee..d874ddc1a65 100644 --- a/src/signet.cpp +++ b/src/signet.cpp @@ -59,10 +59,10 @@ static bool FetchAndClearCommitmentSection(const std::span header static uint256 ComputeModifiedMerkleRoot(const CMutableTransaction& cb, const CBlock& block) { std::vector leaves; - leaves.resize(block.vtx.size()); - leaves[0] = cb.GetHash().ToUint256(); + leaves.reserve((block.vtx.size() + 1) & ~1ULL); // capacity rounded up to even + leaves.push_back(cb.GetHash().ToUint256()); for (size_t s = 1; s < block.vtx.size(); ++s) { - leaves[s] = block.vtx[s]->GetHash().ToUint256(); + leaves.push_back(block.vtx[s]->GetHash().ToUint256()); } return ComputeMerkleRoot(std::move(leaves)); } diff --git a/src/test/fuzz/integer.cpp b/src/test/fuzz/integer.cpp index fcbe76c4c75..a1cd18bf7bc 100644 --- a/src/test/fuzz/integer.cpp +++ b/src/test/fuzz/integer.cpp @@ -80,8 +80,8 @@ FUZZ_TARGET(integer, .init = initialize_integer) } constexpr uint256 u256_min{"0000000000000000000000000000000000000000000000000000000000000000"}; constexpr uint256 u256_max{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}; - const std::vector v256{u256, u256_min, u256_max}; - (void)ComputeMerkleRoot(v256); + std::vector v256{u256, u256_min, u256_max}; + (void)ComputeMerkleRoot(std::move(v256)); (void)DecompressAmount(u64); { if (std::optional parsed = ParseMoney(FormatMoney(i64))) { diff --git a/src/test/merkle_tests.cpp b/src/test/merkle_tests.cpp index ee748c5d1b5..347261df275 100644 --- a/src/test/merkle_tests.cpp +++ b/src/test/merkle_tests.cpp @@ -232,8 +232,9 @@ BOOST_AUTO_TEST_CASE(merkle_test_BlockWitness) { CBlock block; - block.vtx.resize(2); - for (std::size_t pos = 0; pos < block.vtx.size(); pos++) { + constexpr size_t vtx_count{3}; + block.vtx.resize(vtx_count); + for (std::size_t pos = 0; pos < vtx_count; pos++) { CMutableTransaction mtx; mtx.nLockTime = pos; block.vtx[pos] = MakeTransactionRef(std::move(mtx)); @@ -242,12 +243,12 @@ BOOST_AUTO_TEST_CASE(merkle_test_BlockWitness) uint256 blockWitness = BlockWitnessMerkleRoot(block); std::vector hashes; - hashes.resize(block.vtx.size()); - hashes[0].SetNull(); - hashes[1] = block.vtx[1]->GetHash().ToUint256(); + hashes.resize(vtx_count); // Note: leaving odd count to exercise old behavior + for (size_t pos{1}; pos < vtx_count; ++pos) { + hashes[pos] = block.vtx[pos]->GetWitnessHash().ToUint256(); + } uint256 merkleRootofHashes = ComputeMerkleRoot(hashes); - BOOST_CHECK_EQUAL(merkleRootofHashes, blockWitness); } BOOST_AUTO_TEST_SUITE_END()