mini-evm 是一个基于 Rust 的简单以太坊虚拟机(EVM)实现,旨在帮助个人学习和理解 EVM 的指令集与执行机制。本项目主要参考了 WTF Academy 的 Python 实现思路、evm.codes 的文档,并通过单元测试验证结果。
当前版本正在进行模块化重构,目标架构为
Interpreter + Host分离模式,并面向 Shanghai 硬分叉 的 Gas 规则做合规对齐。
我们将 EVM 拆分为两个核心角色:
- Interpreter (
src/interpreter.rs):负责字节码的取指、译码、执行,管理 PC、Stack、Memory 与控制流。 - Host (
src/host.rs): trait 抽象层,定义所有状态访问接口(sload、sstore、balance、call等)。
这种分离让执行引擎与底层状态解耦:
- 单元测试可注入
MockHost(内存实现),无需真实数据库。 - 未来对接链上数据时,只需实现
Hosttrait。
┌─────────────────┐
│ Interpreter │ ← 执行引擎:PC / Stack / Memory
│ interpreter.rs │
└────────┬────────┘
│ 调用 Host trait 方法
┌────────▼────────┐
│ Host (trait) │ ← 状态边界:账户、存储、调用
│ host.rs │
└────────┬────────┘
│
┌──────────┴──────────┐
│ │
┌─────▼──────┐ ┌───────▼────────┐
│ MockHost │ │ Journal │
│(测试专用) │ │(状态回滚日志) │
└────────────┘ └────────────────┘
所有 opcode handler(src/ops/handlers.rs)不再直接修改 Interpreter 内部状态,而是通过 InstructionResult 返回标准化信号:
Continue— 正常进入下一条指令Stop— 执行结束(如STOP、RETURN)Revert— 回滚当前调用帧的所有状态变更CallOrCreate— 需要进入新的子调用帧
Interpreter 的主循环根据该结果统一调度,避免控制流散落在各 opcode 实现中。
src/journal.rs 记录每一次状态修改(storage、balance、nonce),支持多层 checkpoint:
- 子调用前打快照。
- 成功则 commit;失败或
REVERT则按日志逐条恢复。
这让 mini-evm 具备了与生产级客户端(如 revm、geth)一致的事务语义。
当前实现针对 Shanghai 升级的关键 EIP 做了对齐:
| EIP | 内容 | 状态 |
|---|---|---|
| EIP-2929 | 冷热访问列表:首次状态访问加收 cold cost,后续 warm access 仅 100 gas | 部分实现 |
| EIP-3529 | 降低 refund:上限从 gas_used/2 降至 gas_used/5;移除 SELFDESTRUCT refund |
简化实现 |
| EIP-3855 | 新增 PUSH0 指令,固定 2 gas |
已实现 |
| EIP-3860 | Initcode 长度上限 49,152 字节,并加收 jumpdest-analysis 费用 | 未完整 enforcing |
完整偏差列表请见
DEVIATIONS.md。
- Rust >= 1.79
# 运行全部单元测试
cargo test
# 运行指定测试(示例)
cargo test test_push
# 带日志输出调试
cargo test -- --nocapture注意:项目使用
log4rs做日志输出,运行前请确保根目录存在log4rs.yaml。
本项目可直接在 VS Code 中通过 CodeLLDB 或 rust-analyzer 插件进行断点调试。
mini-evm
├── src/
│ ├── interpreter.rs # 字节码执行引擎
│ ├── host.rs # Host trait(状态访问抽象)
│ ├── mock_host.rs # 内存实现的 Host,用于测试
│ ├── journal.rs # 状态变更日志与 revert
│ ├── primitives.rs # 基础类型(U256、Address 等)
│ ├── gas.rs # Gas 计量(EIP-2929 / EIP-3529)
│ ├── ops/
│ │ ├── handlers.rs # Opcode 实现
│ │ └── ... # 其他 opcode 分类模块
│ ├── stack.rs # EVM 栈实现
│ ├── transaction.rs # 交易结构
│ └── ...
├── docs/
│ ├── ARCHITECTURE.md # 架构说明(本文档)
│ └── DEVIATIONS.md # 与主网规范的已知偏差
├── Cargo.toml
└── README.md
MockHost 是一个纯内存的 Host 实现,内置若干预设账户(如 0x9bbfed6889322e016e0a02ee459d306fc19545d8),方便在无数据库环境下验证:
- 合约间调用(
CALL、DELEGATECALL、STATICCALL) - 存储读写(
SLOAD、SSTORE)与冷热 gas 差异 - 余额转账与
SELFDESTRUCT
在 journal.rs 的测试模块中,我们构造了多层嵌套调用场景,验证:
- 子调用修改 storage 后 revert,父层状态保持不变。
- 跨层 balance transfer 在失败时全额回滚。
- Checkpoint 合并后,变更正确渗透到上层。
| Version | Release Date | Major Features | Compatibility |
|---|---|---|---|
| 0.1.0 | 2024-11-02 | Initial implementation of basic EVM instructions | Rust 1.79 |
| 0.2.0 (WIP) | — | Interpreter+Host 重构、Journal 回滚、Shanghai Gas 对齐 | Rust 1.79+ |
- [1] WTF Academy — EVM Opcodes 教程:https://www.wtf.academy/docs/evm-opcodes-101/
- [2] evm.codes — 可视化 EVM 指令参考与执行模拟器:https://www.evm.codes/
- [3] Shanghai 升级规范:https://github.com/ethereum/execution-specs
这个项目目前仍属我个人的 Rust 练手项目,除了帮助我学习 EVM 的指令集之外,也在帮助我熟悉 Rust。欢迎交流和提建议!