This document provides the specification for the Fuel Virtual Machine (FuelVM). The specification covers the types, instruction set, and execution semantics.
name | type | value | note |
---|---|---|---|
CONTRACT_MAX_SIZE | uint64 | Maximum contract size, in bytes. | |
MEM_MAX_ACCESS_SIZE | uint64 | Maximum memory access size, in bytes. | |
VM_MAX_RAM | uint64 | 2**26 | 64 MiB. |
MESSAGE_MAX_DATA_SIZE | uint16 | Maximum size of message data, in bytes. |
FuelVM instructions are exactly 32 bits (4 bytes) wide and comprise of a combination of:
Of the 64 registers (6-bit register address space), the first 16
are reserved:
value | register | name | description |
---|---|---|---|
0x00 | $zero | zero | Contains zero (0 ), for convenience. |
0x01 | $one | one | Contains one (1 ), for convenience. |
0x02 | $of | overflow | Contains overflow/underflow of addition, subtraction, and multiplication. |
0x03 | $pc | program counter | The program counter. Memory address of the current instruction. |
0x04 | $ssp | stack start pointer | Memory address of bottom of current writable stack area. |
0x05 | $sp | stack pointer | Memory address on top of current writable stack area (points to free memory). |
0x06 | $fp | frame pointer | Memory address of beginning of current call frame. |
0x07 | $hp | heap pointer | Memory address below the current bottom of the heap (points to used/oob memory). |
0x08 | $err | error | Error codes for particular operations. |
0x09 | $ggas | global gas | Remaining gas globally. |
0x0A | $cgas | context gas | Remaining gas in the context. |
0x0B | $bal | balance | Received balance for this context. |
0x0C | $is | instrs start | Pointer to the start of the currently-executing code. |
0x0D | $ret | return value | Return value or pointer. |
0x0E | $retl | return length | Return value length in bytes. |
0x0F | $flag | flags | Flags register. |
Integers are represented in big-endian format, and all operations are unsigned. Boolean false
is 0
and Boolean true
is 1
.
Registers are 64 bits (8 bytes) wide. Words are the same width as registers.
Persistent state (i.e. storage) is a key-value store with 32-byte keys and 32-byte values. Each contract has its own persistent state that is independent of other contracts. This is committed to in a Sparse Binary Merkle Tree.
value | name | description |
---|---|---|
0x01 | F_UNSAFEMATH | If set, undefined arithmetic zeroes target and sets $err without panic. |
0x02 | F_WRAPPING | If set, overflowing arithmetic wraps around and sets $of without panic. |
All other flags are reserved, any must be set to zero.
A complete instruction set of the Fuel VM is documented in the following page .
Every time the VM runs, a single monolithic memory of size VM_MAX_RAM
bytes is allocated, indexed by individual byte. A stack and heap memory model is used, allowing for dynamic memory allocation in higher-level languages. The stack begins at 0
and grows upward. The heap begins at VM_MAX_RAM
and grows downward.
To initialize the VM, the following is pushed on the stack sequentially:
byte[32]
, word-aligned), computed as defined here . MAX_INPUTS
pairs of (asset_id: byte[32], balance: uint64)
, of:
MAX_INPUTS
asset IDs, the pair has a value of zero. uint64
, word-aligned). Then the following registers are initialized (without explicit initialization, all registers are initialized to zero):
$ssp = 32 + MAX_INPUTS*(32+8) + size(tx))
: the writable stack area starts immediately after the serialized transaction in memory (see above). $sp = $ssp
: writable stack area is empty to start. $hp = VM_MAX_RAM
: the heap area begins at the top and is empty to start. There are 4 contexts in the FuelVM: predicate estimation , predicate verification , scripts , and calls . A context is an isolated execution environment with defined memory ownership and can be external or internal:
$fp
will be zero. $fp
will be non-zero. Returning from a context behaves differently depending on whether the context is external or internal.
For any input of type InputType.Coin
or InputType.Message
, a non-zero predicateLength
field means the UTXO being spent is a P2SH rather than a P2PKH output.
For each such input in the transaction, the VM is initialized , then:
$pc
and $is
are set to the start of the input's predicate
field. $ggas
and $cgas
are set to the minimum of tx.gasLimit
or MAX_GAS_PER_PREDICATE
. Predicate estimation will fail if gas is exhausted during execution.
During predicate mode, hitting any of the following instructions causes predicate estimation to halt, returning Boolean false
:
In addition, during predicate mode if $pc
is set to a value greater than the end of predicate bytecode (this would allow bytecode outside the actual predicate), predicate estimation halts returning Boolean false
.
A predicate that halts without returning Boolean true
would not pass verification, making the entire transaction invalid. Note that predicate validity is monotonic with respect to time (i.e. if a predicate evaluates to true
then it will always evaluate to true
in the future).
After successful execution, predicateGasUsed
is set to tx.gasLimit - $ggas
.
For any input of type InputType.Coin
or InputType.Message
, a non-zero predicateLength
field means the UTXO being spent is a P2SH rather than a P2PKH output.
For each such input in the transaction, the VM is initialized , then:
$pc
and $is
are set to the start of the input's predicate
field. $ggas
and $cgas
are set to predicateGasUsed
. Predicate verification will fail if gas is exhausted during execution.
During predicate mode, hitting any contract instruction causes predicate verification to halt, returning Boolean false
.
In addition, during predicate mode if $pc
is set to a value greater than the end of predicate bytecode (this would allow bytecode outside the actual predicate), predicate verification halts returning Boolean false
.
A predicate that halts without returning Boolean true
does not pass verification, making the entire transaction invalid. Note that predicate validity is monotonic with respect to time (i.e. if a predicate evaluates to true
then it will always evaluate to true
in the future).
After execution, if $ggas
is non-zero, predicate verification fails.
If script bytecode is present, transaction validation requires execution.
The VM is initialized , then:
$pc
and $is
are set to the start of the transaction's script bytecode. $ggas
and $cgas
are set to tx.gasLimit
minus the sum of predicateGasUsed
for all predicates. Following initialization, execution begins.
For each instruction, its gas cost gc
is first computed. If gc > $cgas
, deduct $cgas
from $ggas
and $cgas
(i.e. spend all of $cgas
and no more), then revert immediately without actually executing the instruction. Otherwise, deduct gc
from $ggas
and $cgas
.
Cross-contract calls push a call frame onto the stack, similar to a stack frame used in regular languages for function calls (which may be used by a high-level language that targets the FuelVM). The distinction is as follows:
Call frames are needed to ensure that the called contract cannot mutate the running state of the current executing contract. They segment access rights for memory: the currently-executing contracts may only write to their own call frame and their own heap.
A call frame consists of the following, word-aligned:
bytes | type | value | description |
---|---|---|---|
Unwritable area begins. | |||
32 | byte[32] | to | Contract ID for this call. |
32 | byte[32] | asset_id | asset ID of forwarded coins. |
8*64 | byte[8][64] | regs | Saved registers from previous context. |
8 | uint16 | codesize | Code size in bytes, padded to word alignment. |
8 | byte[8] | param1 | First parameter. |
8 | byte[8] | param2 | Second parameter. |
1* | byte[] | code | Zero-padded to 8-byte alignment, but individual instructions are not aligned. |
Unwritable area ends. | |||
* | Call frame's stack. |
Whenever memory is written to (i.e. with SB
or SW
), or write access is granted (i.e. with CALL
), ownership must be checked.
If the context is external, the owned memory range is:
[$ssp, $sp)
: the writable stack area. [$hp, VM_MAX_RAM)
: the heap area allocated by this script or predicate. If the context is internal, the owned memory range for a call frame is:
[$ssp, $sp)
: the writable stack area of the call frame. [$hp, $fp->$hp)
: the heap area allocated by this call frame.