4 releases
| 0.1.3 | Oct 24, 2025 |
|---|---|
| 0.1.2 | Oct 24, 2025 |
| 0.1.1 | Oct 24, 2025 |
| 0.1.0 | Sep 9, 2025 |
#22 in #revm
66KB
1K
SLoC
Requirements
- Rust 1.75+ (Cargo)
- Zig 0.15.1+ (Required for building guillotine-mini)
- git (Required for submodule initialization)
Installing Zig
Zig is required to build the underlying EVM engine. Install it before building:
macOS:
brew install zig
Linux / Windows: Download from https://ziglang.org/download/
Verify installation:
zig version # Should show 0.15.1 or later
Installation
WARNING: This repo is currently vibes and hasn't been reviewed by a human yet
Recommended: Build from source with git submodules
git clone --recursive https://github.com/evmts/guillotine-rs.git
cd guillotine-rs
cargo build --release
Alternative: Add to your Cargo.toml
[dependencies]
guillotine-rs = { git = "https://github.com/evmts/guillotine-rs", submodules = true }
Note: Installing directly from crates.io (cargo add guillotine-rs) is not currently supported due to git submodule dependencies. You must clone the repository with --recursive to include the guillotine-mini submodule.
Documentation
tests/ — Example code and usage
LLMS.txt — For LLMs
Overview
High-performance REVM execution backed by the Zig-based guillotine-mini engine. Thin Rust wrapper with FFI to Zig for execution, state sync, logs, refunds, and storage changes.
Architecture
- Zig (
lib/guillotine-mini) — core EVM, opcode handlers, storage manager - FFI layer (
root_c.zigin guillotine-mini) — stable C ABI to create/destroy EVM, set contexts, execute, and extract results - Rust wrapper (
src/guillotine_mini) — REVM adapter, type conversions, and state bridge
Key Features
- REVM-compatible — Drop-in transaction execution with REVM's
ContextandTxEnv - Pre-state sync — Automatically syncs balances, nonces, code, and storage to guillotine-mini
- Post-state extraction — Storage changes grouped by address/slot
- Gas refunds — Direct exposure from guillotine-mini's runtime counter
- Log emission — LOG0–LOG4 captured in Zig and returned as REVM logs
- Typed errors — Proper error handling with
EvmAdapterError<DbErr>
API
Legend: All FFI calls are wrapped with safe Rust interfaces
- REVM Wrapper
GuillotineMiniEvm— main EVM wrapper for REVM integrationEvmAdapterError— typed error handlingDb(DbErr)— database-related error from REVMFfi(&'static str)— FFI call failed (bool=false or null handle)
- Database Bridge
sync_account_to_ffi— sync REVM account state to guillotine-mini (balance, nonce, code)sync_storage_to_ffi— sync single storage slot to guillotine-miniread_storage_from_ffi— read storage value from guillotine-mini
- FFI Bindings
- Lifecycle
evm_create— create EVM instance with hardfork nameevm_destroy— free EVM resources
- Configuration
evm_set_bytecode— set contract bytecode for executionevm_set_execution_context— set caller, address, value, gas, calldataevm_set_block_context— set block number, timestamp, gas limit, etc.
- Execution
evm_execute— execute transaction and return success/failureevm_get_status— check if execution succeededevm_get_gas_used— get gas consumed by executionevm_get_gas_refund— get gas refund counter
- Output
evm_get_output_size— get return data lengthevm_copy_output— copy return data to buffer
- State Management
evm_set_storage— set storage slot valueevm_get_storage— get storage slot valueevm_set_balance— set account balanceevm_set_code— set account codeevm_set_nonce— set account nonce
- Result Introspection
evm_get_log_count— get number of emitted logsevm_get_log— get log entry by index (address, topics, data)evm_get_storage_change_count— get number of storage changesevm_get_storage_change— get storage change by index (address, slot, value)
- Lifecycle
- Type Conversions
address_to_bytes— convert REVM Address to [20]u8address_from_bytes— convert [20]u8 to REVM Addressu256_to_be_bytes— convert U256 to big-endian [32]u8u256_from_be_bytes— convert big-endian [32]u8 to U256i64_to_u64_gas— convert signed gas to unsigned (clamp negative)
Error Handling
- Reverts — Mapped to
ExecutionResult::Revert { gas_used, output }(no panic) - Success — Returns
ExecutionResult::Success { reason: Return, gas_used, gas_refunded, logs, output } - FFI failures — Properly propagated via
EvmAdapterError::Ffi(&'static str) - Database errors — Wrapped in
EvmAdapterError::Db(DbErr)and propagated - Catastrophic failures — Zig panic/unreachable causes process abort (by design)
Fallible constructor available: GuillotineMiniEvm::try_new(ctx) returns Result<Self, EvmAdapterError>. The new(ctx) constructor retains an assert on fatal creation failure for convenience.
Usage
use guillotine_rs::guillotine_mini::evm::GuillotineMiniEvm;
use revm::{
context::Context,
context_interface::result::ExecutionResult,
database_interface::EmptyDB,
primitives::{address, TxEnv, TxKind, U256},
};
// Create REVM context
let ctx = Context::mainnet().with_db(EmptyDB::default());
let mut evm = GuillotineMiniEvm::new(ctx);
// Build transaction
let tx = TxEnv::builder()
.caller(address!("a94f5374fce5edbc8e2a8697c15331677e6ebf0b"))
.kind(TxKind::Call(address!("0000000000000000000000000000000000000001")))
.gas_limit(100_000)
.build()
.unwrap();
// Execute and get results
let result = evm.transact(tx).unwrap();
match result.result {
ExecutionResult::Success { gas_used, gas_refunded, logs, output, .. } => {
println!("Success! Gas used: {}, refunded: {}", gas_used, gas_refunded);
println!("Logs: {}, Output: {:?}", logs.len(), output);
}
ExecutionResult::Revert { gas_used, output } => {
println!("Reverted! Gas used: {}, Output: {:?}", gas_used, output);
}
_ => unreachable!(),
}
Testing
# Run all tests
cargo test
# Run specific test
cargo test test_simple_add
# Run with output
cargo test -- --nocapture
Test coverage:
- Storage writes across multiple slots
- Gas refund behavior on SSTORE operations
- Log emission (LOG0 instruction)
- Revert handling with proper ExecutionResult mapping
- Basic arithmetic operations
Troubleshooting
Build Errors
"Zig compiler not found!"
# Install Zig 0.15.1 or later
brew install zig # macOS
# or download from https://ziglang.org/download/
# Verify installation
zig version
"Zig version too old!"
# Upgrade to Zig 0.15.1+
brew upgrade zig # macOS
# or download latest from https://ziglang.org/download/
"git submodule init failed"
# If you installed via cargo, clone manually instead:
git clone --recursive https://github.com/evmts/guillotine-rs
cd guillotine-rs
cargo build
"Failed to initialize submodules"
# Initialize submodules manually:
git submodule update --init --recursive
cargo build
"zig build failed"
- Ensure Zig version is 0.15.1 or later
- Try cleaning and rebuilding:
cd lib/guillotine-mini rm -rf zig-out zig-cache cd ../.. cargo clean cargo build - Report issues at https://github.com/evmts/guillotine-rs/issues
Notes and Limits
- Storage extraction enumerates final non-zero slots; zeroed slots are not emitted
- Logs are emitted by Zig's LOG handlers and included in results
- All hardforks from Frontier to Osaka are supported via REVM's SpecId mapping
- Submodules required: This package uses git submodules and cannot be installed directly from crates.io. Use git installation method above.
More
Guillotine Mini — Minimal Zig EVM implementation
Primitives — Ethereum primitives and cryptography for Zig
REVM — Rust Ethereum Virtual Machine
Dependencies
~19MB
~370K SLoC