默克尔树概述

在ABCI应用响应Commit请求消息时,需要计算并返回当前状态的哈希,以便Tendermint 将其打包到下一个区块头里(app_hash字段)。

但是,如果我们还按原来的方法计算一个总体哈希,例如:hash: = tmhash.Sum(state), 就存在一个问题 —— 当查询某个特定账户的状态数据时,如何验证该状态是未被篡改的?

显然,单独返回整个状态的哈希是不够的,但是我们也不可能将整个账户表提供给 客户端以便其重算哈希进行验证,因为其中包含了其他用户的账户信息。

这就是默克尔树(Merkle Tree)的用武之地:不需要提供完整的数据(例如整个账户表), 就可以验证某个数据(例如账户A的状态)是否属于该数据集。

在默克尔树中,叶节点对应于各状态的哈希值,根节点则对应于整个状态的哈希, 中间各层的节点则分别由前一层节点两两结对后计算哈希得到。

例如,下图给出了4个账户状态时的默克尔树的构成:

基于默克尔树的生成过程,我们只需要这个树的一部分节点,就可以验证某个状态(例如账户A 的状态)与整体哈希(Hash(ABCD))的对应关系,这部分节点就被称为默克尔证据/Merkle Proot。 例如上图中,对于账户A的状态而言,Hash(B)和Hash(CD)就是其证据 —— 因为利用账户A本身的数据, 以及证据节点,就可以重算出根节点,从而确认指定账户状态与给定状态哈希的对应关系。

计算默克尔哈希

在tendermint中内置了默克尔树的一个简单实现,可以计算有序集合(切片)和无序集合(映射表) 的默克尔树根哈希:

Hasher接口

使用crypto/merkle包计算数据集合的默克尔哈希,要求集合中的成员实现Hasher接口。例如, 我们使用下面的结构来满足这一需求:

type sh struct{value string
}
func (h sh) Hash() []byte {return tmhash.Sum([]byte(h.value))
}

现在,我们可以根据集合是有序还是无序,来分别计算其默克尔哈希了。

有序集合的哈希计算

有序集合(例如:切片)中各成员有确定性的先后顺序,因此可以直接使用SimpleHashFromHashers() 方法进行计算。例如:

data := []merkle.Hasher{ &sh{"one"},&sh{"two"},&sh{"three"},&sh{"four"} }
hash := merkle.SimpleHashFromHashers(data)
fmt.Printf("root hash => %x\n",hash)

无序集合的哈希计算

无序集合(例如:映射表)的各成员没有确定性的先后顺序,因此需要首先进行确定排序,重组为有序 集合后才能使用SimpleHashFromHashers()方法计算该集合的默克尔哈希。对于键类型为string、值类型 为Hasher的映射表而言,可以直接使用SimpleHashFromMap()方法。例如:

data := map[string]merkle.Hasher{"tom":&sh{"actor"},"mary":&sh{"teacher"},"linda":&sh{"scientist"},"luke":&sh{"fisher"}}
hash := merkle.SimpleHashFromMap(data)
fmt.Printf("root hash => %x\n",hash)

状态的默克尔证据

在使用默克尔树时,如果需要验证某个数据是否属于一个特定的集合,除了待验证的数据自身, 还需要以下数据:

数据集合的根哈希:表征特定的数据集合
数据的默克尔证据:配合待验证数据重算根哈希,以便于给定的根哈希比较
tendermint的crypto/merkle包提供了简单的方法返回集合中每个成员对应的默克尔 证据以及集合的根哈希:

数据聚合中每个成员对应的默克尔证据就是一个SimpleProof实例,因此可以直接调用 其Verify()方法进行验证。

同样,获取数据成员的默克尔证据也分为有序集合与无序集合两种情况。

有序集合成员的默克尔证据

使用SimpleProofsFromHashers()方法获取有序集合(例如:切片)中各成员的默克尔证据, 成员必须实现Hasher接口,该方法返回两个值:根哈希以及默克尔证据数组。

还是利用前一节的sh类型,下面的代码展示了如何获取切片中各成员的默克尔证据:

data := []sh{sh("one"),sh("two"),sh("three"),sh("four")}
root,proofs := merkle.SimpleProofsFromHashers(data)
fmt.Printf("proof for one => %+v\n",proofs[0])

在返回结果中的默克尔证据数组,其成员顺序与输入数据一致。

一旦获取了某个数据的默克尔证据、结合数据集合的根哈希,就可以验证这个数据是否属于 给定的根哈希对应的数据集合了。例如:

valid := proofs[0].Verify(0,              // 要验证的数据在集合中的序号4,              // 集合成员总数data[0].Hash(), // 要验证的数据的哈希root           // 集合的根哈希)
fmt.Printf("data[0] is valid? => %t\n",valid)

无序集合成员的默克尔证据

同样,没有确定性成员顺序的映射表,需要使用SimpleProofsFromMap()方法计算每个 成员的默克尔证据,其返回结果是三个值:根哈希、成员默克尔证据映射表和排序后的 成员键。例如:

data := map[string]sh{"tom":sh("actor"),"mary":sh("teacher"),"linda":sh("scientist"),"luke":sh("fisher")}
root,proofs,keys := merkle.SimpleProofsFromMap(data)
fmt.Printf("proof for tom => %+v\n",proofs["tom"])

由于在计算映射表的默克尔证据时首先将无序的键值对转化为了KVPair结构并 进行排序,因此其成员时,也需要首先将其转换为KVPair类型,而不能仅使用键值对 中的值部分:

kvpair := merkle.KVPair{Key:[]byte("tom"),Value: data["tom"].Hash()}
然后根据该成员在排序后的顺序号(keys中的位置),进行验证,例如:

valid := proofs[key].Verify(3,            // 要验证的数据键在keys中的序号4,              // 集合成员总数kvpair.Hash(), // 要验证的数据的哈希root           // 集合的根哈希)
fmt.Printf("data["tom"] is valid? => %t\n",valid)

升级代币状态机

基于默克尔树,我们可以升级代币状态机,在hash.go代码中实现与默克尔计算相关 的三个方法:

首先扩展int类型,实现Hasher接口:

type Balance intfunc (b Balance) Hasher() []byte {v,_ := codec.MarshalBinary(b)return tmhash.Sum(v)
}

stateToHasherMap()方法将应用的状态集合转换为Hasher映射表,以便进行默克尔计算:

func (app *TokenApp) stateToHasherMap() map[string]merkle.Hasher {hashers := map[string]merkle.Hasher{}for addr,val := range app.Accounts { balance := Balance(val)hashers[addr] = &balance}return hashers
}

getRootHash()方法计算应用状态的默克尔哈希:

func (app *TokenApp) getRootHash() []byte {hashers := app.stateToHasherMap()return merkle.SimpleHashFromMap(hashers)
}

getProofBytes()方法获取指定地址状态的默克尔证据:

func (app *TokenApp) getProofBytes(addr string) []byte {hashers := app.stateToHasherMap()_,proofs,_ := merkle.SimpleProofsFromMap(hashers)bz,err := codec.MarshalBinary(proofs[addr])if err != nil  { return  []byte{} }return bz
}

当提交状态修改时,我们可以利用这些方法向tendermint返回根哈希:

func (app *TokenApp) Commit() (rsp types.ResponseCommit){rsp.Data = app.getRootHash()return
}

当查询状态时,也可以利用这些方法返回状态的证据:

func (app *TokenApp) Query(req types.RequestQuery) (rsp types.ResponseQuery) {addr := crypto.Address(req.Data)rsp.Key = req.Datarsp.Value,_ = codec.MarshalBinary(app.Accounts[addr.String()])rsp.Proof = app.getProofBytes(addr.String())return
}

6.tendermint默克尔树相关推荐

  1. 默克尔树_默克尔树:正在使用中

    默克尔树 Ralph C. Merkle (not pictured above), born 1952, is one of the founding fathers of Public Key C ...

  2. tendermint 六:默克尔树

    默克尔树概述 在ABCI应用响应Commit请求消息时,需要计算并返回当前状态的哈希,以便Tendermint 将其打包到下一个区块头里(app_hash字段). 但是,如果我们还按原来的方法计算一个 ...

  3. 区块链基础知识系列 第三课 区块链中的默克尔树

    "区块链是实现无中心分布式总账的一种技术.除了采用块.链结构的典型区块链以外,还有其他的方式实现分布式总账这个需求.总账技术的基本单元是'交易',整个账本是由一条条的交易构成.'块'类似于账 ...

  4. 数据结构与算法 / 默克尔树

    最近在学习 git 原理时,涉及到了默克尔树,这里总结下该数据结构. 默克尔树于 1979 年由美国计算机科学家拉尔夫·默克尔(Ralph Merkle)提出,本质上是一种树状数据结构,由数据块.叶子 ...

  5. 常用的数据结构_三分钟了解区块链常用数据结构「默克尔树」

    免责声明:本文旨在传递更多市场信息,不构成任何投资建议.文章仅代表作者观点,不代表火星财经官方立场. 小编:记得关注哦 来源:万向区块链 原文标题:三分钟了解区块链常用数据结构「默克尔树」 默克尔树是 ...

  6. Merkle Tree(默克尔树)原理解析

    Merkle Tree(默克尔树)原理解析 一.Merkle Tree 1.1 Merkle Tree的特点 二.Hash list 三.Merkle tree VS Hash list 四.Merk ...

  7. 科普 | 什么是稀疏默克尔树多值证明

    译者注:以太坊网络是一台富状态(stateful)的世界计算机,其状态包括状态余额.交易流水号(nonce).合约代码及合约存储内容等.在技术上,这些状态数据是靠一种叫做 "默克尔树&quo ...

  8. 【区块链 | 默克尔树】使用默克尔(Merkle)树实现NFT白名单

    简介 在我们今天所知道和喜爱的区块链出现之前,默克尔树一直是密码学和计算机科学领域的一个方面.如今,我们开始慢慢看到它们在链上更频繁地被用于数据验证的目的.在这篇文章中,我将解释 Merkle Tre ...

  9. 默克尔树(Merkle Tree)总结

    目录 为什么要有默克尔树 简介 Merkle Tree的特点 图解 创建树 检索-文件夹比较 检索-防伪 更新 插入删除 应用 数字签名 P2P网络 可信计算 区块链-简单验证支付 为什么要有默克尔树 ...

最新文章

  1. 《中国人工智能学会通讯》——4.41 两种学习之间有什么区别?
  2. MySql笔记之数据表
  3. 分析对象内部结构,并详解synchronized锁膨胀升级和降级的过程
  4. Nature封面:大团队日趋中庸,小团队更容易出颠覆性创新
  5. HTML5中的自定义属性总结
  6. 如何通过域名访问服务器里的文件,如何通过域名访问云服务器
  7. Typinator for mac(打字员)附注册码支持m1
  8. 我爱刷题系列汇总(51-100)【2017.11.24-2018.01.12】
  9. 这家初创公司用端到端安全保护物联网设备
  10. windows系统安装下GCC编译器
  11. ubuntu显卡驱动下载安装
  12. IP地址规划和设计方法
  13. fgo服务器维护中,FGO维护更新公告 更新内容一览
  14. 使用任意波形(或函数)发生器产生想要的任意信号
  15. VMware 磁碟機未備妥
  16. 广州集体户口办理未婚证流程
  17. 认识客观世界:数学描述与物理意义
  18. SQL注入及其危害、防御手段
  19. 如何把手机app的视频下载到手机上?网页上的视频怎么下载?
  20. PointPillars:基于点云的快速目标检测网络

热门文章

  1. R语言绘图——实用篇 ggplot2绘图
  2. 基于JAVA的日期计算器
  3. nginx(三十二)rewrite模块
  4. 校园网络的规划与实施(思科)
  5. 前端网站性能优化建议
  6. php100以内合数,3D须知工具众览:和差对照表 最稳定组合 质合数 和值跨度
  7. 小胖装机心得 之 耕升显卡造假易迅售后致谢
  8. bluetoothd Protocol not available解决方法
  9. C++入门学习(黑马程序员课程讲义)——第一阶段
  10. MS Office Excel 2007/2003 资料下载汇总