了解EVM、区块

以太坊 EVM

从账本到状态机

我们通常用“分布式账本”的类比来描述像比特币这样的区块链,它使用密码学的基本工具来实现去中心化的货币。而以太坊也有自己的本土加密货币并同样遵循着分布式账本规则,但同时它也支持更强大的功能“智能合约”,这也是以太坊可编程的关键,因此以太坊除了是一个分布式账本之外,还是一个状态机器,可以根据预定义的规则(智能合约)在不同区块之间更改。

组件

  • 程序计数器 (Program Counter, PC):记录当前正在执行的指令在字节码中的位置。

  • 栈 (Stack):用于存放操作数。栈项的大小为 256 位,最大深度为 1024 层。

  • 内存 (Memory):一个按字节寻址的连续字节数组,初始为空,执行时按 32 字节(Word)为单位动态扩展。

  • 剩余 Gas (gasAvailable):当前上下文中可供使用的计算资源总量。

  • 存储 (Storage):这是持久化状态的一部分(属于全局状态 σ),不像栈和内存那样在执行结束后消失

EVM 如何工作

EVM 运行在以太坊的每个节点上,当部署一个智能合约时,合约代码会被编译成字节码,然后通过网络广播到所有节点。EVM 在每个节点上执行这些字节码,确保所有节点对交易和合约状态达成共识。

和 Java 的 JVM 不同,EVM 不是一个需要单独下载的独立软件,而是嵌入在以太坊客户端中的运行时环境。开发者可以通过以下方式获取 EVM 环境:

  • 安装以太坊客户端 :如 ethereumjs-vm(js 实现)、evnone(c++实现)、revm(rust 实现) 或 Py-EVM(python 实现),这些客户端都内置了 EVM。
  • 使用开发框架 :如 Hardhat 或 Truffle,它们在本地启动一个模拟的 EVM 环境,方便开发和测试。
  • 在线 IDE :如 Remix IDE,无需本地安装,直接通过浏览器连接到远程 EVM 实例。

比如用 Solidity 编写的合约,.sol 编译为 EVM 字节码(十六进制操作码)和 ABI 应用二进制接口。每个操作码执行会消耗燃料,具体看EVM 操作码

交易执行的三阶段

一笔交易进入 EVM 到产生结果,通常经历三个阶段:

  1. 第一阶段:检查点与预扣费: 验证交易合法性后,系统会增加发送者的 Nonce,并从其余额中扣除预付成本 (upfrontCost),以建立执行环境。

  2. 第二阶段:消息标准化: EVM 会根据交易类型(合约创建或消息调用)将交易内容统一封装成一个“消息(Message)”结构。

  • 合约创建:计算新合约地址,初始化账户(Nonce=1,Balance 注入,Storage 为空)。

  • 消息调用:根据目标地址获取代码并开始执行。

  1. 第三阶段:主执行循环,这是 EVM 最核心的运算过程,通过一个递归或循环的解释器来运行代码,步骤如下:
  • 获取指令 (Fetch):根据 programCounter 从字节码中读取当前的操作码 (Opcode)。

  • Gas 计算:计算当前指令的基础 Gas 成本,以及是否涉及内存扩展成本。如果剩余 Gas 不足,则触发异常停机。

  • 操作执行:

例如执行 ADD(加法)时,会从栈中弹出两个值,相加后将结果压回栈。

执行 PUSH 指令时,会将字节码中的数据加载到栈中。

执行 JUMP(跳转)指令时,会修改 programCounter 到指定位置,前提是该位置必须是合法的跳转目标(JUMPDEST)。

  • 更新状态:更新 programCounter 到下一条指令的位置

预编译合约 Precompiled Contracts

预编译合约是以太坊中一类特殊的合约,它们并不存储 EVM 字节码,而是将特定的复杂计算逻辑直接集成在以太坊客户端的底层代码中,位于以太坊地址空间中极其靠前的特定地址(1 到 9 号地址)。当以太坊执行层发现交易的目标地址属于这个集合时,它不会启动普通的虚拟机指令解析流程,而是将执行权重定向到客户端实现的“原生函数(Native Functions)”。

根据目前的规格,这些预编译合约主要负责处理高强度的密码学运算,包括:

  • 1号地址:ECDSA 公钥恢复(用于验证签名)。

  • 2号与3号地址:SHA-256 和 RIPEMD-160 哈希运算。

  • 4号地址:标识函数(Identity function)。

  • 5至8号地址:椭圆曲线运算(alt_bn128),常用于零知识证明。

  • 9号地址:BLAKE2 压缩函数。

预编译合约比普通字节码效率更高

预编译合约比普通字节码效率更高,主要源于其执行机制的本质区别:

  1. 免除了解释器的开销:普通的智能合约需要在 EVM 的主执行循环中运行,经历“取指(Fetch)、译码(Decode)、执行(Execute)”的过程,而预编译合约直接运行编译后的机器码,跳过了虚拟机的解释环节。

  2. 由于这些操作直接在底层高效运行,协议可以为这些复杂的密码学任务设置相对较低且固定的 Gas 消耗,而如果用普通的 EVM 指令去实现同样的逻辑,其 Gas 消耗将高到无法在单个区块内完成。

  3. 预编译合约通常是无状态的(Stateless),它们仅根据输入产生输出,不涉及复杂的合约存储状态修改

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计