EVM 对于以太坊状态机就好比处理器对于计算机般至关重要。它主要是用于执行智能合约的逻辑和进行相应的状态转换。在我们深入 EVM 时,现在先简单看看以太坊吧。
以太坊简介
以太坊是开源、去中心化的区块链,具有智能合约的额外功能。它意味着,在网络上发生的交易种类大致是 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)。每个节点 (大致) 从以下方面验证区块:
它们按照以太坊协议验证区块特性 (如区块高度、父块哈希值、时间戳、gas 上限、使用的 gas 量等)
然后这个区块里的交易会被逐一执行。执行每笔交易都会消耗一些 gas 并改变区块链的状态。因此,在区块执行的最后,我们会得到一个结果状态,它由状态树根来表示,这个根是唯一的。如果矿工和验证节点对协议和交易都达成共识,那么状态根应该是唯一的。矿工添加在区块头获得的状态根,随后其他节点根据区块头提到的状态根对其进行验证。
以太坊虚拟机 (Ethereum Virtual Machine, EVM) 负责执行交易和更新区块链状态。让我们在下文了解更多 EVM 的细节。
以太坊虚拟机
智能合约可以被编译成 EVM 字节码。作为类比,把 Solidity 代码 (编写智能合约的通用语言) 想象成 C++代码。把 EVM 字节码看作是机器代码,它是可以被处理器理解和执行的。因此,EVM 可以被认为是以太坊的一个处理器。EVM 字节代码是操作码和数据序列,可以被 EVM 处理,形成状态转换。
因此,EVM 在执行交易中的作用:
使一个账户转账 WEI (1 ETH = 10¹⁸ WEI) 到另一个账户变得可能
如果交易的受款人账户有一些字节码与 EVM 相关,EVM 必须执行相应的字节码 (可能使用从 transaction.data 栏位获取的输入数据)。
现在,任何账户都可以有与 EVM 相关的字节码吗?答案是否定的。以太坊有两种类型的账户——外部所有账户 (EOA) 和合约账户 (CA)。EOA 是具有私人密钥关联的账户,由像个人、组织等的外部实体操作。另一方面,合约账户是通过部署智能合约创建的。它们没有相关私钥,并由外部实体对其进行的代码调用 (通过区块链上的交易) 控制。
基本上,每个节点上都会启动一个 EVM 实例来执行每笔交易。但是,只有当交易的受款方 (或目标) 是一个合约账户时,EVM 实例才会执行字节码。
现在,让我们来看看 EVM 的架构。
图表来自 https://ethereum.org/en/developers/docs/evm/
启动的每个 EVM 实例都是为运行一个特定的字节码 (由于交易的目标是一个合约账户)。因此,字节码就像是 EVM 实例的 ROM,是不能修改的。类似于图灵机,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、堆栈、内存和存储状态的操作码
算数运算和按位运算
环境信息——关于区块、当前交易或某特定账户的属性的信息
日志操作——添加日志记录
系统操作——创建新的合约账户、对另一个账户进行信息调用、销毁已创建的合约账户等
很快会出下一篇更详细的关于操作码的文章。敬请关注!
来源 | medium.com/@somubhargava97
作者 | Somu Bhargava
原文链接: https://medium.com/@somubhargava97/introduction-to-evm-part-1-b27203883a03