一、默克尔树及默克尔根
(一)默克尔树
- Merkle Tree 顾名思义,就是储存hash值的地方
- 比特币交易系统中,区块的根本用途就是储存交易记录。而比特币系统中区块链其实就是一个交易流水账的账本,那么交易时如何上链的的呢?
- 旷工每10分钟产生的交易记录按照共识规则的要求排列(手续费高低、链龄),第一笔交易是挖矿所得的coinbase交易,该交易是由旷工创建的,交易内容是系统奖励给旷工的比特币。又叫做铸币交易。
- 并非10分钟之内产生的交易都会被打包。这取决于每笔交易的字节数。比特币系统中每个区块最大容量是1M,每笔交易平均是250字节,1M空间最多4200笔交易。
- 挖矿系统将每个区块能容纳下的所有交易信息打包,每笔交易信息都具有一个交易的hash值,两两一组进行哈希,最终计算出一个哈希根。如果出现奇数,那么将复制自身继续哈希 - 如果一个区块只有一个coinbase交易,coinbase的交易hash就是该区块的默克尔根的哈希
- 默克尔树中的节点分为:分支节点和叶子节点
(二)默克尔根
- 生成一个完整的Merkle树需要递归地对一组节点进行哈希,并将生成的哈希节点插入到Merkle树中,直到只剩一个哈希节点,该节点就是Merkle树的根。
- 默克尔根是挖矿算法中非常重要的参数。区块链的区块头必须包含区块中所有交易计算得到有效的默克尔根
- 默克尔根参与到PoW挖矿算法,生成当前区块的哈希,而每个区块中都记录着上一个区块的哈希,这样就构成了区块之间的收尾衔接,不可篡改。因为如果对区块中任意一笔交易发生改动,哪怕是及其微小的改动 ,那么该哈希后续的区块的哈希都会随之发生变化。所以说,默克尔根是保证区块中的交易不可篡改的重要手段
二、默克尔根生成算法
(一)只有唯一一次铸币交易
只有唯一一笔coinbase交易的hash就是默克尔根的hash值
(二)默克尔根的生成步骤
将每一个叶子节点下的分支节点进行两两hash,之后大小端颠倒然后拼接到一起,拼接好的数据进行哈希运算,之后在进行一次大小端颠倒
代码如下
package main
import (
"crypto/sha256"
"encoding/hex"
"fmt"
)
//当前区块下交易交易的hash
var txHashList = []string {
"16f0eb42cb4d9c2374b2cb1de4008162c06fdd8f1c18357f0c849eb423672f5f",
"cce2f95fc282b3f2bc956f61d6924f73d658a1fdbc71027dd40b06c15822e061",
}
func main () {
root := GeneraMerkleRoot(txHashList)
fmt.Println(root)
}
type MerkleNode struct {
LeftNode *MerkleNode
RightNode *MerkleNode
Data []byte //保存当前节点哈希
}
type MerkleTree struct {
Node *MerkleNode
}
func NewMerkleNode(left,right *MerkleNode,data []byte) *MerkleNode {
mNode := new(MerkleNode)
mNode.LeftNode = left
mNode.RightNode = right
//叶子节点
if left == nil && right == nil {
mNode.Data = data
} else {
// 对左右两侧分支节点的hash进行双哈希
hashValue := append(left.Data,right.Data...)
hashFirst := sha256.Sum256(hashValue)
hashDouble := sha256.Sum256(hashFirst[:])
mNode.Data = hashDouble[:]
}
return mNode
}
func NewMerkleTree(dataList [][]byte) *MerkleTree {
//包含整个树上的节点的容器
var nodes []MerkleNode
//生成所有的叶子节点
for _,data := range dataList{
node := NewMerkleNode(nil,nil,data)
nodes = append(nodes,*node)
}
j := 0
//生成分支节点
for nSize := len(dataList); nSize > 1 ; nSize = (nSize + 1) / 2 {
//进行两两分组
//i是左侧分支节点的索引。因为两个一组哈希 所以 i+=2
for i := 0 ;i < nSize ; i += 2 {
//ii是跟i配套,凑成一组右侧分支节点的索引
ii := min(i+1,nSize-1)
node := NewMerkleNode(&nodes[j+i],&nodes[j+ii],nil)
nodes = append(nodes,*node)
}
j += nSize
}
return &MerkleTree{&(nodes[len(nodes) - 1])}
}
func min(a,b int) int {
if a > b {
return b
}else {
return a
}
}
func GeneraMerkleRoot(txlist []string) string {
txSlice := [][]byte{}
for _,value := range txlist{
txSlice = append(txSlice,ReversHexStringToBytes(value))
}
//将二维数组作为参数,通过NewMerkleTree()函数进行两两哈希处理
hashedBytes := NewMerkleTree(txSlice).Node.Data
//大小端颠倒后转为字符串
return ReverseBytesToString(hashedBytes)
}
/**
字节数组大小端颠倒
*/
func ReverseBytes(data []byte) {
for i,j := 0 ,len(data) - 1 ; i < j ; i,j = i + 1,j - 1{
data[i],data[j] = data[j],data[i]
}
}
/**
将16进制字符串进行大小端颠倒
*/
func ReversHexStringToBytes(hexString string) []byte {
bytes,_ := hex.DecodeString(hexString)
ReverseBytes(bytes)
return bytes
}
/**
字节数组大端和小端进行颠倒,转成字符串
*/
func ReverseBytesToString(bytes []byte) string {
ReverseBytes(bytes)
return hex.EncodeToString(bytes)
}
当前案例以区块98901为例,计算出的默克尔根如下