java patriciatrie_明明白白以太坊Merkle Patricia Trie

在以太坊数据结构中,Merkle Patricia Trie始终是个绕不过去的坎,世界状态,交易,交易收据等都是以这种树的形式存储在区块链数据库中,并将树root hash保存在区块头里。可以说不弄懂这种树的原理就没有办法真正明白以太坊的数据存储方式。

大家都知道在以太坊cpp版本和go版本采用的是levelDb这种数据库,这种数据库存储的是[key, value]对。而实际上以太坊存储的数据结构也是[key, value]对,以状态state为例,key是address,value是[nonce, balance, storageRoot, codeHash]。那么问题来了,以太坊为什么不直接按[key, value来存储数据,而要费那么大劲用树型结构来存储呢?

答案是为了安全性。

而为了引进Merkle Patricia Trie,我们要先来看一种简单的树。

Radix Trees

中文翻译为前缀树,这是一种用来查询的数据结构。每次遍历key的一个字符,直到找到对应的值。

比如有下面几个数据:('do', 'verb'), ('dog', 'puppy'), ('doge', 'coin'), ('horse', 'stallion'),存储在Radix Trees里是这样的:

d1de297797ba?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

Radix Trees

其中圆圈里是值,key的字符组成查询路径,路径是doge时的查询过程为d->o->g->e,需要查询4次,而路径为horse时查询需要5次,因此这种树查询虽然可行,但是效率不高。我们后面可以看到以太坊是怎么解决这个问题的。

解决了查询,然后为了安全性,我们引入了Merkle Trees。

Merkle Trees

Merkle Trees又名Hash Trees,数据都存储在叶节点,父节点则存储叶节点的hash值,一级一级向上, 直到根节点。因此如果修改了叶节点的数据,则父节点hash值就会改变,根节点hash值也会改变,从而很快就可以验证数据是否正确。

d1de297797ba?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

Merkle Trees

保证数据不被篡改,这是区块链技术的基础。

Merkle Patricia Trie

以太坊Merkle Patricia Trie是结合了上面两种树的数据结构,兼顾了查询和安全性。

查询

前面不是说过Radix Trees查询效率不行吗?那就进行优化,将路径里相同部分提取出来,作为共同路径,从而减少树的高度,提供查询效率。

还是用上面的例子,合并路径后的树变为:

d1de297797ba?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

Modified Radix Trees

可以看到将do和horse作为共同路径后,查询doge的路径变成了do->g->e,只需要查询3次,而horse只需要查询一次就够了。

安全性

节点与节点之间的联系不再采用内存指针的方式,而是采用hash值的方式,比如上图中的puppy这个值的节点存储执行下一个节点的hash值,然后将这个hash值与实际节点对应关系存储在[key, value]的数据库中。当有人篡改coin节点值时,也必须要修改puppy节点里的hash值,然后verb节点里hash值也需要修改,直到根节点,所以我们只需要验证根节点的hash值,就知道底层数据是否正确。

节点类型

按功能不同,存在4种节点:

NULL节点

分支节点,含有17项数据,[ v0 ... v15, vt ]

叶节点,含有2项数据,[ encodedPath, value ]

扩展节点,含有两项数据,[ encodedPath, key ]

Merkle Patricia Trie查询路径是以nibble(半个字节)作为单位。

分支节点中的前16项数据v0-v15分别对应16进制的0-0xF,是用nibble作为索引,可以快速进行查询,比如路径nibble是0xa,则取分支节点0xa位置的值。分支节点对应图上的分叉节点。

叶节点和扩展节点都是两项,只是value不同,那么怎么区分他们呢?

以太坊是采用添加前缀的方式,根据节点类型和路径长度是否是奇偶数来添加不同的前缀。

有一个对应表:

hex char

bits

node type partial

path length

0

0000

extension

even(偶数)

1

0001

extension

odd(奇数)

2

0010

terminating (leaf)

even(偶数)

3

0011

terminating (leaf)

odd(奇数)

可以看到前缀为0或者1时表示扩展节点,2或者3时表示叶节点。

另外前缀为0或者2时,需要变更为00或20.

例子1

我们用一个例子来说明:

d1de297797ba?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

0_t4ziOSL-71LPnkjE.png

图中右上角有4个[key, value]对,我们要存储这4对数据。key每个方框里是一个nibble。

从这四个路径中可以提取出公共路径a7,因此可以建立一个扩展节点A, [00 a7, hashB],a7是一个偶数长度的扩展节点,前缀为00,hashB是下一个节点B的hash值。

下一个nibble取值有1, 7, f,因此节点B为一个分支节点,其中index为1,7,f的位置保存下一个节点的hash值,value为空。这个分支节点为:[ EMPTY, hashC, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, hashD, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, hashE, value]。

hashC, hashD, hashE为下一层节点的hash值

因为出现了分支,我们来看1这个分支,这个分支后没有分支,所以后续的1355可以作为一个叶节点,为[20 1355, 45.0ETH],这里1355为偶数长度的叶节点,所以前缀为20。

再来看7这个分支,后续又有d3这共同路径,因此创建一个扩展节点D,[00 d3, hashF],'d3'是一个偶数长度的扩展节点,前缀为00,hashF是下一个节点F的hash值。

d3之后又出现分支3和9,因此又出现一个分支节点,其中index为3和9的位置保存下一个节点的hash值,value为空。这个节点为:[ EMPTY, EMPTY, EMPTY, hashG, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, hashH, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, value]

剩下的就是两个叶节点,分别为[37, 1.00WEI]和[37, 0.12ETH],这两个都是奇数长度的叶节点,因此前缀为3。

其他节点可以按同样的方法分析。

例子2

我们再用前面的('do', 'verb'), ('dog', 'puppy'), ('doge', 'coin'), ('horse', 'stallion')做例子来说明分析过程。

由于是采用nibble作为路径单位,所以先将路径和值都写为字节形式:

<64 6f> : 'verb'

<64 6f 67> : 'puppy'

<64 6f 67 65> : 'coin'

<68 6f 72 73 65> : 'stallion'

那么在数据库中存储形式为:

rootHash: [ <16>, hashA ]

hashA: [ <>, <>, <>, <>, hashB, <>, <>, <>, hashC, <>, <>, <>, <>, <>, <>, <>, <> ]

hashC: [ <20 6f 72 73 65>, 'stallion' ]

hashB: [ <00 6f>, hashD ]

hashD: [ <>, <>, <>, <>, <>, <>, hashE, <>, <>, <>, <>, <>, <>, <>, <>, <>, 'verb' ]

hashE: [ <17>, hashF ]

hashF: [ <>, <>, <>, <>, <>, <>, hashG, <>, <>, <>, <>, <>, <>, <>, <>, <>, 'puppy' ]

hashG: [ <35>, 'coin' ]

我们来模拟一下真实的查询过程,假如我们要查询doge这个key对应的值是多少。

rootHash已知(存储在区块头中),那么从levelDb中读出key为rootHash的值,也就是[ <16>, hashA ],这是一个扩展节点,路径为6,剩下路径为4,6,f,6,7,6,5,并得到下一个节点hashA

在levelDb中读出key为hashA的值,也就是 [ <>, <>, <>, <>, hashB, <>, <>, <>, hashC, <>, <>, <>, <>, <>, <>, <>, <> ],nibble为4,在位置4读出下一个节点hashB,剩余路径为6,f,6,7,6,5

在levelDb中读出key为hashB的值,也就是[ <00 6f>, hashD ],这是一个路径为6f的扩展节点,因此剩余路径为6,7,6,5,并得到下一个节点hashD

在levelDb中读出key为hashD的值,也就是 [ <>, <>, <>, <>, <>, <>, hashE, <>, <>, <>, <>, <>, <>, <>, <>, <>, 'verb' ],nibble为6,在位置6读出下一个节点hashE,剩余路径为7,6,5

在levelDb中读出key为hashE的值,也就是 [ <17>, hashF ],这是一个路径为7的扩展节点,因此剩余路径为65,并得到下一个节点hashF

在levelDb中读出key为hashF的值,也就是[ <>, <>, <>, <>, <>, <>, hashG, <>, <>, <>, <>, <>, <>, <>, <>, <>, 'puppy' ],nibble为6,在位置6读出下一个节点hashG,剩余路径为5

在levelDb中读出key为hashG的值,也就是[ <35>, 'coin' ],这是一个路径为5的叶节点,正好和我们的剩余路径吻合,因此我们就得到了最终的值coin。

可见以太坊为了安全性真的增加了不少复杂性,降低了效率。

以太坊中的应用

以太坊中实际情况还要复杂,数据还需要通过RLP编码。

State Trie(世界状态树)

路径是sha3(ethereumAddress),value是rlp([nonce,balance,storageRoot,codeHash])

Transactions Trie(交易树)

路径是rlp(transactionIndex),value是rlp(transaction)

Receipts Trie(交易收据树)

路径是rlp(transactionIndex),value是rlp(transaction receipt)

参考

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/349300.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

jdeveloper_适用于JDeveloper 11gR2的Glassfish插件

jdeveloper众所周知&#xff0c; ADF Essentials是使用Java构建Web应用程序的绝佳框架&#xff0c;它可以自由开发和部署。 您在Glassfish&#xff08;3.1&#xff09;服务器上部署ADF Essentials应用程序。 但是&#xff0c;JDeveloper并不带有嵌入式Glassfish服务器&#xff…

Spring Cloud教程– Spring Cloud Config Server简介

问题 SpringBoot在通过属性或YAML文件外部化配置属性方面提供了很大的灵活性。 我们还可以使用特定于配置文件的配置文件&#xff08;例如application.properties &#xff0c; application-dev.properties &#xff0c; application-prod.properties等&#xff09;分别为每个环…

数字孪生体技术白皮书_基于Flownex的数字孪生体解决方案 系列介绍之二:数据中心应用实例...

致力于数字孪生体技术的研究与发展通过解决方案和工程化应用造福人类来源&#xff1a;数字孪生体实验室原创作者&#xff1a;王永康转载请注明来源和出处导 读《基于Flownex的数字孪生体解决方案》是我们最近完成的系列落地方案之一。该方案适用于热力系统、冷却系统、通风空调…

node php聊天室,最简单的Nodejs聊天室示例

今天群里一个同学找我要一个nodejs聊天室的demo。给他了一个简单的例子&#xff0c;顺便记录下&#xff1a;准备工作(前提是已经装好了nodejs)&#xff1a;mkdir nodejs-democd nodejs-demo安装express : npm install express安装socket.io : npm install socket.io安装foreve…

neo4j安装_neo4j 社区版win10 下安装

准备工作&#xff1a;Neo4j下载网址&#xff1a;https://neo4j.com/download-center/#releasesava jdk官网下载&#xff1a;https://www.oracle.com/technetwork/java/javase/downloads/index.html安装 查看是否有用旧版本的java jdk ,如果有请在设置“应用和功能”卸载 旧的ja…

php网站 qq登陆,php写的插件网站接入QQ登录,QQ互联

qq按钮这里的链接是入口&#xff0c;调用你的apiapi_qq.php前端直接链接到此/*** 这个QQ登录简单实用&#xff0c;只要大家看我写的注释会一目了然&#xff0c;请注意看哦。* 带有"todo"这样注释的地方都是要你去改成你自己的逻辑* 这个php怎么进来呢&#xff1f;这是…

Spring MVC中@RequestParam和@PathVariable批注之间的区别?

Spring MVC框架是在Java世界中开发Web应用程序最流行的框架之一&#xff0c;它还提供了一些有用的注释&#xff0c;可以从传入的请求中提取数据并将请求映射到控制器&#xff0c;例如 RequestMapping&#xff0c; RequestParam和PathVariable。 即使将RequestParam和ParthVari…

excel 两列模糊匹配给出结果_北大硕士给大脑植入Excel病毒,工作效率提升了好几倍...

在工作中&#xff0c;我们经常会碰到这样的同事&#xff0c;他们是这样完成工作的&#xff1a;先用计算器算好结果&#xff0c;甚者动用手指头在电脑屏幕上数数&#xff0c;然后把数据填写到Excel表格中。结果可以预见&#xff0c;原本可以在上班时间完成的工作&#xff0c;愣是…

java ee cdi_Java EE CDI Producer方法教程

java ee cdi这是CDI Producer方法的教程。 在CDI中&#xff0c;生产者方法生成一个对象&#xff0c;然后可以将其注入。 当我们要注入本身不是bean的对象&#xff0c;要注入的对象的具体类型在运行时可能有所不同&#xff0c;或者当对象需要一些bean构造函数不执行的自定义初始…

qnap nas web php,如何在QNAP NAS上建立并使用 iSCSI Target

本帖最后由 小Q 于 2015-2-5 13:30 编辑在QNAP Turbo NAS上建立并使用iSCSI Target&#xff0c;快速、便利且便宜建置网络储存系统之方式内容&#xff1a;l 在Windows中使用Microsoft iSCSI启动器来连接iSCSI装置什么是iSCSI且它有什么好处?iSCSI(Internet Small Computer Sy…

openssl php api,PHP7使用openssl解密易班API中的用户数据

PHP7使用openssl解密易班API中的用户数据一、mcrypt扩展解密自从PHP版本更新到了7.1以上以后&#xff0c;mcrypt扩展被废弃&#xff0c;使用mcrypt扩展会出现如下图的报错。只能使用openssl来代替。然而易班轻应用提供的还是旧版本的mcrypt扩展&#xff0c;这将导致php版本升级…

Spring MVC的DispatcherServlet – Java开发人员应该知道的10件事

如果您使用过Spring MVC&#xff0c;那么您应该知道什么是DispatcherServlet&#xff1f; 它实际上是Spring MVC的心脏&#xff0c;确切地说是MVC设计模式或控制器的C语言。 应该由Spring MVC处理的每个Web请求都通过DispatcherServlet处理。 通常&#xff0c;它是Front Contro…

运行时错误7内存溢出_分别从运行时和GC的角度看JAVA8内存管理

运行时区域1.程序计数器程序计数器&#xff08;Program Counter Register&#xff09;是一块较小的内存空间&#xff0c;它可以看作是当前线程所执行的字节码的行号指示器。在虚拟机概念模型里&#xff08;概念模型&#xff0c;各种虚拟机可能会通过一些更高效的方式实现&#…

极域课堂管理系统软件如何取消控制_微缔电子组装业MES系统软件六大功能组成...

电子组装业MES系统软件六大功能组成MES系统软件是制造执行系统的英文简称&#xff0c;MES系统软件在整个企业信息集成系统中承上启下&#xff0c;是生产活动与管理活动信息沟通的桥梁&#xff0c;MES系统软件在产品从工单下发到生产成成品的整个过程中&#xff0c;扮演着促进生…

多个cuda 被单进程沾满_报名 | 提高GPU利用率,听英伟达专家分享这个CUDA工具

随着 NVIDIA GPU 计算性能的不断提升&#xff0c;如何提升 GPU 利用率是开发者普遍关心的问题之一。从 Kepler 架构开始&#xff0c;NVIDIA GPU 支持多个 CUDA kernels 函数的并发执行&#xff0c;称为 Hyper-Q 技术。Hyper-Q 技术支持多个 CUDA streams、多个 CPU threads 或者…

usb转ttl模块与matlab,USB接口转TTL小板的自检测试

现在电脑基本上都不会配置DB9串行数据端口了&#xff0c;这给一些喜欢折腾刷机和单片机加载程序的朋友带来了诸多的不便。还好&#xff0c;随着技术的发展&#xff0c;USB接口转TTL的产品越来越成熟&#xff0c;而这种产品主要以采用PL-2303HX芯片作为主控器的居多&#xff0c;…

matlab 误差椭圆,求3倍标准差误差椭圆分析的程序

根据《白话空间统计之九&#xff1a;方向分布(标准差椭圆)修正版》(有些地方没有理解清楚)&#xff0c;写了下面的程序。但是好像结果不对Zmvnrnd([0.5 1.5], [0.025 0.03 ; 0.03 0.16], 50);XZ(:,1); YZ(:,2);mean_Xnanmean(X); mean_Ynanmean(Y); %椭圆圆心%确定长短半轴…

java ee cdi_Java EE CDI处理程序方法示例

java ee cdi这是CDI Disposer方法的教程。 在CDI中&#xff0c;由于Producer方法生成的对象随后可以注入到应用程序中&#xff0c;因此使用Disposer方法&#xff0c;以便在其工作完成时将其删除。 Disposer方法始终与Producer方法匹配。 Disposer方法使用的一个示例是当应用程…

python皮卡丘编程代码_再接再厉,用python编程13行代码解方程组(纯字符)

因为是示例为主&#xff0c;我们将方程组限制在二元一次方程组&#xff1a;x,y两个变量&#xff0c;两个方程。类似这样&#xff1a;每个方程有两个变量&#xff0c;x和y&#xff0c;形式为&#xff1a;axbycd由于这次有了两个方程&#xff0c;我们提取参数的代码就适合提炼为一…

快速提示:使用Chrome开发工具调试GWT应用程序

调试是软件开发的重要方面。 拥有正确的工具可以节省大量时间和头痛。 在GWT Super Dev模式之前&#xff0c;经典的Dev模式允许使用JVM调试。 开发人员可以在其IDE中设置断点&#xff0c;并使用调试模式来跟踪错误和错误。 现在&#xff0c;在超级开发模式下&#xff0c;情况有…