17 交易树和数据树

——————>> 二刷分界线。

交易树和收据树

每次发布一个区块的时候,这个区块中所包含的那些交易会组织成一棵交易树,也是一棵Merkle tree,和比特币中的情况是类似的;同时,以太坊增加了一棵收据树,每个交易完成之后会形成一个收据,记录这个交易的相关信息,就是交易树和收据树上面的节点是一一对应的,增加这个收据树主要是考虑到以太坊的智能合约执行过程比较复杂,所以增加交易树的结构有利于我们快速查询一些执行的结果,从数据结构上看交易树和收据树都是MPT,这和比特币没有区别,mpt也是一个Merkle tree,但是和比特币中用的不是完全一样,为什么会是这样,肖臻老师猜测是这样代码比较统一,便于管理,并没有更深层次的原因。使用MPT的一个好处就是便于查找,对于状态树来说,查找的键值就是这个账户的地址,对于交易树和收据树来说查找的键值就是交易在发布区块中的序号。交易树和收据树只是把当前区块中包含的交易组织起来了,而状态树是把系统中所有状态信息包含起来了,状态树可能要沿用上一个区块的状态树信息。

交易树和收据树有什么用呀?

提供Merkle proof,交易树可以证明某个交易被打包到某个区块中了,可以向轻节点提供这样的Merkle proof;同时,以太坊中还提供了更加复杂的查询操作:比如要查询过去10天中与某个智能合约相关的交易(一种方法是把过去十天的交易信息都查询一遍,看看其中有哪些交易与之相关,而且轻节点只有块头信息)、所有的发行的新币的时间、所有的众筹时间等。所以以太坊中引入了bloom filter数据结构。

bloom filter

Bloom filter 是由 Howard Bloom 在 1970 年提出的二进制向量数据结构,它具有很好的空间和时间效率,被用来检测一个元素是不是集合中的一个成员。如果检测结果为是,该元素不一定在集合中;但如果检测结果为否,该元素一定不在集合中。因此Bloom filter具有100%的召回率。这样每个检测请求返回有“在集合内(可能错误)”和“不在集合内(绝对不在集合内)”两种情况,可见 Bloom filter 是牺牲了正确率和时间以节省空间。

计算方法

如需要判断一个元素是不是在一个集合中,我们通常做法是把所有元素保存下来,然后通过比较知道它是不是在集合内,链表、树都是基于这种思路,当集合内元素个数的变大,我们需要的空间和时间都线性变大,检索速度也越来越慢。 Bloom filter 采用的是哈希函数的方法,将一个元素映射到一个 m 长度的阵列上的一个点,当这个点是 1 时,那么这个元素在集合内,反之则不在集合内。这个方法的缺点就是当检测的元素很多的时候可能有冲突,解决方法就是使用 k 个哈希 函数对应 k 个点,如果所有点都是 1 的话,那么元素在集合内,如果有 0 的话,元素则不在集合内。

优点缺点

Bloom filter 优点就是它的插入和查询时间都是常数,另外它查询元素却不保存元素本身,具有良好的安全性。它的缺点也是显而易见的,当插入的元素越多,错判“在集合内”的概率就越大了,另外 Bloom filter 也不能删除一个元素,因为多个元素哈希的结果可能在 Bloom filter 结构中占用的是同一个位,如果删除了一个比特位,可能会影响多个元素的检测。

简单样例

下面是一个简单的 Bloom filter 结构,开始时集合内没有元素

17 交易树和数据树_第1张图片

当来了一个元素 a,进行判断,这里哈希函数有两个,计算出对应的比特位上为 0 ,即是 a 不在集合内,将 a 添加进去:

17 交易树和数据树_第2张图片

之后的元素,要判断是不是在集合内,也是同 a 一样的方法,只有对元素哈希后对应位置上都是 1 才认为这个元素在集合内(虽然这样可能会误判):

随着元素的插入,Bloom filter 中修改的值变多,出现误判的几率也随之变大,当新来一个元素时,满足其在集合内的条件,即所有对应位都是 1 ,这样就可能有两种情况,一是这个元素就在集合内,没有发生误判;还有一种情况就是发生误判,出现了哈希碰撞,这个元素本不在集合内。

17 交易树和数据树_第3张图片17 交易树和数据树_第4张图片

 

以太坊中如何使用Bloom filter?

在轻节点处先查找一下哪一个区块的块头Bloom filter里面有我要查询的交易的内容,要是没有的话,就说明这个区块是没有的,要是有的话,我们去区块中去查找每个收据中对应的Bloom filter,看看那个有,但是有可能是没有的,好处就是可以快速筛选,这样对于轻节点来说就是很友好。

 

transaction-driven state machine

通过执行这些交易,驱动系统的状态从当前状态转移到下一个状态,比特币也可以认为是一个交易驱动的状态机,比特币的状态是UTXO,发布的区块会驱动转向下一个状态。从以太坊和比特币中可以看出状态转移必须是确定性的。

一个难问题

我们前面讲,状态树和交易树、收据树的一个区别是,状态树要包含系统中所有账户的状态,无论这些账户是否参与了当前区块的交易,那么能不能将状态树的改成只包含与当前区块包含交易相关的区块,而且能大幅度削减每个区块对应的大小,因为大部分的账户状态是不会变的,这样可以吗?

不能使用这种结构,要是使用这种方式的话,假如A要向B转账,B是一个新建的账户,那么全节点就需要找到创世纪快才知道B是一个新建的节点。

那么我可以不可以这样,我由A和B同时发布交易,并告知全节点,之后才认定两个交易是存在的。

 

newblock函数

17 交易树和数据树_第5张图片

首先判断一下交易列表是否为空。

17 交易树和数据树_第6张图片

判断一下收据列表是否为空。

17 交易树和数据树_第7张图片

处理叔父区块

17 交易树和数据树_第8张图片

derivesha函数

这里创建的数据结构是一棵trie,而trie的数据结构是一棵MPT.

17 交易树和数据树_第9张图片

17 交易树和数据树_第10张图片

以太坊的三棵树用的都是MPT,

17 交易树和数据树_第11张图片

17 交易树和数据树_第12张图片

块头的bloom filter是通过每个交易的bloom filter组合在一起合成的

17 交易树和数据树_第13张图片

这里听不太懂了

17 交易树和数据树_第14张图片

你可能感兴趣的:(17 交易树和数据树)