简介

默克尔树(MerkleTree)是一种典型的二叉树结构,其主要特点为:

  1. 最下面的叶节点包含存储数据或其哈希值;
  2. 非叶子节点(包括中间节点和根节点)的内容为它的两个孩子节点内容的哈希值。

所以底层数据的任何变动,都会传递到其父节点,一层层沿着路径一直到树根。这意味树根的值实际上代表了对底层所有数据的“数字摘要”。

代码实现

package main

import (
"bytes"
"crypto/sha256"
"encoding/hex"
"fmt"
) // MerkleTree 节点
type MerkleNode struct {
Left *MerkleNode
Right *MerkleNode
Data []byte
} // MerkleTree 根
type MerkleTree struct {
RootNode *MerkleNode
} // 创建Merkle节点
func NewMerkleNode(left, right *MerkleNode, data []byte) *MerkleNode {
node := MerkleNode{}
// 创建存储明文信息的叶子节点
if left == nil && right == nil {
node.Data = data
// 创建只有一个分支的MerkleNode
} else if left != nil && right == nil {
hash := sha256.Sum256(left.Data)
node.Data = hash[:]
// 创建有两个分支的MerkleNode
} else {
// slice = append(slice, anotherSlice...) 两个slice拼接在一起时要加...
hash := sha256.Sum256(append(left.Data, right.Data...))
node.Data = hash[:]
}
node.Left = left
node.Right = right return &node
} func NewMerkleTree(data [][]byte) *MerkleTree {
var nodes []MerkleNode // 将所有数据构建为dataNode节点,接入node节点的左分支,并将node节存到nodes数组中
for _, datum := range data {
dataNode := NewMerkleNode(nil, nil, datum)
node := NewMerkleNode(dataNode, nil, nil)
nodes = append(nodes, *node)
} for {
var newLevel []MerkleNode // 根据当前层的节点,构造上一层
// 当前层节点为奇数时
if len(nodes)%2 == 1 {
for j := 0; j < len(nodes)-1; j += 2 {
node := NewMerkleNode(&nodes[j], &nodes[j+1], nil)
newLevel = append(newLevel, *node)
}
node := NewMerkleNode(&nodes[len(nodes)-1], nil, nil)
newLevel = append(newLevel, *node)
// 当前层节点为偶数时
} else {
for j := 0; j < len(nodes); j += 2 {
node := NewMerkleNode(&nodes[j], &nodes[j+1], nil)
newLevel = append(newLevel, *node)
}
} // 更新层节点
nodes = newLevel
if len(nodes) == 1 {
break
}
}
mTree := MerkleTree{&nodes[0]}
return &mTree
} // 先序遍历输出所有节点
func showMeerkleTree(root *MerkleNode) {
if root == nil {
return
} else {
PrintNode(root)
} showMeerkleTree(root.Left)
showMeerkleTree(root.Right)
} func PrintNode(node *MerkleNode) {
fmt.Printf("%p\n", node)
// 输出存储信息明文节点
if node.Left != nil || node.Right != nil {
fmt.Printf("left[%p], right[%p], data(%v)\n", node.Left, node.Right, hex.EncodeToString(node.Data))
// 输出存储哈希值的节点
} else if node.Left == nil || node.Right == nil {
fmt.Printf("left[%p], right[%p], data(%v)\n", node.Left, node.Right, string(node.Data))
}
} // 检查是否满足MerkleTree的条件
func check(node *MerkleNode) bool {
var hashByte32 [32]byte
if node.Left == nil && node.Right == nil {
return true
} else if node.Left != nil && node.Right == nil {
hashByte32 = sha256.Sum256(node.Left.Data)
} else {
hashByte32 = sha256.Sum256(append(node.Left.Data, node.Right.Data...))
}
hash := hashByte32[:]
result := bytes.Equal(hash, node.Data)
fmt.Printf("Is this a MerkleTree? : %v", result)
return result
} func main() {
data := [][]byte{[]byte("node1"), []byte("node2"), []byte("node3"), []byte("node4"),
[]byte("node5")} tree := NewMerkleTree(data)
showMeerkleTree(tree.RootNode)
check(tree.RootNode)
}

输出内容为

0xc0001077a0
left[0xc0001360a0], right[0xc0001360c8], data(e84479b93fed1c8d912b865a7508f42e1e3dc777649cc80ccf59733f1982f40d)
0xc0001360a0
left[0xc0001380a0], right[0xc0001380c8], data(b698d822f9dbf3099c0aa30ba8120f48f3c92753be4eedb3f8cc99eb934cc3fb)
0xc0001380a0
left[0xc00013a000], right[0xc00013a028], data(64b04b718d8b7c5b6fd17f7ec221945c034cfce3be4118da33244966150c4bd4)
0xc00013a000
left[0xc000107410], right[0x0], data(ca12f31b8cbf5f29e268ea64c20a37f3d50b539d891db0c3ebc7c0f66b1fb98a)
0xc000107410
left[0x0], right[0x0], data(node1)
0xc00013a028
left[0xc0001074a0], right[0x0], data(15b18a7243257695704f66a3b1ddc9311194fc7d2e1896f440cc517c777ab7ec)
0xc0001074a0
left[0x0], right[0x0], data(node2)
0xc0001380c8
left[0xc00013a050], right[0xc00013a078], data(30304ac1e6721c1f197ff47b1682794872701e823bc962b79682ce66d3283783)
0xc00013a050
left[0xc000107500], right[0x0], data(3b5bb1c6e7b76daba8afd89516e24140a67fc6be2ba071cc3b97d1b2e08c238d)
0xc000107500
left[0x0], right[0x0], data(node3)
0xc00013a078
left[0xc000107560], right[0x0], data(d2b8f62a7e335bbd5576c8422844760f22ec378009eeea790c41e4dc45f23c33)
0xc000107560
left[0x0], right[0x0], data(node4)
0xc0001360c8
left[0xc0001380f0], right[0x0], data(2a6e2bd6658e6ce0f9f0abb6bc668a997c14e8f5465fcd4cd19678ae0e4dd087)
0xc0001380f0
left[0xc00013a0a0], right[0x0], data(31e81bb6828abbf114edb182849cc89deac585d320f5a1a33d054ca047616a5c)
0xc00013a0a0
left[0xc0001075c0], right[0x0], data(23af406016994347aaa3e894e9a820049ace3656406fb06e7636b692db56026f)
0xc0001075c0
left[0x0], right[0x0], data(node5)
Is this a MerkleTree? : true

【工程实践】go语言实现MerkleTree的更多相关文章

  1. panguan(判官):一个自研的任务执行引擎的工程实践

    来某厂接近半年了,几乎没写过C++代码,说实话还真的有点手生.最近刚好有一个需求,然而我感觉我也没有办法用C++以外的语言去实现它.于是还是花了几天时间用C++完成编码,这是一个简单的任务执行引擎,它 ...

  2. 工程实践:给函数取一个"好"的名字

    工程实践:给函数取一个"好"的名字 早在2013年,国外有个程序员做了一个有意思的投票统计(原始链接请见:<程序员:你认为最难做的事情是什么?>),该投票是让程序员从以 ...

  3. webpack 从入门到工程实践

    from:https://www.jianshu.com/p/9349c30a6b3e?utm_campaign=maleskine&utm_content=note&utm_medi ...

  4. Go 在游戏行业中的工程实践

    在今年 1 月由七牛云主办的 ECUG Con 十周年盛会上,真有趣技术总监陈明达带来了题为< Go 在游戏行业中的工程实践>的精彩分享,深入讲解了 Go 的工程经验,错误和异常处理,in ...

  5. 针对工程实践项目的用例建模Use Case Modeling

    一.什么是用例建模(Use Case Modeling) 1.用例(Use Case) (1)概念:用例是软件工程或系统工程中对系统如何反应外界请求的描述,是一种通过用户的使用场景来获取需求的技术. ...

  6. 基于menu小插件探索工程实践

    目录 一.准备工作 1.C/C++环境搭建 2.VSCode的配置 (1) 安装插件: (2) 设置配置文件: 二.工程化编程实战 1.模块化设计 2.可重用设计:进一步抽象 menu的进一步优化 可 ...

  7. 深入理解 ProtoBuf 原理与工程实践(概述)

    ProtoBuf 作为一种跨平台.语言无关.可扩展的序列化结构数据的方法,已广泛应用于网络数据交换及存储.随着互联网的发展,系统的异构性会愈发突出,跨语言的需求会愈加明显,同时 gRPC 也大有取代R ...

  8. 公司简介 - CCDI悉地国际-工程实践专业服务的引领者

    公司简介 - CCDI悉地国际-工程实践专业服务的引领者 关于悉地国际         CCDI悉地国际(以下简称"CCDI")创立于1994年,是在城市建设和开发领域从事综合专业 ...

  9. Linux开源模块迁移概述暨交叉编译跨平台移植总结--从《嵌入式Linux驱动模板简洁和工程实践》

    本文摘录<嵌入式Linux驱动模板简洁和工程实践>一本书"开发和调试技术". Linux强大的是,有那么多的开源项目可以使用.通常非常需要可以通过寻找相关的源模块被定义 ...

  10. LDA工程实践之算法篇之(一)算法实现正确性验证(转)

    研究生二年级实习(2010年5月)开始,一直跟着王益(yiwang)和靳志辉(rickjin)学习LDA,包括对算法的理解.并行化和应用等等.毕业后进入了腾讯公司,也一直在从事相关工作,后边还在yiw ...

随机推荐

  1. 正则表达式环视匹配(?=pattern)、(?!pattern)、(?<=pattern)、(?<!pattern)怎么用

    今天在处理数据的时候遇到一个,需要用正则表达式匹配不包含某字符的字符串的问题,用到否定匹配,现总结如下: 一个正则小知识 ↓ []:表示范围,匹配其中任何一个 {}:表示重复匹配多次. ():表示分组 ...

  2. (1,3,4,2,5)小和问题【Java】

    01[1,3,4,2,5]求小和 从右往左看:左边比右边小的加和到一起! 1 左边没有数 0 3 1 4 1+3 2 1 5 1+3+4+2 从左往右看:有几个数右边比左边大 1 4个数: 3 4 2 ...

  3. #BFS,二进制#CF1776J Italian Data Centers

    洛谷题面 CF1776J 分析 将原图的点所拆开的点按二进制编号,那么同一个点之间连边当且仅当恰好一个二进制位不同, 不同点之间连边颜色相同则其二进制相同,否则完全相反. 可以钦定起点就是 \((x, ...

  4. #根号分治,树形dp#CF1039D You Are Given a Tree

    题目 给定一棵树,对于 \(k\in [1,n]\) 问最多可以分成多少段长度为 \(k\) 的不交路径 分析 首先考虑对于单个 \(k\) 怎么做. 设 \(dp[x]\) 表示点 \(x\) 往下 ...

  5. Go 编程语言详解:用途、特性、与 Python 和 C++ 的比较

    什么是Go? Go是一个跨平台.开源的编程语言 Go可用于创建高性能应用程序 Go是一种快速.静态类型.编译型语言,感觉上像动态类型.解释型语言 Go由Robert Griesemer.Rob Pik ...

  6. MogDB/openGauss 3.0 扩容及缩容

    MogDB/openGauss 3.0 扩容及缩容 本文出处:https://www.modb.pro/db/452139 一.概述 背景信息 gs_expansion 工具对数据库的备机进行扩容,支 ...

  7. 直播预告丨Hello HarmonyOS进阶系列课程重磅来袭,4月27日开播

    为了帮助初识HarmonyOS的开发者快速入门,我们曾推出Hello HarmonyOS系列一共5期课程(传送门:https://developer.huawei.com/consumer/cn/tr ...

  8. Linux之网络排错

    Linux 网卡收包流程如下 网卡收到数据包 将数据包从网卡硬件缓存移动到服务器内存中(DMA方式,不经过CPU) 通过硬中断通知CPU处理 CPU通过软中断通知内核处理 经过TCP/IP协议栈处理 ...

  9. Cloud-computing 实验镜像 chinaskills_cloud_iaas.iso chinaskills_cloud_paas.iso

    Cloud-computing 实验镜像 最近因新项目再次进行云计算环境的搭建, 找这两个镜像( 找chinaskills_cloud_paas.iso chinaskills_cloud_iaas. ...

  10. 【6】Spring JavaConfig和常见Annotation

    Java 5 的推出,加上当年基于纯 Java Annotation 的依赖注入框架 Guice 的出现,使得 Spring 框架及其社区也"顺应民意",推出并持续完善了基于 Ja ...