Startup#
This page specifies how sub-VMs start: the top-level entry, the startup message, runner initialization, and the derivation of meta-properties when a child sub-VM is created. The meta-property fields themselves are defined in Meta-Properties. Runner syntax and load mechanics are specified in Runners.
Top-Level Startup#
Before executing the root sub-VM, GenVM:
Validates the entry payload against Method Calling Convention (see below).
Reads the contract’s locked slots and root-slot data.
Resolves the contract code from deployment input or from the contract’s configured code slot.
Checks upgrade and ABI-major compatibility as specified in Contract Upgradability.
Reads contract-owned permission bits from the root slot and combines them with node-granted VM permissions.
Creates a deterministic root VM with the initial message described below.
If code is supplied with the execution request, that code is the runner source for the deployment execution. Otherwise, the runner source is the contract code resolved from storage.
Entry Payload Validation#
The entry payload is host-supplied and is checked against Method Calling Convention before permissions are read and before any runner loads. A payload that violates the convention produces the VMError malformed_entry as the execution result, like any other failure at this point.
Messages emitted by PostMessage Message and DeployContract are
executed later as top-level entries, so they are validated here too.
Startup Message#
The executable WASM receives one Calldata Encoding message on standard input. The message contains:
contract_address,sender_address,origin_addresssigner_address: the externally-owned account that signed the transaction. GenVM treats it as opaque and forwards it unchanged to every child sub-VM.stack: the view-call stack, empty for a top-level entry
chain_id,value,is_init,datetimeentry_kind: one of entry_kindentry_data: entry payload bytesentry_stage_data: consensus-stage payload data
For top-level execution, entry_kind is
main and entry_stage_data is absent
data. Sub-VM operations may create sandbox or consensus-stage messages.
Runner Startup#
The selected runner is loaded and its initialization actions are applied until
StartWasm is reached. Actions may map files, set process
arguments, add environment variables, or link additional WASM modules. Linked
modules that export _initialize execute that function before startup
continues.
The StartWasm action instantiates the executable WASM module. Its _start
entrypoint then consumes the startup message and produces the VM result.
Sub-VM Creation#
A new sub-VM is created for:
the initial entry
Creation is rejected with out_of vm_recursion if the new depth is greater than or equal to vm_recursion.
Each RunNondet Message additionally allocates the next
execution-wide call_no, starting at zero. If call_no is greater than or
equal to nondet_blocks, the call fails
with out_of nondet_blocks.
Creating the sub-VM charges vm_spawn_cost octets of RAM Consumption to the new sub-VM; the charge is released when it finishes.
After these checks, startup applies the Custom-Runner Grants for the new sub-VM, then performs the runner load action for the entry runner and continues with Runner Startup.
Meta-Property Derivation#
Every meta-property of a child sub-VM starts as an independent copy of the parent’s value; later changes in the child never affect the parent. The subsections below list only the fields that deviate from that copy. Two deviations apply to every child:
depth is
parent.depth + 1det_subvm_hashes starts empty
The initial entry has no parent, so all of its fields are given explicitly.
Initial Entry#
permissions are the node-granted permission meta-properties, plus can_use_balance_for_message_fees read from the contract’s root slot.
state_mode is the default storage view.
topmost_runner_id is the deployment runner or the contract’s accepted code runner.
det_subvm_hashes and granted_custom are empty.
CallContract Message#
The child is read-only.
permissions deviations:
write_storage is false
send_messages is false
spawn_nondet is false
can_use_balance_for_message_fees is false
state_mode is the requested storage view (param
state); a request of default keeps the parent’s value (the plain copy rule), so by default the callee observes a view at least as recent as its caller’s. Because the child cannot write, its default view is the accepted state: it never includes the calling transaction’s uncommitted writes (see Contract Execution Flow).topmost_runner_id is the callee’s contract runner.
granted_custom is the caller’s entire loaded custom-runner set (see Custom-Runner Grants).
The child’s startup message is a copy of the caller’s, except:
contract_addressis the callee’s addressstack additionally has the caller’s
contract_addressappendedentry_datais the Calldata Encoded method callvalueis0is_initis false
Sandbox Message#
The child runs at the same determinism level as its parent and cannot exceed
the parent’s privileges: each allow_* payload flag can only retain an
inherited permission, never add one.
permissions deviations:
write_storage is cleared unless param
allow_write_storageis setspawn_nondet is false
call_others is false
send_messages is cleared unless param
allow_send_messagesis setcan_use_balance_for_message_fees is cleared unless param
allow_send_messagesis set: balance-funded fees only affect message emission, so they are gated by the same flagregister_runners is cleared unless param
allow_register_runnersis set
topmost_runner_id is the param
runner, resolved in the caller’s scope.granted_custom is derived from param
custom_runnersas specified in Custom-Runner Grants.
RunNondet Message#
The child executes in Non-Deterministic Mode.
permissions are all false, including deterministic.
state_mode is the default storage view.
topmost_runner_id is the param
runner, resolved in the caller’s scope; when absent, the caller’s own runner is used.granted_custom is derived from param
custom_runnersas specified in Custom-Runner Grants.
Custom-Runner Grants#
At creation, the child’s granted_custom is derived from the caller’s loaded custom-runner set (see Custom Runner Loading):
Sandbox Message and RunNondet Message children receive the runners named by param
custom_runners:when absent, every
custom:entry of the caller’s loaded set is granted;when present, exactly the listed runners are granted. Every listed runner must be a
custom:<hash>id loaded in the caller, without duplicates; a list containing any other kind of id (includingname:hashandchain:) is a VMError;if the param
runneris itself acustom:id, it must be loaded in the caller and is granted implicitly.
CallContract Message children receive the caller’s entire custom set.
Each grant is a load action in the child, charged against the child’s RAM
budget. Grants are applied before the entry-runner load, so a custom:
entry point that is also granted is charged once.
Grants never flow back: when a child sub-VM finishes, the parent’s loaded set is unchanged, even if the child registered runners.