比特币之Merkle 树

按照中本聪的原文,SPV指的是“支付验证“,而不是“交易验证”。这两种验证有很大区别。
“交易验证”非常复杂,涉及到验证是否有足够余额可供支出、是否存在双花、脚本能否通过等等,通常由运行完全节点的矿工来完成。
“支付验证”则比较简单,只判断用于“支付”的那笔交易是否已经被验证过,并得到了多少的算力保护(多少确认数)。

spv会在区块链查找交易(为了验证支付),并且需要连接到一个全节点来检索必要的数据。这个机制允许在仅运行一个全节点的情况下有多个轻钱包。
为了实现 SPV,需要有一个方式来检查是否一个区块包含了某笔交易,而无须下载整个区块。这就是 Merkle 树所要完成的事情。

我们还是先看下Merkle 树:
比特币之Merkle 树_第1张图片

每个块都会有一个 Merkle 树,它从叶子节点(树的底部)开始,一个叶子节点就是一个交易哈希(比特币使用双 SHA256 哈希)。叶子节点的数量必须是双数,但是并非每个块都包含了双数的交易。因为,如果一个块里面的交易数为单数,那么就将最后一个叶子节点(也就是 Merkle 树的最后一个交易,不是区块的最后一笔交易)复制一份凑成双数。

从下往上,两两成对,连接两个节点哈希,将组合哈希作为新的哈希。新的哈希就成为新的树节点。重复该过程,直到仅有一个节点,也就是树根。根哈希然后就会当做是整个块交易的唯一标示,将它保存到区块头,然后用于工作量证明。

Merkle 树的好处就是一个节点可以在不下载整个块的情况下,验证是否包含某笔交易。并且这些只需要一个交易哈希,一个 Merkle 树根哈希和一个 Merkle 路径。

我们看下用go怎么实现Merkle 树:
1.先从结构体开始
每个 MerkleNode 包含数据和指向左右分支的指针。MerkleTree 实际上就是连接到下个节点的根节点,然后依次连接到更远的节点,等等:

type MerkleTree struct {
    RootNode *MerkleNode
}

type MerkleNode struct {
    Left  *MerkleNode
    Right *MerkleNode
    Data  []byte
}

2.创建一个新的节点
由两个MerkleNode 和他们拼接成一起然后计算的hash的data组成的小树组成:

func NewMerkleNode(left, right *MerkleNode, data []byte) *MerkleNode {
    mNode := MerkleNode{}
    //左右节点为null表示第一次计算直接hash数据
    if left == nil && right == nil {
        hash := sha256.Sum256(data)
        mNode.Data = hash[:]
    } else {
        prevHashes := append(left.Data, right.Data...)
        hash := sha256.Sum256(prevHashes)
        mNode.Data = hash[:]
    }

    mNode.Left = left
    mNode.Right = right

    return &mNode
}

3.生成Merkle 树
将传过来的交易数据循环hash计算形成Merkle 树

func NewMerkleTree(data [][]byte) *MerkleTree {
    var nodes []MerkleNode
    //因为,如果一个块里面的交易数为单数,那么就将最后一个叶子节点(也就是 Merkle 树的最后一个交易,不是区块的最后一笔交易)复制一份凑成双数
    if len(data)%2 != 0 {
        data = append(data, data[len(data)-1])
    }
    //将所有的交易数据进行第一次hash计算放到一个数组,数组长度为偶数
    for _, datum := range data {
        node := NewMerkleNode(nil, nil, datum)
        nodes = append(nodes, *node)
    }
    //一次循环数组长度变为原来的1/2,所以外层时间复杂度为O(logn)
    for i := 0; i < len(data)/2; i++ {
        var newLevel []MerkleNode
        //数组第1和第2组成新的数组第一位的node,依次递推
        for j := 0; j < len(nodes); j += 2 {
            node := NewMerkleNode(&nodes[j], &nodes[j+1], nil)
            newLevel = append(newLevel, *node)
        }

        nodes = newLevel
    }
    //最终数组里面只会有一个node即Merkle 树根节点
    mTree := MerkleTree{&nodes[0]}

    return &mTree
}

上面就形成了比特币区块头的Merkle 树根节点,在检查交易是否存在的时候就简单多了。

验证某个交易是否真实存在时,理论上,用户可以通过以下方式进行验证:

(为了简化模型,我们假设用tx_hash来定位block。这种方法有被“交易可锻性”攻击的风险,实际应用中可以根据output_point来定位。)

  1. 从网络上获取并保存最长链的所有block header至本地;
  2. 计算该交易的hash值tx_hash;
  3. 定位到包含该tx_hash所在的区块,验证block header是否包含在已知的最长链中;
  4. 从区块中获取构建merkle tree所需的hash值;
  5. 根据这些hash值计算merkle_root_hash;
  6. 若计算结果与block header中的merkle_root_hash相等,则交易真实存在。
  7. 根据该block header所处的位置,确定该交易已经得到多少个确认。

假设需要验证Data01是否在该区块中,那么我需要得到Node12 ,Node22,Node32的Hash,这些都必须通过与全节点的交互获得,得到这些之后,就可以依据构建规则构建树直至根,然后和Merkle 树根节点比对即可确定这个交易是否存在该区块。

比特币之Merkle 树_第2张图片

你可能感兴趣的:(区块链,区块链杂谈)