科普

解读以太坊黄皮书(4/7)

曾汨   |     |   449 次阅读

解读以太坊黄皮书(1/7)
解读以太坊黄皮书(2/7)
解读以太坊黄皮书(3/7)


这次我们要聊的话题是——以太坊上的交易是如何执行的。在本文中,我们会学到交易验证规则,以及它们存在的原因;接着深入理解交易的执行过程,和节点在验证交易时采取的每个步骤。

本文是解读以太坊黄皮书系列的第四篇文章, 该系列文章的宗旨是为了让大家对以太坊黄皮书有更清晰的认识,让更多的人了解以太坊。如果你错过前面几篇文章,以下是相关链接:

(免责声明:本文基于 2019 年 10 月 20 号拜占庭 7e819ec 版本的黄皮书进行介绍)

介绍

在本系列文章中,我们已经讨论过作为分布式计算机,以太坊是如何运作的,以及使用者如何通过向系统发送交易与以太坊进行交互(还谈到交易手续费的概念)。

在第一篇博文里,我们了解了以太坊的状态转换函数,还有以太坊如何通过连续的状态转换实现计算机的功能。

简单来说,状态转换函数使用当前的状态及交易作为输入,计算出下一个状态。

1

-以太坊状态转换函数-

在深入了解以太坊中的节点如何执行交易之前,我们先聊聊一笔交易是如何被验证的。

交易验证

在执行交易之前,节点会先验证该交易是否满足一些基本(固有)规则。如果连这些基本规则都通过不了,节点就不会执行该交易。

这些交易的固有规则如下:

  1. 满足 RLP 编码格式
  2. 具备合法签名
  3. 具备合法 nonce (与交易发送方的当前 nonce 值相同)
  4. 执行交易的固有成本(intrinsic cost)小于该交易设置的 gas 上限交易
  5. 发送方的账户余额大于等于交易所需的预付款

还有一条规则,它不属于交易固有规则——如果一系列已准备好打包到区块中的交易,加上这条交易之后,会使得所有交易的总 Gas Limit 超过区块的 Gas 上限,那么该笔交易就不能和那些交易一起打包到一个区块中。

让我们展开每一条规则,来解释这些规则如何运作及为何存在。

交易必须符合合规的 RLP 编码

这条规则可能最好直观理解。RLP (Recursive Length Prefix,又称为递归长度前缀编码)是一种用于序列化以太坊中的对象的编码方法;和其他方法相同,如果你不按照 RLP 对物件编码,则无法对该物件进行解码,你也就无法通过数据编码得到原始对象的信息。

该规则的目的是确保以太坊客户端收到交易后,能够成功解码并执行。

交易必须具备合法签名

假设你的以太坊账户中有很多以太币,现在有人试图发起一笔交易,从你的帐户把钱转走以据为己有,你怎么看?你绝对不想看到有人冒充你,并偷走你的钱,这就是为什么我们需要交易签名。

以太坊采用非对称加密,确保只有实际控制者能够从账户发起交易。与此同时,这种密码学工具还能让其他人验证该交易的确是由账户的实际控制者发起。

我不会展开讨论 ECDSA (以太坊选择的非对称密码算法)的细节,因为我们只需要知道最基础的概念就行。

在非对称密码学中,公钥和私钥是成对存在的。私钥应该完全保密,而公钥可以分享给任何人;私钥可以用来进行签名,这个签名可以用对应的公钥加以验证。在以太坊上签署一笔由你发起的交易,就相当于为在一封你写的信上签名,不同之处在于密码学签名比手写签名还要难伪造得多!

在以太坊上,账户地址根据个人的公钥来生成。当发送一笔交易时,私钥被用来签署交易(还记得 v 、r 、s 这几个包含在交易里的值吗?),接着所有节点就能确定这笔交易是不是真的由关联账户的私钥所有者签署的。

不具备合法签名的交易没有任何执行的意义,因此必须有合法签名就成了交易的固有规则之一。

交易 nonce 和账户 nonce 必须匹配

在以太坊中,账户 nonce 值代表该账户发送的交易数量(如果是合约账户,则 nonce 值指的是账户所创建的合约数量)。如果没有 nonce ,同一笔交易可能被错误地执行多次(也就是所谓的 “重放攻击”)。考虑到以太坊的分布式特性,不同的节点可能会试图把同一笔交易打包进不同的区块,将重复的交易上链。假设一笔你把钱转给某人的交易被误打包了两次,导致你重复转了两次钱,你心里一定很不是滋味。

每当用户创建一笔新的交易,他们必须设置能匹配当前账户 nonce 值的交易 nonce 值,当执行交易时,节点会检查交易 nonce 是否匹配账户 nonce 。

如果因为某些原因,导致同一笔交易被重复提交给节点,此时,因为账户 nonce 值已经增加,所以重复提交的交易会被视为不合法。

以太坊强制要求交易 nonce 值与账户 nonce 值匹配,这么做除了能避免重放攻击,还能确保一笔交易只会执行及改变状态一次。

交易的固有成本必须小于该交易设置的 gas 上限

前一篇博文中,我们说明了为什么使用以太坊需要付费,以及 gas 的概念。总的来说,每一笔交易都有与之关联的 gas ——发送一笔交易的成本包含两部分:固有成本执行成本

执行成本根据该交易需要使用多少以太坊虚拟机(EVM)的资源来运算而定,执行一笔交易所需的操作越多,则它的执行成本就越高。

固有成本由交易的负载( payload )决定,交易负载分为以下三种负载:

  • 如果该交易是为了创建智能合约,则负载就是创建智能合约的 EVM 代码
  • 如果该交易是为了调用智能合约的函数,则负载就是执行消息的输入数据
  • 如果该交易只是单纯在两个账户间转账,则负载为空

假设 Nzeros 代表交易负载中,字节为 0 的字节总数;Nnonzeros 代表交易负载中,字节不为 0 的字节总数。可以通过下列公式计算出该交易的固有成本(黄皮书 6.2 章,方程式 54、55 和 56):

固有成本 = Gtransaction + Gtxdatazero * Nzeros + Gtxdatanonzero * Nnonzeros + Gtxcreate

在黄皮书的附录 G 中,可以看到一份创建和执行交易的相关成本的费用表。与固有成本相关的内容如下:

  • Gtransaction = 21,000 Wei
  • Gtxcreate = 32,000 Wei
  • Gtxdatazero = 4 Wei
  • Gtxdatanonzero = 68 Wei (在伊斯坦布尔升级时会改为 16 wei)

当我们了解固有成本是什么,就能理解为什么一旦交易的固有成本高于 Gas 限制,则该交易就会被视为非法。Gas Limit 规定了一笔交易在执行时,能够消耗掉的 Gas 上限;如果还没开始执行该交易前,我们就知道它的固有成本高于 Gas 上限,那我们就没有理由执行这笔交易。

交易发送方的账户余额必须大于等于交易所需的预付款

交易预付款指的是在交易执行前,从交易发送方账户,预先扣除的 Gas 数量。

我们可以通过下面的公式算出交易预付款:

预付款 = gasLimit * gasPrice + value

一笔交易的 Gas Limit,指的是交易发送方愿意花在执行该交易上的 Gas 最大值;Gas Price 指的是每一单位 Gas 的单价;交易 Value 指的是发给消息接收者的 Wei 的数量(例如转账金额),或是投入要创建的合约中的准备金。如果要进一步了解什么是 Gas ,以及为什么执行交易要耗费 Gas,可以看我们前一篇博文

因为交易预付款在交易执行前就会先扣除,所以一旦交易发送方的账户余额少于预扣额,这笔交易就没有执行的必要了。

交易的 Gas Limit 必须小于等于区块的 Gas 上限

这条规则不属于固有规则,不过这是节点在选择要打包的交易时,需要遵守的基本要求。区块 Gas 上限是能够 “装在” 该区块中的交易所用总 Gas 数的上限。

当节点在选择要打包的交易时,节点必须确保加入这笔交易后,区块里的交易所用总 Gas 数不会超过区块 Gas 上限。对于要被打包的交易来说,其 Gas Limit 加上其他交易的 Gas Limit 总和,必须小于等于区块 Gas 上限。当然,如果有一笔交易不能被打包进入当前区块,它还是有机会被后面的区块打包的。

执行交易

验证完交易之后,是时候执行它了。在以太坊中,执行交易会改变状态——好几笔交易被打包进一个区块,每个区块就相当一个交易列表;当交易被顺序执行后,会输出新的合法状态。

交易按照以下步骤执行:

  1. 将发送者账户 nonce 值加 1
  2. 从发送者账户扣除交易预付额( gasLimit * gasPrice )
  3. 确定该交易能够用于执行的 gas 值(gasLimit - intrinsic cost)
  4. 执行该交易包含的操作(转账、调用或创建智能合约)
  5. 通过 SELFDESTRUCT 和 SSTORE 函数对发送者退款
  6. 退还交易发送者任何未使用的 gas
  7. 向受益人账户(通常属于挖出包含该交易的区块的矿工)转入挖矿收益

增加发送者账户的 nonce 值

每当发送一笔交易,发送者账户 nonce 就会增加。这个操作在交易执行之初就会完成,如果交易执行失败,则账户 nonce 值回滚。

从发送者账户扣除交易预付额

我们会从发送者账户余额里扣除交易预付额,这个机制很简单——由发送者为自愿付出的执行交易成本(gasLimit * gasPrice)付费。

计算可用于执行交易的 gas

交易的 gas 总额(gas limit)扣掉固有成本后,剩下的就是可用于执行交易的 gas 。

执行该交易所包含的操作

执行交易还涉及 EVM 的操作列表,其中唯一完全不需要 EVM 操作的交易——就是普通转账。

每一项 EVM 操作都有对应的 gas 成本;在交易执行过程中,每做了一项 EVM 操作,就会从可用 gas 中扣掉对应的 gas 成本。直到下列两种情况中的一种出现才停止:

  • 可用 gas 被耗尽,执行失败
  • 执行结束后可用 gas 还有剩,或是刚好为零

通过 SELFDESTRUCT 和 SSTORE 函数对发送者退款

在以太坊中,SELFDESTRUCT 操作码用于销毁不再需要的智能合约。每销毁一个合约,执行者能够收取 24,000 Wei 。

同样的,当使用 SSTORE 操作码写入 0 (有效删除值)的时候,操作者每写入一个 0 ,就能收取 1500 Wei 。

关于退款,有件有趣的事情是,退款也有上限。该上限确保矿工可以算出执行交易所需的计算时间的上界。(更多关于 gas 费用和退款的详细说明,可以在以太坊的设计合理性一文中找到)。

还有一个重点是,必须在交易所包含的操作都执行结束后,才会进行退款。因此任何应该返还的 gas 都不会被交易执行过程所消耗,从而避免了可能出现的永远不会耗尽 gas 的交易

退还交易发送者任何未使用的 gas

如果用于交易的预付款超过交易所使用的 gas,则发送方有权在执行交易后收回剩余的 gas。

向受益人账户支付矿工费用

执行交易所使用的所有 Gas 被视为交易手续费,由矿工获得。这种机制激励矿工持续出块,并在网络安全层面永续合作。

结语

在本文中,我们详细讨论了交易的验证及执行(黄皮书第 6 章内容)。在第 7 、8 章会介绍更多类型的交易(合约创建及调用),我后会继续更新关于这几个章节的博文。

我相信,想要彻底了解交易验证及执行的细节,最好的方法就是阅读随便一个实现协议的以太坊客户端源码。作为 Besu 的贡献者,我很熟悉它的实现,所以我建议即使你不是非常精通 Java ,你还是可以看看它的源码。可以从这两个部分开始读起:

MainnetTransactionValidation.javaMainnetTransactionProcessor.java

再强调一下,如果你发现文中任何错误或是值得改进的地方,或是有疑问,请如往常在评论中告诉我。

下期见!


原文链接: https://www.lucassaldanha.com/transaction-execution-ethereum-yellow-paper-walkthrough-4-7/
作者: Lucas Saldanha
翻译&校对: IAN LIU & 阿剑

本文由原作者授权 EthFans 翻译及再出版。


你可能还会喜欢:

科普 | 以太坊中的账户、交易、Gas 和区块 Gas Limit
Echo | 以太坊简介(注释版)
引介 | 以太坊交易的生命周期

 
0 人喜欢