1

在以太坊中,有三棵树的说法,分别是状态树,收据树,交易树。了解这三棵树,就弄清楚了以太坊的基础数据结构设计。

引入

在前一篇文章中有提过,以太坊采用基于账户的模式,系统中显示记录每个账户的余额。而以太坊这样一个大型分布式系统中,是采用的什么样的数据结构来实现对这些数据的管理的?
首先,我们要实现从账户地址到账户状态的映射。在以太坊中,账户地址为160个字节,表示为40个16进制数。状态包含了余额(balance)、交易次数(nonce),合约账户中还包含了code(代码)、存储(stroge)。
直观的看,其本质为key-value键值对,所以直观想法便用哈希表实现。若不考虑哈希碰撞,查询直接为常数级别的查询效率。但采用哈希表,难以提供Merkle proof。

思考如何组织账户的数据结构

1.我们能否像BTC中,将哈希表的内容组织为Merkle tree?
但当新区块发布,哈希表内容会改变,再次将其组织为新的Merkle tree。如果这样,每当产生新区块(ETH中新区块产生时间为10S左右),都要重新组织Merkle tree,很明显这是不现实的。
需要注意的是,比特币系统中没有账户的概念,交易由区块管理,而区块包含上限为4000个交易左右,所以Merkle tree不是无限增大的。而在ETH中,Merkle tree来组织账户信息,很明显其会越来越庞大,每次构建Merkle tree的代价很大。

2.那我们不要哈希表了,直接使用Merkle tree,每次修改只需要修改其中一部分即可,这样是否可行?
实际中,Merkle tree并未提供一个高效的查找和更新的方案。如果要保证所有节点的一致性和查找性,必须进行排序。

3.那么经过排序,使用sorted merkle tree可以吗?
当新增账户时,由于其地址随机,插入merkle tree时很大可能在tree中间,那么就会使整个tree重构,所以sorted merkle tree在插入的时候代价太大。

既然哈希表和merkle tree都不可行,那我们看下就实际中以太坊采用的数据结构:MPT。

一个简单的数据结构——————trie(字典树,前缀树)

如下为一个通过5个单词组成的trie数据结构(只画出key,未画出value)

特点:
1.trie中每个节点的分支数目取决于key中每个元素的取值范围(图例中最多26个英文字母分叉+一个结束标志位)。
2.trie查找效率取决于key的长度,实际应用中(以太坊地址长度为160Byte)。
3.理论上哈希会出现碰撞,而trie上面不会发生碰撞。
4.给定输入,无论以什么顺序插入,构造的tire都是一样的。
5.更新操作局部性较好,

6.缺点:trie的存储浪费,很多节点只存储一个key,但其儿子只有一个,过于浪费。因此,为了解决这个问题,我们引入了Patricia tire

Patricia tire(Patricia tree)

Patricia tire就是进行了路径压缩的trie。如上图的例子,进行路径压缩后如下图所示:

需要注意的是,如果新插入单词,原本压缩的路径可能需要扩展开。那么,需要考虑什么情况下路径压缩效果较好?树种插入的键值分布较为稀疏的情况下,可见路径压缩效果较好。
在以太坊系统中,160位的地址存在2^160种,该数实际上已经非常大了,和实际种的账户数目相比,可以认为账户地址已经非常稀疏了。
因此,我们可以在以太坊账户管理中使用patricia tree这一数据结构!但实际上,在以太坊中使用的并非简单的patricia tree,
而是MPT(Modified Patricia tree)。

MPT(Modified Patricia Tree)

以太坊中针对MPT(Merkle Patricia Tree)进行了修改,我们称其为MPT(Modified Patricia Tree)。

下图为以太坊中使用的MPT结构示意图。右上角表示四个账户和其状态(只显示账户余额),需要注意这里的指针都是哈希指针。

每次发布新区块,状态树中部分节点状态会改变。但改变并非在原地修改,而是修建一些分支,保留原本状态。如下图中,仅仅有新发生改变的节点才需要修改,其他未修改节点直接指向前一个区块中的对应节点。

所以,系统中全节点并非维护一棵MPT,而是每次发布新区块都要新建MPT。只不过大部分节点共享。

为什么要保存原本状态?为何不直接修改?
为了便于回滚。如下1中产生分叉,而后上面节点胜出,变为2中状态。那么,下面节点中状态的修改便需要进行回滚。因此,需要维护这些历史记录。

通过代码看以太坊中的数据结构

1.block header中的数据结构

2.区块结构

3.区块在网络中真正发布的信息

最后说明
状态树中保存Key-value对,key就是地址,而value状态通过RLP(Recursive Length Prefix,一种进行序列化的方法)编码序列号之后在进行存储.


MockingJay
7 声望3 粉丝