Add fast Merkle branch functions.

A fast Merkle branch uses midstate to perform a single SHA-256 compression per branch, and is not vulnerable to CVE-2012-2459. It produces different hashes though, so can only be used for new hash trees going forward.
This commit is contained in:
Mark Friedenbach 2016-01-22 08:50:33 -08:00 committed by Gregory Sanders
parent 29ad41dd56
commit 3518ae82d6
2 changed files with 76 additions and 6 deletions

View file

@ -42,14 +42,32 @@
root.
*/
typedef enum {
MERKLE_COMPUTATION_MUTABLE = 0x1,
MERKLE_COMPUTATION_FAST = 0x2
} merklecomputationopts;
static void MerkleHash_Hash256(uint256& parent, const uint256& left, const uint256& right) {
CHash256().Write(left.begin(), 32).Write(right.begin(), 32).Finalize(parent.begin());
}
static void MerkleHash_Sha256Midstate(uint256& parent, const uint256& left, const uint256& right) {
CSHA256().Write(left.begin(), 32).Write(right.begin(), 32).Midstate(parent.begin(), NULL, NULL);
}
/* 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) {
static void MerkleComputation(const std::vector<uint256>& leaves, uint256* proot, bool* pmutated, uint32_t branchpos, std::vector<uint256>* pbranch, merklecomputationopts flags) {
if (pbranch) pbranch->clear();
if (leaves.size() == 0) {
if (pmutated) *pmutated = false;
if (proot) *proot = uint256();
return;
}
bool fMutable = flags & MERKLE_COMPUTATION_MUTABLE;
void (*MerkleHash)(uint256&, const uint256&, const uint256&) = MerkleHash_Hash256;
if (flags & MERKLE_COMPUTATION_FAST) {
MerkleHash = MerkleHash_Sha256Midstate;
}
bool mutated = false;
// count is the number of leaves processed so far.
uint32_t count = 0;
@ -80,7 +98,7 @@ static void MerkleComputation(const std::vector<uint256>& leaves, uint256* proot
}
}
mutated |= (inner[level] == h);
CHash256().Write(inner[level].begin(), 32).Write(h.begin(), 32).Finalize(h.begin());
MerkleHash(h, inner[level], h);
}
// Store the resulting hash at inner position level.
inner[level] = h;
@ -106,7 +124,9 @@ static void MerkleComputation(const std::vector<uint256>& leaves, uint256* proot
if (pbranch && matchh) {
pbranch->push_back(h);
}
CHash256().Write(h.begin(), 32).Write(h.begin(), 32).Finalize(h.begin());
if (fMutable) {
MerkleHash(h, h, h);
}
// Increment count to the value it would have if two entries at this
// level had existed.
count += (((uint32_t)1) << level);
@ -121,7 +141,7 @@ static void MerkleComputation(const std::vector<uint256>& leaves, uint256* proot
matchh = true;
}
}
CHash256().Write(inner[level].begin(), 32).Write(h.begin(), 32).Finalize(h.begin());
MerkleHash(h, inner[level], h);
level++;
}
}
@ -132,13 +152,13 @@ static void MerkleComputation(const std::vector<uint256>& leaves, uint256* proot
uint256 ComputeMerkleRoot(const std::vector<uint256>& leaves, bool* mutated) {
uint256 hash;
MerkleComputation(leaves, &hash, mutated, -1, NULL);
MerkleComputation(leaves, &hash, mutated, -1, NULL, MERKLE_COMPUTATION_MUTABLE);
return hash;
}
std::vector<uint256> ComputeMerkleBranch(const std::vector<uint256>& leaves, uint32_t position) {
std::vector<uint256> ret;
MerkleComputation(leaves, NULL, NULL, position, &ret);
MerkleComputation(leaves, NULL, NULL, position, &ret, MERKLE_COMPUTATION_MUTABLE);
return ret;
}
@ -155,6 +175,46 @@ uint256 ComputeMerkleRootFromBranch(const uint256& leaf, const std::vector<uint2
return hash;
}
uint256 ComputeFastMerkleRoot(const std::vector<uint256>& leaves) {
uint256 hash;
MerkleComputation(leaves, &hash, NULL, -1, NULL, MERKLE_COMPUTATION_FAST);
return hash;
}
std::vector<uint256> ComputeFastMerkleBranch(const std::vector<uint256>& leaves, uint32_t position) {
std::vector<uint256> ret;
MerkleComputation(leaves, NULL, NULL, position, &ret, MERKLE_COMPUTATION_FAST);
return ret;
}
uint256 ComputeFastMerkleRootFromBranch(const uint256& leaf, const std::vector<uint256>& vMerkleBranch, uint32_t nIndex) {
size_t max = 0;
for (int i = 0; i < 32; ++i)
if (nIndex & ((uint32_t)1)<<i)
max = i + 1;
while (max > vMerkleBranch.size()) {
int i;
for (i = max-1; i >= 0; --i)
if (!(nIndex & ((uint32_t)1)<<i))
break;
if (i < 0)
return uint256();
nIndex = (((((uint32_t)1)<<(i+1))-1)>>1) |
((((uint32_t)1)<< i )-1);
--max;
}
uint256 hash = leaf;
for (std::vector<uint256>::const_iterator it = vMerkleBranch.begin(); it != vMerkleBranch.end(); ++it) {
if (nIndex & 1) {
MerkleHash_Sha256Midstate(hash, *it, hash);
} else {
MerkleHash_Sha256Midstate(hash, hash, *it);
}
nIndex >>= 1;
}
return hash;
}
uint256 BlockMerkleRoot(const CBlock& block, bool* mutated)
{
std::vector<uint256> leaves;

View file

@ -16,6 +16,16 @@ uint256 ComputeMerkleRoot(const std::vector<uint256>& leaves, bool* mutated = NU
std::vector<uint256> ComputeMerkleBranch(const std::vector<uint256>& leaves, uint32_t position);
uint256 ComputeMerkleRootFromBranch(const uint256& leaf, const std::vector<uint256>& branch, uint32_t position);
/*
* Has similar API semantics, but produces Merkle roots and validates
* branches 3x as fast, and without the mutation vulnerability. Cannot
* be substituted for the non-fast variants because the hash values are
* different.
*/
uint256 ComputeFastMerkleRoot(const std::vector<uint256>& leaves);
std::vector<uint256> ComputeFastMerkleBranch(const std::vector<uint256>& leaves, uint32_t position);
uint256 ComputeFastMerkleRootFromBranch(const uint256& leaf, const std::vector<uint256>& branch, uint32_t position);
/*
* Compute the Merkle root of the transactions in a block.
* *mutated is set to true if a duplicated subtree was found.