回忆:前面说过,比特币交易的解锁和上锁其实是一种基于堆栈的脚本语言,大家可以想一下,这种脚本语言还能干什么?
比特币的脚本语言启发了太坊创始人Vitalik Buterin(那年他20岁)创建以太坊,开发了智能合约。
全节点和轻节点
因为比特币的去中心化特性,网络中的每个节点必须是独立,自给自足的,也就是每个节点必须存储一个区块链的完整副本。
随着越来越多的人使用比特币,这条规则变得越来越难以遵守:因为不太可能每个人都去运行一个全节点。并且,由于节点是网络中的完全参与者,它们负有相关责任:节点必须验证交易和区块。另外,要想与其他节点交互和下载新块,也有一定的网络流量需求。
对这个问题也有一个解决方案:简易支付验证(Simplified Payment Verification, SPV)。SPV 是一个比特币轻节点,它不需要下载整个区块链,也不需要验证区块和交易,它只用维护区块链header的信息。相反,它会在区块链查找交易(为了验证支付),并且需要连接到一个全节点来检索必要的数据。这个机制允许在仅运行一个全节点的情况下有多个轻钱包。
为了实现 SPV,需要有一个方式来检查是否一个区块包含了某笔交易,而无须下载整个区块。这就是 Merkle 树所要完成的事情。
Merkle树
每个块都会有一个 Merkle 树,一个叶子节点就是一个交易哈希。
叶子节点的数量必须是双数。
根哈希会当做是整个块交易的唯一标示,将它保存到区块头,然后用于工作量证明。
//Merkle树
type MerkleTree struct {
//根节点
RootNode *MerkleNode
}
//Merkle节点
type MerkleNode struct {
Left *MerkleNode
Right *MerkleNode
//hash值
Data []byte
}
//创建节点
func NewMerkleNode(left, right *MerkleNode, data []byte) *MerkleNode {
mNode := MerkleNode{}
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
}
//生成MerkleTree
func NewMerkleTree(data [][]byte) *MerkleTree {
var nodes []MerkleNode
if len(data)%2 != 0 {
data = append(data, data[len(data)-1])
}
for _, datum := range data {
node := NewMerkleNode(nil, nil, datum)
nodes = append(nodes, *node)
}
for i := 0; i < len(data)/2; i++ {
var newLevel []MerkleNode
for j := 0; j < len(nodes); j += 2 {
node := NewMerkleNode(&nodes[j], &nodes[j+1], nil)
newLevel = append(newLevel, *node)
}
nodes = newLevel
}
mTree := MerkleTree{&nodes[0]}
return &mTree
}
//获得block的bash值,实际就是取的根节点的hash值
func (b *Block) HashTransactions() []byte {
var transactions [][]byte
for _, tx := range b.Transactions {
transactions = append(transactions, tx.Serialize())
}
mTree := NewMerkleTree(transactions)
return mTree.RootNode.Data
}
SPV支付验证的实现步骤
(轻节点只知道自己的交易的hash和区块链的头部信息)
SPV进行简单支付验证时步骤如下:
- 计算待验证支付的交易哈希值;
- 轻节点会向全节点请求交易所在的区块和区块的MerkleTree;
- 根据哈希认证路径,计算默克尔树的根哈希值,将计算结果与本地区块头中的默克尔树的根哈希值进行比较;
- 验证通过后,再看一下自己的交易是否已经在最长的链上(即后面至少还有6个区块)。