Primitives

185 阅读12分钟

原语(Primitives)

这个 crate 是 revm 系统的核心组件。它旨在为整个应用程序中常用的各种类型和结构提供定义。通过 no_std 属性的标注,该 crate 被设计为可在不包含 Rust 标准库的环境中使用。

模块:

  • bytecode: 此模块提供与 EVM 字节码相关的功能。
  • constants: 此模块包含 EVM 实现中使用的常量值。
  • db: 此模块包含与 EVM 数据库实现相关的数据结构和函数。
  • env: 此模块包含与 EVM 环境相关的类型和函数,包括区块头和环境值。
  • precompile: 此模块包含与以太坊预编译合约相关的类型。
  • result: 此模块提供用于表示 EVM 中执行结果和错误的类型。
  • specification: 此模块定义与以太坊规范(也称为硬分叉)相关的类型。
  • state: 此模块提供用于管理以太坊状态的类型和函数,包括账户和存储。
  • utilities: 此模块提供在 EVM 实现的多个地方使用的实用函数。
  • kzg: 此模块提供与 KZG 承诺相关的类型和函数,主要用于点评估预编译合约中。

外部 Crate:

  • alloc: alloc crate 提供堆分配相关的类型。
  • bitvec: bitvec crate 提供用于处理位序列的数据结构。
  • bytes: bytes crate 提供用于处理字节的实用工具。
  • hex: hex crate 提供用于编码和解码十六进制的实用工具。
  • hex_literal: hex_literal crate 提供一个宏,用于直接在源代码中包含十六进制数据。
  • hashbrown: hashbrown crate 提供高性能的哈希映射和哈希集数据结构。
  • ruint: ruint crate 提供用于大型无符号整数运算的类型和函数。
  • c-kzg: 为 EIP-4844 提供的多项式承诺 API 的最小化实现,用 C 语言编写(带有 rust 绑定)。

重新导出的类型:

  • Address: 表示 160 位(或 20 字节)数组的类型,通常用于以太坊地址。
  • B256: 表示 256 位(或 32 字节)数组的类型,通常用于以太坊哈希值或整数。
  • Bytes: 表示字节序列的类型。
  • U256: 来自 ruint crate 的 256 位无符号整数类型。
  • HashMapHashSet: 来自 hashbrown crate 的高性能哈希映射和哈希集数据结构。

重新导出的模块: 所有来自 bytecodeconstantsenvprecompileresultspecificationstateutilities 的类型、常量和函数,以及 KzgSettingsEnvKzgSettingstrusted_setup_points 类型和方法都被重新导出,允许用户直接从 primitives crate 导入这些项。

我来为您翻译这段关于数据库模块的说明:

数据库(Database)

负责数据库操作。这个模块负责管理区块链的状态持久化。该模块定义了三个主要特征(DatabaseDatabaseCommitDatabaseRef)、一个结构 RefDBWrapper 以及它们相关的方法。

Database 特征定义了与数据库进行可变交互的接口。它有一个泛型关联类型 Error,用于处理在这些交互过程中可能发生的不同类型的错误。它提供以下方法:

  • 获取基本账户信息(basic
  • 通过哈希获取账户代码(code_by_hash
  • 获取某个地址在特定索引位置的存储值(storage
  • 获取特定区块号对应的区块哈希(block_hash

DatabaseCommit 特征定义了一个单独的 commit 方法,用于将更改提交到数据库。这些更改是以太坊式地址(类型 Address)和账户之间的映射关系。

DatabaseRef 特征与 Database 特征类似,但设计用于只读或不可变交互。它具有相同的 Error 关联类型和相同的方法集,但这些方法采用 &self 而不是 &mut self,表明它们不会修改数据库。

RefDBWrapper 结构是对 DatabaseRef 类型引用的包装器。它实现了 Database 特征,本质上是通过将 Database 方法转发到相应的 DatabaseRef 方法,提供了一种将 DatabaseRef 当作 Database 使用的方式。

结果(Result)

这个模块的核心是 ExecutionResult 枚举,它描述了 EVM 执行的可能结果:Success(成功)、Revert(回滚)和 Halt(停止)。

Success 代表交易执行成功,它包含以下重要信息:

  • 成功的原因(一个 Eval 枚举)
  • 使用的燃料(gas)
  • 退还的燃料
  • 日志向量(Vec<Log>
  • 执行的输出结果

这与 EIP-658 中的规定相一致,该提案在交易收据中引入了状态码,用于表明顶层调用是成功还是失败。

Revert 代表由 REVERT 操作码回滚的交易,这种情况下并未消耗所有燃料。它存储了使用的燃料量和输出结果。

Halt 代表因各种原因被回滚且消耗了所有燃料的交易。它存储了停止的原因(一个 Halt 枚举)和使用的燃料量。

ExecutionResult 枚举提供了几个方法来提取执行结果中的重要数据:

  • is_success():检查是否执行成功
  • logs():获取日志
  • output():获取输出
  • into_output():转换为输出
  • into_logs():转换为日志
  • gas_used():获取使用的燃料量

这些方法便于访问交易执行的关键细节。

EVMErrorInvalidTransaction 枚举处理 EVM 中可能发生的不同类型的错误,包括:

  • 数据库错误
  • 交易本身特有的错误
  • 燃料相关的错误等

Output 枚举处理 EVM 执行的不同类型的输出,包括 Call(调用)和 Create(创建)。这里存储了成功执行或回滚交易的输出数据。

环境(Environment)

这是一个管理 EVM 执行环境的重要模块。该模块包含与区块链环境中处理交易和区块相关的对象和方法。它定义了几个重要的结构:

  1. Env:封装 EVM 环境的结构
  2. BlockEnv:区块环境结构
  3. TxEnv:交易环境结构
  4. CfgEnv:环境配置结构
  5. CreateScheme:合约创建方式结构

这些结构分别包含了代表区块数据、交易数据、环境配置、交易接收方详情以及合约创建方法的各种字段。

Env 结构作为 EVM 环境的封装,提供了以下功能:

  • 计算有效燃料价格的方法
  • 验证区块和交易数据的方法
  • 根据账户当前状态检查交易的功能,用于验证:
    • 交易的 nonce(随机数)
    • 账户余额

该模块还考虑了多个以太坊改进提案(EIP)的实现:

  • EIP-1559:关于基础费用的计算
  • EIP-3607:拒绝来自已部署代码的发送方的交易
  • EIP-3298:禁用燃料退还

代码结构设计上考虑了可选功能的包含,并允许 EVM 规范的变更。

规范(Specifications)

这个模块保存了与以太坊技术规范相关的数据,作为从以太坊执行规范中获得的以太坊规则和程序的参考点。该模块主要用于在以太坊虚拟机(EVM)中枚举和处理以太坊的网络升级或"硬分叉"。这些硬分叉在代码中被称为 SpecId,代表以太坊发展的不同阶段。

SpecId 枚举为每个以太坊硬分叉分配了唯一的数值和字符串标识符。这些升级包括:

  • 最早期的硬分叉如 FRONTIERHOMESTEAD
  • 最近的硬分叉如 LONDONMERGESHANGHAILATEST

代码还包括转换方法:

  • try_from_u8():尝试从给定的 u8 整数创建 SpecId
  • from():基于代表硬分叉名称的字符串创建 SpecId

SpecId 中的 enabled() 方法用于检查一个规范是否在另一个规范上启用,考虑了硬分叉实施的顺序。

Spec 特征用于抽象检查给定规范是否启用的过程。它只有:

  • 一个方法:enabled()
  • 一个常量:SPEC_ID

模块然后定义了各种 Spec 结构体,每个结构体代表一个不同的硬分叉。这些结构体:

  • 实现了 Spec 特征
  • 每个结构体的 SPEC_ID 对应于正确的 SpecId 变体

此模块的作用:

  1. 提供必要的框架来处理和交互 EVM 中的不同以太坊硬分叉
  2. 使根据适用的硬分叉规则不同地处理交易和合约成为可能
  3. 通过创建新的 SpecId 和相应的 Spec 结构体,简化了适应未来硬分叉的过程

字节码(Bytecode)

这个模块定义了用于操作以太坊字节码和管理其状态的结构和方法。它主要围绕三个核心组件构建:JumpTableBytecodeStateBytecode

JumpTable 结构:

  • 存储给定以太坊字节码序列中有效的 jump(跳转)目的地映射
  • 本质上是一个 Arc(原子引用计数器)包装的 BitVec(位向量)
  • 可以通过定义的方法访问和修改,如:
    • as_slice():获取切片
    • from_slice():从切片创建
    • is_valid():检查有效性

BytecodeState 枚举捕获字节码的三种可能状态:

  1. Raw(原始)
  2. Checked(已检查)- 包含字节码长度等额外数据
  3. Analysed(已分析)- 包含字节码长度和 JumpTable 等额外数据

Bytecode 结构体包含:

  • 实际的字节码
  • 字节码的哈希值
  • 当前状态(BytecodeState

它提供了多个与字节码交互的方法:

  • 获取字节码长度
  • 检查是否为空
  • 获取其状态
  • 将字节码转换为已检查状态
  • 在不同状态下创建 Bytecode 结构体的新实例

常量(Constants)

这个模块保存了系统各处使用的常量值。它定义了帮助限制和管理以太坊虚拟机(EVM)资源的重要常量:

基本限制常量:

  • STACK_LIMIT:限制解释器栈大小,设为 1024
  • CALL_STACK_LIMIT:限制 EVM 调用栈大小,设为 1024

代码大小限制:

  1. MAX_CODE_SIZE
  • 根据 EIP-170 规范设置为 0x6000(约 25kb)
  • 目的:限制合约代码大小,以减轻以太坊潜在的漏洞和低效问题
  • 重要性:
    • 防止调用合约触发随代码大小增加而变得昂贵的操作(如从磁盘读取代码、预处理代码、添加区块有效性证明数据)
    • 确保这些操作成本保持可控,即使在未来遇到高燃料级别
    • 为潜在的 DoS 攻击提供关键保护
    • 维护效率,特别是对于未来验证有效性或无效性证明的轻客户端
  1. MAX_INITCODE_SIZE
  • 根据 EIP-3860 设置为 2 * MAX_CODE_SIZE
  • 背景:扩展了 EIP-170,为初始化代码(initcode)引入最大大小限制
  • 功能:
    • 对每 32 字节的 initcode 块强制收取燃料费用,以计算跳转目标分析的成本
    • 解决了 EIP-3860 之前 initcode 分析未计费且无大小上限的问题
  • 意义:
    • 确保 initcode 分析成本与其大小成比例
    • 通过设置明确限制简化 EVM 引擎
    • 为未来创建可扩展的成本系统
    • 实现公平收费

预编译(precompile)

这个模块在 EVM 中实现预编译合约,添加了一层预设功能。这些将在下一节中有更详细的文档说明。该模块定义了用于处理预编译合约的类型和枚举。

PrecompileResult

  • 这是一个 Result 类型的类型别名
  • Ok 变体包含一个元组 (u64, Vec<u8>):
    • u64 整数表示预编译合约使用的燃料量
    • Vec<u8> 保存输出数据
  • Err 变体包含一个 PrecompileError

函数指针类型别名:

  1. StandardPrecompileFn

    • 用于内置预编译合约
    • 接受字节切片和 u64(可用燃料)作为参数
    • 返回 PrecompileResult
  2. CustomPrecompileFn

    • 用于自定义(用户定义)合约
    • 参数和返回值与 StandardPrecompileFn 相同

PrecompileError

  • 这是一个描述预编译合约执行过程中可能发生的不同类型错误的枚举
  • 错误变体包括:
    • 燃料消耗相关错误
    • Blake2 哈希函数相关错误
    • 模幂运算("Modexp")相关错误
    • Bn128(密码学中使用的特定椭圆曲线)相关错误

状态(State)

负责管理 EVM 的状态,包括账户余额、合约存储等。这个模块对以太坊账户及其状态(包括余额、nonce、代码、存储和状态标志)进行建模,并提供与账户状态交互的方法。

Account 结构体包含以下字段:

  • info:类型为 AccountInfo
  • storage:一个将 U256 值映射到 StorageSlotHashMap
  • status:类型为 AccountStatus

AccountInfo 代表以太坊账户的基本信息,包括:

  • balance:余额
  • nonce:交易序号
  • code:代码
  • code_hash:代码哈希

AccountStatus 是一组位标志,表示账户的状态。包括:

  • Loaded:已加载
  • Created:已创建
  • SelfDestructed:已自毁
  • Touched:已触及
  • LoadedAsNotExisting:作为不存在的账户加载

StorageSlot 结构体代表 EVM 中的存储槽:

  • 包含 original_value(原始值)和 present_value(当前值)
  • 提供创建新槽和检查槽值是否被修改的方法

类型别名:

  1. State:从 Address 地址到 Account 的映射
  2. Storage:从 U256 键到 StorageSlot 的映射

Account 实现的方法包括:

  • mark_selfdestruct/unmark_selfdestruct:标记/取消标记自毁
  • is_selfdestructed:检查是否已自毁
  • mark_touch/unmark_touch:标记/取消标记触及
  • is_touched:检查是否被触及
  • mark_created:标记创建
  • is_newly_created:检查是否新创建
  • is_empty:检查是否为空
  • new_not_existing:创建不存在的新账户

实用工具(Utilities)

这个 Rust 模块提供了处理 Keccak 哈希(用于以太坊)和通过传统及 CREATE2 方法创建以太坊地址的实用函数和常量。它还包括用于表示字节数组的十六进制字符串的序列化和反序列化方法。

主要组件:

  • KECCAK_EMPTY:表示空输入的 Keccak-256 哈希的常量
  • keccak256 函数:接受字节切片输入并返回其作为 B256 值的 Keccak-256 哈希

KZG 随着 EIP4844 的引入,使用 blob 来实现更高效的短期存储。存储在共识层的 blob 的有效性通过 Point Evaluation(点评估)预编译来验证。这是一种验证已提交多项式在给定点上的评估是否有效的高级方法,在更大的规模上,意味着"数据可用"。

该模块包含:

  1. KzgSettings
  • 存储用于计算和验证 KZG 证明所需的设置和参数
  • KZG 原语提供从受信任设置仪式获得的默认 KZGSettings
  • 如果需要,也可以在 env.cfg 中使用自定义的 KZGSettings
  1. trusted_setup_points
  • 包含用于解析和利用受信任设置的函数和类型
  • 这些用于 KzgSettings 的实现