From 7df45173bc3472a78dc207585c0c88a5e790db83 Mon Sep 17 00:00:00 2001 From: Hannes Karppila Date: Sun, 6 Oct 2024 10:57:29 +0300 Subject: [PATCH 1/9] Add LDC mode 2, which allows code from memory --- src/fuel-vm/instruction-set.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/fuel-vm/instruction-set.md b/src/fuel-vm/instruction-set.md index ec416ac2..53d73163 100644 --- a/src/fuel-vm/instruction-set.md +++ b/src/fuel-vm/instruction-set.md @@ -91,7 +91,7 @@ - [`CCP`: Code copy](#ccp-code-copy) - [`CROO`: Code Merkle root](#croo-code-merkle-root) - [`CSIZ`: Code size](#csiz-code-size) - - [`LDC`: Load code from an external contract](#ldc-load-code-from-an-external-contract-or-blob) + - [`LDC`: Load code from an external contract, blob or memory](#ldc-load-code-from-an-external-contract-blob-or-memory) - [`LOG`: Log event](#log-log-event) - [`LOGD`: Log data event](#logd-log-data-event) - [`MINT`: Mint new coins](#mint-mint-new-coins) @@ -1796,33 +1796,35 @@ Panic if: - `$rB + 32` overflows or `> VM_MAX_RAM` - Contract with ID `MEM[$rB, 32]` is not in `tx.inputs` -### `LDC`: Load code from an external contract or blob +### `LDC`: Load code from an external contract, blob or memory | | | |-------------|---------------------------------------------------------------------------------------------------------------------------------------------------| -| Description | Copy `$rC` bytes of code at offset `$rB` from object with 32 byte id starting at `$rA` into memory starting at `$ssp`. Object type is in `imm`. | -| Operation | `id = mem[$rA,32]; code = match imm { 0 => contract_code($id), 1 => blob_payload($id) }; MEM[$ssp, $rC] = code[$rB, $rC];` | +| Description | Copy `$rC` bytes of code at offset `$rB` from object identified with `$rA` into memory starting at `$ssp`. Object type is in `imm`. | +| Operation | `code = match imm { 0 => contract_code(mem[$rA,32]), 1 => blob_payload(mem[$rA,32]), 2 => mem[$ra, ..] }; MEM[$ssp, $rC] = code[$rB, $rC];` | | Syntax | `ldc $rA, $rB, $rC, imm` | | Encoding | `0x00 rA rB rC imm` | | Notes | If `$rC` is greater than the code size, zero bytes are filled in. | -Object type from `imm` determined the source for loading as follows: +Object type from `imm` determines the source for loading as follows: | `imm` | Object type | |-------|---------------| | `0` | Contract code | | `1` | Blob payload | +| `2` | VM memory | | other | _reserved_ | Panic if: - `$ssp + $rC` overflows or `> VM_MAX_RAM` -- `$rA + 32` overflows or `> VM_MAX_RAM` +- `imm <= 1` and `$rA + 32` overflows or `> VM_MAX_RAM` - `$ssp + $rC >= $hp` - `imm == 0` and `$rC > CONTRACT_MAX_SIZE` - `imm == 0` and contract with ID `MEM[$rA, 32]` is not in `tx.inputs` -- `imm == 1` and contract with ID `MEM[$rA, 32]` is not found in the chain state -- `imm >= 2` (reserved value) +- `imm == 1` and blob with ID `MEM[$rA, 32]` is not found in the chain state +- `imm == 2` and `$rA + $rB + $rC` overflows or `> VM_MAX_RAM` +- `imm >= 3` (reserved value) Increment `$fp->codesize`, `$ssp` by `$rC` padded to word alignment. Then set `$sp` to `$ssp`. From 9c122c84b070d17c6eb99cf458da38180a79cec8 Mon Sep 17 00:00:00 2001 From: Hannes Karppila Date: Sun, 6 Oct 2024 11:01:18 +0300 Subject: [PATCH 2/9] Fix link to LDC --- src/fuel-vm/instruction-set.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fuel-vm/instruction-set.md b/src/fuel-vm/instruction-set.md index 53d73163..480fd40b 100644 --- a/src/fuel-vm/instruction-set.md +++ b/src/fuel-vm/instruction-set.md @@ -1752,7 +1752,7 @@ Panic if: | Notes | If `$rD` is greater than the code size, zero bytes are filled in. | This is used only for reading and inspecting code of other contracts. -Use [`LDC`](#ldc-load-code-from-an-external-contract-or-blob) to load code for executing. +Use [`LDC`](#ldc-load-code-from-an-external-contract-blob-or-memory) to load code for executing. Panic if: From fd5de5e76c7b9377fa2b6f7693d5a4bf1f07e903 Mon Sep 17 00:00:00 2001 From: Hannes Karppila Date: Sun, 6 Oct 2024 11:56:47 +0300 Subject: [PATCH 3/9] Rewrite predicate section --- src/fuel-vm/index.md | 47 ++++++++++++++++---------------------------- 1 file changed, 17 insertions(+), 30 deletions(-) diff --git a/src/fuel-vm/index.md b/src/fuel-vm/index.md index fb443a20..5b74d52a 100644 --- a/src/fuel-vm/index.md +++ b/src/fuel-vm/index.md @@ -95,54 +95,37 @@ Then the following registers are initialized (without explicit initialization, a ## Contexts -There are 4 _contexts_ in the FuelVM: [predicate estimation](#predicate-estimation), [predicate verification](#predicate-verification), [scripts](#script-execution), and [calls](./instruction-set.md#call-call-contract). A context is an isolated execution environment with defined [memory ownership](#ownership) and can be _external_ or _internal_: +There are 4 _contexts_ in the FuelVM: [predicate estimation and verification](#predicate-estimation-and-verification), [scripts](#script-execution), and [calls](#call). A context is an isolated execution environment with defined [memory ownership](#ownership) and can be _external_ or _internal_: - External: predicate and script. `$fp` will be zero. - Internal: call. `$fp` will be non-zero. [Returning](./instruction-set.md#return-return-from-call) from a context behaves differently depending on whether the context is external or internal. -## Predicate Estimation +### Predicate Estimation and Verification -For any input of type [`InputType.Coin`](../tx-format/index.md) or [`InputType.Message`](../tx-format/index.md), a non-zero `predicateLength` field means the UTXO being spent is a [`P2SH`](https://en.bitcoin.it/wiki/P2SH) rather than a [`P2PKH`](https://en.bitcoin.it/P2PKH) output. - -For each such input in the transaction, the VM is [initialized](#vm-initialization), then: - -1. `$pc` and `$is` are set to the start of the input's `predicate` field. -1. `$ggas` and `$cgas` are set to `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`: - -1. Any [contract instruction](./instruction-set.md#contract-instructions). - -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 `MAX_GAS_PER_PREDICATE - $ggas`. - -## Predicate Verification +There are two ways to run predicates on the VM: +1. Estimation: runs the predicate and updates the amount of gas used +1. Verification: runs the predicate and verifies the amount of gas used matches the input For any input of type [`InputType.Coin`](../tx-format/input.md#inputcoin) or [`InputType.Message`](../tx-format/input.md#inputmessage), a non-zero `predicateLength` field means the UTXO being spent is a [`P2SH`](https://en.bitcoin.it/P2SH) rather than a [`P2PKH`](https://en.bitcoin.it/P2PKH) output. For each such input in the transaction, the VM is [initialized](#vm-initialization), then: 1. `$pc` and `$is` are set to the start of the input's `predicate` field. -1. `$ggas` and `$cgas` are set to `predicateGasUsed`. +1. `$ggas` and `$cgas` are set to `MAX_GAS_PER_PREDICATE` for estimation, and `predicateGasUsed` for verification. -Predicate verification will fail if gas is exhausted during execution. +Predicate execution will fail if gas is exhausted during execution. -During predicate mode, hitting any [contract instruction](./instruction-set.md#contract-instructions) causes predicate verification to halt, returning Boolean `false`. +During predicate mode, hitting any [contract instruction](./instruction-set.md#contract-instructions) (except `ldc` with non-contract target) 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` would not pass verification, making the entire transaction invalid. Note that predicate return value is monotonic with respect to time (i.e. if a predicate evaluates to `true` then it will always evaluate to `true` in the future). -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 successful execution, the run mode is determines the final step: +1. Estimation: `predicateGasUsed` is set to `MAX_GAS_PER_PREDICATE - $ggas`. +1. Verification: if `$ggas` is non-zero, predicate verification fails. -After execution, if `$ggas` is non-zero, predicate verification fails. - -## Script Execution +### Script Execution If script bytecode is present, transaction validation requires execution. @@ -157,6 +140,10 @@ For each instruction, its gas cost `gc` is first computed. If `gc > $cgas`, dedu After the script has been executed, `tx.receiptsRoot` is updated to contain the Merkle root of the receipts, [as described in the `TransactionScript` spec](../tx-format/transaction.md#`TransactionScript`). +### Call + +Call context is entered via [`CALL` instruction](./instruction-set.md#call-call-contract). It's also called *internal context*, or *contract context*. Call context is used to access state of a contract. + ## Call Frames 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: From d8d39105885660e24c9a9907fe3970be324a8df7 Mon Sep 17 00:00:00 2001 From: Hannes Karppila Date: Sun, 6 Oct 2024 12:57:10 +0300 Subject: [PATCH 4/9] Update headers --- src/fuel-vm/index.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/fuel-vm/index.md b/src/fuel-vm/index.md index 5b74d52a..3f2ea2d7 100644 --- a/src/fuel-vm/index.md +++ b/src/fuel-vm/index.md @@ -7,9 +7,9 @@ - [Instruction Set](#instruction-set) - [VM Initialization](#vm-initialization) - [Contexts](#contexts) -- [Predicate Estimation](#predicate-estimation) -- [Predicate Verification](#predicate-verification) -- [Script Execution](#script-execution) + - [Predicate Estimation and Verification](#predicate-estimation-and-verification) + - [Script Execution](#script-execution) + - [Call](#call) - [Call Frames](#call-frames) - [Ownership](#ownership) From 9270a2ee2efbd0575c87a7f80ec8f87041e40ac2 Mon Sep 17 00:00:00 2001 From: Hannes Karppila Date: Sun, 6 Oct 2024 13:23:04 +0300 Subject: [PATCH 5/9] Fix formatting --- src/fuel-vm/index.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/fuel-vm/index.md b/src/fuel-vm/index.md index 3f2ea2d7..051f1f3f 100644 --- a/src/fuel-vm/index.md +++ b/src/fuel-vm/index.md @@ -82,7 +82,7 @@ To initialize the VM, the following is pushed on the stack sequentially: 1. Transaction hash (`byte[32]`, word-aligned), computed as defined [here](../identifiers/transaction-id.md). 1. Base asset ID (`byte[32]`, word-aligned) 1. [`MAX_INPUTS`](../tx-format/consensus_parameters.md) pairs of `(asset_id: byte[32], balance: uint64)`, of: - 1. For [predicate estimation](#predicate-estimation) and [predicate verification](#predicate-verification), zeroes. + 1. For [predicate estimation and verification](#predicate-estimation-and-verification), zeroes. 1. For [script execution](#script-execution), the free balance for each asset ID seen in the transaction's inputs, ordered in ascending order. If there are fewer than `MAX_INPUTS` asset IDs, the pair has a value of zero. 1. Transaction length, in bytes (`uint64`, word-aligned). 1. The [transaction, serialized](../tx-format/index.md). @@ -105,6 +105,7 @@ There are 4 _contexts_ in the FuelVM: [predicate estimation and verification](#p ### Predicate Estimation and Verification There are two ways to run predicates on the VM: + 1. Estimation: runs the predicate and updates the amount of gas used 1. Verification: runs the predicate and verifies the amount of gas used matches the input @@ -142,7 +143,7 @@ After the script has been executed, `tx.receiptsRoot` is updated to contain the ### Call -Call context is entered via [`CALL` instruction](./instruction-set.md#call-call-contract). It's also called *internal context*, or *contract context*. Call context is used to access state of a contract. +Call context is entered via [`CALL` instruction](./instruction-set.md#call-call-contract). It's also called _internal context_, or _contract context_. Call context is used to access state of a contract. ## Call Frames From 5ddaee5bf298d2ded9d6dec5d8a523c7f8dfd05f Mon Sep 17 00:00:00 2001 From: Hannes Karppila Date: Sun, 6 Oct 2024 13:29:19 +0300 Subject: [PATCH 6/9] fmt --- src/fuel-vm/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/fuel-vm/index.md b/src/fuel-vm/index.md index 051f1f3f..0f34b3f7 100644 --- a/src/fuel-vm/index.md +++ b/src/fuel-vm/index.md @@ -123,6 +123,7 @@ During predicate mode, hitting any [contract instruction](./instruction-set.md#c A predicate that halts without returning Boolean `true` would not pass verification, making the entire transaction invalid. Note that predicate return value 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, the run mode is determines the final step: + 1. Estimation: `predicateGasUsed` is set to `MAX_GAS_PER_PREDICATE - $ggas`. 1. Verification: if `$ggas` is non-zero, predicate verification fails. From 6aa02acc3d7ed24c4036e5623f64deda900623a9 Mon Sep 17 00:00:00 2001 From: Hannes Karppila Date: Mon, 7 Oct 2024 20:49:26 +0300 Subject: [PATCH 7/9] Clarify padding behavior --- src/fuel-vm/instruction-set.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fuel-vm/instruction-set.md b/src/fuel-vm/instruction-set.md index 480fd40b..a0120951 100644 --- a/src/fuel-vm/instruction-set.md +++ b/src/fuel-vm/instruction-set.md @@ -1804,7 +1804,7 @@ Panic if: | Operation | `code = match imm { 0 => contract_code(mem[$rA,32]), 1 => blob_payload(mem[$rA,32]), 2 => mem[$ra, ..] }; MEM[$ssp, $rC] = code[$rB, $rC];` | | Syntax | `ldc $rA, $rB, $rC, imm` | | Encoding | `0x00 rA rB rC imm` | -| Notes | If `$rC` is greater than the code size, zero bytes are filled in. | +| Notes | If `$rC` is greater than the code size, zero bytes are filled in. Length final is always padded to word boundary. | Object type from `imm` determines the source for loading as follows: From 697d0eb07d42763e3e80bf65d6c0c280f13d32f5 Mon Sep 17 00:00:00 2001 From: Hannes Karppila <2204863+Dentosal@users.noreply.github.com> Date: Tue, 8 Oct 2024 13:46:53 +0300 Subject: [PATCH 8/9] Update src/fuel-vm/instruction-set.md Co-authored-by: Brandon Kite --- src/fuel-vm/instruction-set.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fuel-vm/instruction-set.md b/src/fuel-vm/instruction-set.md index a0120951..bddf7520 100644 --- a/src/fuel-vm/instruction-set.md +++ b/src/fuel-vm/instruction-set.md @@ -1804,7 +1804,7 @@ Panic if: | Operation | `code = match imm { 0 => contract_code(mem[$rA,32]), 1 => blob_payload(mem[$rA,32]), 2 => mem[$ra, ..] }; MEM[$ssp, $rC] = code[$rB, $rC];` | | Syntax | `ldc $rA, $rB, $rC, imm` | | Encoding | `0x00 rA rB rC imm` | -| Notes | If `$rC` is greater than the code size, zero bytes are filled in. Length final is always padded to word boundary. | +| Notes | If `$rC` is greater than the code size, zero bytes are filled in. Final length is always padded to word boundary. | Object type from `imm` determines the source for loading as follows: From e4ee95b01c286d58230c0c89e621ffe685dc1165 Mon Sep 17 00:00:00 2001 From: Hannes Karppila <2204863+Dentosal@users.noreply.github.com> Date: Tue, 8 Oct 2024 13:47:05 +0300 Subject: [PATCH 9/9] Update src/fuel-vm/instruction-set.md --- src/fuel-vm/instruction-set.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/fuel-vm/instruction-set.md b/src/fuel-vm/instruction-set.md index bddf7520..47bb3d8c 100644 --- a/src/fuel-vm/instruction-set.md +++ b/src/fuel-vm/instruction-set.md @@ -1822,6 +1822,7 @@ Panic if: - `$ssp + $rC >= $hp` - `imm == 0` and `$rC > CONTRACT_MAX_SIZE` - `imm == 0` and contract with ID `MEM[$rA, 32]` is not in `tx.inputs` +- `imm == 0` and context is a predicate - `imm == 1` and blob with ID `MEM[$rA, 32]` is not found in the chain state - `imm == 2` and `$rA + $rB + $rC` overflows or `> VM_MAX_RAM` - `imm >= 3` (reserved value)