Skip to content

raulk/evmi

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

evmi

An EVM bytecode interpreter extracted from Filecoin's builtin-actors. Running on Filecoin mainnet since March 2023. FVM logo

Overview

evmi interprets EVM bytecode. It does not include gas metering, state management, or transaction processing. These are responsibilities of the embedding engine, and are either exposed via two traits (Platform and Storage), or transparently performed.

This separation exists because evmi was built for the Filecoin VM, where gas is metered at the Wasm level and state is managed by the client. The same separation makes evmi re-usable in other contexts: zkVM guests, alternative L1s, rollups, or anywhere you need EVM semantics without Ethereum's full state model.

  • Size: around 4,400 LoC
  • Opcodes: 142/145 (through Cancun, missing blob opcodes and CLZ)
  • Precompiles: 9 standard + 7 BLS12-381 (optional)
  • Targets: native, wasm32, riscv64gc
  • Dependencies: no_std compatible, no async

What evmi provides vs. what's needed

evmi provides the execution core for a zkEVM guest program. The proving-friendly infrastructure around it remains to be built.

Component evmi evmi (needed) Guest program
Opcode execution
Stack, memory
Precompiles (including BLS12-381)
JUMPDEST validation
Call semantics (63/64 rule)
Deterministic execution
Gas metering
State access and commitment
Transaction validation
Receipts, state roots
Witness generation

A complete zkEVM guest requires roughly twice what evmi currently provides. evmi is the interpreter half.

Tradeoffs

Evolving evmi into a zkEVM guest is not trivial. Gas metering needs to be added to evmi. The guest program needs a state commitment scheme suited to the proof system, witness generation, and transaction processing.

The upside: building up from a minimal core rather than adapting a stack designed for native execution. Every component can be optimized for proving from the start. State commitments can use Poseidon or other circuit-friendly hashes instead of Keccak. Witness structures can match the zkVM's memory model. No abstractions inherited from existing clients that made sense for different goals but add overhead in a SNARK.

Forking clients and making them prover-friendly is viable as various teams have demonstrated. But that path inherits design decisions made for unrelated constraints. Starting from ~4,400 lines of interpreter and building exactly what's might involve less work than adapting large codebases lines built for a different purpose.

Extension points

The Platform trait provides block context, account lookups, and external calls. The Storage trait provides contract storage with EIP-2929 access tracking. evmi calls these during execution; the platform decides what happens under the hood.

EVM compatibility

Fork support

Fork Date Status Notes
Cancun Mar 2024 Partial Missing blob opcodes (0x49, 0x4a)
Pectra May 2025 No new opcodes
Osaka/Fusaka Dec 2025 Missing CLZ (0x1e)

Supported opcodes

All opcodes through Cancun except blob-related:

Range Opcodes
0x00 STOP
0x01-0x0b ADD, MUL, SUB, DIV, SDIV, MOD, SMOD, ADDMOD, MULMOD, EXP, SIGNEXTEND
0x10-0x1d LT, GT, SLT, SGT, EQ, ISZERO, AND, OR, XOR, NOT, BYTE, SHL, SHR, SAR
0x20 KECCAK256
0x30-0x3f ADDRESS, BALANCE, ORIGIN, CALLER, CALLVALUE, CALLDATALOAD, CALLDATASIZE, CALLDATACOPY, CODESIZE, CODECOPY, GASPRICE, EXTCODESIZE, EXTCODECOPY, RETURNDATASIZE, RETURNDATACOPY, EXTCODEHASH
0x40-0x48 BLOCKHASH, COINBASE, TIMESTAMP, NUMBER, PREVRANDAO, GASLIMIT, CHAINID, SELFBALANCE, BASEFEE
0x50-0x5f POP, MLOAD, MSTORE, MSTORE8, SLOAD, SSTORE, JUMP, JUMPI, PC, MSIZE, GAS, JUMPDEST, TLOAD, TSTORE, MCOPY, PUSH0
0x60-0x7f PUSH1-PUSH32
0x80-0x8f DUP1-DUP16
0x90-0x9f SWAP1-SWAP16
0xa0-0xa4 LOG0-LOG4
0xf0-0xff CREATE, CALL, RETURN, DELEGATECALL, CREATE2, STATICCALL, REVERT, INVALID, SELFDESTRUCT (note: CALLCODE 0xf2 not implemented)

Missing opcodes

Opcode Hex EIP Fork Notes
CALLCODE 0xf2 Frontier Deprecated; use DELEGATECALL
BLOBHASH 0x49 4844 Cancun Blob versioned hash
BLOBBASEFEE 0x4a 7516 Cancun Blob base fee
CLZ 0x1e 7939 Osaka Count leading zeros

Blob opcodes require Platform trait extensions. If your target doesn't use blob transactions, these can be safely ignored.

Precompiles

Address Name Status
0x01 ecrecover
0x02 SHA-256
0x03 RIPEMD-160
0x04 identity
0x05 modexp
0x06 ecAdd
0x07 ecMul
0x08 ecPairing
0x09 blake2f
0x0a KZG point eval ✗ (EIP-4844)
0x0b-0x11 BLS12-381 ✓ (feature flag)

Integration

Two traits define the boundary between evmi and your runtime. Implement these to provide execution context:

pub trait Platform {
    type Error: Error + Send + Sync + 'static;

    fn block_number(&self) -> u64;
    fn block_timestamp(&self) -> u64;
    fn balance(&self, address: EthAddress) -> Result<U256, Self::Error>;
    fn extcode(&self, address: EthAddress) -> Result<Vec<u8>, Self::Error>;
    fn call(&mut self, gas: u64, to: EthAddress, value: U256, input: &[u8])
        -> Result<CallOutput, Self::Error>;
    fn create(&mut self, gas: u64, value: U256, init_code: &[u8])
        -> Result<CreateOutput, Self::Error>;
    // See src/platform.rs for full interface
}

pub trait Storage {
    type Error;

    fn get(&mut self, key: U256) -> Result<U256, Self::Error>;
    fn set(&mut self, key: U256, value: U256) -> Result<(), Self::Error>;
    fn get_transient(&mut self, key: U256) -> Result<U256, Self::Error>;
    fn set_transient(&mut self, key: U256, value: U256) -> Result<(), Self::Error>;
    // See src/storage.rs for full interface
}

Basic usage

use evmi::{execute, ExecutionState, System, StandardPrecompiles};

let precompiles = StandardPrecompiles;
let mut system = System::new(platform, storage, precompiles, bytecode, false);
let mut state = ExecutionState::new(input_data, caller, value);

let output = execute(&mut system, &mut state)?;

Cargo features

Flag Default Description
std Standard library; disable for no_std
bls12-381 BLS12-381 precompiles via blst crate
# no_std without BLS
evmi = { version = "0.1", default-features = false }

# no_std with BLS
evmi = { version = "0.1", default-features = false, features = ["bls12-381"] }

Building

Requires just. Install cross-compilation targets with just install-targets.

Command Description
just Build for host (default)
just test Run tests
just wasm Build for wasm32 (no_std)
just riscv Build for riscv64gc (no_std)
just riscv-bls Build for riscv64gc with BLS
just release Release build for host
just wasm-release Release build for wasm32
just riscv-release Release build for riscv64gc
just check-all Verify all targets compile
just clean Remove build artifacts

TODO

Interpreter

  • Gas metering
    • Opcode gas cost table (static costs)
    • Dynamic costs (memory expansion, EXP bit length, LOG data size)
    • SSTORE accounting (EIP-2200 refunds, EIP-2929 warm/cold)
    • Call stipends (2300 for value transfers)
    • Out-of-gas error propagation
  • Missing opcodes: BLOBHASH, BLOBBASEFEE, CLZ
  • KZG point evaluation precompile (EIP-4844)
  • Code size limit enforcement (24KB, EIP-170)
  • Call depth limit (1024) (this is enforced client-side in Filecoin)
  • Jumpdest table as bitset (8x memory reduction)
  • Validation against ethereum/tests
  • Benchmarks

zkEVM guest program

Components needed beyond the interpreter. These could be separate crates or implemented in the guest program itself.

  • Transaction processing
    • Nonce validation and increment
    • Signature verification and sender recovery (ECDSA)
    • Intrinsic gas (21000 base + calldata costs)
    • Access list processing (EIP-2930)
    • Balance validation (value + gas × price)
  • State layer
    • Account model (nonce, balance, code hash, storage root)
    • State commitment scheme (MPT, or proving-friendly variants)
    • Storage proofs for witness generation
    • State root computation
  • Post-execution
    • State mutation application (storage, balances, contract creation)
    • Receipt generation (status, cumulative gas, logs)
    • Logs bloom filter
    • Block-level state root update

Credits

Extracted from the FEVM runtime in filecoin-project/builtin-actors. Credits to Filecoin Core Devs.

License

MIT OR Apache-2.0

About

a standalone, embeddable EVM interpreter, originally developed by the Filecoin project

Resources

License

Unknown, MIT licenses found

Licenses found

Unknown
LICENSE-APACHE
MIT
LICENSE-MIT

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors