Merkle Patricia Tree (MPT) 以太坊中的默克尔树
本篇博文是自己学习mpt的过程,边学边记录,很多原理性内容非自己原创,好的博文将会以链接形式进行共享。
一、什么是mpt
MPT是以太坊中的merkle改进树,基于基数树,即前缀树改进而来,大大提高了查找效率。
二、前缀树
MPT中的P,就是前缀树,也叫trie或字典树。trie每个节点是一个确定长度的数组,每个节点的值指向子节点的指针,最后还有一组标志位,用来标志到此是否是一个完整的字符串,并且有几个这样的字符串。
如下图是一个常见的用来存英文单词的trie:(图转自http://blog.csdn.net/zslomo/article/details/53434883)
传统trie树也存在一些不足:
1.当某一字符串很长或者与其他字符串没有相同前缀,构造的树会极其不平衡;
2.传统trie是由内存指针链接,且字符串的值没有编码,明文直接显示,安全系数低。
三、以太坊的改进
针对传统trie的不足,以太坊进行了一些改进,http://www.cnblogs.com/fengzhiwu/p/5584809.html 这篇博文对于原理的讲解详细易懂,GitHub的文章https://github.com/ethereum/wiki/wiki/Patricia-Tree也很好,该篇文章的翻译http://me.tryblockchain.org/Ethereum-MerklePatriciaTree.html
下文进行简单的阐述:
1.以太坊增加了两个节点,叶子结点和扩展节点,所以MPT中共存在四类节点:空节点、叶子节点、扩展节点和分支节点。
标准的叶子结点,形式为[key,value]的列表,其中key是key的一种十六进制编码,value是value的RLP编码(RLP编码将在下文解释)。
扩展节点,也是[key,value]的形式,但不同于叶子节点,这里的value是可以链接到其他节点的hash,可以被用来查询数据库中的节点。
分支节点,MPT树中的key被编码成一种特殊的16进制,再加上value,所以分支节点长度为17,前16个元素对应着key中16个十六进制字符,如果有一个[key,value]对在这个分支节点终止,最后一个元素代表一个值,即分支节点既可以搜索路径的终止也可以是路径的中间节点。
MPT还有一个重要的概念是特殊的十六进制前缀编码(GitHub文中有提到),十六进制序列的带可选结束标记的压缩编码。传统的编码十六进制字符串的方式,是将他们转为了十进制。比如0f1248
表示的是三个字节的[15,18,72]
。然而,这个方式有点小小的问题,如果16进制的字符长度为奇数呢。在这种情况下,就没有办法知道如何将十六进制字符对转为十进制了。额外的,MPT
需要一个额外的特性,十六进制字符串,在结束节点上,可以有一个特殊的结束标记(一般用T
表示)。结束标记仅在最后出现,且只出现一次。或者说,并不存在一个结束标记,而是存在一个标记位,标记当前节点是否是一个最终结点,存着我们要查找的值。如果不含结束标记,则是表明还需指向下一个节点继续查找。
为了解决上述的这些问题。我们强制使用最终字节流第一个半字节(半个字节,4位,也叫nibble),编码两个标记位。标记是否是结束标记和当前字节流的奇偶性(不算结束标记),分别存储在第一个半字节的低两位。如果数据是偶数长,我们引入一个0值的半字节,来保证最终是偶数长,由此可以使用字节来表示整个字节流。
一个例子:
- > [ 1, 2, 3, 4, 5 ]
- '\x11\x23\x45' ( Here in python, '\x11#E' because of its displaying unicodes. )
- //不含结束,所以没有结束标记,由于字节流是奇数,标记位取值1,不补位,所以前面只补一个半字节就好。
- > [ 0, 1, 2, 3, 4, 5 ]
- '\x00\x01\x23\x45'
- //不含结束标记的偶数,且由于是偶数第一个nibble是0,由于是偶数位,需要补一个值为零的nibble,所以是00。紧跟后面的值。
- > [ 0, 15, 1, 12, 11, 8, T ]
- '\x20\x0f\x1c\xb8'
- //由于有结束标记,除结束标记的长度为偶数,所以第一个nibblie是2,由于是偶数长补位一个值为0的nibble,所以最后加20。
- > [ 15, 1, 12, 11, 8, T ]
- '\x3f\x1c\xb8'
- //由于有结束标记,且为奇数,第一个值为3,又由于是奇数不需要补位,值是3加后面的值。
python编码:
- def compact_encode(hexarray):
- term = 1 if hexarray[-1] == 16 else 0
- if term: hexarray = hexarray[:-1]
- oddlen = len(hexarray) % 2
- flags = 2 * term + oddlen
- if oddlen:
- hexarray = [flags] + hexarray
- else:
- hexarray = [flags] + [0] + hexarray
- // hexarray now has an even length whose first nibble is the flags.
- o = ''
- for i in range(0,len(hexarray),2):
- o += chr(16 * hexarray[i] + hexarray[i+1])
- return o
2.安全性
首先,为了保证树的加密安全,每个节点通过他的hash被引用,而非32bit或64bit的内存地址,即树的Merkle部分是一个节点的确定性加密的hash。一个非叶节点存储在leveldb关系型数据库中,数据库中的key是节点的RLP编码的sha3哈希,value是节点的RLP编码。想要获得一个非叶节点的子节点,只需要根据子节点的hash访问数据库获得节点的RLP编码,然后解码就行了。通过这种模式,根节点就成为了整个树的加密签名,如果一颗给定trie的跟hash是公开的,那么所有人都可以提供一种证明,通过提供每步向上的路径证明特定的key是否含有给定的值。
MPT例子:
假设我们有一个树有这样一些值('dog', 'puppy'), ('horse', 'stallion'), ('do', 'verb'), ('doge', 'coin')
。首先,我们将它们转为十六进制格式:
- <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' ]
树的构造逻辑是root结点,要构造一个指向下一个结点的kv节点。先对键编码,由于当前节点不是结束结点,存值的键为奇数字符数,所以前导值为1,又由于奇数不补位,最终存键为0x16
。它指向的是一个全节点A。下一层级,要编码的是d和h的第二个半字节,4和8。所以在A节点的第五个位置(从零开始)和第九个位置,我们可以看到分别被指向到了B和C两个节点。对于B节点往后do,dog,doge来说。他们紧接着的都是一个编码为6f的o字符。所以这里,B节点被编码为指向D的kv结点,数据是指向D节点的。其中键值存6f,由于是指向另一个节点的kv节点,不包含结束标记,且是偶数,需要补位0,得到00,最终的编码结果是006f。后续节点也以此类推。
附上GitHub的python代码:
- def get_helper(node,path):
- if path == []: return node
- if node = '': return ''
- curnode = rlp.decode(node if len(node) < 32 else db.get(node))
- if len(curnode) == 2:
- (k2, v2) = curnode
- k2 = compact_decode(k2)
- if k2 == path[:len(k2)]:
- return get(v2, path[len(k2):])
- else:
- return ''
- elif len(curnode) == 17:
- return get_helper(curnode[path[0]],path[1:])
- def get(node,path):
- path2 = []
- for i in range(len(path)):
- path2.push(int(ord(path[i]) / 16))
- path2.push(ord(path[i]) % 16)
- path2.push(16)
- return get_helper(node,path2)
Merkle Patricia Tree (MPT) 以太坊中的默克尔树的更多相关文章
- 区块链~Merkle Tree(默克尔树)算法解析~转载
转载~Merkle Tree(默克尔树)算法解析 /*最近在看Ethereum,其中一个重要的概念是Merkle Tree,以前从来没有听说过,所以查了些资料,学习了Merkle Tree的知识,因为 ...
- 区块链 - 默克尔树(Merkle Tree)
章节 区块链 – 介绍 区块链 – 发展历史 区块链 – 比特币 区块链 – 应用发展阶段 区块链 – 非对称加密 区块链 – 哈希(Hash) 区块链 – 挖矿 区块链 – 链接区块 区块链 – 工 ...
- 区块链入门到实战(12)之区块链 – 默克尔树(Merkle Tree)
目的:解决由于区块链过长,导致节点硬盘存不下的问题. 方法:只需保留交易的哈希值. 区块链作为分布式账本,原则上网络中的每个节点都应包含整个区块链中全部区块,随着区块链越来越长,节点的硬盘有可能放不下 ...
- 区块链学习1:Merkle树(默克尔树)和Merkle根
☞ ░ 前往老猿Python博文目录 ░ 一.简介 默克尔树(Merkle tree,MT)又翻译为梅克尔树,是一种哈希二叉树,树的根就是Merkle根. 关于Merkle树老猿推荐大家阅读<M ...
- Merkle Patricia Tree (MPT) 树详解
1. 介绍 Merkle Patricia Tree(简称MPT树,实际上是一种trie前缀树)是以太坊中的一种加密认证的数据结构,可以用来存储所有的(key,value)对.以太坊区块的头部包 ...
- 014-数据结构-树形结构-基数树、Patricia树、默克尔树、梅克尔帕特里夏树( Merkle Patricia Tree, MPT)
一.基数树 Radix树,即基数树,也称压缩前缀树,是一种提供key-value存储查找的数据结构.与Trie不同的是,它对Trie树进行了空间优化,只有一个子节点的中间节点将被压缩.同样的,Radi ...
- Merkle Tree(默克尔树)算法解析
Merkle Tree概念 Merkle Tree,通常也被称作Hash Tree,顾名思义,就是存储hash值的一棵树.Merkle树的叶子是数据块(例如,文件或者文件的集合)的hash值.非叶节点 ...
- 转 Merkle Tree(默克尔树)算法解析
Merkle Tree概念 Merkle Tree,通常也被称作Hash Tree,顾名思义,就是存储hash值的一棵树.Merkle树的叶子是数据块(例如,文件或者文件的集合)的hash值.非叶节 ...
- 以太坊中的Ghost协议
https://blog.csdn.net/t46414704152abc/article/details/81191804 写得超好,终于弄懂了什么是叔块,怎么确定哪条链最长,以太坊与比特币出块的差 ...
随机推荐
- Reading Notes : 180212 冯诺依曼计算机
读书<计算机组成原理>,百度百科 现在大部分接触过计算机的人,都会知道冯诺依曼计算机,但是这个概念是怎么来的呢?本节我们就通过聊一下计算机的存储程序控制,来认识”冯诺依曼”. 存储程序控制 ...
- es6 Set 和Map 数据结构
ES6提供了新的数据结构Set,它类似于数组,但是成员的值都是唯一的,没有重复的值. Set 本身是一个数据结构,用来生成Set 数据结构. const s = new Set(); [2,3,5,4 ...
- python 创建虚拟环境
创建一个文件夹:mkdir tf_env 进入到文件夹内:cd tf_env 创建虚拟环境:python3 -m venv tensorflow-dev 激活虚拟环境:source tensorflo ...
- mysql小特性:change buffer
change buffer是在其他数据库中没有的一个概念,说白了就是一块系统表空间分配的空间,针对的对象是辅助索引的叶子节点(为什么不是主键索引?因为主键索引是聚集索引,在磁盘上的排列是有序的,磁盘的 ...
- bootstrap-01-学习记录
1.bootstrap所有插件依赖JQ,必须在JQ之后引入. 2.bootstrap分预编译版(css,js,fonts)和源码版(less,js,fonts,dist->预编译版内容,docs ...
- Webstorm新建vue类型文件设置
今天安装了Node.js,配置了vue需要的框架,发现原有的wenstorm新建文件的时候没有vue文件选项,因此,学习了一下webstorm如何配置创建vue文件 具体过程如下: 第一步,打开web ...
- 【memcached启动报错】
#前台启动不了 #指定-u root #后台启动 #扩展选项: #利用telnet连接memcached 的端口登录memcached服务 #error表示有语法错误 #store表示正确
- PredicateBuilder
using System; using System.Linq; using System.Linq.Expressions; namespace Oyang.Tool { public static ...
- swoole学习(二)----搭建server和client
1.搭建server 1.1搭建server.php 1.搭建websocket服务器,首先建立 server.php 文件, <?php $server = new swoole_websoc ...
- seleniun 爬取淘宝网
import re from selenium import webdriver from selenium.common.exceptions import TimeoutException fro ...