主页 > imtoken安卓官方下载 > 比特币核心技术开发交易二(下)
比特币核心技术开发交易二(下)
继续我们之前的内容,接下来要说的就是Merkle树……
默克尔树
在这篇文章中比特币的核心技术是什么,我想讨论另一种优化机制。
如上所述,完整的比特币数据库(又名区块链)需要超过 140 Gb 的磁盘空间。 由于比特币的去中心化特性,网络中的每个节点都必须是独立和自给自足的,即每个节点都必须存储区块链的完整副本。 随着越来越多的人使用比特币,这条规则变得越来越难以遵循:不太可能每个人都会运行一个完整的节点。 而且,由于节点是网络的完全参与者,它们有相关的责任:节点必须验证交易和区块。 此外,为了与其他节点交互,下载新区块,也有一定的网络流量要求。
在中本聪最初的比特币论文中,也有解决这个问题的方法:Simplified Payment Verification (SPV)。 SPV是比特币轻节点,不需要下载整个区块链,也不需要验证区块和交易。 相反,它会在区块链上查找交易(以验证支付),并且需要连接到一个完整的节点以检索必要的数据。 这种机制允许多个轻钱包同时只运行一个全节点。
为了实现 SPV,需要有一种方法可以在不下载整个区块的情况下检查一个区块是否包含某个交易。 这就是 Merkle 树应该完成的任务。
比特币使用 Merkle 树来获取交易哈希,这些哈希存储在区块头中并用于工作量证明系统。 到目前为止,我们只是将每个交易的哈希值连接到一个块中,并对其应用 SHA-256 算法。 虽然这是获得块交易的唯一表示的好方法,但它没有利用 Merkle 树。
+
看一下 Merkle 树:
每个区块都有一棵从叶节点(树的底部)开始的 Merkle 树,其中叶节点是交易哈希(比特币使用双 SHA256 哈希)。 叶子节点的数量必须是偶数,但并不是每个区块包含的交易数量都是偶数。 因为,如果一个区块中的交易数是奇数,那么最后一个叶子节点(即默克尔树的最后一笔交易,而不是该区块的最后一笔交易)被复制成偶数。
从下到上,成对连接两个节点的哈希值,将合并后的哈希值作为新的哈希值。 新的散列成为新的树节点。 重复此过程,直到只有一个节点,即树的根节点。 然后根哈希用于唯一标识整个区块交易,存储在区块头中,然后用于工作量证明。
Merkle 树的好处是节点可以在不下载整个块的情况下验证交易的包含。 而这些只需要一个交易哈希、一个默克尔根哈希和一个默克尔路径。
最后我们来写代码:
类型 MerkleTree 结构 {
RootNode *MerkleNode}type MerkleNode struct {
左 *MerkleNode
右 *MerkleNode
数据[]字节}
让我们先从结构开始。 每个都包含数据和指向左右分支的指针。 它实际上连接到下一个节点的根节点,而下一个节点又连接到更多节点,依此类推。
让我们首先创建一个新节点:
func NewMerkleNode(left, right *MerkleNode, data []byte) *MerkleNode {
mNode := 默克尔节点{}
如果左 == 无 && 右 == 无 {
散列:= sha256.Sum256(数据)
mNode.Data = 散列[:]
} 别的 {
prevHashes := append(left.Data, right.Data...)
哈希 := sha256.Sum256(prevHashes)
mNode.Data = 散列[:]
}
mNode.Left = 左
mNode.Right = 右返回 &mNode}
每个节点包含一些数据。 当节点在叶子节点时,数据从外界传入(这里,也就是序列化的事务)。 当一个节点与其他节点相关联时,它会获取其他节点的数据,连接然后哈希。
func NewMerkleTree(data [][]byte) *MerkleTree {
var nodes []MerkleNode if len(data)%2 != 0 {
数据=追加(数据,数据[len(数据)-1])
}
对于_,数据:=范围数据{
节点 := NewMerkleNode(nil, nil, datum)
节点=追加(节点,*节点)
}
因为我:= 0; 一世
var newLevel[]MerkleNodefor j := 0; j
节点 := NewMerkleNode(&nodes[j], &nodes[j+1], nil)
newLevel = append(newLevel, *节点)
}
节点 = 新级别 }
mTree := MerkleTree{&nodes[0]}
返回 &mTree}
在生成一棵新树时,首先要保证叶子节点必须是double。 然后将数据(即一系列序列化交易)转换为树叶,从中慢慢形成一棵树。
btcsuite/btcd都是用数组实现的merkle树,因为这样做可以减少一半的内存占用。
现在,让我们修改它,它用于在工作量证明系统中获取交易哈希:
func (b *Block) HashTransactions() []byte {
变种交易[][]字节
对于 _, tx := range b.Transactions {
交易=追加(交易,tx.Serialize())
}
mTree := NewMerkleTree(交易)
返回 mTree。 根节点。 数据}
首先,事务被序列化(消费),然后使用序列化的事务构建 Mekle 树。 树的根将作为区块交易的唯一标识符。
P2PKH
我还想再谈一件事。
你应该记得,比特币中有一种脚本编程语言,用于锁定交易输出; 交易输入提供解锁输出的数据。 这种语言很简单,用这种语言写的代码其实就是一系列的数据和运算符。 例如下面的例子:
5 2 OP_ADD 7 OP_EQUAL
5、2、7是数据,是运算符。 脚本代码从左到右执行:将数据一个一个入栈,遇到运算符,从栈中取出数据,对数据应用运算符,然后将结果作为栈顶元素堆栈。 脚本栈实际上是一个先进后出的内存存储:栈中的第一个元素最后取出,后面的每个元素都会放在前一个元素之上。
让我们执行上面的部分脚本:
从栈中取出两个元素,将它们相加,然后将结果放回栈中。 从栈中取出两个元素,比较两个元素:相等则放一个入栈,否则放一个。 脚本执行的结果是堆栈的顶部元素:在我们的例子中,如果是,则脚本成功执行。
现在让我们看看如何在比特币中使用脚本执行支付:
OP_DUP OP_HASH160
OP_EQUALVERIFY OP_CHECKSIG
这个脚本叫做 Pay to Public Key Hash (P2PKH),这是比特币最常用的脚本。 它所做的只是支付给一个公钥哈希,即用某个公钥锁定一些币。 这就是比特币支付的核心:没有账户,没有资金划转; 只是一个脚本,用于检查提供的签名和公钥是否正确。
这个脚本实际上分为两部分存储:
第一部分,存储在输入字段中。
第二部分,存储在输出里面。
因此,输出定义了解锁的逻辑,输入提供了解锁输出的“钥匙”。 让我们执行这个脚本:
复制栈顶元素。 取栈顶元素,用 散列,然后将结果放回栈中。 比较栈顶的两个元素,如果不相等则终止脚本。 交易的签名通过散列交易并使用总和来验证。 final 运算符有点复杂:它制作交易的修剪副本,对其进行哈希处理(因为它是已签名的交易哈希),并使用提供的金额检查签名是否正确。
使用这样的脚本语言,实际上也可以使比特币成为智能合约平台:除了使用单个公钥转移资金外,该语言还支持许多其他支付方案。
总结
今天就到这里! 我们已经实现了基于区块链的加密货币的几乎所有关键功能。 我们已经有了区块链、地址、挖矿和交易。 但要赋予所有这些机制以生命,让比特币成为一个全球性的系统,还有一个不可或缺的环节:共识。 在下一篇文章中比特币的核心技术是什么,我们将开始“去中心化”区块链。 收听!