{"content":{"title":"关于 EVM ， 你需要知道的一些概念","body":">- 原文链接：[www.evm.codes/about](https://www.evm.codes/about)\r\n>- 译者：[AI翻译官](https://learnblockchain.cn/people/19584)，校对：[翻译小组](https://learnblockchain.cn/people/412)\r\n>- 本文链接：[learnblockchain.cn/article…](https://learnblockchain.cn/article/11854)\r\n    \r\n\r\n \r\n\r\n## 介绍\r\n\r\n### 什么是以太坊虚拟机？\r\n\r\n以太坊虚拟机（或 [EVM](https://ethereum.org/en/developers/docs/evm/)）是一个基于栈的计算机，负责执行智能合约指令。所有 EVM 指令的参数都来自栈，除了 [PUSHx](https://www.evm.codes/#60)，它的参数来自代码。每条指令都有栈输入，即它们可能需要的参数，以及栈输出（它们的返回值）。这些指令及其操作码的列表可以在我们的 [参考](https://www.evm.codes/) 中找到。\r\n\r\n### 什么是智能合约？\r\n\r\n智能合约是一组指令。每条指令都是一个操作码（带有自己的助记符以供参考，文本表示其在 0 到 255 之间的分配值）。当 EVM 执行智能合约时，它按顺序读取和执行每条指令，除了 [JUMP](https://www.evm.codes/#56) 和 [JUMPI](https://www.evm.codes/#57) 指令。如果某条指令无法执行（例如，当 gas 不足或栈上没有足够的值时），执行将回退。交易回退也可以通过 [REVERT](https://www.evm.codes/#FD) 操作码触发（它会退还其调用上下文中未使用的 gas 费用，而与其他所有回退情况中消耗的所有 gas 相对）。在交易回退的情况下，交易指令所规定的任何状态更改将恢复到交易之前的状态。\r\n\r\n \r\n## 执行环境\r\n\r\n当 EVM 执行智能合约时，会为其创建一个上下文。该上下文由多个数据区域组成，每个区域有不同的目的，以及一些变量，例如程序计数器、当前调用者、被调用者和当前代码的地址。\r\n\r\n[](https://www.evm.codes/about#code)\r\n\r\n### 代码\r\n\r\n代码是存储指令的区域。存储在代码中的指令数据作为合约账户状态字段的一部分是持久的。外部拥有账户（或 EOA）具有空的代码区域。代码是 EVM 在智能合约执行期间读取、解释和执行的字节。代码是不可变的，这意味着它不能被修改，但可以通过指令 [CODESIZE](https://www.evm.codes/#38) 和 [CODECOPY](https://www.evm.codes/#39) 进行读取。一个合约的代码可以被其他合约读取，使用指令 [EXTCODESIZE](https://www.evm.codes/#3B) 和 [EXTCODECOPY](https://www.evm.codes/#3C)。\r\n\r\n[](https://www.evm.codes/about#counter)\r\n\r\n### 程序计数器\r\n\r\n程序计数器（PC）编码了 EVM 应该下一个读取的存储在代码中的指令。程序计数器通常按一个字节递增，以指向下一个指令，但有一些例外。例如， [PUSHx](https://www.evm.codes/#60) 指令的长度超过一个字节，并导致 PC 跳过其参数。[JUMP](https://www.evm.codes/#56) 指令不会增加 PC 的值，而是将程序计数器修改为栈顶指定的位置。[JUMPI](https://www.evm.codes/#57) 也是如此，如果其条件为真（非零代码值），否则它像其他指令一样递增 PC。\r\n\r\n \r\n\r\n### 栈\r\n\r\n栈是一个 32 字节元素的列表，用于存储智能合约指令的输入和输出。每个调用上下文创建一个栈，并在调用上下文结束时销毁。当一个新值放入栈中时，它被放在顶部，只有顶部的值被指令使用。栈当前的最大限制为 1024 个值。所有指令都与栈交互，但可以通过指令如 [PUSH1](https://www.evm.codes/#60)、[POP](https://www.evm.codes/#50)、[DUP1](https://www.evm.codes/#80) 或 [SWAP1](https://www.evm.codes/#90) 直接操作栈。\r\n\r\n \r\n\r\n### 内存\r\n\r\nEVM 内存不是持久的，并在调用上下文结束时被销毁。在调用上下文开始时，内存初始化为 0。读取和写入内存通常通过指令 [MLOAD](https://www.evm.codes/#51) 和 [MSTORE](https://www.evm.codes/#52) 分别完成，但也可以通过其他指令如 [CREATE](https://www.evm.codes/#F0) 或 [EXTCODECOPY](https://www.evm.codes/#F3) 访问。我们将在本文后面讨论 [内存大小计算](https://www.evm.codes/about#memoryexpansion)。\r\n\r\n \r\n\r\n### 存储\r\n\r\n存储是 32 字节槽到 32 字节值的映射。存储是智能合约的持久内存：合约写入的每个值在调用完成后都被保留，除非其值被更改为 0，或执行了 [SELFDESTRUCT](https://www.evm.codes/#FF) 指令。从未写入的键读取存储的字节也会返回 0。每个合约都有自己的存储，不能读取或修改其他合约的存储。存储通过指令 [SLOAD](https://www.evm.codes/#54) 和 [SSTORE](https://www.evm.codes/#55) 进行读取和写入。\r\n\r\n \r\n\r\n### calldata\r\n\r\ncalldata 区域是作为智能合约交易的一部分发送到交易的数据。例如，在创建合约时，calldata 将是新合约的构造函数代码。calldata 是不可变的，可以通过指令 [CALLDATALOAD](https://www.evm.codes/#35)、[CALLDATASIZE](https://www.evm.codes/#36) 和 [CALLDATACOPY](https://www.evm.codes/#37) 进行读取。重要的是要注意，当合约执行 xCALL 指令时，它也会创建一个内部交易。因此，在执行 xCALL 时，新的上下文中会有一个 calldata 区域。\r\n\r\n \r\n\r\n### 返回数据\r\n\r\n返回数据是智能合约在调用后返回值的方式。它可以通过 [RETURN](https://www.evm.codes/#F3) 和 [REVERT](https://www.evm.codes/#FD) 指令由合约调用设置，并可以通过调用合约使用 [RETURNDATASIZE](https://www.evm.codes/#3D) 和 [RETURNDATACOPY](https://www.evm.codes/#3E) 进行读取。\r\n\r\n \r\n\r\n## Gas 费用\r\n\r\n以太坊区块链上的每笔交易在添加到区块链之前都经过第三方验证者的审核。这些验证者因进行此审核过程并将交易添加到区块链而获得激励费用的补偿。费用因交易而异，取决于不同分叉的不同变量。计算费用的一些变量包括：\r\n\r\n  \r\n*   **当前一个 gas 单位的价格：** Gas 或 gwei 是以太坊的一种计量单位，用于费用支付。Gas 价格随时间变化，基于当前对区块空间的需求，以 ETH 每 gas 进行衡量。\r\n  \r\n*   **calldata 大小：** 每个 calldata 字节都需要消耗 gas，交易数据的大小越大，gas 费用越高。calldata 对于等于 0 的每个字节需要 4 gas，对于其他字节需要 16 gas（在硬分叉 Istanbul 之前为 64）。\r\n  \r\n*   **内在 Gas：** 每笔交易的内在成本为 21000 gas。创建合约的成本为 32000 gas，此外还有交易成本。再次强调：calldata 对于等于 0 的每个字节需要 4 gas，对于其他字节需要 16 gas（在硬分叉 **Istanbul** 之前为 64）。此成本在执行任何操作码或转账之前从交易中支付。\r\n  \r\n*   **操作码固定执行成本：** 每个操作码在执行时都有一个固定成本，以 gas 计量。此成本在所有执行中是相同的，尽管在新的硬分叉中可能会有所变化。请参阅我们的 [参考](https://www.evm.codes/) 以了解每个操作码和分叉的具体成本。\r\n  \r\n*   **操作码动态执行成本：** 一些指令根据其参数执行的工作量不同。因此，除了固定成本外，一些指令还有动态成本。这些动态成本取决于几个因素（在不同的硬分叉中有所不同）。请参阅我们的 [参考](https://www.evm.codes/) 以了解每个操作码和分叉的具体计算。\r\n\r\n要获得程序的 gas 成本的完整估算，结合你的编译器选项和特定状态及输入，请使用 [Remix](https://remix.ethereum.org/) 或 [Truffle](https://trufflesuite.com/) 等工具。\r\n\r\n[](https://www.evm.codes/about#memoryexpansion)\r\n\r\n### 内存扩展\r\n\r\n在智能合约执行期间，可以通过操作码访问内存。当首次访问一个偏移量（无论是读取还是写入）时，内存可能会触发扩展，这会消耗 gas。\r\n\r\n当访问的字节偏移量（模 32）大于先前的偏移量时，可能会触发内存扩展。如果发生更大的偏移量触发内存扩展，则访问更高偏移量的成本会被计算并从当前调用上下文的总 gas 中扣除。\r\n\r\n给定内存大小的总成本计算如下：\r\n\r\n    memory_size_word = (memory_byte_size + 31) / 32\r\n    memory_cost = (memory_size_word ** 2) / 512 + (3 * memory_size_word)\r\n\r\n当触发内存扩展时，仅需为额外的内存字节支付费用。因此，特定操作码的内存扩展成本为：\r\n\r\n    memory_expansion_cost = new_memory_cost - last_memory_cost\r\n\r\n`memory_byte_size` 可以通过操作码 [MSIZE](https://www.evm.codes/#59) 获得。通过 [MSIZE](https://www.evm.codes/#59) 触发的内存扩展成本是平方增长的，从而抑制了通过使更高偏移量变得更昂贵来过度使用内存。任何访问内存的操作码都可能触发扩展（例如 [MLOAD](https://www.evm.codes/#51)、[RETURN](https://www.evm.codes/#F3) 或 [CALLDATACOPY](https://www.evm.codes/#37)）。请使用我们的 [参考](https://www.evm.codes/) 来查看哪些操作码能够访问内存。请注意，字节大小参数为 0 的操作码无论其偏移量参数如何都不会触发内存扩展。\r\n\r\n \r\n\r\n### 访问列表\r\n\r\n访问列表是按外部交易定义的，而不是按调用定义的。每笔交易可以通过其发送者、calldata 或被调用者的某种组合来定义。交易可以是外部的或内部的。外部交易是发送到以太坊网络的交易。内部交易是由执行了 xCALL 指令的外部交易触发的。因此，内部交易也被称为调用。访问列表可以被视为两种独立类型的列表：触及地址的列表和触及合约存储槽的列表。\r\n\r\n当交易、指令访问某个地址，或用作调用者或被调用者时，该地址会被放入访问列表。调用操作码 [BALANCE](https://www.evm.codes/#31) 时，如果地址不在访问列表中，则成本会更高。其他可以修改访问列表的操作码包括 [EXTCODESIZE](https://www.evm.codes/#3B)、[EXTCODECOPY](https://www.evm.codes/#3C)、[EXTCODEHASH](https://www.evm.codes/#3F)、[CALL](https://www.evm.codes/#F1)、[CALLCODE](https://www.evm.codes/#F2)、[DELEGATECALL](https://www.evm.codes/#F4)、[STATICCALL](https://www.evm.codes/#FA)、[CREATE](https://www.evm.codes/#F0)、[CREATE2](https://www.evm.codes/#F5) 和 [SELFDESTRUCT](https://www.evm.codes/#FF)。每个操作码在修改访问集时都有其自身的成本。\r\n\r\n触及槽列表是由合约地址访问的存储槽键的列表。槽列表初始化为空。当操作码访问不在列表中的槽时，它会将其添加到列表中。可以修改触及槽列表的操作码是 [SLOAD](https://www.evm.codes/#54) 和 [SSTORE](https://www.evm.codes/#55)。同样，这两个操作码在修改访问列表时都有其自身的成本。\r\n\r\n如果地址或存储槽在集合中，则称为“热”；否则称为“冷”。在交易中首次触及的存储槽在交易期间从冷变为热。交易可以通过 EIP-2930 访问列表预先指定合约为热。某些操作码的动态成本取决于地址或槽是热还是冷。\r\n\r\n在交易执行开始时，触及地址集初始化为包括以下地址，因此始终是“热”的：\r\n\r\n  \r\n*   在 **Berlin** 硬分叉之后：所有预编译合约地址以及 **tx.sender** 和 **tx.to**（或如果是合约创建交易，则为正在创建的地址）。\r\n  \r\n*   在 **Shanghai** 硬分叉之后：[COINBASE](https://www.evm.codes/#41) 地址。\r\n\r\n如果上下文被回退，访问加热效果将恢复到上下文之前的状态。\r\n\r\n \r\n\r\n### Gas 退款\r\n\r\n某些操作码可以触发 gas 退款，从而减少交易的 gas 成本。Gas 退款在交易结束时应用。如果交易的 gas 不足以达到其运行的结束，则无法触发其 gas 退款，交易将失败。随着 **London** 硬分叉的引入，gas 退款的两个方面发生了变化。首先，退款的 gas 量限制从总交易成本的一半降低到总交易成本的五分之一。其次，[SELFDESTRUCT](https://www.evm.codes/#FF) 操作码不能触发 gas 退款，只有 [SSTORE](https://www.evm.codes/#55)。\r\n\r\n \r\n\r\n## 其他 EVM 相关资源\r\n\r\n\r\n## Other EVM Related Resources\r\n\r\n[以太坊EVM插图（2018）](https://takenobu-hs.github.io/downloads/ethereum_evm_illustrated.pdf)，[以太坊的历史和分叉](https://ethereum.org/en/history/), [EVM：从solid到字节码，内存和存储](https://www.youtube.com/watch?v=RxL_1AfV7N4), [EVM Deep Dives by noxx](https://noxx.substack.com/p/evm-deep-dives-the-path-to-shadowy), [The EVM Chapter in The Mastering Ethereum book](https://github.com/ethereumbook/ethereumbook/blob/develop/13evm.asciidoc), [EOF （EVM对象格式）：完整指南](https://x.com/uttam_singhk/status/1830526179105001771) \r\n \r\n*感谢[wolflo]（https://github.com/wolflo/evm-opcodes）提供成本说明。查看[我们的博客]（https://blog.smlxl.io/）了解更多关于EVM和其他区块链概念的文章\r\n\r\n\r\n> 我是 [AI 翻译助手](https://learnblockchain.cn/people/19584)，为大家转译优秀英文文章，如有翻译不通的地方，还请包涵～"},"author":{"user":"https://learnblockchain.cn/people/26469","address":null},"history":null,"timestamp":1740987471,"version":1}