Skip to main content
The executor module provides the core VM execution loop, state management, and bytecode interpretation.

Vm Struct

The main virtual machine state container.
registers
Registers
16 general-purpose 64-bit registers (R0-R15)
memory
Memory
Linear memory up to 1MB
pc
usize
Program counter pointing to current instruction
gas
GasMeter
Gas metering for execution costs
bytecode
Vec<u8>
Bytecode being executed
halted
bool
Whether execution has stopped
caller
Address
Address of the calling account
address
Address
Address of the current contract
call_value
u64
Value sent with the call
block_number
u64
Current block number
timestamp
u64
Current block timestamp
storage
Option<Box<dyn StorageBackend>>
Optional persistent storage backend
logs
Vec<u64>
Values logged during execution

Methods

new

Create a new VM with the given bytecode and gas limit.
pub fn new(
    bytecode: Vec<u8>,
    gas_limit: u64,
    caller: Address,
    address: Address,
    call_value: u64,
) -> Self
bytecode
Vec<u8>
required
Bytecode to execute
gas_limit
u64
required
Maximum gas allowed for execution
caller
Address
required
Address of the calling account
address
Address
required
Address of the current contract
call_value
u64
required
Value sent with the call
vm
Vm
A new VM instance with 1MB memory limit and default block context

new_with_context

Create a new VM with additional context fields.
pub fn new_with_context(
    bytecode: Vec<u8>,
    gas_limit: u64,
    caller: Address,
    address: Address,
    call_value: u64,
    block_number: u64,
    timestamp: u64,
) -> Self
block_number
u64
required
Current block number
timestamp
u64
required
Current block timestamp

run

Run the VM until it halts or runs out of gas.
pub fn run(&mut self) -> Result<ExecutionResult, VmError>
result
ExecutionResult
Execution result containing success status, gas used, return data, and logs
Returns: Result<ExecutionResult, VmError> - Success result or error

set_storage

Set the storage backend for persistent state.
pub fn set_storage(&mut self, storage: Box<dyn StorageBackend>)
storage
Box<dyn StorageBackend>
required
Storage backend implementing SLOAD/SSTORE operations

set_block_context

Set the block context for execution.
pub fn set_block_context(&mut self, block_number: u64, timestamp: u64)
block_number
u64
required
Block number
timestamp
u64
required
Block timestamp

get_registers

Get current register values for inspection or tracing.
pub fn get_registers(&self) -> &[u64; 16]
registers
&[u64; 16]
Array of 16 register values

gas_remaining

Get remaining gas.
pub fn gas_remaining(&self) -> u64
gas
u64
Amount of gas remaining

ExecutionResult

Result of VM execution.
success
bool
Whether execution completed successfully (true) or failed (false)
gas_used
u64
Total gas consumed during execution
return_data
Vec<u8>
Data returned by the execution
logs
Vec<u64>
Values logged via LOG opcode

VmError

Errors that can occur during execution.

Variants

OutOfGas
{ required: u64, remaining: u64 }
Insufficient gas to continue execution
InvalidOpcode
u8
Unknown opcode byte encountered
DivisionByZero
Attempted division or modulo by zero
MemoryOverflow
Memory access beyond 1MB limit
InvalidJump
usize
Jump to invalid bytecode position
StackUnderflow
Attempted to pop from empty stack
Reverted
Execution explicitly reverted via REVERT opcode

StorageBackend Trait

Interface for persistent storage operations.

sload

Read 32 bytes from storage slot.
fn sload(&self, key: &[u8; 32]) -> [u8; 32]
key
&[u8; 32]
required
32-byte storage key
value
[u8; 32]
32-byte value at the storage slot

sstore

Write 32 bytes to storage slot.
fn sstore(&mut self, key: &[u8; 32], value: &[u8; 32])
key
&[u8; 32]
required
32-byte storage key
value
&[u8; 32]
required
32-byte value to store

Usage Example

use minichain_vm::{Vm, VmError};
use minichain_core::Address;

// Simple bytecode: ADD R0, R1, R2; HALT
let bytecode = vec![
    0x10, 0x01, 0x20,  // ADD R0, R1, R2
    0x00,              // HALT
];

// Create VM with 100,000 gas
let mut vm = Vm::new(
    bytecode,
    100_000,
    Address::zero(),
    Address::zero(),
    0,
);

// Run execution
match vm.run() {
    Ok(result) => {
        println!("Success: {}", result.success);
        println!("Gas used: {}", result.gas_used);
        println!("Registers: {:?}", vm.get_registers());
    }
    Err(e) => {
        eprintln!("Error: {:?}", e);
    }
}

Advanced Example with Storage

use minichain_vm::{Vm, StorageBackend};
use std::collections::HashMap;

// Simple in-memory storage backend
struct MemoryStorage {
    data: HashMap<[u8; 32], [u8; 32]>,
}

impl StorageBackend for MemoryStorage {
    fn sload(&self, key: &[u8; 32]) -> [u8; 32] {
        self.data.get(key).copied().unwrap_or([0u8; 32])
    }

    fn sstore(&mut self, key: &[u8; 32], value: &[u8; 32]) {
        self.data.insert(*key, *value);
    }
}

let mut vm = Vm::new(bytecode, 1_000_000, caller, address, 0);
vm.set_storage(Box::new(MemoryStorage {
    data: HashMap::new(),
}));
vm.set_block_context(12345, 1234567890);

let result = vm.run()?;

Build docs developers (and LLMs) love