Skip to content

Commit

Permalink
Move BlockMerkleBranch back to merkle.{h,cpp}
Browse files Browse the repository at this point in the history
The Mining interface uses this function in the next commit
to calculate the coinbase merkle path. Stratum v2 uses
this to send a compact work template.

This partially undoes the change in 4defdfa,
but is not a revert, because the implementation changed in the meantime.

This commit also documents the function.
  • Loading branch information
Sjors committed Sep 26, 2024
1 parent 65f6e70 commit 63d6ad7
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 104 deletions.
103 changes: 103 additions & 0 deletions src/consensus/merkle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,106 @@ uint256 BlockWitnessMerkleRoot(const CBlock& block, bool* mutated)
return ComputeMerkleRoot(std::move(leaves), mutated);
}

/* This implements a constant-space merkle root/path calculator, limited to 2^32 leaves. */
static void MerkleComputation(const std::vector<uint256>& leaves, uint256* proot, bool* pmutated, uint32_t branchpos, std::vector<uint256>* pbranch) {
if (pbranch) pbranch->clear();
if (leaves.size() == 0) {
if (pmutated) *pmutated = false;
if (proot) *proot = uint256();
return;
}
bool mutated = false;
// count is the number of leaves processed so far.
uint32_t count = 0;
// inner is an array of eagerly computed subtree hashes, indexed by tree
// level (0 being the leaves).
// For example, when count is 25 (11001 in binary), inner[4] is the hash of
// the first 16 leaves, inner[3] of the next 8 leaves, and inner[0] equal to
// the last leaf. The other inner entries are undefined.
uint256 inner[32];
// Which position in inner is a hash that depends on the matching leaf.
int matchlevel = -1;
// First process all leaves into 'inner' values.
while (count < leaves.size()) {
uint256 h = leaves[count];
bool matchh = count == branchpos;
count++;
int level;
// For each of the lower bits in count that are 0, do 1 step. Each
// corresponds to an inner value that existed before processing the
// current leaf, and each needs a hash to combine it.
for (level = 0; !(count & ((uint32_t{1}) << level)); level++) {
if (pbranch) {
if (matchh) {
pbranch->push_back(inner[level]);
} else if (matchlevel == level) {
pbranch->push_back(h);
matchh = true;
}
}
mutated |= (inner[level] == h);
h = Hash(inner[level], h);
}
// Store the resulting hash at inner position level.
inner[level] = h;
if (matchh) {
matchlevel = level;
}
}
// Do a final 'sweep' over the rightmost branch of the tree to process
// odd levels, and reduce everything to a single top value.
// Level is the level (counted from the bottom) up to which we've sweeped.
int level = 0;
// As long as bit number level in count is zero, skip it. It means there
// is nothing left at this level.
while (!(count & ((uint32_t{1}) << level))) {
level++;
}
uint256 h = inner[level];
bool matchh = matchlevel == level;
while (count != ((uint32_t{1}) << level)) {
// If we reach this point, h is an inner value that is not the top.
// We combine it with itself (Bitcoin's special rule for odd levels in
// the tree) to produce a higher level one.
if (pbranch && matchh) {
pbranch->push_back(h);
}
h = Hash(h, h);
// Increment count to the value it would have if two entries at this
// level had existed.
count += ((uint32_t{1}) << level);
level++;
// And propagate the result upwards accordingly.
while (!(count & ((uint32_t{1}) << level))) {
if (pbranch) {
if (matchh) {
pbranch->push_back(inner[level]);
} else if (matchlevel == level) {
pbranch->push_back(h);
matchh = true;
}
}
h = Hash(inner[level], h);
level++;
}
}
// Return result.
if (pmutated) *pmutated = mutated;
if (proot) *proot = h;
}

static std::vector<uint256> ComputeMerkleBranch(const std::vector<uint256>& leaves, uint32_t position) {
std::vector<uint256> ret;
MerkleComputation(leaves, nullptr, nullptr, position, &ret);
return ret;
}

std::vector<uint256> BlockMerkleBranch(const CBlock& block, uint32_t position)
{
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();
}
return ComputeMerkleBranch(leaves, position);
}
10 changes: 10 additions & 0 deletions src/consensus/merkle.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,14 @@ uint256 BlockMerkleRoot(const CBlock& block, bool* mutated = nullptr);
*/
uint256 BlockWitnessMerkleRoot(const CBlock& block, bool* mutated = nullptr);

/**
* Compute merkle path to the specified transaction
*
* @param[in] block the block
* @param[in] position transaction for which to calculate the merkle path, defaults to coinbase
*
* @return merkle path ordered from the deepest
*/
std::vector<uint256> BlockMerkleBranch(const CBlock& block, uint32_t position = 0);

#endif // BITCOIN_CONSENSUS_MERKLE_H
104 changes: 0 additions & 104 deletions src/test/merkle_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,110 +23,6 @@ static uint256 ComputeMerkleRootFromBranch(const uint256& leaf, const std::vecto
return hash;
}

/* This implements a constant-space merkle root/path calculator, limited to 2^32 leaves. */
static void MerkleComputation(const std::vector<uint256>& leaves, uint256* proot, bool* pmutated, uint32_t branchpos, std::vector<uint256>* pbranch) {
if (pbranch) pbranch->clear();
if (leaves.size() == 0) {
if (pmutated) *pmutated = false;
if (proot) *proot = uint256();
return;
}
bool mutated = false;
// count is the number of leaves processed so far.
uint32_t count = 0;
// inner is an array of eagerly computed subtree hashes, indexed by tree
// level (0 being the leaves).
// For example, when count is 25 (11001 in binary), inner[4] is the hash of
// the first 16 leaves, inner[3] of the next 8 leaves, and inner[0] equal to
// the last leaf. The other inner entries are undefined.
uint256 inner[32];
// Which position in inner is a hash that depends on the matching leaf.
int matchlevel = -1;
// First process all leaves into 'inner' values.
while (count < leaves.size()) {
uint256 h = leaves[count];
bool matchh = count == branchpos;
count++;
int level;
// For each of the lower bits in count that are 0, do 1 step. Each
// corresponds to an inner value that existed before processing the
// current leaf, and each needs a hash to combine it.
for (level = 0; !(count & ((uint32_t{1}) << level)); level++) {
if (pbranch) {
if (matchh) {
pbranch->push_back(inner[level]);
} else if (matchlevel == level) {
pbranch->push_back(h);
matchh = true;
}
}
mutated |= (inner[level] == h);
h = Hash(inner[level], h);
}
// Store the resulting hash at inner position level.
inner[level] = h;
if (matchh) {
matchlevel = level;
}
}
// Do a final 'sweep' over the rightmost branch of the tree to process
// odd levels, and reduce everything to a single top value.
// Level is the level (counted from the bottom) up to which we've sweeped.
int level = 0;
// As long as bit number level in count is zero, skip it. It means there
// is nothing left at this level.
while (!(count & ((uint32_t{1}) << level))) {
level++;
}
uint256 h = inner[level];
bool matchh = matchlevel == level;
while (count != ((uint32_t{1}) << level)) {
// If we reach this point, h is an inner value that is not the top.
// We combine it with itself (Bitcoin's special rule for odd levels in
// the tree) to produce a higher level one.
if (pbranch && matchh) {
pbranch->push_back(h);
}
h = Hash(h, h);
// Increment count to the value it would have if two entries at this
// level had existed.
count += ((uint32_t{1}) << level);
level++;
// And propagate the result upwards accordingly.
while (!(count & ((uint32_t{1}) << level))) {
if (pbranch) {
if (matchh) {
pbranch->push_back(inner[level]);
} else if (matchlevel == level) {
pbranch->push_back(h);
matchh = true;
}
}
h = Hash(inner[level], h);
level++;
}
}
// Return result.
if (pmutated) *pmutated = mutated;
if (proot) *proot = h;
}

static std::vector<uint256> ComputeMerkleBranch(const std::vector<uint256>& leaves, uint32_t position) {
std::vector<uint256> ret;
MerkleComputation(leaves, nullptr, nullptr, position, &ret);
return ret;
}

static std::vector<uint256> BlockMerkleBranch(const CBlock& block, uint32_t position)
{
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();
}
return ComputeMerkleBranch(leaves, position);
}

// Older version of the merkle root computation code, for comparison.
static uint256 BlockBuildMerkleTree(const CBlock& block, bool* fMutated, std::vector<uint256>& vMerkleTree)
{
Expand Down

0 comments on commit 63d6ad7

Please sign in to comment.