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:

  1. Validates the entry payload against Method Calling Convention (see below).

  2. Reads the contract’s locked slots and root-slot data.

  3. Resolves the contract code from deployment input or from the contract’s configured code slot.

  4. Checks upgrade and ABI-major compatibility as specified in Contract Upgradability.

  5. Reads contract-owned permission bits from the root slot and combines them with node-granted VM permissions.

  6. 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_address

  • signer_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, datetime

  • entry_kind: one of entry_kind

  • entry_data: entry payload bytes

  • entry_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:

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:

The initial entry has no parent, so all of its fields are given explicitly.

Initial Entry#

CallContract Message#

The child is read-only.

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.

RunNondet Message#

The child executes in Non-Deterministic Mode.

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 (including name:hash and chain:) is a VMError;

    • if the param runner is itself a custom: 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.