北大肖臻老师《区块链技术与应用》系列课程学习笔记[13]以太坊-状态树

目录

一、什么是以太坊中的状态树

二、实现从账户地址到账户状态的映射

        方案一、哈希表+Merkle tree

        方案二、不排序的Merkle tree

        方案三、Sorted Merkle tree

一、什么是以太坊中的状态树

        以太坊中采用的是一种基于账户的模式,系统中显示地维护每个账户上有多少余额,接下来我们了解一下这种基于账户的模式是如何实现的?想要实现这种模式需要完成从账户地址(Address)到账户状态(State)的映射。以太坊中的账户地址是160位的,也就是20字节,一般将其表示为40个十六进制的数。状态是指外部账户和合约账户的状态,包括余额,交易次数Nonce,对于合约账户还包括代码和存储。

二、实现从账户地址到账户状态的映射

方案一、哈希表+Merkle tree

        用哈希表实现,系统中的全节点维护一个哈希表,每次有一个新的账户,插入到哈希表里面,查询账户的余额,就直接在哈希表中查询,如果不考虑哈希碰撞的话,基本上查询的效率是在常数时间内完成的,更新也是很容易在哈希表中更新的。

        如果用这个哈希表要提供merkle proof怎么提供?比如说你要跟一个人签合同,希望他能证明一下他有多少钱,怎么提供证明呢?一种方法是类似比特币系统中的做法,把哈希表中的元素组织成一个Merkle Tree,然后算出一个根哈希值,这个根哈希值存在Block Header里,只要根哈希值是正确的,就能保证底下的树不会被篡改。如果有新区块发布,新区块中包含新的交易,执行这个交易必然会使哈希表的内容发生变化,发布下一个区块的时候,还需要重新把哈希表中的内容组织成一个Merkle tree,这个代价是不是太大了。实际操作中,账户状态发生变化的只是一小部分,大多数是不变的,所以每次都重新构造一次Merkle tree,这个代价是很大的。

       比特币系统当中也是每出现一个区块都要重新构造一个Merkle tree,这为什么没有问题?比特币系统中的Merkle tree是把区块里包含的交易组织成一个Merkle tree,那区块中的交易每次发布一个新的区块又有一系列新的交易,比特币中的Merkle tree是不变(immutable)的。每次发布一个新的区块对应一个Merkle tree,然后这棵Merkle tree构建完之后是不会再改的,下次再发布一个新的区块再构建一个新的Merkle tree,那区块里有多少个交易呢?最多差不多4000个(按照1M字节,每个交易大概是250M字节左右),其实是一个上限,很多区块的交易数目根本到不了4000个,有好多区块就只有几百个,甚至有可能还有更少的,所以每次发布一个区块,比特币里构建一个Merkle tree,是要把这几百个到几千个交易构成一个Merkle tree。

        如果以太坊中想要采取类似比特币中构建Merkel tree的方法,则需要把所有的以太坊账户一起构成一个Merkle tree,这个就比比特币系统中每个区块中的几百、几千个交易要高出好几个数量级,相当于每次发布一个区块要把所有的账户遍历一遍构建出一个Merkle tree,下次再有一个区块,再把所有的账户遍历一遍,再构建出一个Merkle tree。除了提供Merkle proof证明账户有多少钱之外,这个Merkle tree还有另外一个很重要的作用,就是维护各个全节点之间状态的一致性,如果没有根哈希值不去发不出来,每个节点就是在本地维护一个数据结构,那怎么知道你的数据结构的状态跟别人的数据结构的状态是不是一致呢,要各个全节点保持状态的一致才行,这也是为什么比特币中把根哈希值写在块头里的原因,就是对于当前区块中包含哪些交易,所有的全节点要有一个共识。

        综上所述,如果每个全节点在本地维护一个哈希表,然后需要构建Merkle tree的时候构建出Merkle tree来,然后根哈希值发到区块头里,这个方法是不行的。哈希表本身的效率是挺好的,插入、更改效率都很好,但是每次构建Merkle tree的代价太大了。

方案二、不排序的Merkle tree

        能不能不要哈希表了,直接用一个Merkle tree把所有的账户都放进去,要修改账户状态的时候直接在Merkle tree里改,因为每个区块更新的只是一小部分账户,所以改的时候只是Merkle tree里的一小部分?这个方法的问题就在于Merkle tree没有提供一个高效的查找、更新的方法,比特币的Merkle tree是怎么构建的,就最底下一层是tx,然后哈希值放到上面节点里,两两结合,然后再取一个哈希往上整,他没有提供一个快速查找,更新的方法。还有一个问题是,如果这样构建Merkle tree,就直接把账户放到Merkle tree里,这个Merkle tree需不需要排序,之前提到过Sorted Merkle tree,如果不排序查找速度会慢,比特币中如果证明一个交易在这个区块中是不需要排序的,如果证明一个交易不在这个区块中,是需要排序的,否则证明的代价就非常大了。如果不排序还有一个问题:叶节点是这些账户的信息,如果不规定这些账户在叶节点出现的顺序,那么这样构建出来的Merkle tree不是唯一的,比如,系统中有很多全节点,每个全节点按照自己的某个顺序排序,比如,他按照他听到某个交易的顺序构建一个Merkle tree,那么叶结点的顺序是乱的,每个叶节点的顺序都是自己决定的,最后构建出的Merkle tree是不一样的,算出的根哈希值也是不一样的。

        比特币中的节点也是不排序的,那为什么比特币就没有问题呢?因为比特币中的每个全节点收到的交易的顺序也是不一样的,理论上说构建的Merkle tree的根哈希值也是不一样的,但是比特币有一个区别,比特币中虽然也没用排序的Merkle tree,但是他那个顺序是唯一的(每个节点决定有哪些区块需要被打包在这个区块里,然后组装之后进行挖矿,新区块的Merkle tree由挖到矿的节点决定,也就是发布区块的那个节点确定的)。

       为什么以太坊不能这样做?因为如果也要这么做的话,需要把账户的状态发布到区块里,也可以说每个全节点自己决定怎么把账户组织成一个Merkle tree,算出根哈希值,挖出矿,但是想让别人知道这个顺序,需要把它发布在区块里。但是这里发布的是所有账户的状态,不是发布的区块里发布的交易,这里的交易个数跟所有账户的状态个数差了很多个数量级,而且交易是必须要发布的,这个代价必须要付出,账户状态可以维护在本地,而且大部分账户状态是不变的,一个区块里的交易只能改很少的账户,大多数账户是不变的,而且重复发布,每隔十几秒发布一个新的区块,把所有状态都打包发布一遍,下次再过十几秒再发布一遍,这个是不可行,刚才说明了不排序的Merkle tree是不行的。

方案三、Sorted Merkle tree

        Sorted Merkle tree也会有问题,如新增一个账户时,产生一个账户的地址是随机的,他的叶节点的位置很可能是插在中间的,那后面这些树的结构都得变,插入,删除代价都很大,每次更新都需要新产生一遍Merkle tree。区块链是不可篡改的,是说添东西容易,删东西难,其实以太坊中没有显式地删除账户的操作,有的账户上就一点钱,就一两个Wei,也不能把他删掉。        上面的方案都不行。那么以太坊中采用的方法是用一个叫MPT的结构,在此之前先学习一个简单的数据结构——trie。

猜你喜欢

转载自blog.csdn.net/YSL_Lsy_/article/details/126361741
今日推荐