区块链学习笔记16——以太坊中的交易树和收据树

2023-11-12

十六、以太坊中的交易树和收据树

每次发布一个交易的时候,那些交易会组织成一个交易树,也是一颗Merkle tree跟比特币中的情况是类似的,同时以太坊还增加了一个收据树,每个交易执行完之后会形成一个收据,记录这个交易的相关信息,交易树和收据树上面的节点是一一对应的。增加这个收据树,主要是考虑到以太坊的智能合约执行过程比较复杂,所以通过增加收据树的结构有利于我们快速查询一些执行的结果,从数据结构上,交易树和收据树都是MPT,这个跟比特币中有所区别,比特币的交易树就是用普通的Merkle tree,区块里的所有交易就组织成一个普通的Merkle tree,MPT也是一种Merkle tree,叫Merkle Patricia tree,但是跟比特币当中用的不是完全一样的,其实是为了方便,以太坊中的三棵树都用同样的数据结构,这样代码比较统一,便于管理,不一定要有什么更深层次的原因,当然用MPT的一个好处是支持查找操作,可以通过键值从顶向下沿着这个树进行查找,对于状态树来说,查找的键值就是这个账户的地址,对于交易树和收据树来说,查找的键值就是这个交易在发布的区块里的序号,就他的ID,这个交易的排列顺序是由发布区块的那个节点决定的。

这三棵树有一个重要的区别:

交易树和收据树都是只把当前发布的这个区块里的交易组织起来的,而状态树是把系统中所有账户的状态都要包含进去,不管这些账户跟当前区块的交易有没有什么关系。

从数据结构上来说,多个区块的状态树是共享节点的,每次新发布一个区块的时候,只有这个区块中的交易改变了状态的那些节点需要新建一个分支,其他节点都是沿用原来状态树上的节点,相比之下,每个区块的交易树和收据树都是独立的,是不会共享节点的,一个区块跟另一个区块发布的交易本身也认为是独立的

交易树和收据树的用途

  1. 提供Merkle proof,像比特币当中,交易树可以证明某个交易被打包到某个区块里面,可以向轻节点提供这样的Merkle proof,收据树也是类似的,要证明某个交易的结果,也可以再收据树里面提供一个Merkle proof,除此之外,以太坊还支持一些更加复杂的查询操作,比如说,想找到过去十天当中,所有跟某个智能合约有关的交易,一种方法是把过去十天产生的所有区块都扫描一遍,看看其中有哪些交易是和智能合约相关的,但是这种方法的复杂度较高,而且对于轻节点来说,实际上,轻节点没有交易列表,只有一个块头的信息,所以也没有办法通过扫描所有交易列表的方法来找到符合这个查询条件的交易,与之类似的一些查询,比如说,找到过去十天当中符合某种类型的所有事件,比如说所有的众筹事件或者所有的发行新币的事件,这些都是需要一个比较高效的方法才能支持,怎么办呢

    以太坊中引用了bloom filter,bloom filter这种数据结构可以支持比较高效的查找某个元素是不是在一个比较大的集合里面,比如说有一个集合,里面有很多元素,现在想知道某个指定的元素是不是在这个集合里。

    一个最笨的方法是,把这集合里面的元素遍历一遍,看看有没有我想找的那个元素,这个复杂度是线性的,另外一个前提是得有足够得存储来保存整个集合的元素,对于轻节点来说,他其实没有这个交易列表,没有整个集合的元素信息,所以这种方法是用不了的。

    bloom filter用一个很巧妙的思想给这个大的,包含很多元素的集合计算出一个很紧凑的摘要,比如说一个128位的向量,比如说,这个例子当中有一个(a,b,c)的集合,要计算出一个digest,底下是一个向量,这个向量初始的时候都是零,然后有一个哈希函数H,他把每一个元素映射到向量中的某个位置,比如说a这个元素,取哈希之后,映射到这个位置,然后把这个位置的元素从0变成1

在这里插入图片描述

然后,b,c也映射到相应的位置,如下图所示,就是把每个元素都取哈希,找到向量中的对应位置,然后把它置成1,所有元素都处理完了,得到这个向量就是原来集合的一个摘要,这个摘要比原来的集合要小很多,这个例子当中用128bits就可以代表了

在这里插入图片描述

摘要的用处:比如说有一个元素d,想知道这个d元素是不是在这个集合里,但是这个集合本身我们不一定能够保存下来,可以用这个哈希函数H对d取哈希值,
比如说,取完之后发现,映射到这个地方,映射到一个0的位置,说明这个元素一定不在这集合里

在这里插入图片描述

假设取完哈希,映射到这个位置了,是个1的位置,有可能确实是集合中的元素,d=a,也有可能不在这个集合里,而是出现了哈希碰撞,恰好映射到了,跟集合某个元素一样的位置,所以用这个bloom filter要注意,有可能会出现false positive,但是不会出现false negative,就是可能出现误报,但是不会出现漏报,在里面一定说在里面,不在里面也有可能说在里面,这是bloom filter的一个基本的工作原理。

在这里插入图片描述

bloom filter有各种各样的变种,比如说,解决这样的哈希碰撞,有的bloom filter的设计用的不是一个哈希函数,而是一组哈希函数,每个哈希函数独立的把这个元素映射到这个向量里面一个位置,用一组哈希函数的好处是如果出现哈希碰撞,那么一般来说,不会所有的哈希函数都出现哈希碰撞,这里讲的是bloom filter,high level的工作原理,后面会结合代码看具体是怎么工作的,回到这个例子当中,如果从这个集合中删除一个元素,该怎么操作,实际上没法操作,这也是bloom filter的一个局限性,不支持删除操作,比如把a删掉了,对应的向量1要不要改,如果改成0的话,集合中可能有另外一个元素也映射到这个位置(哈希碰撞是有可能的),所以简单的bloom filter是不支持删除操作的,如果要支持删除操作,这个地方就不能是0和1了,得改成一个计数器,记录这个位置有多少个元素映射过来,而且还要考虑到计数器会不会溢出,这样数据结构就复杂得多了,和当初设计bloom filter的初衷是相违背的,所以一般来说,bloom filter是不支持删除操作的。

以太坊中bloom filter的用途

每个交易执行完之后会形成一个收据,这个收据里面就包含一个bloom filter,记录交易的类型,地址等其他信息,发布的区块,在他的块头里也有一个总的bloom filter,这个总的bloom filter是这个区块里所有交易的一个bloom filter的并集,所以回到刚才的例子,比如说要查找过去十天发生的跟智能合约相关的交易,可以找一些有哪些区块的块头的bloom filter有要的交易类型,如果没有,这个区块就不是我们想要的,如果有,再去查找区块里面包含的交易所对应的收据树里面的那些bloom filter,就每个收据的bloom filter,看看哪个有,也可能都没有,因为有可能是false positive,如果是有的话,再找到相对应的交易直接进行一下确认,好处是通过bloom filter的结构能够快速过滤掉大量无关的区块,就很多区块,一看块头的bloom filter就知道肯定没有我们要的交易,然后剩下的一些少数的候选区块,再仔细查看。比如说一个轻节点,只有块头信息,根据块头就已经能够过滤掉很多区块了,剩下有可能是想要的区块,再问全节点要进一步的信息。

这三棵树的根哈希值都是包括在块头里面的,以太坊的运行过程可以看作是一个交易驱动的状态机(transaction-driven state machine),这个状态机的状态是所有账户的状态,就是状态树中包含的那些内容,交易是每次发布区块里包含的交易,通过执行这些交易会驱动系统从当前状态转移到下一个状态,比特币也可以认为是一个交易驱动的状态机,比特币中的状态是UTXO(没有被花掉的那些输出),每次新发布一个区块,会从UTXO里用掉一些输出,又会增加一些新的输出,所以发布的区块会驱动系统从当前状态转移到下一个状态。而且这两个状态机有一个共同的特点,就是状态转移都得是确定的,对一个给定的当前状态,一组给定的交易,就这个区块中包含的交易,能够确定性地转移到下一个状态,因为所有的全节点,所有地矿工,都要执行同样的状态转移,所以状态转移必须是确定性的。

问题1

某人在以太坊发布一个交易,有人收到这个交易,转账交易A->B,有没有可能这个收款人的地址从来没听说过?

以太坊和比特币是一样的,创建账户是不需要通知其他人的,只有这个账户第一次收到钱的时候,其他的节点才会知道这个账户的存在,这个时候要在状态树中写下新插入的一个节点,因为这个是新插入的账户。

问题2

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

如果这样设计的结果是每个区块没有一颗完整的状态树,只有当前区块中所包含的交易涉及到的账户的状态,这么设计的一个问题就是,如果要想查找某个账户的状态就不方便了。比如说有一个转账交易A->B(10ETH),要检查A账户里是不是真的有10个以太币,问题是A账户当前区块和最近一个区块对应的那个状态树可能没有这个账户,往前一直找,找到最近的一个包含A账户的区块,才能知道A的账户余额是多少。不方便就在于如果A有较长的一段时间没有发生交易,可能要从后往前,扫描很多个区块,才能找到最近一次的账户状态。还有一个更大的问题,就是,A转给B钱的时候,要知道A账户的状态,才能知道A是不是有足够的钱转给B10个以太币,也要知道B账户的状态,余额是多少,因为要往B账户余额里加10个以太币,所以也要找B账户的状态,而B账户有可能是个新建的账户,这个时候就要找到创世纪块去,从当前区块一直找到创世纪块,发现这个账户没有,才知道原来是个新建的账户。

代码中具体的数据结构

交易树和收据树的创建过程,在NewBlock函数里创建了交易树和收据树,并且得到了他们的根哈希值

在这里插入图片描述

先看一下交易树的代码,首先判断交易列表是否为空,如果是空的话,那么这个区块里块头的交易树的根哈希值就是一个空的哈希值,否则通过调用DeriveSha函数来得到交易树的根哈希值,然后创建区块的交易列表

在这里插入图片描述

中间这个代码是收据树,首先判断一下收据列表是否为空,如果为空,块头里收据树的根哈希值就是一个空的哈希值,如果不为空,通过调用DeriveSha函数来得到收据树的根哈希值,然后创建块头里的bloom filter,前面见过,每个交易执行完之后会得到一个收据,所以交易列表的长度和收据列表的长度是一样的

在这里插入图片描述

下面这个代码是叔父区块,首先判断叔父列表是否为空,如果是的话,那么块头里叔父区块的哈希值就是一个空的哈希值,否则,通过调用CalcUncleHash函数计算出哈希值,然后通过循坏构建出区块里的叔父数组,这个代码和今天讲的交易树和收据树没有关系,是下篇文章讲GHOST协议要用到的

在这里插入图片描述

下面看一下DeriveSha函数

前面NewBlock函数创建交易树和收据树的时候,调用的都是这个函数,这里调用的数据结构是trie

在这里插入图片描述

而trie的数据结构是一棵MPT,以太坊的三棵树,交易树,收据树,还有状态树用的都是MPT,

在这里插入图片描述

这是Receipt的数据结构,每个交易执行完之后形成的一个收据,记录了这个交易的执行结果

在这里插入图片描述

这里的Bloom就是这个收据的bloom filter

在这里插入图片描述

这个Logs是个数组,每个收据可以包含多个Log,这些收据的bloom filter就是根据这些Log产生出来的

在这里插入图片描述

这是区块块头的数据结构

在这里插入图片描述

里面的Bloom域就是整个区块的bloom filter,这个是由刚才看到的每个收据的bloom filter合在一起得到的

在这里插入图片描述

这是刚刚的NewBlock函数,红框里的代码就是创建块头里的bloom filter,通过调用CreateBloom这个函数

在这里插入图片描述

这是相关的三个函数的代码实现

在这里插入图片描述

CreateBloom的参数是这个区块的所有收据,这个for循环对每个收据调用LogsBloom函数来生成这个收据的bloom filter,然后把这些bloom filter用Or操作合并起来得到整个区块的bloom filter

在这里插入图片描述

LogsBloom函数的功能是生成每个收据的bloom filter,他的参数是这个收据的Log数,刚才看过Receipt的数据结构,每个Receipt里面包含一个Logs的数组,这个函数有两层for循环,外层循环对Logs数组里的每一个Log进行处理,首先把Log的地址取哈希后,加到bloom filter里面,这里的bloom9是bloom filter中用到的哈希函数,然后内层循环把Log中包含的每个Topics加入到bloom filter里,这样就得到了这个收据的bloom filter
在这里插入图片描述

bloom9是bloom filter中用到的哈希函数,这个和我们讲的有点不太一样,我们那个例子中的哈希函数是把集合中的每个元素映射到digest中的一个位置,这个集合要生成一个digest,这个哈希函数就是把每个元素映射到要生成的digest中的某一个位置,把这个位置置为1。这里的bloom9函数是把输入映射到digest中的三个位置,把三个位置都置为1。

第一行调用crypto里面的函数,生成一个256位的哈希值,b或者变量是个32个字节的哈希值。

第二行的r是要返回的bloom filter,在这里初始化为0。

接下来是个循环,把刚才生成的32个字节的哈希值取前六个字节,每两个字节组成一组拼接在一起,然后and上2047,相当于对2048取余,得到一个位于0到2047这个区间里的数,这样做是因为以太坊中bloom filter的长度是2048位,这个循环的最后一行,把t左移这么多位然后合并到上一轮得到的bloom filter里,通过Or运算合并到前面的bloom filter里,经过三轮循环,把三个位置置为1后,返回所创建的bloom filter

前面是生成bloom filter的过程,那么怎么查询某个bloom filter里面是否包含了某感兴趣的topic呢?

这是通过调用BloomLookup函数来实现的,查一下bin bloom filter里有没有包含要找的第二个参数topic,首先用刚才讲的bloom9函数把topic转化成一个bytesBacked,然后把他跟bloom filter取and操作,看看得到的结果是不是和bytesBacked相等。注意bloom filter里面可能包含除了我们要查找的topic之外其他的topic,所以要做一个and,然后再跟他自身比较,相当于判断一下我们要查找的这个topic在bloom filter中对应的位置是不是都是1
在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

区块链学习笔记16——以太坊中的交易树和收据树 的相关文章

  • 自制黑科技------桌面整理工具

    桌面文件太多 找不到文件 通过使用程序 科学的区域划分和文件归类 帮你的桌面变得更整齐 提高你的使用效率 附上桌面图片一张 点击打开链接

随机推荐

  • BottomNavigationView与Navigation使用

    目录 1 as自动生 2 xml代码 如果要修改底部的图标和文字需要去bottom nav menu修改 如果你要修改默认点击颜色需要去主题颜色修改 如果你要修改底部背景颜色可以在BottomNavigationView 的背景颜色修改 如
  • QFile的文件清空操作

    QFile file etc wpa supplicant conf file open QIODevice ReadOnly QTextStream io file file close file open QIODevice Trunc
  • ARM第五章平时作业

    第 5 章 S3C2440 嵌入式系统 共 63 分 一 简述启动代码存储在 NAND Flash 存储器上时 S3C2440 的启动过程 6 分 为了支持 NAND Flash 的 boot loader S3C2440A 配备了一个内部
  • java环境变量配置详细教程

    1 什么是环境变量 环境变量 environment variables 一般是指在操作系统中用来指定操作系统运行环境的一些参数 如 临时文件夹位置和系统文件夹位置等 环境变量是在操作系统中一个具有特定名字的对象 它包含了一个或者多个应用程
  • Java 访问权限 内部类总结

    在Java中 可以将一个类定义在另一个类里面或者一个方法里边 这样的类称为内部类 广泛意义上的内部类一般包括四种 成员内部类 局部内部类 匿名内部类 静态内部类 1 成员内部类 1 该类像是外部类的一个成员 可以无条件的访问外部类的所有成员
  • Java桥接模式

    基本介绍 1 桥接模式 Bridge模式 是指 将实现与抽象放在两个不同的类层次中 使两个层次可以独立改变 2 是一种结构型设计模式 3 Bridge模式基于类的最小设计原则 通过使用封装 聚合及继承等行为让不同的类承担不同的职责 它主要特
  • vue2 维护状态key的作⽤和原理

    1 key定义 为了给 Vue 个提示 以便它能跟踪每个节点的身份 从 重 和重新排序现有元素 你需要为每项提供 个唯 key 2 写法 li item name li 3 作 key 值使 数组的索引index 或者不加 在数组元素顺序打
  • 2023最新版本Activiti7系列-源码篇-初始化过程

    源码分析 1 设计模式 1 1 命令模式 https dpb bobokaoya sm blog csdn net article details 89115420 1 2 责任链模式 https dpb bobokaoya sm blog
  • xss攻击的了解

    常见的xss攻击方法 1 绕过XSS Filter 利用 lt gt 标签注入Html JavaScript代码 2 利用HTML标签的属性值进行xss攻击 例如 img src 当然并不是所有的Web浏览器都支持Javascript伪协议
  • 【LeetCode刷题】203 移除链表元素 java

    题目 给你一个链表的头节点 head 和一个整数 val 请你删除链表中所有满足 Node val val 的节点 并返回 新的头节点 示例 方法一 先对头节点做处理 使其不为val class Solution public ListNo
  • java一个数如果恰好等于它的因子之和,这个数就称为 "完数 "。(java50道经典编程题)

    题目 一个数如果恰好等于它的因子之和 这个数就称为 完数 例如6 1 2 3 编程 找出1000以内的所有完数 对于这道题其实乍一看可能觉得比较困难 但是你只要 知道一个问题作为求因子 只需要从1开始让你输入的这个数一直除就好了 记得每回合
  • C++对于表达式临时对象的处理

    在表达式中如果使用了一个类的操作符重载函数 或者调用了一个返回类对象的函数 都会产生临时对象 临时对象的生存周期就在表达式中 甚至是表达式中的子语句 临时对象的销毁应该是在完整表达式的最后一句执行 比如下面的例子 T c c a b 另外
  • Remove Duplicates from Sorted Array II

    还是原地重写法 保留的条件是A j A i 2 注意后面的下标是i 2 不是j 2 int removeDuplicates int A int n if n lt 3 return n int i 2 for int j 2 j
  • java关于文件记录篇章之文件夹创建篇

    今天 创建一个文件夹目录的时候 创建多级目录的时候发现 自己老是创建失败 但是系统显示文件夹创建成功 但是你去找文件夹的时候 又发现创建失败 这里在我成功之后封装了一个创建文件夹的创建对象 首先这个文件夹是用来解决本地存储文件和linux上
  • dockers报错:Cannot connect to the Docker daemon

    异常信息 22 01 14 13 58 44 Reporter INFO YarnAllocator Completed container container e118 5690061100801 24379300 01 000066 o
  • 更换gradle,引起文件缺失报错 Could not resolve all dependencies for configuration ':classpath'.

    因为公司项目需要低版本gradle 加上同事其他项目也是需要低版本gradle 要更换gradle 使用2 14 1 于是遇到了如下报错 百度了 很多人都没有直接的办法 直接放弃去找已经下载的使用 看了一篇文章https www cnblo
  • 【Web3.0大势所趋】下一代互联网的未来

    前言 Web3 0 是一个越来越受到关注的话题 它被认为将会带来天翻地覆的变化 本文我们一起来谈谈 Web3 0 的概念 特点和优势 并探讨它为什么如此重要和具有革命性的 文章目录 前言 Web3 0是什么 区块链技术 智能合约 总结 We
  • elasticsearch之explain的使用

    explain查看怎么计算得分的 format将json格式结果转为yaml展示 POST tlsmz search format yaml explain true query bool must term fz keyword valu
  • phpstorm显示页面不停的在indexing转圈中,并且文件名还一直在刷新

    打开 File下的 Invalidate Caches Restart 下的 Invalidate and Restart 便可以了
  • 区块链学习笔记16——以太坊中的交易树和收据树

    十六 以太坊中的交易树和收据树 每次发布一个交易的时候 那些交易会组织成一个交易树 也是一颗Merkle tree跟比特币中的情况是类似的 同时以太坊还增加了一个收据树 每个交易执行完之后会形成一个收据 记录这个交易的相关信息 交易树和收据