VM(Py) Overview — Deterministic Python Subset & Runtime Architecture
Architecture of the deterministic Python VM: the safe Python subset, compilation to IR, the gas-metered interpreter, and how contracts interact with state and host capabilities.
5 min read 1,059 words
View docs/vm/OVERVIEW.md on GitHub
Source: docs/vm/OVERVIEW.md — this page mirrors the repository documentation.
Status: Stable (v1)
Scope: Deterministic smart-contract VM implemented in Python with a safe Python subset, compiled to a small IR and executed by a gas-metered interpreter.
See also: vm_py/specs/*, execution/*, capabilities/*, sdk/*
1) Design Goals
- Determinism first. Same code + inputs ⇒ same outputs, logs, and gas on every node.
- Auditability. Human-readable source is validated → lowered → IR with stable encoding.
- Tight resource bounds. Static upper-bound gas estimator + runtime gas metering; numeric caps.
- Small surface. Int/bytes/bool/address scalars, structured via ABI only; no general I/O.
- Composable. Standard library shims (
storage,events,hash,treasury,syscalls) with identical behavior across nodes.
2) Deterministic Python Subset
Contracts are authored in a strict Python subset and compiled by vm_py:
Allowed
- Pure Python constructs:
def,if/elif/else,while,for over range(...), assignment, comparisons. - Types:
int(bounded; see caps),bytes,bool,Address(ABI wrapper),Optional[T]in ABI only. - Containers (limited):
tupleandlistliterals for small, bounded sequences (size caps enforced at validation & ABI). - Operators: integer arithmetic
+ - * // %, bit ops& | ^ << >>,len(bytes), slicingb[a:b]with caps. - Errors:
abi.revert(msg)andabi.require(cond, msg). - Imports:
from stdlib import storage, events, hash, abi, treasury, syscallsonly.
Forbidden
- Floating point (
float),decimal, arbitraryopen()/os/subprocess/socket/threading,time,random. - Reflection & dynamic code:
eval,exec,__import__, metaclasses. - Unbounded recursion, generators, async/await, context managers with I/O.
- Non-deterministic globals or module-level code with side effects.
Builtins
Allowlist (exact list in vm_py/compiler/builtins_allowlist.py): len, range, min, max, abs, enumerate (bounded), int, bytes. Everything else is rejected at validation.
3) Runtime Architecture
source.py │ (AST Validator: syntax, imports, caps) ▼ Lowering (ast_lower.py) ──▶ Typecheck (typecheck.py) ──▶ Gas upper-bound (gas_estimator.py) │ ▼ IR (compiler/ir.py) ⇄ Encode/Decode (compiler/encode.py, msgspec/CBOR; stable) │ ▼ Interpreter (runtime/engine.py) ├─ GasMeter (runtime/gasmeter.py) ├─ Context (runtime/context.py: BlockEnv/TxEnv) ├─ Stdlib shims: │ • storage (runtime/storage_api.py) │ • events (runtime/events_api.py) │ • hash (runtime/hash_api.py) │ • treasury(runtime/treasury_api.py) │ • syscalls(runtime/syscalls_api.py) ← capabilities/* └─ Deterministic PRNG (runtime/random_api.py)
IR: A small instruction set with explicit control flow & stack model; every op maps to an entry in vm_py/gas_table.json.
Encoding: Canonical msgspec/CBOR with stable field ordering to ensure identical code hashes.
4) Gas & Resource Model
- Static bound:
compiler/gas_estimator.pytraverses IR to produce a safe upper bound. - Runtime metering:
GasMeterdebits per IR op and stdlib call (costs fromgas_table.json). - Refunds: Certain operations (e.g., storage delete) may incur bounded refunds; finalized via
execution/gas/refund.py. - Caps:
- Int magnitude ≤
2^256-1(configurable invm_py/config.py), arithmetic saturates or reverts per op rules. - Bytes length, list/tuple sizes, loop bounds checked statically where possible; enforced dynamically otherwise.
- Int magnitude ≤
- Deterministic OOG: Out-of-gas aborts at the exact same instruction across nodes.
5) ABI & Entry Points
Contracts expose callable functions defined in a manifest (see spec/abi.schema.json and vm_py/examples/*/manifest.json).
- Encoding: Canonical, length-prefixed scalars & arrays (
vm_py/abi/encoding.py). - Dispatch:
runtime/abi.pydecodes call data → invokes method → encodes return. - Events:
stdlib.events.emit(name: bytes, args: dict)produces canonical logs.
Example manifest snippet
{
"name": "Counter",
"abi": {
"functions": [
{"name":"inc","inputs":[],"outputs":[]},
{"name":"get","inputs":[],"outputs":[{"type":"int"}]}
]
}
}
6) Stdlib Surfaces (Deterministic)
- storage — Key/value bytes store: get(key: bytes) -> bytes, set(key: bytes, value: bytes); helpers for ints.
- events — emit(topic: bytes, args: dict); ordering is deterministic (program order).
- hash — keccak256(b), sha3_256(b), sha3_512(b); byte-only, no streams.
- treasury — balance() -> int, transfer(to: Address, amount: int) (local sim is inert; chain mode bridges execution).
- syscalls — capability shims:
- blob_pin(ns: int, data: bytes) -> commitment
- ai_enqueue(model: bytes, prompt: bytes) -> task_id
- quantum_enqueue(circuit: bytes, shots: int) -> task_id
- zk_verify(circuit_id: bytes, proof: bytes, public: bytes) -> bool Determinism is enforced by capabilities/runtime/determinism.py (size caps, transcript hashing).
7) Determinism Rules (Non-Exhaustive)
- No wall-clock or external I/O. State & inputs come only from call data and host-provided context (height, coinbase, etc.).
- Stable hashing & encoding. All hashes use explicit domain tags; maps/logs are serialized with stable key order.
- Event order == program order. No concurrency; single-threaded interpreter.
- Error semantics: abi.revert → REVERT; OOG → OOG; all are canonical and reflected in receipts.
8) Development Flow
CLI tools:
# Compile to IR
omni vm compile vm_py/examples/counter/contract.py --out out.ir
# Run in local simulator (no chain state writes)
omni vm run --manifest vm_py/examples/counter/manifest.json --call inc
omni vm run --manifest vm_py/examples/counter/manifest.json --call get
# Inspect IR & gas
omni vm inspect_ir out.ir
Programmatic (Python):
from vm_py.runtime.loader import load
ctr = load(manifest_path="examples/counter/manifest.json")
ctr.call(“inc”)
print(ctr.call("get")) # -> 1
9) Integration with Execution/State
The VM plugs into execution/runtime/contracts.py via execution/adapters/vm_entry.py when enabled:
- Tx decode → apply_tx → dispatch transfer/deploy/call.
- Deploy stores code hash; calls load and execute code deterministically.
- Receipts built via execution/state/receipts.py with logs and bloom.
- Optional access-list generation via execution/access_list/build.py.
10) Upgrades & Feature Flags
- vm_py/config.py exposes strict mode and caps.
- Gas table lives in vm_py/gas_table.json derived from spec/opcodes_vm_py.yaml.
- Protocol upgrades are version-gated and coordinated via docs/spec/UPGRADES.md.
11) Security Notes
- Subset enforcement: The AST validator rejects disallowed nodes and imports. Never bypass the validator.
- Numeric hazards: Shifts/mults are capped; exceeding caps reverts (or saturates where specified).
- ABI validation: Strict types and lengths; malformed inputs revert before execution.
- Syscalls: Inputs sanitized; results are not visible same-block unless explicitly designed (e.g., enqueue now, read next block).
12) Minimal Example (Counter)
contract.py
from stdlib import storage, events, abi
KEY = b"count"
def inc():
cur = int.from_bytes(storage.get(KEY) or b"\x00", "big")
nxt = cur + 1
storage.set(KEY, nxt.to_bytes(32, "big"))
events.emit(b"Inc", {"value": nxt})
def get() -> int:
cur = storage.get(KEY) or b"\x00"
return int.from_bytes(cur, "big")
manifest.json matches ABI for inc() and get().
13) Reproducibility
- Code hash: CBOR/msgspec of IR → SHA3-256; becomes the canonical code identifier.
- Deterministic PRNG: Seeded from tx hash via runtime/random_api.py.
- Lockfiles: Toolchain versions (Python, msgspec, CBOR libs) are pinned; see docs/vm/REPRODUCIBILITY.md (and repo lockfiles).
14) References
- vm_py/README.md, vm_py/specs/{DETERMINISM,IR,ABI,GAS}.md
- execution/specs/* — gas, state, receipts
- capabilities/specs/* — syscalls & determinism
- sdk/* — client codegen & examples
- website/src/docs/WALLET.mdx — wallet interaction (sign & send)
This page mirrors a file in the animicaorg/all repository. If the repository and this page ever disagree, the repository is authoritative. For long-form explainers written for newcomers, see Learn.