Interpreter

139 阅读7分钟

Interpreter (解释器)

interpreter crate 负责执行 EVM 操作码,并作为事件循环逐步执行操作码。解释器负责处理 gas、合约、内存、栈和返回执行结果等属性。它的结构如下:

模块

  • gas: 处理 EVM 中的 gas 机制,例如计算操作的 gas 成本。
  • host: 定义 EVM 上下文 Host 特征。
  • interpreter_action: 包含在 EVM 实现中使用的数据结构。
  • instruction_result: 定义指令执行的结果。
  • instructions: 定义 EVM 操作码(即指令)。

外部 Crate

  • alloc: alloc crate 用于提供在堆上分配内存的能力。它是 Rust 标准库的一部分,可以在没有完整主机操作系统的环境中使用。
  • core: core crate 是 Rust 标准库的无依赖基础。它包括基本类型、宏和特征。

重新导出

  • 为了让用户更容易访问,重新导出了几个类型和函数,比如 GasHostInstructionResultOpCodeInterpreterMemoryStack 等。这允许用户直接从库的根目录导入这些项,而不是从它们各自的模块导入。

  • revm_primitives: 这个 crate 被重新导出,提供了在 EVM 实现中使用的原始类型或功能

gas.rs 模块

gas.rs 模块在这个 Rust EVM 实现中管理以太坊网络中的"gas"概念。在以太坊中,"gas"表示执行操作所需的计算工作量,无论是简单的以太币转账还是智能合约函数的执行。每个操作都有一个 gas 成本,交易必须指定它们愿意消耗的最大 gas 数量。

数据结构

Gas 结构体

Gas 结构体表示特定操作或交易的 gas 状态。该结构体定义如下:

Gas 结构体中的字段
  • limit: 操作或交易允许的最大 gas 数量。
  • all_used_gas: 使用的总 gas 数量,包括内存扩展成本。
  • used: 使用的 gas 数量,不包括内存扩展成本。
  • memory: 用于内存扩展的 gas 数量。
  • refunded: 退还的 gas 数量。以太坊中的某些操作允许退还 gas,最多可退还交易使用 gas 的一半。
Gas 结构体的方法

Gas 结构体还包括几个管理 gas 状态的方法。以下是它们功能的简要总结:

  • new: 使用指定的 gas 限制创建一个新的 Gas 实例,使用量和退款为零。

  • 获取器方法:

    • limit: 返回 gas 限制
    • memory: 返回内存 gas 使用量
    • refunded: 返回退还的 gas 数量
    • spend: 返回已使用的 gas 数量
    • remaining: 返回剩余的 gas 数量
  • erase_cost: 减少指定数量的 gas 使用量。

  • record_refund: 增加指定数量的退还 gas。

  • record_cost: 增加使用的 gas 数量。它还会检查 gas 限制是否溢出。如果新的总使用 gas 超过 gas 限制,它会返回 false 并且不改变状态。

  • record_memory: 此方法类似于 record_cost,但专门用于内存扩展 gas。只有当新的内存 gas 使用量大于当前使用量时,才会更新状态。

  • gas_refund: 增加指定数量的退还 gas。

host.rs 模块

host.rs 模块在这个 Rust EVM 实现中定义了一个关键特征 HostHost 特征概述了 EVM 解释器与其环境(或"主机")交互的接口,包含了诸如账户和存储访问、创建日志以及调用交易等基本操作。

Evm 结构体实现了这个 Host 特征。

特征方法

  • env: 此方法提供对 EVM 环境的访问,包括当前区块和交易的信息。

  • load_account: 获取给定以太坊账户的信息。

  • block_hash: 获取给定区块号的区块哈希。

  • 获取账户信息的方法:

    • balance: 获取账户余额
    • code: 获取账户代码
    • code_hash: 获取账户代码哈希
    • sload: 获取账户特定存储槽的值
  • sstore: 此方法设置给定以太坊账户中特定存储槽的值。

  • log: 创建带有指定地址、主题和数据的日志条目。智能合约使用日志条目来发出事件。

  • selfdestruct: 将以太坊账户标记为自我销毁,将其资金转移到目标账户。

Host 特征为任何 EVM 的主机环境必须实现的功能提供了标准接口。这种抽象允许 EVM 代码以通用方式与以太坊网络的状态进行交互,从而增强了模块化和互操作性。Host 特征的不同实现可以用于模拟不同的测试环境或连接到不同的类以太坊网络。

interpreter_action.rs 模块

interpreter_action.rs 模块包含了一系列在 EVM 内部使用的数据结构。这些模型表示了 EVM 操作的各个方面,如调用和创建输入、调用上下文、价值转移以及自毁操作的结果。

数据结构

CallInputs 结构体

CallInputs 结构体用于封装 EVM 中智能合约调用的输入。这个结构体包括:

  • 目标合约地址
  • 要转移的值(如果有)
  • 输入数据
  • 调用的 gas 限制
  • 调用上下文
  • 一个布尔值表示是否是静态调用(只读操作)

CallScheme 枚举

CallScheme 枚举表示对智能合约进行调用的类型。不同类型的调用(CALL, CALLCODE, DELEGATECALL, STATICCALL)代表了与智能合约交互的不同模式,每种模式在消息发送者处理、值转移和执行调用代码的上下文方面都有其独特的语义。

CallValue 枚举

CallValue 枚举表示两个账户之间的值转移。

CallOutcome 结构体

表示虚拟机中调用操作的结果。此结构体封装了解释器执行指令的结果,包括:

  • 执行结果本身
  • gas 使用信息
  • 存储输出数据的内存偏移量

CreateInputs 结构体

CreateInputs 结构体封装了创建新智能合约的输入,包括:

  • 创建者地址
  • 创建方案
  • 要转移的值
  • 新合约的初始化代码
  • 创建操作的 gas 限制

CreateOutcome 结构体

表示解释器中创建操作的结果。此结构体包含:

  • 操作结果
  • 可选的地址 它提供了基于操作结果确定下一步操作的方法。

EOFCreateInput 结构体

用于 EOF 创建调用的输入。

EOFCreateOutcome 结构体

表示解释器中 EOF 创建操作的结果。

总之,interpreter_action.rs 模块提供了几个关键的数据结构,这些结构有助于在 Rust EVM 实现中表示和处理各种 EVM 操作及其相关数据。

instruction_result.rs 模块

instruction_result.rs 模块包含了 InstructionResultSuccessOrHalt 枚举的定义,它们表示 EVM 指令执行的可能结果,以及用于处理这些类型的函数。

InstructionResult 枚举

InstructionResult 枚举对执行 EVM 指令可能产生的不同类型的结果进行分类。这个枚举使用 #[repr(u8)] 属性,意味着它的变体有一个显式的 8 位无符号整数存储表示。不同的指令结果代表以下结果:

  • 成功继续
  • 停止
  • 返回
  • 自我销毁
  • 回滚
  • 深层调用
  • 资金不足
  • gas 不足
  • 各种错误条件

SuccessOrHalt 枚举

SuccessOrHalt 枚举表示交易执行的结果,区分了:

  • 成功操作
  • 回滚
  • 停止条件
  • 致命的外部错误
  • 内部继续

它还提供了几个方法来:

  • 检查结果的类型
  • 提取成功评估或停止的值

From for SuccessOrHalt 实现

此实现提供了将 InstructionResult 转换为 SuccessOrHalt 的方法。它将每个指令结果映射到相应的 SuccessOrHalt 变体。

返回指令结果的宏

该模块提供了两个宏:

  • return_ok!: 简化返回一些常见的成功指令结果集
  • return_revert!: 简化返回一些常见的回滚指令结果集

instruction.rs 模块

instruction.rs 模块定义了 EVM 字节码的解释映射。它提供了 Instruction 结构体的定义,以及 Opcode 枚举和用于执行特定指令的 execute 函数。

Opcode 枚举

Opcode 枚举表示以太坊虚拟机中可用的操作码。每个变体对应一个可以执行的操作,例如:

  • 加法
  • 乘法
  • 减法
  • 跳转
  • 内存操作

Instruction 结构体

Instruction 结构体表示 EVM 中的单个指令。它包含:

  • opcode: 要执行的操作
  • 一个字节列表,表示指令的操作数

step 函数

step 函数用于解释指令。它:

  • 使用操作码来确定要执行的操作
  • 然后使用指令中的操作数执行该操作