Minimal, correct, and well-tested Ethereum Virtual Machine (EVM) implementation in Zig, prioritizing specification compliance, clarity, and hardfork support (Berlin through Prague).
zig build # Build all modules
zig build test # Run unit + spec tests
zig build specs # Run ethereum/tests validation
zig build wasm # Build WebAssembly library
zig build test-watch # Interactive test runner
# Debug failing tests (recommended)
bun scripts/isolate-test.ts "test_name" # Max debug output + analysis
bun scripts/test-subset.ts "pattern" # Filter test categories- Zig 0.15.1+ (core build system)
- Cargo (required for BN254/ARK cryptographic dependencies)
- Python 3.8+ (test generation and reference implementation)
- uv (Python package manager for spec fixture generation):
brew install uv - Bun (TS helpers/agents):
brew install bun
src/
├── evm.zig # Orchestrator: state, storage, gas refunds, nested calls
├── frame.zig # Bytecode interpreter: stack, memory, PC, per-opcode logic
├── host.zig # Abstract state backend interface
├── hardfork.zig # Hardfork detection and feature flags
├── opcode.zig # Opcode definitions and utilities
├── trace.zig # EIP-3155 trace generation
└── errors.zig # Error types
External Dependencies (fetched via zig build):
├── primitives # Ethereum types (Address, u256, gas constants, RLP, ABI, etc.)
│ # Source: https://github.com/evmts/primitives
├── crypto # Cryptographic primitives (keccak256, secp256k1, BLS12-381)
└── precompiles # Ethereum precompiled contracts
| Component | Responsibility |
|---|---|
| Evm | State management, storage, gas refunds, warm/cold tracking, nested calls |
| Frame | Stack, memory, PC, bytecode interpretation, opcode execution |
| Host | Pluggable state backend (balances, nonces, code, logs, self-destruct) |
| Hardfork | Gas cost adjustments, feature flag guards (isAtLeast(), isBefore()) |
Allocation strategy: Arena allocator for transaction-scoped memory (freed at transaction end).
| Type | Purpose | Command |
|---|---|---|
| Unit tests | Inline test blocks |
zig build test |
| Spec tests | ethereum/tests GeneralStateTests | zig build specs |
| Filtered tests | By hardfork/EIP/opcode | TEST_FILTER="Cancun" zig build specs |
| Trace tests | EIP-3155 trace capture/comparison | zig build test-trace |
| Watch mode | Auto-reload on changes | zig build test-watch |
| Engine tests | Consensus-layer format tests | INCLUDE_ENGINE_TESTS=1 zig build specs |
blockchain_test_engine format tests are disabled by default because they test consensus-layer functionality (block validation, Engine API payloads) rather than core EVM execution. To include them:
INCLUDE_ENGINE_TESTS=1 zig build specs- Hardfork:
Cancun,Shanghai,London,Berlin,Merge,Prague - EIP:
transientStorage,push0,MCOPY,warmcoinbase - Opcode:
add,mul,sstore,sload,call,create2 - Category:
vmArithmeticTest,vmBitwiseLogicOperation,vmIOandFlowOperations
isolate-test.ts - Test Isolation Helper (recommended)
bun scripts/isolate-test.ts "exact_test_name"Features:
- Runs a single test with maximum debug output
- Automatic failure type detection (crash/gas/behavior)
- Trace divergence analysis (PC, opcode, gas, stack)
- Next-step debugging guidance
- Quick reference commands
test-subset.ts - Test Subset Runner
# Using helper scripts
bun scripts/test-subset.ts Cancun
bun scripts/test-subset.ts transientStorage
bun scripts/test-subset.ts MCOPY
# Or using shell scripts
./scripts/test-subset.sh Cancun
./scripts/test-subset.sh "exact_test_name"
# Direct filtering with zig build
TEST_FILTER="Cancun" zig build specs
TEST_FILTER="transientStorage" zig build specs
TEST_FILTER="push0" zig build specsUse for: Running entire test categories by hardfork, EIP, or pattern.
Large hardforks are split into smaller sub-targets for faster iteration:
# Berlin
zig build specs-berlin-acl
zig build specs-berlin-intrinsic-gas-cost
zig build specs-berlin-intrinsic-type0
zig build specs-berlin-intrinsic-type1
# Frontier
zig build specs-frontier-precompiles
zig build specs-frontier-identity
zig build specs-frontier-create
zig build specs-frontier-call
zig build specs-frontier-calldata
zig build specs-frontier-dup
zig build specs-frontier-push
zig build specs-frontier-stack
zig build specs-frontier-opcodes
# Shanghai
zig build specs-shanghai-push0
zig build specs-shanghai-warmcoinbase
zig build specs-shanghai-initcode
zig build specs-shanghai-withdrawals
# Cancun
zig build specs-cancun-tstore-basic
zig build specs-cancun-tstore-reentrancy
zig build specs-cancun-tstore-contexts
zig build specs-cancun-mcopy
zig build specs-cancun-selfdestruct
zig build specs-cancun-blobbasefee
zig build specs-cancun-blob-precompile
zig build specs-cancun-blob-opcodes
zig build specs-cancun-blob-tx-small
zig build specs-cancun-blob-tx-subtraction
zig build specs-cancun-blob-tx-insufficient
zig build specs-cancun-blob-tx-sufficient
zig build specs-cancun-blob-tx-valid-combos
# Prague
zig build specs-prague-calldata-cost-type0
zig build specs-prague-calldata-cost-type1-2
zig build specs-prague-calldata-cost-type3
zig build specs-prague-calldata-cost-type4
zig build specs-prague-calldata-cost-refunds
zig build specs-prague-bls-g1
zig build specs-prague-bls-g2
zig build specs-prague-bls-pairing
zig build specs-prague-bls-map
zig build specs-prague-bls-misc
zig build specs-prague-setcode-calls
zig build specs-prague-setcode-gas
zig build specs-prague-setcode-txs
zig build specs-prague-setcode-advanced
# Osaka
zig build specs-osaka-modexp-variable-gas
zig build specs-osaka-modexp-vectors-eip
zig build specs-osaka-modexp-vectors-legacy
zig build specs-osaka-modexp-misc
zig build specs-osaka-other# 1. Find failures
bun scripts/test-subset.ts transientStorage
# 2. Isolate and debug (get automated analysis)
bun scripts/isolate-test.ts "transStorageReset"
# 3. Fix in src/frame.zig or src/evm.zig
# 4. Verify
bun scripts/isolate-test.ts "transStorageReset"The scripts/fix-specs.ts pipeline enforces a mandatory 7-checkpoint methodology for systematic, evidence-based debugging.
buninstalled andcd scripts && bun installANTHROPIC_API_KEYexported in your shell (or.envat repo root)
# All suites
bun run scripts/fix-specs.ts
# One suite
bun run scripts/fix-specs.ts suite shanghai-push0Reports are saved in reports/spec-fixes/. If no API key is set, it skips auto-fix and just runs the tests.
Each checkpoint requires actual data (no placeholders):
- Run test and confirm failure (capture output, identify failing tests)
- Generate trace comparison (
bun scripts/isolate-test.ts, identify divergence) - Read Python reference (quote actual code)
- Compare Zig implementation (quote actual code)
- Diagnose root cause and propose fix
- Implement fix (minimal changes, preserve hardfork guards)
- Verify fix (re-run tests)
Location: scripts/known-issues.json
Tracks historical debugging insights:
- Common failure patterns and root causes
- Relevant file locations (with line ranges)
- Python reference locations
- Key invariants
- Expected gas costs
Schema:
{
"test-suite-name": {
"test_suite": "test-suite-name",
"description": "Brief description",
"common_causes": ["Cause 1", "Cause 2"],
"relevant_files": ["src/file.zig:function", "src/other.zig:line-range"],
"python_ref": "execution-specs/.../reference.py",
"key_invariants": ["Invariant 1", "Invariant 2"],
"gas_costs": { "OPERATION": 100 }
}
}Evm (src/evm.zig)
Orchestrates:
- State management (storage, balances, nonces, code)
- Call stack (nested CALL/CREATE, max depth 1024)
- Gas accounting (refunds, warm/cold access via EIP-2929)
- Transient storage (EIP-1153, transaction-scoped)
- Hardfork rules (automatic gas adjustment)
Key methods:
call()- Main entry pointinner_call()- CALL/STATICCALL/DELEGATECALLinner_create()- CREATE/CREATE2accessAddress(),accessStorageSlot()- EIP-2929 trackingget_storage(),set_storage()- Persistent storageget_transient_storage(),set_transient_storage()- EIP-1153
Frame (src/frame.zig)
Manages single execution context:
- 256-word stack (ArrayList of u256)
- Expandable memory (byte array)
- Program counter (PC) and gas tracking
- Bytecode interpretation
Key methods:
execute()- Main execution loopstep()- Single instruction (for tracing)- Opcode implementations (arithmetic, bitwise, storage, control flow, calls)
Host Interface (src/host.zig)
Abstract interface for external state access:
pub const HostInterface = struct {
ptr: *anyopaque,
vtable: *const VTable,
pub const VTable = struct {
getBalance: *const fn (ptr: *anyopaque, address: Address) u256,
setBalance: *const fn (ptr: *anyopaque, address: Address, balance: u256) void,
getCode: *const fn (ptr: *anyopaque, address: Address) []const u8,
setCode: *const fn (ptr: *anyopaque, address: Address, code: []const u8) void,
getStorage: *const fn (ptr: *anyopaque, address: Address, slot: u256) u256,
setStorage: *const fn (ptr: *anyopaque, address: Address, slot: u256, value: u256) void,
getNonce: *const fn (ptr: *anyopaque, address: Address) u64,
setNonce: *const fn (ptr: *anyopaque, address: Address, nonce: u64) void,
};
};Note: The host interface is used only for external state backends. Nested calls (CALL, DELEGATECALL, etc.) are handled internally by Evm.inner_call() and do not use this interface.
Test implementation: test/specs/test_host.zig
Hardfork Support (src/hardfork.zig)
pub const Hardfork = enum(u8) {
FRONTIER, HOMESTEAD, TANGERINE, SPURIOUS, BYZANTIUM,
CONSTANTINOPLE, ISTANBUL, BERLIN, LONDON, MERGE,
SHANGHAI, CANCUN, PRAGUE,
pub const DEFAULT = Hardfork.CANCUN;
};Methods: isAtLeast(fork), isBefore(fork), fromString(name)
Primitives Module (External Dependency)
The primitives library is an external dependency fetched via zig fetch from https://github.com/evmts/primitives. It is no longer included as a git submodule.
Modules provided:
- Address - 20-byte Ethereum address
- Uint(N) - Arbitrary precision (u256, u512)
- GasConstants - Per-opcode costs, hardfork-aware
- Rlp - RLP encoding/decoding
- Abi - ABI encoding/decoding
- Transaction - Legacy, EIP-2930, EIP-1559, EIP-4844
- AccessList - EIP-2930 support
- Blob - EIP-4844 blob transactions
- Hex - Hex encoding/decoding
- Crypto - Cryptographic primitives (keccak256, secp256k1, BLS12-381)
- Precompiles - Ethereum precompiled contracts
| EIP | Feature | Hardfork | Status |
|---|---|---|---|
| EIP-2929 | State access gas costs | Berlin | OK |
| EIP-2930 | Access lists | Berlin | OK |
| EIP-1559 | Fee market | London | OK |
| EIP-3198 | BASEFEE opcode | London | OK |
| EIP-3529 | Reduced gas refunds | London | OK |
| EIP-3541 | Reject code starting with 0xEF | London | OK |
| EIP-3651 | Warm coinbase | Shanghai | OK |
| EIP-3855 | PUSH0 instruction | Shanghai | OK |
| EIP-3860 | Limit init code size | Shanghai | OK |
| EIP-1153 | Transient storage (TLOAD/TSTORE) | Cancun | OK |
| EIP-4844 | Blob transactions (BLOBHASH/BLOBBASEFEE) | Cancun | OK |
| EIP-5656 | MCOPY instruction | Cancun | OK |
| EIP-6780 | SELFDESTRUCT only in same tx | Cancun | OK |
| EIP-7516 | BLOBBASEFEE opcode | Cancun | OK |
Location: src/primitives/gas_constants.zig
- Base costs: per-opcode execution (ADD, MUL, SSTORE)
- Memory expansion: quadratic cost for growth
- Call stipend: 2300 gas for value transfers
- Warm/cold access: EIP-2929 (Berlin+): warm=100, cold=2600
- Gas refunds: capped at 1/2 (pre-London) or 1/5 (London+)
- Intrinsic gas: 21000 + calldata costs
Full EIP-3155 trace support:
zig build test-traceIncludes PC, opcode, gas remaining, stack, memory, storage changes. Compare traces against reference implementations (geth, execution-specs) to identify divergences.
zig build wasm
# Output: zig-out/bin/guillotine_mini.wasm (~100-200 KB)Exported C API Functions
evm_create(),evm_destroy()evm_set_bytecode(),evm_set_execution_context(),evm_set_blockchain_context()evm_execute(),evm_get_gas_remaining(),evm_get_gas_used()evm_is_success(),evm_get_output_len(),evm_get_output()evm_set_storage(),evm_get_storage(),evm_set_balance(),evm_set_code()
See src/root_c.zig for full API.
| Command | Purpose |
|---|---|
zig build |
Build all modules |
zig build test |
Unit + spec tests |
zig build specs |
Spec tests only |
zig build wasm |
WASM library + size report |
zig build test-watch |
Interactive test runner |
zig build test-trace |
Trace capture tests |
- Implement in
src/frame.zig(opcodes) orsrc/evm.zig(behavior) - Update gas in
src/primitives/gas_constants.zigif needed - Add guards:
self.hardfork.isAtLeast(.FORK_NAME) - Test: run relevant spec tests
- Debug: use trace divergence analysis
- Naming:
snake_case(functions/variables),PascalCase(types) - Errors: explicit unions, propagate with
try - Comments: explain why, not what
- Docs: use
///for public APIs - Format: run
zig fmtbefore commit
Critical for debugging: Python execution-specs are authoritative. When in doubt, trust Python code over intuition, docs, or the Yellow Paper.
Important: execution-specs/ is a git submodule containing official Ethereum execution specs. Never commit, clean, or modify any files within this submodule.
| Problem Area | Python Reference | Zig Implementation |
|---|---|---|
| Opcode logic | execution-specs/.../vm/instructions/*.py |
src/frame.zig |
| Gas calculation | execution-specs/.../vm/gas.py |
src/primitives/gas_constants.zig + src/frame.zig |
| Call/Create | execution-specs/.../vm/instructions/system.py |
src/evm.zig (inner_call, inner_create) |
| Storage ops | execution-specs/.../vm/instructions/storage.py |
src/evm.zig (get/set storage) |
| Transient storage | execution-specs/forks/cancun/.../storage.py |
src/evm.zig (get/set transient) |
| State management | execution-specs/.../state.py |
src/evm.zig (balances, nonces, code) |
| Hardfork activation | execution-specs/forks/<hardfork>/ |
src/hardfork.zig |
| Warm/cold tracking | execution-specs/.../vm/__init__.py |
src/evm.zig (warm_addresses, warm_storage_slots) |
| Memory ops | execution-specs/.../vm/memory.py |
src/frame.zig (expandMemory) |
| Stack ops | execution-specs/.../vm/stack.py |
src/frame.zig (pushStack, popStack) |
Python: Single Evm class
- Location:
execution-specs/.../vm/__init__.py - One dataclass:
evm.stack,evm.memory,evm.pc,evm.gas_left,evm.message.block_env.state
Zig: Split Evm + Frame
- Evm (
src/evm.zig): State, storage, gas refunds, nested calls- Storage maps:
storage,transient_storage,original_storage - Warm/cold tracking:
warm_addresses,warm_storage_slots - Gas:
gas_refund
- Storage maps:
- Frame (
src/frame.zig): Single call frame execution- Stack, memory, PC, gas, bytecode
- Per-frame context: caller, address, value, calldata
Key mapping: Python's evm.stack -> Zig's frame.stack. Python's evm.message.block_env.state -> Zig's evm.storage/evm.balances.
1. Gas Metering Bugs (SSTORE example)
Python pattern:
def sstore(evm: Evm) -> None:
key = pop(evm.stack).to_be_bytes32()
new_value = pop(evm.stack)
# Check gas stipend FIRST
if evm.gas_left <= GAS_CALL_STIPEND:
raise OutOfGasError
# Then calculate dynamic cost
gas_cost = Uint(0)
if (target, key) not in evm.accessed_storage_keys:
evm.accessed_storage_keys.add((target, key))
gas_cost += GAS_COLD_SLOAD
if original_value == current_value and current_value != new_value:
if original_value == 0:
gas_cost += GAS_STORAGE_SET
else:
gas_cost += GAS_STORAGE_UPDATE - GAS_COLD_SLOAD
else:
gas_cost += GAS_WARM_ACCESS
charge_gas(evm, gas_cost)Common mistakes:
- Forgetting
SstoreSentryGas(2300) check - Not tracking
original_storageseparately fromstorage - Wrong gas refund calculation
- Not adding cold access cost before warm/set/update
2. Warm/Cold Access Tracking (EIP-2929)
Python:
accessed_addresses: Set[Address]
accessed_storage_keys: Set[Tuple[Address, Bytes]]
if address not in evm.accessed_addresses:
evm.accessed_addresses.add(address)
charge_gas(evm, GAS_COLD_ACCOUNT_ACCESS)
else:
charge_gas(evm, GAS_WARM_ACCESS)Zig:
warm_addresses: ArrayHashMap(Address, void, AddressContext, false)
warm_storage_slots: ArrayHashMap(StorageSlotKey, void, StorageSlotKeyContext, false)
if (!self.warm_addresses.contains(address)) {
try self.warm_addresses.put(address, {});
return ColdAccountAccessCost;
} else {
return WarmStorageReadCost;
}Key difference: Python uses sets; Zig uses hash maps with void values.
3. Transient Storage (EIP-1153)
Python:
def tload(evm: Evm) -> None:
key = pop(evm.stack).to_be_bytes32()
charge_gas(evm, GAS_WARM_ACCESS) # Always warm, never cold
value = get_transient_storage(...)
push(evm.stack, value)
def tstore(evm: Evm) -> None:
key = pop(evm.stack).to_be_bytes32()
new_value = pop(evm.stack)
charge_gas(evm, GAS_WARM_ACCESS) # Always warm
if evm.message.is_static:
raise WriteInStaticContext
set_transient_storage(...)Critical rules:
- Transient storage is always warm (100 gas), never cold
- Cleared at transaction boundaries, not call boundaries
- Must check
is_staticfor TSTORE - No gas refunds
4. Hardfork-Specific Behavior
Python: Separate directories (execution-specs/forks/berlin/, cancun/), each fork inherits and overrides.
Zig: Runtime checks in one codebase:
if (self.hardfork.isAtLeast(.CANCUN)) {
// Cancun-specific (EIP-1153, EIP-4844)
} else if (self.hardfork.isAtLeast(.SHANGHAI)) {
// Shanghai-specific (PUSH0, warm coinbase)
}Common mistakes:
- Wrong hardfork for feature (e.g., PUSH0 before Shanghai)
- Not using
isAtLeastfor backward compatibility - Breaking earlier hardforks when adding new feature
Must match exactly between Python and Zig.
Gas Constants Table
| Operation | Python | Zig | Value | Hardfork |
|---|---|---|---|---|
| Warm storage read | GAS_WARM_ACCESS |
WarmStorageReadCost |
100 | Berlin+ |
| Cold SLOAD | GAS_COLD_SLOAD |
ColdSloadCost |
2100 | Berlin+ |
| Cold account access | GAS_COLD_ACCOUNT_ACCESS |
ColdAccountAccessCost |
2600 | Berlin+ |
| SSTORE set (0->nonzero) | GAS_STORAGE_SET |
SstoreSetGas |
20000 | All |
| SSTORE update (nonzero->nonzero) | GAS_STORAGE_UPDATE |
SstoreResetGas |
5000 | All |
| SSTORE clear refund | GAS_STORAGE_CLEAR_REFUND |
SstoreClearRefund |
4800 | London+ |
| SSTORE stipend check | GAS_CALL_STIPEND |
SstoreSentryGas |
2300 | All |
| Call value transfer | GAS_CALL_VALUE |
CallValueCost |
9000 | All |
| Call stipend | GAS_CALL_STIPEND |
CallStipend |
2300 | All |
Locations:
- Python:
execution-specs/src/ethereum/forks/<hardfork>/vm/gas.py - Zig:
src/primitives/gas_constants.zig
- Run with trace
TEST_FILTER="exact_test_name" zig build specs # Or: bun scripts/isolate-test.ts "exact_test_name"
- Identify divergence (PC, opcode, gas remaining, stack state)
- Find Python reference
cd execution-specs/src/ethereum/forks/cancun/vm/instructions/ grep -r "def <opcode_name>" .
- Compare Zig implementation
- Opcodes:
src/frame.zig - Calls/creates:
src/evm.zig - Storage:
src/evm.zig
- Opcodes:
- Fix minimally, preserve hardfork guards
- Verify
TEST_FILTER="exact_test_name" zig build specs
- Gas calculation order matters: match Python's exact sequence
- Original vs current storage: track both for refund calculations
- Warm/cold is cumulative: once warm, stays warm for entire transaction
- Refund counter can go negative: cap only at transaction end
- Static context propagates: STATICCALL -> all child calls inherit
is_static - Memory expansion is quadratic:
size_in_words ** 2 // 512 - Call depth limit is 1024: check before any CALL/CREATE
- Guessing gas costs (must match Python exactly)
- Skipping trace comparison (shows exact divergence point)
- Mixing hardfork behaviors (use
isAtLeastguards) - Ignoring error conditions (Python
raisemust map to Zig errors) - Hardcoding test-specific logic (fix general implementation)
- Forgetting to charge gas before operations (Python charges first)
- Modifying test files (only change
src/implementations) - Silently ignoring errors with
catch {}(forbidden; handle ortry) - Creating
.backup/.bak/.oldfiles (use git instead)
- ethereum/tests: https://github.com/ethereum/tests
- execution-specs: https://github.com/ethereum/execution-specs
- EIP Index: https://eips.ethereum.org/
- EIP-3155 (Trace Format): https://eips.ethereum.org/EIPS/eip-3155
- Yellow Paper: https://ethereum.github.io/yellowpaper/paper.pdf
- Zig Documentation: https://ziglang.org/documentation/
Core Implementation Files
| File | Purpose | Key Exports |
|---|---|---|
src/root.zig |
Main module | Evm, Frame, Host, Hardfork, Tracer |
src/evm.zig |
EVM orchestrator | Evm struct, call(), inner_call(), inner_create() |
src/frame.zig |
Bytecode interpreter | Frame struct, execute(), step() |
src/host.zig |
Host interface | HostInterface, CallResult, CallType |
src/hardfork.zig |
Hardfork logic | Hardfork enum, isAtLeast(), fromString() |
src/opcode.zig |
Opcode utilities | getOpName(), opcode constants |
src/trace.zig |
EIP-3155 tracing | Tracer, TraceEntry |
src/errors.zig |
Error types | CallError enum |
src/primitives/root.zig |
Primitives exports | All primitive types |
build.zig |
Build config | Build targets, dependencies |
test_runner.zig |
Test runner | Test execution, reporting |
test/specs/runner.zig |
Spec test execution | runJsonTest(), trace comparison |
When making changes:
- Ensure all tests pass (
zig build test) - Run spec tests for affected hardfork
- Format code (
zig fmt src/ test/) - Add tests for new features
- Update documentation
This repo is evolving from an EVM-only library into a full Ethereum execution client ("Guillotine"), mirroring Nethermind's architecture in Zig.
Plan: See prd/GUILLOTINE_CLIENT_PLAN.md for the phased implementation plan.
Spec reference: See prd/ETHEREUM_SPECS_REFERENCE.md for the full source map.
| Submodule | Purpose |
|---|---|
execution-specs/ |
EELS - authoritative Python EL spec (state transitions, fork rules) |
EIPs/ |
Ethereum Improvement Proposals (normative change log) |
yellowpaper/ |
Yellow Paper (background only, outdated past Shanghai) |
devp2p/ |
Networking specs: RLPx, eth/68, snap/1, discv4/v5, ENR |
execution-apis/ |
JSON-RPC + Engine API (OpenRPC spec) |
ethereum-tests/ |
Classic JSON test fixtures (TrieTests, GeneralStateTests, BlockchainTests) |
execution-spec-tests/ |
Python-generated EVM + state transition test fixtures |
hive/ |
End-to-end integration test harness (RPC, Engine API, devp2p, sync) |
consensus-specs/ |
Consensus-layer specs (beacon chain, SSZ - future reference) |
nethermind/ |
C# reference implementation (architecture reference only) |
| Library | Location | Provides |
|---|---|---|
| Voltaire | /Users/williamcory/voltaire/packages/voltaire-zig/ |
All primitives, RLP, SSZ, Crypto, Precompiles, JSON-RPC types, JournaledState, Blockchain, StateManager |
| Guillotine-Mini | src/ (this repo) |
EVM engine (frame, opcodes, gas, hardforks Berlin->Prague) |
execution-specs/+EIPs/- for EVM + state transition behaviordevp2p/- for networking wire formatsexecution-apis/- for JSON-RPC and Engine APIethereum-tests/+execution-spec-tests/- for correctness validationnethermind/- architecture reference only (not behavioral truth)
- Never modify files in submodules (
execution-specs,EIPs,devp2p, etc.) - Use Voltaire primitives for all types (Address, Block, Tx, Hash, RLP, Crypto, etc.)
- Use guillotine-mini EVM for execution (do not build a new EVM)
- Validate every phase against official test suites (see plan for mapping)
- Mirror Nethermind's module boundaries for architecture consistency