04 ETH-交易树和收据树
04 ETH-交易树和收据树
每个交易执行完之后会形成一个收据,记录这个交易的相关信息。
交易树与收据树上的节点是一一对应的。
增加收据树只要考虑到以太坊中的智能合约执行比较复杂,增加收据树有利于我们快速查询一些执行的结果。
交易树和收据树都是MPT。
以太坊中的三棵树都用同样的数据结构,这样代码比较统一,便于管理。(猜的)
对于状态树来说,查找的键值就是账户的地址。
对于交易树和收据树来说,查找的键值就是交易在发布的区块里面的序号。交易的排列顺序由发布区块的节点确定的。
交易树和收据树只把当前发布的区块的交易组织起来的。
状态树是把系统中所有账户的状态都要包含进去,不管当前的账户与之前的交易有没有什么关系。
多个区块的状态树是共享节点的。新发布区块时,只有区块中交易改变了账户状态的需要新建分支,其他节点都是沿用原来的树节点就行。
每个区块的交易树和收据树都是独立的。
交易树和收据树的用处?
提供 Merkle proof
以太坊中支持复杂的操作,比如:过去10天,所有跟某个智能合约有关的交易。符合某种类型的所有事件(众筹事件、发行新币的事件)。这些需要高效的方法支持。
以太坊中引入了 bloom filter 数据结构(支持查找某个元素是不是在一个比较大的集合中)。
笨方法,直接遍历,复杂度是线性的。前提:有足够的存储来保存整个集合的元素。对于轻节点来说,没有交易列表信息,所以这种方式行不通。
bloom filter 数据结构:
集合中的每一个元素,都取hash,找到向量中的对应位置,然后把它变为1,所有集合处理完毕,得到一个向量,便是原来集合的摘要。
新元素d,如果映射到一个是0的位置,可以判断出该元素不在集合中;
如果映射到一个是1的位置,什么也不能说明。(新元素可能集合中存在该元素;或者hash碰撞,该元素刚好和集合中的元素碰撞)
有可能出现误报,但是不会出现漏报(在里面一定说明在里面,不在里面也有可能说在里面)。
如果从该集合中删除一个元素,该怎么操作?
bloom filter 的一个局限性是不支持删除操作。简单的 bloom filter 是不支持删除操作的。
那么以太坊中的 bloom filter 有什么用呢?
每个交易执行完毕,会形成一个收据,收据中包含 bloom filter,记录该交易的类型、地址等其它信息,发布的区块在块头中,也有一个总的 bloom filter,总的 bloom filter 是区块中所有交易的 bloom filter 的一个并集。
需求:查找过去10天与所有跟某个智能合约有关的所有交易。
先查一个区块的块头中的 bloom filter 是否有需要的交易类型,如果块头中的 bloom filter 没有的话,那么我们知道,这个区块不是我们想要的,如果块头的 bloom filter有的话,我们再去查找区块中所包含的交易所对应的收据里面的那些 bloom filter,如果有的话,找到相对应的交易,直接进行确认。
好处:通过 bloom filter 的结构,快速过滤掉大量无关的区块。
轻节点根据块头中的bloom filter可以过滤到很多区块,剩下可能的区块找全节点要就行。
以太坊的运行过程可以看作是一个交易驱动的状态机(transaction-driven state machine)。
(执行交易驱动系统从当前状态转移到下一个状态)
比特币也可以叫做是交易驱动的状态机。
(交易驱动UTXO)
状态转移都得是确定的,对于一个给定的当前状态,一组给定的交易,能够确定性的转移到下一个状态。(所有的全节点都得执行同样的状态转移)
问题:以太坊发布交易,节点收到交易,转账A—>B,有没有可能收款人B的地址,节点没有听说过?
可能。要在状态数中新增加一个账户。
问题:状态树中能不能只包含与该交易相关的状态?可以大幅度减小每个区块对应的状态树的大小,因为大部分的账户状态是不会变的。(类似于交易树和收据树)
这样设计的话,每个区块没有一个完整的状态树,只有当前区块所包含的交易涉及到的账户的状态,查找某个账户的状态不方便(A—>B,有可能A账户在当前很长一段事件没有发生过交易)。
同时,转账 A—>B,B的账户可能是新建的账户,这个时候要找到创世纪快、块去,没有找到才发现是个新建的账户。
区块的数据结构:
函数中创建交易树和收据树,并得到根hash值。
创建交易树:
创建收据树:
创建叔父区块:
DeriveSha 函数:
trie数据结构:
收据的数据结构:
区块头的数据结构:
查询 bloom filter 中是否包含查找元素:
bloom9 是个hash函数。