Merkle Patricia Tree简称MPT树,提供了一个基于加密学的,自校验防篡改的数据结构,采用[key,value]键值对来储存数据,在以太坊范围内,限定键值的类型只能是字符串。MPT树插入、删除、查找操作的时间复杂度均为O(log(n)),但相对于红黑树来说,MPT更好理解和编码。
MPT树结合了基数树(Radix Tree)、Merkle Tree、Patricia Tree的优点。Radix Tree的结构如下:
[i0, i1, ..., iN, value],i0到iN要么指向一个节点,要么是NULL,value存储一个值,这是MPT树用来索引值的最基本结构。Patricia Tree解决了Radix Tree的低效和空间浪费问题。Merkle Tree解决了数据校验及防篡改问题。
为了保证树的加密安全,每个节点通过hash被引用key == sha3(rlp(value)),而非32bit或64bit的内存地址,即树的Merkle部分是一个节点的确定性加密的hash。一个非叶节点存储在leveldb关系型数据库中,数据库中的key是节点的RLP编码的sha3哈希,value是节点的RLP编码。
如果一个树根的hash值是公开的,那么所有人都可以通过计算每一步的值来证明这棵树在特定的路径上具有给定的值。攻击者不可能提供一个不存在的键值对,任何修改都会改变根节点的值。
传统的Merkle树的效率低且空间占用大,MPT树对这些缺点进行了优化。MPT树插入、删除、查找操作的时间复杂度均为O(log(n))。
标准叶子节点 Leaf Node
一个长度为2的[key,value]列表,key是路径的十六进制编码,value是交易的RLP编码。
扩展节点 Extension Node
长度为2的[key,value]列表,value是其他节点hash,可以链接到其他节点。
分支节点 Branch Node
长度为17的列表,因为我们采用16进制编码,所以前16个元素可以是NULL或者指向其他Node,第17个元素是确定的值。
[i0, i1, ..., i16, value]
空节点 NULL
HP用来对key进行编码,来表示这个节点是一个leaf还是extension。HP是一个4bit的二进制数字,最低位表示奇偶,倒数第二位表示是leaf还是extension。如下图:
如果key是偶数长度,要加上一个0来保持奇偶性。最后把HP加到key前面。
Examples:
假设我们有一些键值对('dog', 'puppy'), ('horse', 'stallion'), ('do', 'verb'), ('doge', 'coin'),首先我们要把key转化为16进制,在路径中我们用<>表示key,用''表示value:
<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是以16索引开头的,下一位索引分叉为'4'和'8',所以我们用branchNode,hashC下面无分叉,所以用leafNode储存'stallion'剩余的key索引<6f 72 73 65>前面加20标识。hashB继续分叉以此类推。
区块头
交易树Transactions Trie :区块中所有的交易,由index作为key,k0为第一笔交易,k1为第二笔...
状态树State Trie :代表访问区块后的整个状态,全局唯一,随着时间更新。
[sha3(ethAddress), rlp(ethAccount)], 其中account是一个数组[nonce,balance,storageRoot,codeHash],storageRoot是另外一棵树:StorageTrie。
Storage Trie:所有的合同数据储存的地方
收据树ReceiptsTrie :每笔交易对应的数据。每个区块有他自己的收据树。key是rlp(transactionIndex), transactionIndex是区块自己的索引,不会改变。
参考:
https://github.com/ethereum/wiki/wiki/Patricia-Tree