Golang实现默克尔树merkle tree

什么是默克尔树??

默克尔树是一种哈希二叉树,1979年由RalphMerkle发明。哈希树可以用来验证任何一种在计算机中和计算机之间存储、处理和传输的数据。它们可以确保在点对点网络中数据传输的速度不受影响,数据跨越自由的通过任意媒介,且没有损坏,也没有改变。

简单来说,哈希树(默克尔树)中,每个节点都标有一个数据块的加密哈希值。

Merkle树结构

  • 一个根节点(root)
  • 一组中间节点
  • 一组叶节点(leaf)组成

叶节点(leaf)包含存储数据或其哈希值,中间节点是它的两个子节点内容的哈希值,根节点也是由它的两个子节点内容的哈希值组成。所以Merkle树也称哈希树。

默克尔树

哈希树的特点

任意底层数据的任何变动,都会传递到其父节点,一直到树根。因此只需要判断Hash Root是否一致,就可以判断整个数据书否一致。

因此,在区块链系统中,区块头只需要封装默克尔根(也就是Hash Root),通过对默克尔根的比对,从而验证区块交易数据是否一致。 极大的提升了数据校验的效率。

Golang实现默克尔树

完整代码已发布至 https://github.com/wk331100/MerkleTree,可使用go get github.com/wk331100/MerkleTree 获取代码库。

package merkleTree

import (
	"bytes"
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"errors"
	"hash"
	"math"
)

const (
	errEmptyData = "empty data"
)

// MerkleTree 默克尔树
type MerkleTree struct {
	Root        *Node
	Leaves      []*Node
	hashHandler func() hash.Hash
}

// Node 节点
type Node struct {
	Parent *Node
	Left   *Node
	Right  *Node
	leaf   bool
	single bool
	Hash   []byte
	Data   []byte
}

// GetRootHash 获取默克尔树根Hash
func (m *MerkleTree) GetRootHash() []byte {
	return m.Root.Hash
}

// NewMerkleTree 创建一个新的 默克尔树
func NewMerkleTree(hashType string, data [][]byte) (*MerkleTree, error) {
	mk := &MerkleTree{}
	mk.hashHandler = mk.buildHash(hashType)

	root, leaves, err := mk.buildMerkleTreeRoot(data)
	if err != nil {
		return nil, err
	}
	mk.Root = root
	mk.Leaves = leaves
	return mk, nil
}

// VerifyData 验证数据是否在默克尔树中
func (m *MerkleTree) VerifyData(data []byte) (bool, error) {
	dataHash := m.calHash(data)
	for _, leaf := range m.Leaves {
		if bytes.Compare(dataHash, leaf.Hash) == 0 {
			return true, nil
		}
	}
	return false, nil
}

// VerifyTree 验证默克尔树Hash
func (m *MerkleTree) VerifyTree() (bool, error) {
	calRoot, err := m.Root.verifyNode(m)
	if err != nil {
		return false, err
	}

	if bytes.Compare(calRoot, m.Root.Hash) == 0 {
		return true, nil
	}
	return false, err
}

// verifyNode 重新计算节点Hash
func (n *Node) verifyNode(mk *MerkleTree) ([]byte, error) {
	if n.leaf {
		return mk.calHash(n.Data), nil
	}
	leftNodeHash, err := n.Left.verifyNode(mk)
	if err != nil {
		return nil, err
	}
	rightNodeHash, err := n.Right.verifyNode(mk)
	if err != nil {
		return nil, err
	}

	return mk.calHash(append(leftNodeHash, rightNodeHash...)), nil
}

// buildMerkleTree 构建 默克尔树 节点
func (m *MerkleTree) buildMerkleTreeRoot(data [][]byte) (*Node, []*Node, error) {
	if len(data) <= 0 {
		return nil, nil, errors.New(errEmptyData)
	}
	leaf := m.buildMerkleTreeLeaf(data)
	root, err := m.buildMerkleTreeNode(leaf)
	return root, leaf, err
}

// buildMerkleTreeNode 构建 默克尔树 中间节点
func (m *MerkleTree) buildMerkleTreeNode(nodes []*Node) (*Node, error) {
	length := int(math.Ceil(float64(len(nodes)) / 2))

	var nodeSlice []*Node
	var single bool
	for i := 0; i < length; i++ {
		leftNode := nodes[i*2]
		var rightNode = new(Node)
		if i*2+1 < len(nodes) {
			rightNode = nodes[i*2+1]
		} else {
			single = true
		}

		node := &Node{
			Parent: nil,
			Left:   leftNode,
			Right:  rightNode,
			leaf:   false,
			single: single,
			Hash:   nil,
			Data:   nil,
		}
		// 将两个子节点Hash拼接后,计算自身Hash
		if !single {
			leftNode.Hash = append(leftNode.Hash, rightNode.Hash...)
		}

		node.Hash = m.calHash(leftNode.Hash)

		// 将当前节点设置为 两个子节点的父节点
		nodes[i*2].Parent = node
		nodes[i*2+1].Parent = node

		nodeSlice = append(nodeSlice, node)
	}

	if len(nodeSlice) > 1 {
		return m.buildMerkleTreeNode(nodeSlice)
	}
	return nodeSlice[0], nil
}

// buildMerkleTreeLeaf 构建 默克尔树 叶节点
func (m *MerkleTree) buildMerkleTreeLeaf(data [][]byte) []*Node {
	var leaf []*Node
	for _, item := range data {
		node := &Node{
			Parent: nil,
			Left:   nil,
			Right:  nil,
			leaf:   true,
			single: false,
			Hash:   m.calHash(item),
			Data:   item,
		}
		leaf = append(leaf, node)
	}

	return leaf
}

// buildHash 根据Hash类型,构建Hash
func (m *MerkleTree) buildHash(hashType string) func() hash.Hash {
	switch hashType {
	case "md5":
		return md5.New
	case "sha1":
		return sha1.New
	case "sha256":
		return sha256.New
	case "sha512":
		return sha512.New
	default:
		return sha1.New
	}
}

func (m *MerkleTree) calHash(data []byte) []byte {
	hashHandler := m.hashHandler()
	hashHandler.Write(data)
	return hashHandler.Sum(nil)
}

单测及结果

package merkleTree

import (
	"encoding/json"
	"fmt"
	"testing"

	"github.com/stretchr/testify/require"
)

var (
	testData []byte
	testHash []byte
)

func TestMerkleTree(t *testing.T) {
	data := initData()
	mk, err := NewMerkleTree("md5", data)
	require.Nil(t, err)
	fmt.Sprintf("%x", mk.GetRootHash())
	printHash(mk)

	res, _ := mk.VerifyData(testData)
	require.True(t, res)

	res2, _ := mk.VerifyTree()
	require.True(t, res2)
}

func printHash(mk *MerkleTree) {
	if mk.Root.leaf {

		return
	}
	cyclePrint(mk.Root)
}

func cyclePrint(node *Node) {
	if node.leaf {
		fmt.Println(fmt.Sprintf("Leaf: hash[%x], data[%s], leaf[%v]\n", node.Hash, node.Data, node.leaf))
	} else {
		fmt.Println(fmt.Sprintf("Node: hash[%x], data[%s], leaf[%v]\n", node.Hash, node.Data, node.leaf))
	}

	if node.Left != nil {
		cyclePrint(node.Left)
	}
	if node.Right != nil {
		cyclePrint(node.Right)
	}
}

func TestBuildMerkleTreeLeaf(t *testing.T) {
	m := &MerkleTree{}
	data := initData()
	m.hashHandler = m.buildHash("sha256")
	leaf := m.buildMerkleTreeLeaf(data)
	fmt.Println(leaf)
}

func initData() [][]byte {
	var data [][]byte

	for i := 0; i < 4; i++ {
		str := fmt.Sprintf("test data %d", i)
		bz, _ := json.Marshal(str)
		if i == 3 {
			testData = bz
		}
		data = append(data, bz)
	}
	return data
}

结果

=== RUN   TestMerkleTree
Node: hash[3c95e6ec5965e5c1b797709a8e649c14], data[], leaf[false]

Node: hash[e1c340b19bee346c95fe9ef64ad992e09820b47149a1893754e7557b0d7fdb13], data[], leaf[false]

Leaf: hash[56f0aade6664b4bf7ef30ab6de77ecd1481cf84fb2f19c94e04293027b39ad90], data["test data 0"], leaf[true]

Leaf: hash[481cf84fb2f19c94e04293027b39ad90], data["test data 1"], leaf[true]

Node: hash[9820b47149a1893754e7557b0d7fdb13], data[], leaf[false]

Leaf: hash[3381b0b53c7f1e8bb44d469dec35051565997690d1ade051def10b273ae49fe5], data["test data 2"], leaf[true]

Leaf: hash[65997690d1ade051def10b273ae49fe5], data["test data 3"], leaf[true]

--- PASS: TestMerkleTree (0.00s)
PASS

原文地址:https://cloud.tencent.com/developer/article/2137259

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


类型转换 1、int转string 2、string转int 3、string转float 4、用户结构类型转换
package main import s &quot;strings&quot; import &quot;fmt&quot; var p = fmt.Println func main() { p(&quot;Contains: &quot;, s.Contains(&quot;test&quo
类使用:实现一个people中有一个sayhi的方法调用功能,代码如下: 接口使用:实现上面功能,代码如下:
html代码: beego代码:
1、读取文件信息: 2、读取文件夹下的所有文件: 3、写入文件信息 4、删除文件,成功返回true,失败返回false
配置环境:Windows7+推荐IDE:LiteIDEGO下载地址:http://www.golangtc.com/downloadBeego开发文档地址:http://beego.me/docs/intro/ 安装步骤: 一、GO环境安装 二、配置系统变量 三、Beego安装 一、GO环境安装 根
golang获取程序运行路径:
Golang的文档和社区资源:为什么它可以帮助开发人员快速上手?
Golang:AI 开发者的实用工具
Golang的标准库:为什么它可以大幅度提高开发效率?
Golang的部署和运维:如何将应用程序部署到生产环境中?
高性能AI开发:Golang的优势所在
本篇文章和大家了解一下go语言开发优雅得关闭协程的方法。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。1.简介本文将介绍首先为什么需要主...
这篇文章主要介绍了Go关闭goroutine协程的方法,具有一定借鉴价值,需要的朋友可以参考下。下面就和我一起来看看吧。1.简介本文将介绍首先为什么需要主动关闭gor...
本篇文章和大家了解一下go关闭GracefulShutdown服务的几种方法。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。目录Shutdown方法Regi...
这篇文章主要介绍了Go语言如何实现LRU算法的核心思想和实现过程,具有一定借鉴价值,需要的朋友可以参考下。下面就和我一起来看看吧。GO实现Redis的LRU例子常
今天小编给大家分享的是Go简单实现多租户数据库隔离的方法,相信很多人都不太了解,为了让大家更加了解,所以给大家总结了以下内容,一起往下看吧。一定会...
这篇“Linux系统中怎么安装NSQ的Go语言客户端”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希
本文小编为大家详细介绍“怎么在Go语言中实现锁机制”,内容详细,步骤清晰,细节处理妥当,希望这篇“怎么在Go语言中实现锁机制”文章能帮助大家解决疑惑,下面...
今天小编给大家分享一下Go语言中interface类型怎么使用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考