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
| Status | Value | Description |
|---|---|---|
| HALT | 0 | Normal termination, registers[7] contains return code |
| PANIC | 1 | Abnormal termination (trap instruction, invalid operation) |
| OUT_OF_GAS | 2 | Gas exhausted during execution |
| PAGE_FAULT | 3 | Memory access to unmapped page |
| HOST_CALL | 4 | Host call requested (execution suspends) |
Registers
The PVM has 13 registers, each 32-bit:
| Register | Index | ABI Name | Purpose |
|---|---|---|---|
| RA | 0 | Return Address | Function call return pointer |
| SP | 1 | Stack Pointer | Current stack top |
| T0 | 2 | Temporary 0 | Caller-saved temporary |
| T1 | 3 | Temporary 1 | Caller-saved temporary |
| T2 | 4 | Temporary 2 | Caller-saved temporary |
| S0 | 5 | Saved 0 | Callee-saved register |
| S1 | 6 | Saved 1 | Callee-saved register |
| A0 | 7 | Argument 0 / Return | First arg, return value |
| A1 | 8 | Argument 1 | Second argument |
| A2 | 9 | Argument 2 | Third argument |
| S2 | 10 | Saved 2 | Callee-saved register |
| S3 | 11 | Saved 3 | Callee-saved register |
| S4 | 12 | Saved 4 | Callee-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:
- PAGE_FAULT status returned with faulting address
- Host allocates memory page
- 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-pvmrecompiler
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 looptessera/jam/execution/invocations/- Invocation contextstsrkit-pvm/- Underlying PVM runtime
Invocations:
invocations/is_authorized.py- Authorization validationinvocations/refine.py- Work-item executioninvocations/accumulate.py- State accumulation
Host Call Functions:
invocations/functions/general_fns.py- Common host callsinvocations/functions/refine_fns.py- Refine-specificinvocations/functions/accumulate_fns.py- Accumulate-specific
References
- Gray Paper: Appendix A - Virtual Machine & Invocations
- Implementation:
tessera/jam/execution/andtsrkit-pvm/ - Test Vectors: W3F PVM test suite
Next: Explore Host Calls, Instructions, or Recompilation