《区块链宝典》比特币默克尔根生成算法及原理

一、默克尔树及默克尔根

(一)默克尔树
  • 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为例,计算出的默克尔根如下
在这里插入图片描述
在这里插入图片描述

发布了27 篇原创文章 · 获赞 105 · 访问量 5333

猜你喜欢

转载自blog.csdn.net/qq_45828877/article/details/104547787
今日推荐