Ethereum(以太坊)架构与组成——数据结构与存储

以太坊区块链系统中使用了MPT树结构,关于MPT树的内容这里不做过多介绍,想了解可以查阅百度或者书籍论文资料。

需要指出的是,以太坊区块头不是只包括一棵MPT树,针对三种对象,以太坊设计了三种树:交易树(Transaction Tree)、状态树(State Tree)和收据树(Receipt Tree)。下图展示了三棵树在区块头的存储情况,分别存储了三棵树的树根节点的散列值。

在以太坊中,区块链客户端需要具备以下功能:

  1. 验证某笔交易是否包含在特定区块中;
  2. 查询曾经某个地址(即账户)发出某种类型时间的所有实例(比如,判断一个众筹合约是否达成目标);
  3. 查询目前某个账户的余额;
  4. 判断一个账户是否存在;
  5. 查询在某个合约中进行的一笔交易的交易输出。

使用三种MPT树即可完成上述功能,其中交易树处理第一种业务,收据树处理第二种业务,状态树负责处理后三种业务。

交易树

①什么是交易

以太坊中,交易主要指一条外部账户【由人创建,可以存储以太币,由公钥和私钥控制的账户,区别于“合约账户”】发送到区块链上另一账户的消息的签名数据包,其中主要包含发送者的签名、接收者的地址以及发送者转移给接收者的以太币数量等内容。

②交易费用

为了防止用户在区块链公有链中发送过多无意义交易,浪费矿工资源,例如转账金额为0的转账交易,以太坊中每一笔交易都需要支付一定的费用,即交易的发送放需要为每笔交易付出一定的代价,即交易费。这笔费用用于支付交易执行所需要的计算开销,由将交易打包进主链的矿工收取。

③什么是Gas

Gas是用来衡量一笔交易所消耗的计算资源的基本单位。当矿工收到一笔交易时,会根据交易的内容扣取相应数值的Gas,当以太坊节点执行一笔交易的计算步骤越多、越复杂,这笔交易消耗的Gas越多。例如转账交易,矿工会根据转账的金额对交易的发送方和接收方的账户余额进行修改,消耗的计算量不高且相对固定,因此收取的Gas较少且相对固定。而对于创建或调用智能合约的交易,矿工会根据对应的字节码在EVM里执行相应的操作,需要消耗巨大的计算资源【EVM一些操作在计算上比较复杂且增加了在状态中需要存储的数据量】,因此这类交易消耗的Gas通常比较多。一般情况下,一笔普通的转账交易会消耗21000Gas,而创建智能合约的交易可能会消耗几万甚至几百万Gas。

④什么是Gas Price

Gas只衡量了计算资源,而以太坊中流通的数字货币是以太币(ETH),因此Gas需要换算成ETH进行支付。Gas Price(Gas的价格)就是一个单位Gas所需的手续费(即以太币Ether)。例如一笔转账交易消耗了21000Gas,假设Gas Price为1 Gwei/Gas,那么这笔交易的手续费为0.000021 Ether。

显然,对用户来说Gas Price越低越好,甚至是0。而对于矿工来说则相反,因此为了协调这种矛盾,以太坊钱包客户端默认的Gas Price是0.000000001 Ether/Gas(1Gwei,/Gas=1G wei Gas)。但这个价格依然可以根据需求进行浮动。因为矿工有选择收纳交易和收取费用的权利,他们都希望将收益最大化,所以一般来说,矿工会对接收到的交易按照Gas Price或者Gas * Gas Price进行排序,优先选择收益高的纳入区块。当某个时间段交易量激增时,交易发送至为了今早让矿工接受交易可以提高这笔交易的Gas Price以激励矿工。

⑤什么是Gas Limit

因为在某些场景下,交易完成前并不能准确获知交易将消耗的Gas,例如调用智能合约的交易会根据不同的执行时间触发不同的操作,因此恶意用户可以发送一个数十亿步骤的交易,没有人能够处理它,而矿工对此事不知情的,一旦接受交易将导致拒绝服务攻击。为了解决这一问题,以太坊设置了Gas Limit,对于单个交易,Gas Limit也称为Start Gas,表示交易发送者愿意为这笔交易执行所支付的最大Gas数量,需要发送者在发送交易时设置,一方面可以避免拒绝服务攻击,另一方面也可以保护用户的资产,面手错误代码影响而消耗过多的交易费。当交易的实际消耗小于Gas Limit时,矿工将按照实际计算开销收取手续费,而如果实际消耗大于Gas Limit,那么矿工执行过程中发现Gas已经被耗尽而交易没有执行完成,此时矿工会回滚到程序执行前的状态并收取Gas Limit作为手续费。因此,每笔交易无论是否成功,其消耗的最高金额将不会超过Gas Limit。

对于区块,同样也有Gas Limit的概念,它表示一个区块所包含的所有交易消耗的Gas上限,由矿工决定。例如,若区块的Gas Limit是100,有5个没有添加到区块的交易,其Gas Limit为10、20、30、40、50,互不相同,此时矿工的打包策略可以是前四个交易,即10+20+30+40=100,进而放弃第五个,也可以打包第一个、第四个和第五个,即10+40+50=100,完全取决于矿工的意愿,但打包交易的Gas Limit数量之和不能超过区块的Gas Limit。区块的Gas Limit设置越大,矿工可以获取更多交易费,但也需要更多的带宽,同时也会增加出现大区块的频率,造成挖出的区块无法形成最长的交易链。因此Gas Limit不能随意更改,根据以太坊协议,当前区块的Gas Limit只能基于上一个区块的Gas Limit上下波动1/1024。

⑥交易内容

以太坊中的交易(Transaction)是指存储一条从外部账户发送到区块链上另一个账户的消息的签名数据包,既可以是简单的数字货币,即以太坊转账,也可以是包含智能合约代码的消息。

一条交易包含的内容有:

⑦交易树

每个区块都有一棵独立的交易树。区块中交易的顺序主要由“矿工”决定,在这个块被挖出前这些数据都是未知的。不过“矿工” 一般会根据交易的 Gas Price 和 nonce 【该账户地址交易的次数,用于保障每笔交易只能被处理一次的计数器,进而避免 replay 攻击】对交易进行排序。首先会将交易列表中的交易划分到各个发送账户,每个账户的交易根据这些交易的 nonce 来排序。每个账户的交易排序完成后,再通过比较每个账户的第一条交易,选出最高价格的交易,这些是通过一个堆(heap)来实现的。每挖出一个新块,更新一次交易树。在交易树包含的键值对中,其中每个键是交易的编号,值是交易内容。

状态树

以太坊中的状态树基本上包含了一个 键值映射 ,其中的 键是地址 ,而值是账户内容,包括账户的声明、账户余额、nonce、代码以及每一个账户的存储 (其中存储本身就是一颗树),即{ nonce, balance,codeHash, storageRoot }。其中,nonce 是账户交易的序数, balance 是账户余额, codeHash 是代码的散列值, storageRoot 是另一棵树的根节点。状态树代表访问区块后的整个状态。

以太坊是一个以账户为基础的区块链应用平台,账户的状态不是直接存储在每个区块中,所有的账户状态都是以“状态数据”的形式存储在以太坊的节点中。

状态数据是一种隐式数据,要从实际的区块链数据中计算出来。状态树记录了各个账户的状态,需要经常地进行更新:一方面,账户余额和账户的随机数nonce经常会更变;另一方面,新的账户会频繁地插入,存储的键( key)也会经常被插入以及删除。

收据树

每个区块都有自己的收据树,收据树不需要更新,收据树代表每笔交易相应的收据。

收据树也包含一个键值映射,其中键是索引编号,用来指引这条收据相关交易的位置,值是收据的内容。

交易的收据是一个RLP 编码的数据结构:[medstate, Gas_ used, logbloom,logs ]。其中, medstate 是交易处理后树根的状态; Gas_used 是交易处理后Gas 的使用量;logs 是表格 [address, [topicl, topic2 ,…], data] 元素的列表,表格由交易执行期间调用的操作码LOGO …LOG4 生成(包含主调用和子调用),address 是生成日志的合约地址, topicn是最多4 个32 字节的值, data 是任意字节大小的数组; logbloom 是交易中所有logs 的address 和topic 组成的布隆过滤器(由Howard Bloom在1970年提出的二进制向量数据结构,它具有很好的空间和时间效率,被用来检测一个元素是不是集合中的一个成员)。区块头中也存在一个布隆过滤器,使用布隆过滤器可以减少查询的工作量,这样的构造使得以太坊协议对轻客户端尽可能的友好。

参考:

https://ethereum.stackexchange.com/questions/268/ethereum-block-architecture/
https://www.jianshu.com/p/c1082a054aa4
https://www.jianshu.com/p/d3eba79cc475
https://blog.csdn.net/taifei/article/details/78225145
https://blog.csdn.net/Metal1/article/details/80203691

发布了6 篇原创文章 · 获赞 7 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Ximerr/article/details/102747431
今日推荐