主页 > imtoken钱包安全吗 > 以太坊黄皮书解读(4/7)

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

imtoken钱包安全吗 2023-09-10 05:07:51

这次我们要聊的话题是——以太坊上的交易是如何执行的。 在本文中,我们将了解交易验证规则及其存在的原因; 然后更深入地了解交易的执行方式以及节点验证交易的每个步骤。

本文是以太坊黄皮书解读系列文章的第四篇。 本系列文章旨在让大家对以太坊黄皮书有一个更清晰的认识,让更多人了解以太坊。

(免责声明:本文基于2019年10月20日拜占庭7e819ec版本黄皮书)

介绍

在本系列文章中,我们讨论了以太坊如何作为分布式计算机工作,以及用户如何通过向系统发送交易来与以太坊进行交互(还涉及交易费用的概念)。

在第一篇博文中,我们了解了以太坊的状态转换功能,以及以太坊是如何通过不断的状态转换来实现计算机功能的。

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

以太坊

-以太坊状态转换函数-

在深入探讨以太坊中的节点如何执行交易之前,让我们先谈谈如何验证交易。

交易验证

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

这些交易的内在规则如下:

还有一个规则,不属于交易的固有规则——如果一系列准备打包进区块的交易,再加上这个交易,就会使得所有交易的总Gas Limit超过gas limit的该块,则该交易不能与这些交易一起包含在一个区块中。 让我们展开每条规则来解释它们的工作原理以及它们存在的原因。

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

这条规则可能最能直观地理解。 RLP(Recursive Length Prefix,也称为递归长度前缀编码)是以太坊中一种序列化对象的编码方式; 和其他方法一样,如果你不按照RLP对一个对象进行编码,那么它就不能被编码,你不能通过数据编码得到原始对象的信息。 该规则的目的是确保以太坊客户端在收到交易后能够成功解码并执行。

交易必须有合法签名

假设你的以太坊账户里有很多以太币,现在有人试图发起一笔交易,想从你的账户中转出钱据为己有,你怎么看? 你肯定不希望看到有人冒充你并偷走你的钱,这就是为什么我们需要交易签名。 以太坊使用非对称加密确保只有实际控制人才能从账户发起交易。 同时,这种加密工具允许他人验证交易确实是由账户的实际控制人发起的。 ECDSA(以太坊选择的非对称加密算法)的细节我就不讨论了,因为我们只需要了解最基本的概念即可。 在非对称密码学中,公钥和私钥成对存在。

私钥应完全保密,而公钥可以与任何人共享; 私钥可以用来签名,这个签名可以用对应的公钥来验证。 在以太坊上签署您发起的交易等同于签署您写的一封信,不同之处在于密码签名比手写签名更难伪造! 在以太坊上,账户地址是根据个人的公钥生成的。 发送交易时,使用私钥对交易进行签名(还记得交易中包含的值v、r、s吗?),然后所有节点就可以判断交易是否真的由关联的签名者发出帐户私钥的所有者。 没有合法签名的交易没有执行的意义,因此需要合法签名成为交易的内在规则之一。

交易随机数和账户随机数必须匹配

在以太坊中,账户nonce值代表账户发送的交易数量(如果是合约账户,nonce值是指账户创建的合约数量)。 如果没有随机数,同一笔交易可能会被错误执行多次(所谓的“重放攻击”)。 考虑到以太坊的分布式特性,不同的节点可能会尝试将同一笔交易打包到不同的区块中,并将重复的交易上传到链上。 假设一笔你转账给别人的交易被错误地打包了两次,导致你转账两次,你一定觉得很不爽。

每当用户创建新交易时,他们必须设置与当前帐户随机数值匹配的交易随机数值。 执行交易时,节点会检查交易nonce是否与账户nonce匹配。 如果由于某种原因,同一笔交易被重复提交给节点,此时由于账户nonce值增加,重复提交的交易将被视为非法。 以太坊强制要求交易的nonce值与账户的nonce值相匹配,这不仅可以避免重放攻击以太坊合法吗,还可以保证一笔交易只会被执行一次并改变状态。

交易的固有成本必须小于交易设定的gas limit

在上一篇博文中,我们解释了为什么使用以太坊需要付费,以及 gas 的概念。 一般来说,每笔交易都有与之相关的gas——发送交易的成本由两部分组成:固有成本和执行成本。 执行成本取决于交易使用了多少以太坊虚拟机 (EVM) 资源。 执行交易所需的操作越多,其执行成本就越高。 固有成本由交易负载(payload)决定,交易负载分为以下三种负载:

假设Nzeros表示交易负载中bytes为0的总字节数; Nnonzeros表示交易负载中bytes不为0的字节总数。交易的固有成本可以通过以下公式计算(黄皮书6.2章,方程54、55和56): Inherent Cost = Gtransaction + Gtxdatazero * Nzeros + Gtxdatanonzero * Nnonzeros + Gtxcreate 在黄皮书的附录 G 中,您可以看到与创建和执行交易相关的成本的费用表。 与固有成本相关的有:

当我们了解了固有成本是什么之后,我们就可以理解为什么一旦交易的固有成本高于gas limit,交易就会被认为是非法的。 Gas Limit 规定了交易执行时可以消耗的Gas上限; 如果我们在执行交易之前就知道它的固有成本高于Gas上限以太坊合法吗,那么我们就没有理由执行交易。

交易发送方的账户余额必须大于或等于交易所需的保证金

交易预付款是指交易执行前从交易发送方账户中预扣的Gas数量。 我们可以通过以下公式计算交易预付款: Advance payment = gasLimit * gasPrice + value 交易的Gas Limit是指交易发送方愿意为执行交易花费的最大Gas数量; Gas Price 指每笔交易的gas limit 一单位Gas的单价; transaction Value 是指发送给消息接收方的 Wei 数量(如转账金额),或者是投入到要创建的合约中的准备金。 如果您想了解更多关于 Gas 是什么以及为什么执行交易需要 Gas 的信息,您可以阅读我们之前的博文。 由于交易预付款会在交易执行前扣除,一旦交易发送方的账户余额低于预扣金额,则无需执行交易。

交易的gas limit必须小于或等于区块的gas limit

该规则并非固有规则,而是节点在选择交易打包时需要遵守的基本要求。 区块gas limit是区块中可以“装”的交易使用的gas总量的上限。 当节点选择要打包的交易时,节点必须保证添加该交易后,区块中交易使用的gas总量不会超过区块gas的上限。 对于要打包的交易,其 Gas Limit 加上其他交易的 Gas Limit 之和必须小于或等于该区块的 Gas Limit。 当然,如果一个交易不能被打包到当前区块中,它仍然有机会被打包到下一个区块中。

执行交易

验证交易后,就该执行它了。 在以太坊中,执行交易会改变状态——若干笔交易被打包成一个区块,每个区块相当于一个交易列表; 当交易顺序执行时,会输出一个新的合法状态。 交易按以下步骤执行:

增加发件人帐户的随机数值

每次发送交易时,发件人帐户随机数都会增加。 此操作将在事务执行开始时完成。 如果交易执行失败,账户nonce值将被回滚。

从发件人账户中扣除交易预付款

我们将从发送方的账户余额中扣除交易预付款金额。 这种机制非常简单——发送方支付自愿执行的交易成本(gasLimit * gasPrice)。

计算可用于执行交易的气体

从交易的总 gas limit 中扣除固有成本后,剩下的就是可用于执行交易的 gas。

执行交易中包含的动作

执行交易还涉及一系列 EVM 操作,并且唯一根本不需要任何 EVM 操作的交易是正常传输。 每个 EVM 操作都有相应的 gas 成本; 在交易执行过程中,每执行一次EVM操作,就会从可用gas中扣除相应的gas费用。 在出现以下两种情况之一之前不要停止:

通过 SELFDESTRUCT 和 SSTORE 函数向发件人退款

在以太坊中,SELFDESTRUCT 操作码用于销毁不再需要的智能合约。 每销毁一份合约,执行人可以收取 24,000 Wei。 同样,当使用 SSTORE 操作码写入 0(有效删除值)时,操作员可以为每写入一个 0 收取 1500 Wei。 关于退款的一件有趣的事情是退款也有上限。 这个上限确保矿工可以计算出执行交易所需计算时间的上限。 (关于 gas 费用和退款的更详细解释可以在 Ethereum 的 Design Rationality 中找到)。

还有一点很重要,只有在交易中包含的所有操作都执行完毕后,才会进行退款。 因此,任何应该返回的gas都不会被交易执行过程消耗掉,从而避免了可能永远不会用完gas的交易。

将任何未使用的气体退还给交易发送者

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

将矿工费支付到受益人账户

所有用于执行交易的 Gas 都被视为交易费,由矿工获得。 这种机制鼓励矿工在网络安全层面上持续生产区块并持续合作。

结语

在这篇文章中,我们详细讨论了交易的验证和执行(黄皮书第 6 章)。 在第 7 章和第 8 章中,将介绍更多类型的交易(合约创建和调用),我将继续更新有关这些章节的博文。 我认为,彻底了解交易验证和执行细节的最佳方式是阅读任何实现该协议的以太坊客户端的源代码。 作为Besu的贡献者,我很熟悉它的实现,所以我建议即使你Java不是很精通,也可以看看它的源码。 您可以从这两个部分开始阅读:MainnetTransactionValidation.java 和 MainnetTransactionProcessor.java。 下次见!

原文链接: