stoffel repository:
| Crate | Purpose |
|---|---|
crates/stoffel-vm-types | Shared compiler/runtime data model: values, instructions, registers, activations, functions, and .stflb bytecode. |
crates/stoffel-vm | Runtime execution engine, standard library, MPC builtins, async MPC effect scheduling, storage, networking internals, and FFI. |
Execution model
Instruction values are useful at compiler/direct-construction boundaries; ResolvedInstruction values and packed runtime instructions are used for efficient execution.
VirtualMachine and VMState
VirtualMachine is a public wrapper around VMState.
VirtualMachine::new() uses the VM builder defaults:
- registers the standard library;
- registers MPC builtins;
- uses the default register layout;
- uses in-memory table storage;
- uses stdout as the output sink;
- starts without a configured MPC engine or local storage backend.
VirtualMachine::without_builtins() creates an empty VM for tests or custom embedding.
VMState owns the runtime state:
| Component | Role |
|---|---|
| Program registry | VM and foreign function definitions. |
| Activation stack | Current call frames and return flow. |
| Runtime function cache | Lowered instruction data per active frame. |
| Register layout | Maps absolute register operands to clear or secret banks. |
| Table memory | Object and array storage backend. |
| Foreign object store | Rust objects exposed to VM code. |
| Hook manager | Optional execution/debug hooks. |
| MPC runtime | Optional engine metadata and online operation handle. |
| Output sink | Destination used by print. |
| Local storage | Optional app-provided storage backend for LocalStorage.*. |
Registers and frames
The bytecode ABI uses absolute register indices. The default layout treatsr0..r15 as clear registers and r16.. as secret registers. r0 is the return register.
Each function has a frame register count. Registration normalizes that count so the frame is wide enough for:
- at least the return register;
- all parameters;
- every referenced clear or secret register.
- local variables;
- the register file;
- captured upvalues;
- a volatile argument stack;
- a stable spill area;
- compare flag;
- instruction pointer;
- optional closure metadata.
PUSHARGpushes onto the volatile argument stack for calls;LDreads from that argument stack;STS/LDSaccess a separate per-frame spill area used by the register allocator.
Clear and secret bank transitions
The register file stores clear and secret banks separately. The layout maps absolute bytecode registers into a bank and bank-local address. A write or move across banks may have MPC meaning:| Transition | Runtime behavior |
|---|---|
| clear → clear | normal value copy |
| secret → secret | share/value copy |
| clear → secret | clear value is represented as a share value when needed |
| secret → clear | reveal/open operation; may yield an async MPC effect |
Value variants.
Values and table memory
The VM value model includes scalar values, typed table handles, closures, foreign object handles, unit, and share values. Object and array storage is abstracted behindTableMemory. The default backend is an in-memory ObjectStore, but the trait boundary is designed so future backends, including access-tracking or ORAM-like storage, can preserve read/write metadata. For this reason, execution-path table reads go through mutable table-memory APIs even when a read looks logically immutable.
Arrays are 0-indexed. The default array implementation uses dense inline storage for small numeric indices plus an extra field map for large or non-numeric keys, with a cached length hint.
Functions and calls
AVMFunction carries:
- name;
- parameter names;
- upvalue names;
- optional parent function;
- register count;
- labels;
- symbolic instructions, or resolved instructions with constants and call targets.
Hooks and debugging
The VM has an optional hook system for debugging and tooling. When no hooks are enabled, the runtime takes a fast path and does not build hook snapshots. Hook events can cover:- before/after instruction execution;
- register reads/writes with absolute and bank-local register information;
- variable and upvalue access;
- object and array field access;
- function calls;
- closure creation;
- stack push/pop.
Async MPC effects
The VM can execute ordinary local instructions synchronously. When execution reaches work that needs an MPC engine, async execution yields a typed effect and resumes after the engine returns a result. Examples of operations that can yield effects:- client input sharing;
- secret multiplication;
- secret boolean bit operations;
- opening/revealing a share;
Share.batch_open;Share.random/Share.random_int;MpcOutput.send_to_client;- lower-level RBC, field-open, and exponent-open operations.
Builtins and standard library
VirtualMachine::new() registers two broad groups:
| Group | Examples |
|---|---|
| Standard library | arrays/lists, objects, closures, local storage, print, type, slice, contains, assert, ClientStore.*, MpcOutput.send_to_client |
| MPC builtins | Share.*, Mpc.*, Bytes.*, Crypto.*, Field.*, Rbc.*, Avss.* |
print writes through the configured VM output sink. LocalStorage.* builtins require a local-storage backend to be configured on the VM builder.
Bytecode and manifests
.stflb bytecode is defined by CompiledBinary in stoffel-vm-types. The current format uses magic bytes STFL and format version 9.
A compiled binary carries:
- a scalar constant pool;
- compiled function records;
- function type metadata;
- client input/output schema metadata;
- MPC backend and curve selections;
- preprocessing demand estimates.
dynamic flag indicates that runtime demand may exceed the static estimate.
Lower-level embedding
Direct VM construction is useful for VM tests and bytecode tooling, but most application integrations should usestoffel build plus Stoffel::load_file(...) from the Rust SDK.