Skip to content

Syscalls

View as Markdown

Use this page when you need the low-level mutation and control operations the Thru VM exposes to Rust programs.

Every wrapper lives in thru_core::syscall and is compiled only for riscv64 targets. All of them except sys_exit are unsafe, because they let the VM write to memory your program may still be holding references to. Prefer the AccountManager method in the last column: it checks borrows at runtime first.

Most wrappers return SyscallCode, an enum over the VM’s status values (Success = 0, SuccessExit = 1, errors negative). See Error Handling and Return Codes.

Two wrappers differ:

  • sys_invoke returns (SyscallCode, SyscallCode): the syscall-level result and the callee-level result.
  • sys_increment_anonymous_segment_sz returns (SyscallCode, *mut u8): the result and the new segment address.
  • sys_log and sys_emit_event return the raw u64 from the VM.
  • sys_exit never returns.
WrapperSignatureSafe equivalent
sys_set_account_data_writable(account_idx: u64) -> SyscallCodemgr.set_account_data_writable(idx)
sys_account_transfer(from_idx: u64, to_idx: u64, amount: u64) -> SyscallCodemgr.account_transfer(from, to, amount)
sys_account_create(account_idx: u64, seed: &[u8; SEED_SIZE], proof: *const u8, proof_sz: u64) -> SyscallCodemgr.account_create(idx, seed, proof)
sys_account_create_ephemeral(account_idx: u64, seed: &[u8; SEED_SIZE]) -> SyscallCodemgr.account_create_ephemeral(idx, seed)
sys_account_create_eoa(account_idx: u64, signature: &Signature, proof: *const u8, proof_sz: u64) -> SyscallCodemgr.account_create_eoa(idx, signature, proof)
sys_account_delete(account_idx: u64, signature: Option<&Signature>) -> SyscallCodemgr.account_delete(idx) or mgr.account_delete_signed(idx, signature)
sys_account_resize(account_idx: u64, new_size: u64) -> SyscallCodemgr.account_resize(idx, new_size)
sys_account_compress(account_idx: u64, proof: *const u8, proof_sz: u64) -> SyscallCodemgr.account_compress(idx, proof)
sys_account_decompress(account_idx: u64, meta: *const u8, data: *const u8, proof: *const u8, proof_sz: u64) -> SyscallCodemgr.account_decompress(idx, meta, data, proof)
sys_account_set_flags(account_idx: u16, flags: u8) -> SyscallCodemgr.account_set_flags(idx, flags)

The AccountManager variants take a &StateProof and pass its pointer and footprint for you, and they panic if the target account is currently borrowed. mgr.create_and_init(idx, seed, proof, data_size) combines create, make-writable, resize, and borrow into one call.

Account seeds are always SEED_SIZE (32) bytes.

WrapperSignatureNotes
sys_invoke(instr_data: *const u8, instr_data_sz: u64, program_account_idx: u16, auth: *const u8) -> (SyscallCode, SyscallCode)Use mgr.invoke(...); see Cross-Program Invocation.
sys_exit(exit_code: u64, revert: u64) -> !Safe to call. Prefer program_utils::succeed / program_utils::revert.
sys_log(data: *const u8, data_sz: u64) -> u64Prefer tvm_println!.
sys_emit_event(data: *const u8, data_sz: u64) -> u64Emits an event payload for indexers and clients.
WrapperSignatureNotes
sys_set_anonymous_segment_sz(addr: *mut u8) -> SyscallCodeSizes an anonymous segment; used by the generated boot shim for the stack.
sys_increment_anonymous_segment_sz(ptr: *mut (), delta: u64) -> (SyscallCode, *mut u8)Grows a segment. program_utils::grow_stack(delta) wraps this for the stack.

The syscall ids are public constants, useful when cross-referencing the VM syscalls specification:

ConstantValue
SET_ANONYMOUS_SEGMENT_SZ0x00
INCREMENT_ANONYMOUS_SEGMENT_SZ0x01
SET_ACCOUNT_DATA_WRITABLE0x02
ACCOUNT_TRANSFER0x03
ACCOUNT_CREATE0x04
ACCOUNT_CREATE_EPHEMERAL0x05
ACCOUNT_DELETE0x06
ACCOUNT_RESIZE0x07
ACCOUNT_COMPRESS0x08
ACCOUNT_DECOMPRESS0x09
INVOKE0x0A
EXIT0x0B
LOG0x0C
EMIT_EVENT0x0D
ACCOUNT_SET_FLAGS0x0E
ACCOUNT_CREATE_EOA0x0F

VM addresses are seg_type << 40 | seg_idx << 24 | offset. thru_core::mem exposes the pieces:

use thru_core::mem::{compute_segment_addr, vm_ptr, SEG_TYPE_ACCOUNT_DATA};
let addr = compute_segment_addr(SEG_TYPE_ACCOUNT_DATA, account_idx as usize, 0);
let ptr: *const u8 = vm_ptr(SEG_TYPE_ACCOUNT_DATA, account_idx as usize, 0);
Segment typeValue
SEG_TYPE_READONLY_DATA0x00
SEG_TYPE_ACCOUNT_METADATA0x02
SEG_TYPE_ACCOUNT_DATA0x03
SEG_TYPE_STACK0x05
SEG_TYPE_HEAP0x07

Read-only data segments are indexed by SEG_IDX_TXN_DATA (0x0001), SEG_IDX_SHADOW_STACK (0x0002), SEG_IDX_PROGRAM (0x0003), and SEG_IDX_BLOCK_CTX (0x0004).

For pointer work that must keep VM provenance, use VmPtr<T> / VmPtrMut<T> and the helpers copy_nonoverlapping, copy_bytes_strict, and memset from thru_core::vmptr instead of raw core::ptr calls.