bitcoin代码之MerkleTree

1.Merkle Tree概念

这里写图片描述

Merkle树是一种哈希二叉树,它是一种用作快速归纳和校验大规模数据完整性的数据结构

2.Merkle Tree在区块链作用

merkle数在bitcoin里有2个主要作用

2.1归纳一个区块里全部交易为一个32字节值hash值。

这里写图片描述

​ 比特币使用Merkle树的方式进行验证,相对于哈希列表,Merkle树是一种哈希二叉树,它的明显的一个好处是可以单独拿出一个分支来(作为一个小树)对部分数据进行校验,更加高效。

我们回看下上面的区块结构图,区块体就包含这样一个Merkle树,Merkle树被用来归纳一个区块中的所有交易

2.2区块中缺少哪些交易,可以快速验证,复杂度在2log2(N)

当N个数据元素经过加密后插入Merkle树时,你至多计算2*log2(N)次就能检查出任意某数据元素是否在该树中,这使得该数据结构非常高效

3.bitcoin里的MerkleTree

3.1代码位置
bitcoin里merkle代码使用与移位<<和与&操作,代码不是很好理解
代码位于merkle.h/merkle.cpp里

3.2代码详解

入口函数,bitcoin在构建一个区块调用该函数计算merkle的根hash值

**uint256 BlockMerkleRoot(const CBlock& block, bool* mutated)**
{
    std::vector<uint256> leaves;
    //获取一个区块交易的数量,也就是merkle的(调用resize设置vector大小,避免)
    //避免在下面赋值需要重新分配内存
    leaves.resize(block.vtx.size());
    for (size_t s = 0; s < block.vtx.size(); s++) 
    {
        leaves[s] = block.vtx[s]->GetHash(); //获取每一笔交易的hash值
    }
    return ComputeMerkleRoot(leaves, mutated);
}
uint256 ComputeMerkleRoot(const std::vector<uint256>& leaves, bool* mutated)
{
    //uint256继承模板类base_blob的特化base_blob<256>,base_blob<256>可以看做是对char[32]的一个封装类
    //hash函数特点就是任何输入,hash后输出都是32字节
    uint256 hash;
    MerkleComputation(leaves, &hash, mutated, -1, nullptr);
    return hash;
}

//merkle tree的根hash256计算(代码进行了简化,只保留根节点hash计算逻辑)

static void MerkleComputation(const std::vector<uint256>& leaves, uint256* proot, bool* pmutated, uint32_t branchpos, std::vector<uint256>* pbranch)
{
    //如果没有叶子节点,
    if (leaves.size() == 0) 
    {
       if (proot) *proot = uint256();
        return;
    }

    uint32_t count = 0;
    /*
        计算所有叶子节点的hash值计算,存储在inner数组
        如果leaves.size()个数如果是2的幂次方,在下面第一个循环一次可获得根的hash值
        ,并且存储在2^k=leaves.size(),inner[k]的位置,假设有8个叶子节点
        那么计算完 inner[3]就是根的hash值,8个叶子节点代码计算过程如下。代码65行 
        1.计算hash(ab) 存储在inner[1]
        2.计算hash(cd) 存储在inner[2]
        3.计算hash(ab+cd=N) 存储在inner[2]  (代码83行 2,3步骤在一个循环执行)
        4.计算hash(ef)  存储在inner[1],这是会覆盖步骤1
        5.计算hash(gh)  (5,6,7在一个循环连续执行,代码73)
        6.计算hash(ef+gh=M) 
        7.计算hash(M+N) 存储在inner[3]

           abcdefgh 
              /\
          abcd  efgh
           /\     /\
         ab cd ef  gh
         /\  /\  /\  /\
        a b  c d e f g h

    */
    uint256 inner[32];
    while (count < leaves.size()) 
    {
        uint256 h = leaves[count];
        count++;
        int level;

        //count为偶数循环条件满足,奇数叶子节点hash先暂时存储在inner[0]
        for (level = 0; !(count & (((uint32_t)1) << level)); level++) 
        {
           //循环执行次数和count相关
           //count作为二进制看,从右到左0的个数就是循环次数
           //为什么有时候要执行多次,其实是和前面的hash节点在做拼接后做hash计算
           //如果count=4(0100),会执行循环2次,第一次计算hash(cd),循环第二次计算就是hash(ab+cd)
           CHash256().Write(inner[level].begin(), 32).Write(h.begin(), 32).Finalize(h.begin());
        }

         inner[level] = h;
     }


    int level = 0;
    /*
     这里用循环计算level分三种情况
     1.count是2的幂 ,假设count=8,那么inner[2^3] = 8 ,inner[level]就是树根hash值
     后面第二个循环不会被执行,函数返回root的hash。

            abcdefgh == inner[3]
              /\
          abcd  efgh
           /\     /\
         ab cd   ef  gh   
         /\  /\  /\  /\ 
        a b  c d e f g h
     2.count不是2的幂并且是奇数,假设count=5,那么inner[0]存储的是最后一个节点hash值,
     这个节点找不到配对节点来计算hash,只能复制一份和自己做hash。

            abcdefee 
              /\
 inner[1]==abcd  efee
           /\     /\
         ab cd ef [ee]   
         /\  /\  /\
        a b  c d e [e] 复制一份和自己做hash(e+e)


     3.count不是2的幂并且是偶数,假设count=6,level[1]==hash(ef),进入后面循环线执行hash(ef+ef)
     ,然后在执行hash(abcd+efef)

           abcdefee 
              /\
          abcd  efef
           /\     /\
         ab cd   ef [ef] 复制一份和自己做hash
         /\  /\  /\
        a b  c d e f

    */
    while (!(count & (((uint32_t)1) << level))) 
    {
        level++;
    }
   //这里的level值参考上面说明
    uint256 h = inner[level];

    while (count != (((uint32_t)1) << level)) {

        //当merkle树level层节点是奇数个,需要自己和自己算一次hash
        CHash256().Write(h.begin(), 32).Write(h.begin(), 32).Finalize(h.begin());

        /*
         //不管count是多少,计算出第一个大于count的2的幂的数
         //如果count是6,计算结果是8
         //如果count是7,计算结果是8
         //如果count是13,计算结果是16

        */
        count += (((uint32_t)1) << level);
        level++; 
        //如果count不是2的幂的,下面循环不会被执行
        while (!(count & (((uint32_t)1) << level))) 
        {
            //和前面节点hash值拼接再次计算hash
            CHash256().Write(inner[level].begin(), 32).Write(h.begin(), 32).Finalize(h.begin());
            level++;
        }
    }
    // Return result.
    if (proot) 
       *proot = h;
}

老版本的merkle,这个很好理解

static uint256 BlockBuildMerkleTree(const CBlock& block, bool* fMutated, std::vector<uint256>& vMerkleTree)
{
    vMerkleTree.clear();
    vMerkleTree.reserve(block.vtx.size() * 2 + 16); // Safe upper bound for the number of total nodes.
    for (std::vector<CTransactionRef>::const_iterator it(block.vtx.begin()); it != block.vtx.end(); ++it)
        vMerkleTree.push_back((*it)->GetHash());
    int j = 0;
    bool mutated = false;
    for (int nSize = block.vtx.size(); nSize > 1; nSize = (nSize + 1) / 2)
    {
        for (int i = 0; i < nSize; i += 2)
        {
            //因为循环步骤是2,取一个最小值防止数组越界
            int i2 = std::min(i+1, nSize-1);
            if (i2 == i + 1 && i2 + 1 == nSize && vMerkleTree[j+i] == vMerkleTree[j+i2]) {
                // Two identical hashes at the end of the list at a particular level.
                mutated = true;
            }
            //hash计算2个字符串拼接的hash256
            //从前到后,每次取相邻2个节点计算hash,并放入数组尾部
            vMerkleTree.push_back(Hash(vMerkleTree[j+i].begin(), vMerkleTree[j+i].end(),
                                       vMerkleTree[j+i2].begin(), vMerkleTree[j+i2].end()));
        }
        j += nSize;
    }
    if (fMutated) {
        *fMutated = mutated;
    }
    return (vMerkleTree.empty() ? uint256() : vMerkleTree.back());
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值