首先,所有的交易都必须满足一组基本要求才能够被执行:
- 交易必须是格式正确的 RLP。 “RLP”代表“Recursive Length Prifix(递归长度前缀)”,是一种用于编码二进制数据嵌套数组的数据格式。 RLP 是以太坊用来序列化对象的格式。
- 合法的交易签名
- 合法的交易nonce。回想一下我们之前提到的,账户的 nonce 是从该帐户发送的交易计数。所以,交易的noce必须等于交易发送方账户地址的nonce
- 一个交易的gas limit必须大于等于这个交易的固有gas,固有gas包括:执行交易的预定义成本 21,000 gas与交易一起发送的数据的 gas 费(每个等于 0 的数据或代码字节需要 4 个 gas,每个非零数据或代码字节需要 68 个 gas)如果这个交易包括合约创建交易,额外增加32,000gas交易执行过程中的操作步骤消耗的gas
- 发送方的账户余额必须有足够的以太币来支付必须支付的“前期”gas费用。 前期 gas 成本的计算很简单:首先,交易的 gas limit乘以交易的 gas price来确定最大的 gas 成本。 然后,将此最大成本添加到从发送方转移到接收方的总价值中。
如果一笔交易通过上面的验证,那么它将进入到下一阶段:
首先,我们先从发送方余额直接减去刚才的提到的前期gas费用,并在发送方账户的nonce加1。此时,我们可以计算出剩余气体 = 交易的总gas limit - 已使用的固有气体。
下一步,这个交易开始执行。在整个交易执行过程中,以太坊都会跟踪“子状态”。 子状态是一种记录在事务期间累积的信息的方式,这些信息将在交易完成时使用。 它包含:
- 需要销毁的账户集合:交易完成后将被丢弃的一组账户(如果存在的话)。
- 日志系列:虚拟机代码执行的存档和索引的检查点
- 退款余额:交易完成后要退还给发送方账户的金额。 还记得我们如何提到以太坊中的存储需要花钱,并且发件人会因清理存储而获得退款吗? 以太坊使用退款计数器跟踪这一点, 退款计数器从0开始,每次合约删除存储中的内容时都会递增。
接下来,处理交易所需的各种计算。
如果交易所需的所有步骤都处理完成了,而且没有出现异常状态,则通过确定要退还给发送方的没消耗gas的数量来最终确定状态。 除了没消耗的gas,发送方还会从我们上面描述的“退款余额”中得到一些补偿。
一旦发送方收到退款:
- gas的费用会被奖励给矿工
- 使用的gas会被添加记录到区块的gas计数器上(用于记录这个区块消耗的总gas,这个值会在验证区块的时候被使用到)
- 销毁的丢弃的账户(如果存在的话)
最后,这笔交易留下了一个新的状态和一系列的日志。
现在,我们已经大概理解了交易执行过程的全部知识点。接下来我们来看看合约创建和消息调用这两种情况有什么不同之处吧。
合约创建
回想一下上文里面有提到,以太坊中有两种类型的账户:合约账户和外部账户。当我们说一个交易是一个“合约创建”交易的时候,这意味着这笔交易的目的是创建一个合约账户地址。
为了创建一个合约账户的地址,我们需要先用一个特定的方式声明新账户的地址。然后初始化一个新的账户地址:
- 设置合约账户地址的nonce为0
- 如果发送方在这个交易中有发送以太币,那么将这些以太币用作这个账户的余额
- 从发送方的账户上减去要发送的以太币的数值
- 设置存储为空
- 设置这个合约的codeHash为一个空字符串的hash值
一旦我们初始化了账户,我们就可以使用与交易一起发送的初始化代码来创建账户。 在执行此初始化代码期间可能会发生很多情况。根据合约的构造函数,它可能会更新账户的存储、创建其他合约账户、进行其他消息调用等。
在执行初始化合约的代码时,它会使用 gas。 交易消耗的gas不得超过剩余的gas。 如果是这样,执行将遇到gas耗尽 (OOG)的异常并退出,此时状态将恢复到交易之前的状态。发送方不会退还用完之前消耗的gas。
但是如果发送方在交易中发送了任何以太币值,即使合约创建失败,以太币也会被退还。
如果初始化代码成功执行,则支付最终的合约创建成本。 这个成本是一个存储成本,与创建的合约代码的大小成正比(记住天下没有免费的午餐!)。如果没有足够的剩余gas 来支付这个最终的存储成本,那么交易会出现声明一个gas耗尽异常并且被中止。
如果一切顺利的话,那么没有被消耗的 gas 都将退还给交易的发送方。
消息调用
消息调用的执行和合约创建过程基本类似,但也有一些不同点。
消息调用不会包含任何需要初始化的代码,但是它可以输入数据,这个输入数据是由交易的发送方提供的。消息调用还有一个包含输出数据的额外组件,如果后续执行需要这个输出数据,则需要使用该组件。
与创建合约一样,如果消息调用执行过程中出现gas耗尽或交易无效(例如堆栈溢出、无效跳转目标或无效指令)而退出,则已经消耗的 gas 不会退还给原始发送方 。所有剩余的未使用gas都被消耗掉,并且状态被重置到之前的状态。
最初以太坊是不允许在消耗提供的gas前停止或者恢复交易的,例如,如果你的一个合约被一个没有权限的调用者给调用了,那么在之前的以太坊中,剩余的gas将依然被消耗,不会退还给发送方。但在拜占庭更新后,以太坊新增了一个revert的代码,允许合约执行中断或者恢复,并且退还还没有消耗的gas。
执行模式
前文中我们已经看到了一个交易从开始执行到结束的各个步骤。现在,我们来看一下交易具体在EVM(以太坊虚拟机)中是如何执行的。
EVM 是一个图灵完备的虚拟机。 EVM 的唯一限制是 gas,这点在其他的图灵完备的虚拟机上是没有的。所以在EVM上可以完成的计算总量,本质上受到gas提供量的限制。