# 温故知新：EVM 101

By [EthereumCN](https://paragraph.com/@ethereumcn-2) · 2021-11-19

---

来源 | [medium.com/@somubhargava97](https://medium.com/@somubhargava97/introduction-to-evm-part-1-b27203883a03)

作者 | Somu Bhargava

![图片来源： executium ，unsplash](https://storage.googleapis.com/papyrus_images/9f307a8bdf02280f2a0fc7c4453049bfd39df023e0489e3528d68e4911d49993.jpg)

图片来源： executium ，unsplash

EVM 对于以太坊状态机就好比处理器对于计算机般至关重要。它主要是用于执行[智能合约](https://ethereum.org/en/developers/docs/smart-contracts/)的逻辑和进行相应的状态转换。在我们深入 EVM 时，现在先简单看看以太坊吧。

以太坊简介
=====

以太坊是开源、去中心化的区块链，具有[智能合约](https://ethereum.org/en/developers/docs/smart-contracts/)的额外功能。它意味着，在网络上发生的交易种类大致是 ETH 转账 (或) 智能合约部署 (或) 智能合约调用——所有这些都会改变以太坊区块链的状态。一堆这些交易按照特定顺序组合成一个区块，执行该区块里交易合起来所需的 gas 应小于或等于区块 gas 上限 (译者注：EIP-1559 后 gas 上限是 30 m) 。

一笔交易大概如下：

    Transaction -
        nonce - 由交易发件人发送的交易号
        gas price - 这笔交易的 gas 价格 (单位为 wei)
        gas limit - 这笔交易可以使用的最高 gas 量
        to - 这笔交易的受款人
        value - 转给受款人的 wei 数值
        data - 信息调用的输入数据
        v, r, s - 可恢复的发件人 Secp256K1 签名
    

以太坊的世界状态就是地址和其对应的账户状态之间的映射。账户状态大概如下：

    Account -
        nonce - 到现在从该相应账户发送的交易数量的值
        balance - 这个账户持有的 wei 数量
        storageRoot - 256 位键值对之间的映射
        codeHash- 属于这个账户的的 EVM 代码的不可变哈希值。当有人与这个账户进行交易时，此代码将被执行。
    

矿工通过汇集一些交易来形成一个区块，并获取打包该区块的工作量证明。这个区块随后会在网络被广播到其他参与以太坊区块链的节点。现在，其他接收到此区块的节点必须验证其有效性，因为矿工可能是恶意的，可能会出现传输错误，中间人攻击 ([man-in-the-middle attack](https://en.wikipedia.org/wiki/Man-in-the-middle_attack))。每个节点 (大致) 从以下方面验证区块：

*   它们按照以太坊协议验证区块特性 (如区块高度、父块哈希值、时间戳、gas 上限、使用的 gas 量等)
    
*   然后这个区块里的交易会被逐一执行。执行每笔交易都会消耗一些 gas 并改变区块链的状态。因此，在区块执行的最后，我们会得到一个结果状态，它由[状态树](https://eth.wiki/fundamentals/patricia-tree)根来表示，这个根是唯一的。如果矿工和验证节点对协议和交易都达成共识，那么状态根应该是唯一的。矿工添加在区块头获得的状态根，随后其他节点根据区块头提到的状态根对其进行验证。
    

**以太坊虚拟机 (Ethereum Virtual Machine, EVM)** 负责执行交易和更新区块链状态。让我们在下文了解更多 EVM 的细节。

以太坊虚拟机 (EVM)
============

[智能合约](https://ethereum.org/en/developers/docs/smart-contracts/)可以被编译成 EVM 字节码。作为类比，把 Solidity 代码 (编写智能合约的通用语言) 想象成 C++代码。把 EVM 字节码看作是机器代码，它是可以被处理器理解和执行的。因此，EVM 可以被认为是以太坊的一个处理器。EVM 字节代码是操作码和数据序列，可以被 EVM 处理，形成状态转换。

因此，EVM 在执行交易中的作用：

*   使一个账户转账 WEI (1 ETH = 10¹⁸ WEI) 到另一个账户变得可能
    
*   如果交易的受款人账户有一些字节码与 EVM 相关，EVM 必须执行相应的字节码 (可能使用从 `transaction.data` 栏位获取的输入数据)。
    

现在，任何账户都可以有与 EVM 相关的字节码吗？答案是否定的。以太坊有两种类型的账户——外部所有账户 [(EOA)](https://ethdocs.org/en/latest/contracts-and-transactions/account-types-gas-and-transactions.html#account-interactions-example-betting-contract) 和合约账户 [(CA)](https://ethdocs.org/en/latest/contracts-and-transactions/account-types-gas-and-transactions.html#account-interactions-example-betting-contract)。EOA 是具有私人密钥关联的账户，由像个人、组织等的外部实体操作。另一方面，合约账户是通过部署智能合约创建的。它们没有相关私钥，并由外部实体对其进行的代码调用 (通过区块链上的交易) 控制。

基本上，每个节点上都会启动一个 EVM 实例来执行每笔交易。但是，只有当交易的受款方 (或目标) 是一个合约账户时，EVM 实例才会执行字节码。

现在，让我们来看看 EVM 的架构。

![图表来自 https://ethereum.org/en/developers/docs/evm/](https://storage.googleapis.com/papyrus_images/650209483ba4c7d35fdcd95fd6ae3927d351d6640b9eae2d2de3ea1e783eec06.png)

图表来自 https://ethereum.org/en/developers/docs/evm/

启动的每个 EVM 实例都是为运行一个特定的字节码 (由于交易的目标是一个合约账户)。因此，字节码就像是 EVM 实例的 ROM，是不能修改的。类似于[图灵机](https://etherscan.io/address/www.notion.so))，EVM 由一个程序计数器 (Program Counter)、堆栈 (Stack)、内存 (Memory) 和外部存储器。这个外部存储器对所有交易进行永久存储，但其余组件的存储是易失的，并会对 EVM 的每个实例进行重新实例化。

让我们来逐个了解这些组件——

*   程序计数器 (PC) 只是一个指向字节码中下一个操作码的指针，由 EVM 执行。它是一个非负整数，范围是 \[0, number\_of\_bytes(bytecode)-1\]
    
*   EVM 里的堆栈可以有最多 1024 个条目，每个条目都是 256 位 (32 字节) 的无符号整数。
    
*   EVM 里的内存是可以无限扩充的 (尽管你必须为内存扩充本身支付额外的费用) 且每个条目都有一个 8 位 (1字节) 的无符号整数。
    
*   这里的外部存储就是所有账户存储的集合。( EVM 的字节码可以写到目标账户或外部账户的存储空间 )
    

与计算机处理器如何根据每个指令集理解特定的操作码类似，EVM 也需要理解操作码。每个 EVM 操作码都是一个字节，因此根据理论，最多可以有 256 个操作码，但这里就不列出全部 256 个操作码了。EVM 操作码主要可分为以下几类——

*   控制像 PC、堆栈、内存和存储状态的操作码
    
*   算数运算和按位运算
    
*   环境信息——关于区块、当前交易或某特定账户的属性的信息
    
*   日志操作——添加日志记录
    
*   系统操作——创建新的合约账户、对另一个账户进行信息调用、销毁已创建的合约账户等
    

很快会出下一篇更详细的关于操作码的文章。敬请关注！

ECN的翻译工作旨在为中国以太坊社区传递优质资讯和学习资源，文章版权归原作者所有，转载须注明原文出处以及ethereum.cn，若需长期转载，请联系[eth@ecn.co](mailto:eth@ecn.co)进行授权。

---

*Originally published on [EthereumCN](https://paragraph.com/@ethereumcn-2/evm-101)*
