MPT(实现快速查找以节省存储空间)
背景
- Trie:用于快速检索的多叉树,查找速度快、但是需要耗费大量的存储空间
- Patricia Trie:耗费的空间更小
- Merkle Tree:Merkle树是一种用于快速验证内容完整性的数据结构,其基本原理是分别计算树的叶⼦结点的hash值,然后把叶⼦结点的hash值拼接在⼀起,再计算⼀次hash作为其⽗结点的hash值,依次向上直到根结点,根结点的hash值称为Merkle Root。 可以看出,如果想要获得相同的Merkle Root,所有⼦结点的内容都必须相同,这样就可以⽤来验证树的内容有没有被篡改
概念
- MPT:MPT融合了了上⾯面3种数据结构,但是⼜又有⼀一些细微的差别。
- MPT是⽤用来检索字节数据的,因此是16叉树,分别代表0x0~0xF。
结构
MPT定义4种结点类型:
- 空结点(NULL)
- 分⽀支结点(branch node):包含16个分⽀支,以及1个value
- 扩展结点(extension node):只有1个子结点
- 叶子结点(leaf node):没有⼦子结点,包含⼀一个value
HP编码
-
MPT树中另外一个重要的概念是一个特殊的十六进制前缀(hex-prefix,
HP)编码,用来对key进行编码。因为字母表是16进制的,所以每个节点可能有16个孩子。因为有两种[key,value]节点(叶节点和扩展节点),引进一种特殊的终止符标识来标识key所对应的是值是真实的值,还是其他节点的hash。如果终止符标记被打开,那么key对应的是叶节点,对应的值是真实的value。如果终止符标记被关闭,那么值就是用于在数据块中查询对应的节点的hash。无论key奇数长度还是偶数长度,HP都可以对其进行编码。最后我们注意到一个单独的hex字符或者4bit二进制数字,即一个nibble。 - HP编码很简单。一个nibble被加到key前(下图中的prefix),对终止符的状态和奇偶性进行编码。最低位表示奇偶性,第二低位编码终止符状态。如果key是偶数长度,那么加上另外一个nibble,值为0来保持整体的偶特性。
源码分析
-
以太坊中的MPT的实现源代码:trie/trie.go以及trie/node.go。trie.go主要实现struct
Trie以及MPT树的各种操作:查询,添加,删除。node.go实现了四种节点以及从持久化数据库中解析node。理解了Node的实现,Trie的实现很容易理解。有关MPT中的四种节点,node.go的实现如下:
总结type ( // 分支节点 fullNode struct { Children [17]node // Actual trie node data to encode/decode (needs custom encoder) flags nodeFlag } // 实现叶子节点和扩展节点 shortNode struct { Key []byte Val node flags nodeFlag } hashNode []byte valueNode []byte ) // 每个节点的哈希信息 type nodeFlag struct { hash hashNode // cached hash of the node (may be nil) gen uint16 // cache generation counter dirty bool // whether the node has changes that must be written to the database } - MPT是以太坊用来存储“状态”的数据结构,综合了Trie,Patrica Trie以及Merkletree的优点
- MPT的key是用HEX编码,在持久化存储时,会转换为HP编码。以太坊的go语言实现中,用fullNode表达分支节点,用shortNode表示叶子节点和扩展节点。
-
学院Go语言视频主页
https://edu.csdn.net/lecturer/1928 -
扫码获取海量视频及源码 QQ群:721929980