VM Execution Result#

Result Kinds#

Return

Represents successful execution of a sub-VM

VMError

Represents a VM produced error, such as non-zero exit code or exceeding resource limits.

It uses predefined string error codes.

VM Error Code Format#

vm-error-code := public-code [ " # " detail ]
  • public-code — a sequence of snake_case components separated by single spaces, drawn from the predefined codes. It never contains #.

  • detail — optional free-form UTF-8 diagnostic. When present, it is separated from the public code by # surrounded by a single space on each side.

The detail MUST be a deterministic function of execution (the full string is covered by the Execution Hash), but its content carries no compatibility promise — see VM Error Code Compatibility.

Consumers MUST compare and match VM error codes only by the public code (the part before the first `` # ). A consumer matching a known code ``P MUST treat a code as matching iff its public code equals P or extends P with further space-separated components.

UserError

Represents a user-produced error in utf-8 format.

InternalError#

It is a special Result Kinds that represents an internal error in the VM, such as: Host communication failures or Module unavailability.

Internal errors are not visible by the contracts. Most likely Host will vote timeout if encounters such an error

Non-Deterministic Block Result Encoding#

Contract Result Encoding#

Return#

Arbitrary structure in Calldata Encoding

UserError and VMError#

The error value (UserError value or VMError error code string) is reported alongside two fingerprint fields, each Calldata Encoding encoded:

backtrace — the WASM call stack captured at the failure point:

[
  {
    "module_name": "<module_name>",
    "func": "<number: function_index>"
  }
]

wasm_store_hashes — per-module memory fingerprint:

{
  "<module_name>": {
    "memories": [
      "<bytes: 32_byte_blake3_hash>"
    ]
  }
}

For sake of preventing skipping execution for error results, validators are obligated to calculate the VM fingerprint on error.

Both fields are serialized using Calldata Encoding to be deterministic, and have the following structure:

  1. backtrace frames are ordered from most recent to oldest one (most likely, _start)

  2. Function index is an index of function in WASM module

  3. Memories are ordered by their index in WASM module

  4. Memories are hashed using BLAKE3 hash function, which is cryptographically secure and provides acceptable performance

Execution Hash#

Every run produces an execution hash: a SHA3-256 digest over a Calldata Encoding encoding of the consensus-visible result, as a map with the following keys (in this order):

  1. backtrace

  2. data — the contract result value

  3. data_fees_remaining

  4. kind — the Result Kinds result code

  5. storage_changes

  6. subvm_hashes — see Sub-VM Result Hash

  7. wasm_store_hashes

Two runs that agree on the deterministic result produce the same execution hash, so consensus can compare a single 32-byte value instead of the full result.

Sub-VM Result Hash#

A deterministic run accumulates the result of each deterministic sub-VM call into a rolling SHA3-256 accumulator. The finalized accumulator is the subvm_hashes field of the parent’s Execution Hash.

For each deterministic sub-VM, its small hash is folded into the accumulator. The small hash is a SHA3-256 digest over a Calldata Encoding encoded map:

  1. kind — the exact string "Return", "UserError", or "VMError"

  2. result — for Return/UserError the result value in Calldata Encoding; for VMError the error code string

  3. subvm_hashes — that sub-VM’s own finalized accumulator, making the hash recursive over the whole deterministic call tree

  4. wasm_store_hashes

Non-deterministic sub-calls do not contribute. A run with no deterministic sub-calls finalizes its accumulator to a fixed digest, so that error and edge results hash uniformly.

Post-Execution Result Validation#

A consumer in Sync Mode or Validator Mode consumes one leader-proposed result per non-deterministic block it runs. If the leader supplied more results than the run consumed, the surplus blocks were never reached: the run’s result is replaced by leader_output extra, whose parameter is the first 6 characters of the GVM32 (Base32) encoding of the sha3_256 digest of the complete [result_code][data] sub-VM result buffer the run would otherwise have returned — the whole wire buffer as emitted, not a decoded payload or alternate representation. No non-deterministic disagreement is caused by this.