上一篇主要讲解了MPT的基本原理,这篇分析一下以太坊数据存储相关的流程。
首先介绍一下MPT的存储流程,然后依次分析StateDB、Transactions、Receipts的存储,这3棵树的Merkle Root最终会存储到区块Header中的Root、TxHash、ReceiptHash字段。
1.MPT存储流程
从图中可以看出,MPT的存储涉及3种编码方式:
- KeyBytes编码
- Hex编码
- Compact编码
在完成Compact编码后,会通过折叠操作把子结点替换成子结点的hash值,然后以键值对的形式将所有结点存储到数据库中。下面详细介绍上面3中编码方式。
1.1 KeyBytes编码
即原始关键字,比如图中的0x811344、0x879337等。每个字节中包含2个nibble(半字节,4 bits),每个nibble的数值范围时0x0~0xF。
1.2 Hex编码
由于我们需要以nibble为单位进行编码并插入MPT,因此需要把一个字节拆分成两个,转换为Hex编码。
编码转换是在Trie.TryUpdate()中触发的,具体转换代码参见trie/encoding.go:
func keybytesToHex(str []byte) []byte {
l := len(str)*2 + 1
var nibbles = make([]byte, l)
for i, b := range str {
nibbles[i*2] = b / 16
nibbles[i*2+1] = b % 16
}
nibbles[l-1] = 16
return nibbles
}
1.3 Compact编码
当我们需要把内存中MPT存储到数据库中时,还需要再把两个字节合并为一个字节进行存储,这时候会碰到2个问题: