This repository contains the core crates of the Stoffel Virtual Machine, a register-based VM built for both local execution and networked multiparty computation (MPC).
In its current form, Stoffel is designed to handle both simple and complex programs. The VM supports basic values such as integers, booleans, strings, and floating point numbers, along with more complex runtime types such as objects, arrays, closures, foreign objects, and secret shares. The VM is designed as a register machine to make execution predictable and to map cleanly onto optimized runtimes and physical MPC backends.
The instruction set covers memory operations, arithmetic, bitwise operations, control flow, and function calls. Stoffel also has a closure system with true lexical scoping, where functions can capture values from their surrounding environment as upvalues and continue using them after the original scope has exited.
Stoffel supports Rust <> Stoffel FFI out of the box. This lets you extend the runtime with native Rust functions and objects while keeping the VM execution model intact. The runtime also exposes a configurable hook system that can intercept instruction execution, register access, stack events, object and array access, closure creation, and more for debugging or instrumentation.
The workspace currently includes:
crates/stoffel-vm: the runtime, networking layer, MPC integrations, CLI binaries, and C FFIcrates/stoffel-vm-types: shared VM types, instruction definitions, and the compiled bytecode formatinclude/: the public C header and FFI notes
Stoffel VM currently supports the following instructions:
LD(dest_reg, stack_offset): Load a value from the current activation record into a registerLDI(dest_reg, value): Load an immediate value into a registerMOV(dest_reg, src_reg): Move a value from one register to anotherPUSHARG(reg): Push a register value as a function argument
ADD(dest_reg, src1_reg, src2_reg): Add two registersSUB(dest_reg, src1_reg, src2_reg): Subtract two registersMUL(dest_reg, src1_reg, src2_reg): Multiply two registersDIV(dest_reg, src1_reg, src2_reg): Divide two registersMOD(dest_reg, src1_reg, src2_reg): Modulo operation
AND(dest_reg, src1_reg, src2_reg): Bitwise ANDOR(dest_reg, src1_reg, src2_reg): Bitwise ORXOR(dest_reg, src1_reg, src2_reg): Bitwise XORNOT(dest_reg, src_reg): Bitwise NOTSHL(dest_reg, src_reg, amount_reg): Shift leftSHR(dest_reg, src_reg, amount_reg): Shift right
JMP(label): Unconditional jumpJMPEQ(label): Jump if equalJMPNEQ(label): Jump if not equalJMPLT(label): Jump if less thanJMPGT(label): Jump if greater thanCMP(reg1, reg2): Compare two registersCALL(function_name): Call a functionRET(reg): Return from the current function with the value in a register
Stoffel VM currently exposes the following runtime value variants:
Value::I64(i64): 64-bit signed integerValue::I32(i32): 32-bit signed integerValue::I16(i16): 16-bit signed integerValue::I8(i8): 8-bit signed integerValue::U8(u8): 8-bit unsigned integerValue::U16(u16): 16-bit unsigned integerValue::U32(u32): 32-bit unsigned integerValue::U64(u64): 64-bit unsigned integerValue::Float(F64): 64-bit floating pointValue::Bool(bool): Boolean valueValue::String(String): String valueValue::Object(usize): Object referenceValue::Array(usize): Array referenceValue::Foreign(usize): Foreign object referenceValue::Closure(Arc<Closure>): Function closure with captured environmentValue::Unit: Unit/void/nil valueValue::Share(ShareType, ShareData): Secret-shared value for MPCValue::PendingReveal(usize): Internal marker used by batched reveal optimization
Stoffel VM registers the following general runtime builtins by default:
print: Print values to the consoletype: Get the type of a value as a stringcreate_object: Create a new objectcreate_array: Create a new arrayget_field: Get a field from an object or arrayset_field: Set a field in an object or arrayarray_length: Get the length of an arrayarray_push: Append one or more values to an arraycreate_closure: Create a closurecall_closure: Call a closureget_upvalue: Read a captured upvalue from a closureset_upvalue: Update a captured upvalue in a closureClientStore.get_number_clients: Get the number of connected clientsClientStore.take_share: Load a client share into the VMClientStore.take_share_fixed: Load a client fixed-point share into the VMMpcOutput.send_to_client: Send a share result to a client
The VM also registers MPC-focused module-style builtins:
Share.*: clear-to-share conversion, arithmetic on shares, opening, random share generation, client output, local interpolation, and commitment inspectionMpc.*: runtime MPC metadata such as party id, threshold, instance id, readiness, and randomness helpersRbc.*: reliable broadcast helpersAba.*: asynchronous binary agreement helpersCrypto.*: hashing and curve/field conversion helpersBytes.*: byte-array helpersAvss.*: AVSS-specific helpers when theavssfeature is enabled
At the moment, the most direct way to use the runtime is to embed it in a Rust program and register VMFunction values. VirtualMachine::new() automatically registers the standard library and MPC builtins.
use std::collections::HashMap;
use stoffel_vm::core_types::Value;
use stoffel_vm::core_vm::VirtualMachine;
use stoffel_vm::functions::VMFunction;
use stoffel_vm::instructions::Instruction;
fn main() -> Result<(), String> {
let mut vm = VirtualMachine::new();
let hello_world = VMFunction::new(
"hello_world".to_string(),
vec![],
vec![],
None,
2,
vec![
Instruction::LDI(0, Value::String("Hello, World!".to_string())),
Instruction::PUSHARG(0),
Instruction::CALL("print".to_string()),
Instruction::LDI(1, Value::Unit),
Instruction::RET(1),
],
HashMap::new(),
);
vm.register_function(hello_world);
let result = vm.execute("hello_world")?;
println!("Program returned: {:?}", result);
Ok(())
}Now that you're familiar with the basics of Stoffel VM, good places to explore next are:
crates/stoffel-vm-types/examples/generate_client_mul_program.rsfor a bytecode-generation examplecrates/stoffel-vm/src/tests/vm_mpc_integration.rsfor VM + MPC execution flowstests/p2p_integration.rsfor QUIC networking coverage
StoffelVM also ships a portable compiled binary format through stoffel-vm-types::compiled_binary::CompiledBinary. The format uses the magic bytes STFL and can round-trip between VMFunction definitions and serialized binaries.
You can generate a compiled binary from Rust-defined functions like this:
use stoffel_vm_types::compiled_binary::{utils::save_to_file, CompiledBinary};
// Assume `functions: Vec<VMFunction>` already exists.
let binary = CompiledBinary::from_vm_functions(&functions);
save_to_file(&binary, "program.stflb").unwrap();This repository does not currently include a source-language compiler, so compiled binaries are usually produced either by Rust-side tooling or by an external compiler that targets the same format.
Build everything:
cargo buildRun the test suite:
cargo test
cargo test --all-features
cargo test -- --ignoredBuild the runtime and CLI in release mode:
cargo build --release -p stoffel-vmstoffel-vm enables the honeybadger and avss features by default.
A CLI is included to run a compiled Stoffel bytecode file locally or as part of a distributed MPC session.
Build the CLI:
cargo build --release -p stoffel-vmShow the available flags:
cargo run -p stoffel-vm --bin stoffel-run -- --helpRun a compiled program locally (default entry function is main):
./target/release/stoffel-run path/to/program.stflb
./target/release/stoffel-run path/to/program.stflb main --trace-instrRun a leader node for a 5-party MPC session:
STOFFEL_AUTH_TOKEN=replace-with-random-secret \
./target/release/stoffel-run path/to/program.stflb main \
--leader \
--bind 127.0.0.1:9000 \
--n-parties 5 \
--threshold 1Join as another party:
STOFFEL_AUTH_TOKEN=replace-with-random-secret \
./target/release/stoffel-run path/to/program.stflb main \
--party-id 1 \
--bootstrap 127.0.0.1:9000 \
--bind 127.0.0.1:9002 \
--n-parties 5 \
--threshold 1Run in client mode to submit inputs to the party servers:
./target/release/stoffel-run --client \
--inputs 10,20 \
--servers 127.0.0.1:10000,127.0.0.1:9002,127.0.0.1:9003,127.0.0.1:9004,127.0.0.1:9005 \
--n-parties 5Notes:
STOFFEL_AUTH_TOKENis required for authenticated discovery in bootnode, leader, and party flows- The CLI accepts any file path; this repository conventionally stores compiled fixtures as
.stflb --mpc-backendsupportshoneybadgerandavss--mpc-curvesupportsbls12-381,bn254,curve25519, anded25519
stoffel-vm builds as both an rlib and a cdylib, so the runtime can also be embedded from C-compatible environments.
Relevant files:
include/stoffel_vm.hinclude/README.md
Platform-specific library names:
- Linux:
libstoffel_vm.so - macOS:
libstoffel_vm.dylib - Windows:
stoffel_vm.dll