我们先了解一下以太坊MPT树的基本节点。

可以看到有四种节点,代码目录结构在/tire.node.go中
1,fullnode 有17个子节点,叫做分支节点。
2,shortNode对应了黄皮书里面的扩展节点和叶子节点(通过shortNode.Val的类型来判断当前节点是叶子节点(shortNode.Val为valueNode)还是拓展节点(通过shortNode.Val指向下一个node))。
3,hashNode用于指向下一个节点
4,valueNode存储value
然后我们再看看树的结构

可以看到,结构比较简单。db是数据库,每个节点的内容都是保存在数据库里。
root 也就是根部的node node是一个接口 接口中的两个方达
fstring(string)string
cache() (hashNode,bool)
我们知道,以太坊和比特币的最大区别有两个。一个是数据结构,第二个是虚拟机。
比特币词用utxo模型,在此不展开来讲。以太坊是账户余额模型。
因为每个地址一旦转账就发生余额改变。mpt地址余额账户就会从叶子更新到根hash
这样对比一个根部hash的改变就可以知道两个账本是否一致。shortnode是为了防止树过深。影响查找效率。
为了了解树的数据结构,我们先来了解一下字典树

字典树是这个样子的
例如我们需要查找节点7我们只需要输入to就可以了,节点9就是inn
这里面的to 和inn 就是以太坊说的key
那么Key是怎么来的呢。比如说我要寻找一个账户余额。那么先得得到这个账户的key。沿着树往下找。
通过源码知道,以太坊的key是经过keccak256计算而来的结果
keccak256计算是256位。换成十六进制就是64位字符
于是这64位支付就成为以太坊的key

但是字典树有个缺陷,就是高度不可控。如图

如果是64位Key,那么他的最高高度可以达到64位。所以我们还得了解一下Patricia树

如果它没有更多的子节点,是可以把多个字符合并成一个节点的。比如说ulus。这样就可以显著的减少高度。
mpt树还兼具另外一种数据结构。merkel树

事实上,merkel树的特性是为了方便维持各个节点之间的数据一致性,更改状态的。
一旦某个节点的值改变或者删除,都会影响到值根节点的所有hash值。
以上的预备工作就结束了。让我来画一个mpt树吧
在网上找了个图

叶子节点(leaf),存储的是真实的数据,
扩展节点(extension),扩展节点合并相同的前缀,扩展节点下面是分支节点,上图中共享"d3"
分支节点(branch),因为在以太坊协议中,不管是地址还是hash,都是一个16进制串,所以是数字,16进制一共16个数字表示0~9,还有a,b,c,d,e,f;不会出现其他字母,MPT树中的key被编码成一种特殊的16进制的表示,再加上最后的value,所以分支节点是一个长度为17的list,前16个元素对应着key中的16个可能的十六进制字符,如果有一个[key,value]对在这个分支节点终止,最后一个元素代表一个值,即分支节点既可以搜索路径的终止也可以是路径的中间节点。分支节点的父亲必然是extension node。由于分支节点是16长度数组,故该节点减少了层高和存储空间
空节点,代码中用null表示
知道了树的结构。下面我们就来看增删改查
首先我们新建一个mpt树,Trie树的初始化调用New函数,函数接受一个hash值和一个Database参数,如果hash值不是空值的话,就说明是从数据库加载一个已经存在的Trie树, 就调用trei.resolveHash方法来加载整颗Trie树,这个方法后续会介绍。 如果root是空,那么就新建一颗Trie树返回。

树的查找,采用递归的查找方法

树的更新,修改

树的插入,这个就是下一篇文章中我们要改的点。
树的插入是递归查找,然后插入一条数据。然后从叶子节点到根节点的hash更新一遍。而这个函数每次只能更新一条数据。然后从叶子到hash会花费大量机器时间。如果是一批数据,时间就成批增长。不巧的是,以太坊每时每刻都有交易在发生。
能不能有一种方式,减少hash的次数
