invoke
Overview
Section titled “Overview”The invoke syscall calls another program, creating a new execution frame. This enables cross-program invocation and modularity in the ThruVM runtime.
Syscall Code
Section titled “Syscall Code”Code: 0x0A (TN_SYSCALL_CODE_INVOKE)
C SDK Function
Section titled “C SDK Function”ulong tsys_invoke(void const* instr_data, ulong instr_data_sz, ushort program_account_idx, tsdk_invoke_auth_t const* auth, ulong* invoke_err_code);Arguments
Section titled “Arguments”instr_data · void const · required*
Pointer to the instruction data to pass to the invoked program.
instr_data_sz · ulong · required
Length of the instruction data in bytes.
program_account_idx · ushort · required
Index of the program account to invoke. Must be a valid program account.
auth · tsdk_invoke_auth_t const · required*
Optional invoke authorization descriptor. Pass NULL to use default authorization behavior.
When provided, the descriptor is validated by the SDK before the syscall is issued.
invoke_err_code · ulong · required*
Pointer to store the error code returned by the invoked program. This will be set to the exit code of the invoked program.
Return Value
Section titled “Return Value”Returns a syscall result code:
Success · ulong
TN_VM_SYSCALL_SUCCESS(0) - Program invocation initiated successfully
Error Codes · ulong
TN_VM_ERR_SYSCALL_INVALID_ACCOUNT_INDEX(-8) - Program account index out of boundsTN_VM_ERR_SYSCALL_ACCOUNT_DOES_NOT_EXIST(-9) - Program account does not existTN_VM_ERR_SYSCALL_ACCOUNT_IS_NOT_PROGRAM(-17) - Account is not a valid programTN_VM_ERR_SYSCALL_CALL_DEPTH_TOO_DEEP(-24) - Maximum call depth exceeded
Resource Consumption
Section titled “Resource Consumption”Compute Units
Section titled “Compute Units”- Base cost:
TN_VM_SYSCALL_BASE_COST(512 units)- Represents the overhead of saving and restoring VM register state
- Implementation details:
- Save operation: All 32 registers (256 bytes) are saved to the public shadow stack
- Restore operation: When returning from the invoked program (via exit syscall), the registers (256 bytes) are restored from the public shadow stack back to the VM
- Total memory operations per invoke/exit cycle: 512 bytes (256 saved + 256 restored)
- Cost calculation: 32 registers × 8 bytes × 2 operations (save + restore) = 512 CUs
- Additional cost: None for the invoke syscall itself
- Note: The invoked program will consume additional compute units during its execution
Memory Pages
Section titled “Memory Pages”- Page usage: No direct page allocation
- Shadow stack: Uses existing shadow stack pages to save execution state
- Call overhead: Minimal memory overhead for call frame management
Side Effects
Section titled “Side Effects”- Execution context: Creates new execution frame and updates call stack
- Register setup: Sets a0 to instruction data address, a1 to data length
- Program counter: Resets PC to 0 for the new program
- Call tracking: Increments frame and call indices
- Shadow stack: Saves current execution state on shadow stack
- Stores all 32 registers (256 bytes) in the public shadow stack
- Public shadow stack is read-only memory accessible to programs via memory segment 0x0002
- Preserves program context including stack/heap page counts
- Creates accessible snapshot for cross-program inspection and state restoration
Call Stack Management
Section titled “Call Stack Management”The syscall manages a call stack with the following updates:
- Frame index: Incremented to create new frame
- Call depth: Updated to reflect nesting level
- Shadow stack: Current state saved for restoration on return
- Program context: Switches to the invoked program’s bytecode
Program Validation
Section titled “Program Validation”- The target account must have the PROGRAM flag set
- Program bytecode is validated before invocation
- Account must exist and be accessible in the transaction
Depth Limiting
Section titled “Depth Limiting”- Maximum call depth is enforced (
TN_VM_CALL_DEPTH_MAX) - Prevents infinite recursion and stack overflow
- Call depth is tracked per execution frame
Register State
Section titled “Register State”Upon successful invocation:
- a0: Set to instruction data virtual address
- a1: Set to instruction data length
- PC: Reset to 0 (start of invoked program)
- Other registers: Preserved from calling context
Usage Notes
Section titled “Usage Notes”- Instruction data can be any format understood by the target program
- The invoked program executes with the same transaction context
- Account permissions and writability are preserved
- Return values are passed through program exit mechanisms
- If
authis non-NULL, SDK-side validation runs before the syscall:- bad auth magic:
0xBAD0A170 - auth account not owned by current program:
0xBAD0A171 - invalid account index in auth/deauth lists:
0xBAD0A173
- bad auth magic:
Example
Section titled “Example”#include <thru-sdk/c/tn_sdk_syscall.h>#include <stdint.h>
// Invoke a calculator program with operation dataushort calculator_program_idx = 5;
// Prepare instruction datastruct calc_instruction { uint32_t operation; // 1 = add, 2 = multiply uint64_t operand_a; uint64_t operand_b;} instruction = { .operation = 1, // Addition .operand_a = 100, .operand_b = 200};
ulong invoke_err = (ulong)-1;ulong result = tsys_invoke( &instruction, sizeof(instruction), calculator_program_idx, NULL, &invoke_err );
if (result == TN_VM_SYSCALL_SUCCESS && invoke_err == TN_VM_SYSCALL_SUCCESS) { // Calculator program is now executing // It will receive instruction data in a0, a1 // Current program state saved on shadow stack // Execution will continue in calculator program}