Merkle 树是一种哈希二叉树, 从数据结构角度说 它是一个完美二叉树(perfect binary tree), 在比特币中用来归纳区块所包含的所有交易
1. merkle 树的构建
假如某区块包含4个交易TxA TxB TxC TxD, 其构建的merkle树由下图所示:
Hash(TxA) == SHA256(SHA256(TxA))
上例中由于交易个数正好是2^2个, 所以可以构建出一个perfect二叉树(节点数为 2^n-1)
如果交易的个数是3个, 则会把最后一个交易的哈希值复制一份,从而构建perfect 二叉树。
构造merkle树的源码(原始版0.1.0)如下:
uint256 BuildMerkleTree() const
{
vMerkleTree.clear(); //vMerkleTree的类型为vector
foreach(const CTransaction& tx, vtx) //vtx:保存块中所有交易的vector
vMerkleTree.push_back(tx.GetHash()); //先把所有交易的hash值(叶子节点) push到 vMerkleTree
int j = 0; //存放merkle树的每层第一个节点在数组中的位置
for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2)//第一重循环构造树的层 nSize为每层节点个数
{
for (int i = 0; i < nSize; i += 2)//第二重循环构建每层的节点
{
int i2 = min(i+1, nSize-1);//i2为i节点后面的节点,当到最后一个节点时,如果nsize为奇数 则i2 = 最后一个节点
vMerkleTree.push_back(Hash(BEGIN(vMerkleTree[j+i]), END(vMerkleTree[j+i]),
BEGIN(vMerkleTree[j+i2]), END(vMerkleTree[j+i2])));//计算第i节点和i2节点hash连接后的hash值
}
j += nSize; //每构建一层,j就增加本层节点个数 从而"指向"下一层第一个节点
}
return (vMerkleTree.empty() ? 0 : vMerkleTree.back()); //返回merkle根
}
2. 通过merkle branch 验证一个交易是否包含在某个区块中
merkle branch是merkle tree的一条路径(由merkle树节点组成),每一个交易都对应一个merkle branch
通过交易的hash值 和其merkle branch可以很容易计算出merkle树的根,用计算出的merkle 根和区块头中记录的merkle根比较,如果相等 就说明次交易存在于这个区块中
下面是构造merkle branch的源码:
vector CBolck::GetMerkleBranch(int nIndex) const
{
if (vMerkleTree.empty())
BuildMerkleTree();
vector vMerkleBranch;
int j = 0; //j指向每一层的起点
for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2)
{
int i = min(nIndex^1, nSize-1);// nIndex:交易在vTx中的索引
vMerkleBranch.push_back(vMerkleTree[j+i]);
nIndex >>= 1;
j += nSize;
}
return vMerkleBranch;
}
交易的merkle branch存放在交易结构中:
class CMerkleTx : public CTransaction
{
public:
uint256 hashBlock;
vector vMerkleBranch;
int nIndex;
// memory only
mutable bool fMerkleVerified;
// 方法省略
};
验证函数 CheckMerkleBranch :
static uint256 CBlock::CheckMerkleBranch(uint256 hash, const vector & vMerkleBranch, int nIndex)
{
if (nIndex == -1)
return 0;
foreach(const uint256& otherside, vMerkleBranch)
{
if (nIndex & 1)
hash = Hash(BEGIN(otherside), END(otherside), BEGIN(hash), END(hash));
else
hash = Hash(BEGIN(hash), END(hash), BEGIN(otherside), END(otherside));
nIndex >>= 1;
}
return hash; //返回的是计算出来的merkle根
}
验证CheckMerkleBranch方法返回的结果是否和CBlock中对应的merkle根的值一样,即如下面代码所示:
if (CBlock::CheckMerkleBranch(GetHash(), vMerkleBranch, nIndex) != pBlock->hashMerkleRoot)
return 0;