Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LDC support in predicates #612

Merged
merged 11 commits into from
Oct 23, 2024
53 changes: 21 additions & 32 deletions src/fuel-vm/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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).
Expand All @@ -95,54 +95,39 @@ 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.
There are two ways to run predicates on the VM:

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
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`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, it looks like before we should allow usage of blob instructions in the VM as well(because they are not contract instruction)=D So now it works as expected


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:

After execution, if `$ggas` is non-zero, predicate verification fails.
1. Estimation: `predicateGasUsed` is set to `MAX_GAS_PER_PREDICATE - $ggas`.
1. Verification: if `$ggas` is non-zero, predicate verification fails.

## Script Execution
### Script Execution

If script bytecode is present, transaction validation requires execution.

Expand All @@ -157,6 +142,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
Comment on lines +145 to 149
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it is better to do like this?

Suggested change
### 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
## 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

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. Call is a subheader of Contexts.


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:
Expand Down
23 changes: 13 additions & 10 deletions src/fuel-vm/instruction-set.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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:

Expand Down Expand Up @@ -1796,33 +1796,36 @@ 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. |
| 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` 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 == 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)

Increment `$fp->codesize`, `$ssp` by `$rC` padded to word alignment. Then set `$sp` to `$ssp`.

Expand Down
Loading