Skip to main content

Polkadot Virtual Machine (PVM)

The Polkadot Virtual Machine (PVM) is JAM's deterministic execution environment, providing a RISC-V-inspired register-based architecture for running service code. The PVM is central to JAM's computational model, executing code during three distinct invocation contexts.

What is the PVM?

The PVM is a register-based virtual machine with:

  • 13 general-purpose registers (RA, SP, T0-T2, S0-S5, A0-A2)
  • Deterministic execution: Same inputs always produce same outputs
  • Gas metering: Execution bounded by gas limits
  • Memory management: Byte-addressable RAM with page-fault mechanism
  • Host calls: System functions for I/O, crypto, and state access

PVM Invocations

JAM uses the PVM in three distinct contexts:

1. Is-Authorized (Ψ_I)

Purpose: Validate work-package authorization

Context: Stateless, read-only access to authorization data

Host Calls Available:

  • Environment inspection (constants, parameters)
  • Authorization data lookup
  • No state modification

Example Use: Check if a work-package hash matches authorization pool requirements

Gas Limit: 10 million (G_I = 10^7)

2. Refine (Ψ_R)

Purpose: Execute work-items and produce results off-chain

Context: Read-only historical state access, can spawn child PVM instances

Host Calls Available:

  • Historical lookup (past block state)
  • Inner PVM invocation (child instances)
  • Data export (segment creation)
  • Service state read access
  • No state modification (off-chain execution)

Example Use: Process transactions, compute merkle proofs, validate data

Gas Limit: Variable per work-item (specified in work-package)

3. Accumulate (Ψ_A)

Purpose: Integrate work-results into service state on-chain

Context: Full read-write access to service state and privileged operations

Host Calls Available:

  • Service state read/write
  • Account balance transfers
  • New service creation
  • Privilege management
  • Preimage storage
  • Core authorization
  • Validator assignment
  • All general host calls

Example Use: Update token balances, create new services, modify account state

Gas Limit: Per work-digest accumulation gas

Execution Model

Program Structure

PVM programs consist of:

┌──────────────────────────────────────┐
│ Instruction Data (c) │ ← Actual instructions
├──────────────────────────────────────┤
│ Opcode Bitmask (k) │ ← Marks instruction starts
├──────────────────────────────────────┤
│ Jump Table (j) │ ← Dynamic jump targets
└──────────────────────────────────────┘

Instruction Data: Sequence of octets encoding instructions Opcode Bitmask: Bitmap marking which octets are instruction opcodes Jump Table: Valid targets for dynamic jumps

Execution Cycle

def execute_pvm(program, pc, gas, registers, memory):
while True:
# 1. Fetch instruction at PC
opcode = program.code[pc]

# 2. Execute instruction
result = execute_instruction(opcode, registers, memory)

# 3. Consume gas
gas -= instruction_cost(opcode)
if gas < 0:
return OUT_OF_GAS

# 4. Handle result
if result == HALT:
return HALT
elif result == PANIC:
return PANIC
elif result == HOST_CALL:
return HOST_CALL
elif result == PAGE_FAULT:
return PAGE_FAULT

# 5. Advance PC
pc += instruction_length(opcode)

Exit Conditions

StatusValueDescription
HALT0Normal termination, registers[7] contains return code
PANIC1Abnormal termination (trap instruction, invalid operation)
OUT_OF_GAS2Gas exhausted during execution
PAGE_FAULT3Memory access to unmapped page
HOST_CALL4Host call requested (execution suspends)

Registers

The PVM has 13 registers, each 32-bit:

RegisterIndexABI NamePurpose
RA0Return AddressFunction call return pointer
SP1Stack PointerCurrent stack top
T02Temporary 0Caller-saved temporary
T13Temporary 1Caller-saved temporary
T24Temporary 2Caller-saved temporary
S05Saved 0Callee-saved register
S16Saved 1Callee-saved register
A07Argument 0 / ReturnFirst arg, return value
A18Argument 1Second argument
A29Argument 2Third argument
S210Saved 2Callee-saved register
S311Saved 3Callee-saved register
S412Saved 4Callee-saved register

Host Call Convention: Register A0 (r7) used for host call return values

Memory Model

Page-Based Memory

Memory is organized in 4096-byte (4KB) pages:

  • Page 0: Always inaccessible (traps on access)
  • Pages 1+: Dynamically allocated via page faults
  • Maximum: 2^32 bytes (4GB addressable space)

Page Fault Mechanism

When accessing unmapped memory:

  1. PAGE_FAULT status returned with faulting address
  2. Host allocates memory page
  3. Execution resumes at same instruction

This allows lazy memory allocation and gas-bounded growth.

Memory Operations

# Read from memory
value = memory.read_u32(address)

# Write to memory
memory.write_u32(address, value)

# Check accessibility
if memory.is_accessible(address):
# Safe to access

Gas System

Gas Costs

Different instructions have different gas costs:

  • Basic ALU: 1-2 gas
  • Multiplication: 2 gas
  • Division: 4 gas
  • Memory load/store: 2 gas
  • Branches: 2 gas
  • Host calls: Variable (10+)

Gas Tracking

initial_gas = 1_000_000
gas_consumed = initial_gas - remaining_gas

Gas is checked after each instruction. Execution terminates with OUT_OF_GAS when gas becomes negative.

Tessera Implementation

Execution Modes

Tessera supports multiple PVM execution backends:

1. Interpreter Mode (Default):

from tsrkit_pvm.cpvm.cy_pvm import CyInterpreter as PVM
  • Cython-optimized bytecode interpreter
  • Balance of speed and compatibility

2. Recompiler Mode:

from tsrkit_pvm import Recompiler as PVM
  • JIT compilation to native code
  • Maximum performance
  • Requires tsrkit-pvm recompiler

3. Pure Python Mode:

from tsrkit_pvm import Interpreter as PVM
  • Fallback for compatibility
  • No native dependencies

Host Call Dispatch

Host calls are dispatched through a table-based system:

def dispatch(host_call: int, gas: int, registers: list, memory: Memory, context: Context):
# Look up host call handler
table_entry = INVOCATION_TABLE.get(host_call)

if table_entry is None:
# Unknown host call
registers[7] = HostStatus.WHAT.value
return CONTINUE, gas - 10, registers, memory, context

# Execute host call function
handler, args = table_entry
return handler.execute(host_call, gas, registers, memory, context, args)

Documentation Structure

This PVM section covers:

Host Calls

Detailed reference for all PVM host call functions:

  • General functions (gas, lookup, write)
  • Is-Authorized specific
  • Refine specific (historical lookup, inner PVM, export)
  • Accumulate specific (transfer, new account, privileges)

Instructions

Complete PVM instruction set:

  • Arithmetic and logic operations
  • Memory operations
  • Control flow (jumps, branches, calls)
  • Instruction encoding and formats

Recompilation

Optimization and JIT compilation:

  • Interpreter → native code compilation
  • Performance characteristics
  • Basic block analysis
  • Optimization techniques

Key Concepts

Determinism

Critical Property: Same input → Same output, always

Achieved through:

  • No system calls or non-deterministic operations
  • All randomness provided via arguments (entropy pools)
  • Fixed instruction semantics
  • Bounded execution (gas limits)

Isolation

Programs cannot:

  • Access other services' state (except via explicit host calls)
  • Perform I/O directly (only through host calls)
  • Exceed resource limits (gas, memory)
  • Affect protocol correctness (validated off-chain, accumulated on-chain)

Efficiency

Performance optimizations:

  • Register-based (vs stack-based) for fewer memory accesses
  • Simple instruction encoding for fast decode
  • Page-based memory for sparse allocation
  • JIT compilation support

Implementation Files

Core PVM:

  • tessera/jam/execution/host_call.py - Main execution loop
  • tessera/jam/execution/invocations/ - Invocation contexts
  • tsrkit-pvm/ - Underlying PVM runtime

Invocations:

  • invocations/is_authorized.py - Authorization validation
  • invocations/refine.py - Work-item execution
  • invocations/accumulate.py - State accumulation

Host Call Functions:

  • invocations/functions/general_fns.py - Common host calls
  • invocations/functions/refine_fns.py - Refine-specific
  • invocations/functions/accumulate_fns.py - Accumulate-specific

References

  • Gray Paper: Appendix A - Virtual Machine & Invocations
  • Implementation: tessera/jam/execution/ and tsrkit-pvm/
  • Test Vectors: W3F PVM test suite

Next: Explore Host Calls, Instructions, or Recompilation