From 05680227c7ac3fb4591e947e53718434febfad95 Mon Sep 17 00:00:00 2001 From: Brandon Kite Date: Thu, 24 Aug 2023 15:17:11 -0700 Subject: [PATCH 01/40] add dynamic policy configuration transactions --- src/tx-format/policy.md | 52 ++++++++++++++++++++++++++ src/tx-format/transaction.md | 71 +++++++++++++++++------------------- 2 files changed, 86 insertions(+), 37 deletions(-) create mode 100644 src/tx-format/policy.md diff --git a/src/tx-format/policy.md b/src/tx-format/policy.md new file mode 100644 index 00000000..25bd3708 --- /dev/null +++ b/src/tx-format/policy.md @@ -0,0 +1,52 @@ +# Policy + +```c++ +enum PolicyType : uint8 { + GasLimit = 0, + WitnessLimit = 1, + Maturity = 2, +} +``` + +| name | type | description | +|--------|---------------------------------------------------------------------------------------|----------------| +| `type` | [PolicyType](#policy) | Type of input. | +| `data` | One of [GasLimit](#gaslimit), [WitnessLimit](#witnesslimit), or [Maturity](#maturity) | Input data. | + + +Transaction is invalid if: + +- `type > PolicyType.Maturity` + +## GasLimit + +| name | type | description | +|------------|----------|-----------------------------------------------------| +| `gasLimit` | `uint64` | Gas limit for transaction (including predicate gas) | + +Transaction is invalid if: + +- `gasLimit > MAX_GAS_PER_TX` +- `gasLimit` is less than the sum of all `predicateGasUsed` for `InputType.Coin` or `InputType.Message` where predicate length is greater than zero. + +## WitnessLimit + +| name | type | description | +|----------------|----------|----------------------------------------------------------------| +| `witnessLimit` | `uint64` | The maximum amount of witness data allowed for the transaction | + +Given helper `len()` that returns the number of bytes of a field. + +Transaction is invalid if: + +- `witnessLimit > len(tx.witnesses)` + +## Maturity + +| name | type | description | +|------------|----------|------------------------------------------| +| `maturity` | `uint32` | Block until which tx cannot be included. | + +Transaction is invalid if: + +- `blockheight() < maturity` diff --git a/src/tx-format/transaction.md b/src/tx-format/transaction.md index 3ece7d46..04ef02c8 100644 --- a/src/tx-format/transaction.md +++ b/src/tx-format/transaction.md @@ -16,8 +16,6 @@ enum TransactionType : uint8 { Transaction is invalid if: - `type > TransactionType.Create` -- `gasLimit > MAX_GAS_PER_TX` -- `blockheight() < maturity` - `inputsCount > MAX_INPUTS` - `outputsCount > MAX_OUTPUTS` - `witnessesCount > MAX_WITNESSES` @@ -51,27 +49,27 @@ enum ReceiptType : uint8 { TransferOut = 8, ScriptResult = 9, MessageOut = 10, - Mint = 11 - Burn = 12 + Mint = 11, + Burn = 12, } ``` -| name | type | description | -|--------------------|-----------------------------|------------------------------------------------------| -| `gasPrice` | `uint64` | Gas price for transaction. | -| `gasLimit` | `uint64` | Gas limit for transaction (including predicate gas). | -| `maturity` | `uint32` | Block until which tx cannot be included. | -| `scriptLength` | `uint16` | Script length, in instructions. | -| `scriptDataLength` | `uint16` | Length of script input data, in bytes. | -| `inputsCount` | `uint8` | Number of inputs. | -| `outputsCount` | `uint8` | Number of outputs. | -| `witnessesCount` | `uint8` | Number of witnesses. | -| `receiptsRoot` | `byte[32]` | Merkle root of receipts. | -| `script` | `byte[]` | Script to execute. | -| `scriptData` | `byte[]` | Script input data (parameters). | -| `inputs` | [Input](./input.md)`[]` | List of inputs. | -| `outputs` | [Output](./output.md)`[]` | List of outputs. | -| `witnesses` | [Witness](./witness.md)`[]` | List of witnesses. | +| name | type | description | +|--------------------|-----------------------------|----------------------------------------| +| `gasPrice` | `uint64` | Gas price for transaction. | +| `scriptLength` | `uint16` | Script length, in instructions. | +| `scriptDataLength` | `uint16` | Length of script input data, in bytes. | +| `policyCount` | `uint8` | Number of policies. | +| `inputsCount` | `uint8` | Number of inputs. | +| `outputsCount` | `uint8` | Number of outputs. | +| `witnessesCount` | `uint8` | Number of witnesses. | +| `receiptsRoot` | `byte[32]` | Merkle root of receipts. | +| `policies` | [Policy](./policy.md)`[]` | List of policies. | +| `script` | `byte[]` | Script to execute. | +| `scriptData` | `byte[]` | Script input data (parameters). | +| `inputs` | [Input](./input.md)`[]` | List of inputs. | +| `outputs` | [Output](./output.md)`[]` | List of outputs. | +| `witnesses` | [Witness](./witness.md)`[]` | List of witnesses. | Given helper `len()` that returns the number of bytes of a field. @@ -82,7 +80,6 @@ Transaction is invalid if: - `scriptDataLength > MAX_SCRIPT_DATA_LENGTH` - `scriptLength * 4 != len(script)` - `scriptDataLength != len(scriptData)` -- `gasLimit` is less than the sum of all `predicateGasUsed` for `InputType.Coin` or `InputType.Message` where predicate length is greater than zero. > **Note:** when signing a transaction, `receiptsRoot` is set to zero. > @@ -94,22 +91,22 @@ The receipts root `receiptsRoot` is the root of the [binary Merkle tree](../prot ## TransactionCreate -| name | type | description | -|------------------------|-----------------------------|------------------------------------------------------| -| `gasPrice` | `uint64` | Gas price for transaction. | -| `gasLimit` | `uint64` | Gas limit for transaction (including predicate gas). | -| `maturity` | `uint32` | Block until which tx cannot be included. | -| `bytecodeLength` | `uint16` | Contract bytecode length, in instructions. | -| `bytecodeWitnessIndex` | `uint8` | Witness index of contract bytecode to create. | -| `storageSlotsCount` | `uint16` | Number of storage slots to initialize. | -| `inputsCount` | `uint8` | Number of inputs. | -| `outputsCount` | `uint8` | Number of outputs. | -| `witnessesCount` | `uint8` | Number of witnesses. | -| `salt` | `byte[32]` | Salt. | -| `storageSlots` | `(byte[32], byte[32]])[]` | List of storage slots to initialize (key, value). | -| `inputs` | [Input](./input.md)`[]` | List of inputs. | -| `outputs` | [Output](./output.md)`[]` | List of outputs. | -| `witnesses` | [Witness](./witness.md)`[]` | List of witnesses. | +| name | type | description | +|------------------------|-----------------------------|---------------------------------------------------| +| `gasPrice` | `uint64` | Gas price for transaction. | +| `bytecodeLength` | `uint16` | Contract bytecode length, in instructions. | +| `bytecodeWitnessIndex` | `uint8` | Witness index of contract bytecode to create. | +| `policyCount` | `uint8` | Number of policies. | +| `storageSlotsCount` | `uint16` | Number of storage slots to initialize. | +| `inputsCount` | `uint8` | Number of inputs. | +| `outputsCount` | `uint8` | Number of outputs. | +| `witnessesCount` | `uint8` | Number of witnesses. | +| `salt` | `byte[32]` | Salt. | +| `policies` | [Policy](./policy.md)`[]` | List of policies. | +| `storageSlots` | `(byte[32], byte[32]])[]` | List of storage slots to initialize (key, value). | +| `inputs` | [Input](./input.md)`[]` | List of inputs. | +| `outputs` | [Output](./output.md)`[]` | List of outputs. | +| `witnesses` | [Witness](./witness.md)`[]` | List of witnesses. | Transaction is invalid if: From 769cd39dba4d589c4e64cdc39f39f58401afabc8 Mon Sep 17 00:00:00 2001 From: Brandon Kite Date: Thu, 24 Aug 2023 15:21:03 -0700 Subject: [PATCH 02/40] remove extra newline --- src/tx-format/policy.md | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tx-format/policy.md b/src/tx-format/policy.md index 25bd3708..3bcada43 100644 --- a/src/tx-format/policy.md +++ b/src/tx-format/policy.md @@ -13,7 +13,6 @@ enum PolicyType : uint8 { | `type` | [PolicyType](#policy) | Type of input. | | `data` | One of [GasLimit](#gaslimit), [WitnessLimit](#witnesslimit), or [Maturity](#maturity) | Input data. | - Transaction is invalid if: - `type > PolicyType.Maturity` From 5cd6f11d146d2ac0c8fbe3414e10a031032322f0 Mon Sep 17 00:00:00 2001 From: Brandon Kite Date: Thu, 24 Aug 2023 15:24:07 -0700 Subject: [PATCH 03/40] add to summary --- src/SUMMARY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 55f8bcf0..a9a38e5c 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -5,6 +5,7 @@ - [Transaction Format](./tx-format/index.md) - [Constants](./tx-format/constants.md) - [Transaction](./tx-format/transaction.md) + - [Policy](./tx-format/policy.md) - [Input](./tx-format/input.md) - [Output](./tx-format/output.md) - [Witness](./tx-format/witness.md) From 0d89c7e6686329c81fd8fbb685529f51715d9131 Mon Sep 17 00:00:00 2001 From: Voxelot Date: Mon, 25 Sep 2023 18:30:00 -0700 Subject: [PATCH 04/40] PR feedback --- src/protocol/tx-validity.md | 6 ++++-- src/tx-format/policy.md | 7 +++---- src/tx-format/transaction.md | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/protocol/tx-validity.md b/src/protocol/tx-validity.md index 0ae8cf77..e3b33bce 100644 --- a/src/protocol/tx-validity.md +++ b/src/protocol/tx-validity.md @@ -120,7 +120,6 @@ def available_balance(tx, asset_id) -> int: def unavailable_balance(tx, asset_id) -> int: sentBalance = sum_outputs(tx, asset_id) gasBalance = gasPrice * gasLimit / GAS_PRICE_FACTOR - # Size excludes witness data as it is malleable (even by third parties!) bytesBalance = size(tx) * GAS_PER_BYTE * gasPrice / GAS_PRICE_FACTOR # Total fee balance feeBalance = ceiling(gasBalance + bytesBalance) @@ -179,7 +178,10 @@ Once the free balances are computed, the [script is executed](../fuel-vm/index.m 1. The unspent free balance `unspentBalance` for each asset ID. 1. The unspent gas `unspentGas` from the `$ggas` register. -The fees incurred for a transaction are `ceiling(((size(tx) * GAS_PER_BYTE) + (tx.gasLimit - unspentGas)) * tx.gasPrice / GAS_PRICE_FACTOR)`. +The fees incurred for a transaction are `ceiling(((size(tx) * GAS_PER_BYTE) + (tx.gasLimit - unspentGas) + sum(tx.inputs[i].predicateGasUsed)) * tx.gasPrice / GAS_PRICE_FACTOR)`. + +`size(tx)` includes the entire transaction serialized according to the transaction format, including witness data. +This ensures every byte of block space either on Fuel or corresponding DA layer can be accounted for. If the transaction as included in a block does not match this final transaction, the block is invalid. diff --git a/src/tx-format/policy.md b/src/tx-format/policy.md index 3bcada43..7205c9fa 100644 --- a/src/tx-format/policy.md +++ b/src/tx-format/policy.md @@ -19,14 +19,13 @@ Transaction is invalid if: ## GasLimit -| name | type | description | -|------------|----------|-----------------------------------------------------| -| `gasLimit` | `uint64` | Gas limit for transaction (including predicate gas) | +| name | type | description | +|------------|----------|------------------------------------------------------------| +| `gasLimit` | `uint64` | Gas limit for transaction (applicable to script execution) | Transaction is invalid if: - `gasLimit > MAX_GAS_PER_TX` -- `gasLimit` is less than the sum of all `predicateGasUsed` for `InputType.Coin` or `InputType.Message` where predicate length is greater than zero. ## WitnessLimit diff --git a/src/tx-format/transaction.md b/src/tx-format/transaction.md index 04ef02c8..5c708ff7 100644 --- a/src/tx-format/transaction.md +++ b/src/tx-format/transaction.md @@ -64,9 +64,9 @@ enum ReceiptType : uint8 { | `outputsCount` | `uint8` | Number of outputs. | | `witnessesCount` | `uint8` | Number of witnesses. | | `receiptsRoot` | `byte[32]` | Merkle root of receipts. | -| `policies` | [Policy](./policy.md)`[]` | List of policies. | | `script` | `byte[]` | Script to execute. | | `scriptData` | `byte[]` | Script input data (parameters). | +| `policies` | [Policy](./policy.md)`[]` | List of policies. | | `inputs` | [Input](./input.md)`[]` | List of inputs. | | `outputs` | [Output](./output.md)`[]` | List of outputs. | | `witnesses` | [Witness](./witness.md)`[]` | List of witnesses. | From 137e3a6cb3e16a25fcd263bf47ab155f20823db5 Mon Sep 17 00:00:00 2001 From: Voxelot Date: Wed, 11 Oct 2023 14:17:15 -0700 Subject: [PATCH 05/40] move gas price to policies add gtf args for policies pad gtf args to avoid conflicts --- src/fuel-vm/instruction-set.md | 142 ++++++++++++++++----------------- src/tx-format/policy.md | 14 +++- src/tx-format/transaction.md | 3 + 3 files changed, 85 insertions(+), 74 deletions(-) diff --git a/src/fuel-vm/instruction-set.md b/src/fuel-vm/instruction-set.md index 28822cd0..7898aa87 100644 --- a/src/fuel-vm/instruction-set.md +++ b/src/fuel-vm/instruction-set.md @@ -2425,77 +2425,77 @@ Get [fields from the transaction](../tx-format/transaction.md). | name | `imm` | set `$rA` to | |-------------------------------------------|---------|--------------------------------------------------| | `GTF_TYPE` | `0x001` | `tx.type` | -| `GTF_SCRIPT_GAS_PRICE` | `0x002` | `tx.gasPrice` | -| `GTF_SCRIPT_GAS_LIMIT` | `0x003` | `tx.gasLimit` | -| `GTF_SCRIPT_MATURITY` | `0x004` | `tx.maturity` | -| `GTF_SCRIPT_SCRIPT_LENGTH` | `0x005` | `tx.scriptLength` | -| `GTF_SCRIPT_SCRIPT_DATA_LENGTH` | `0x006` | `tx.scriptDataLength` | -| `GTF_SCRIPT_INPUTS_COUNT` | `0x007` | `tx.inputsCount` | -| `GTF_SCRIPT_OUTPUTS_COUNT` | `0x008` | `tx.outputsCount` | -| `GTF_SCRIPT_WITNESSES_COUNT` | `0x009` | `tx.witnessesCount` | -| `GTF_SCRIPT_RECEIPTS_ROOT` | `0x00A` | Memory address of `tx.receiptsRoot` | -| `GTF_SCRIPT_SCRIPT` | `0x00B` | Memory address of `tx.script` | -| `GTF_SCRIPT_SCRIPT_DATA` | `0x00C` | Memory address of `tx.scriptData` | -| `GTF_SCRIPT_INPUT_AT_INDEX` | `0x00D` | Memory address of `tx.inputs[$rB]` | -| `GTF_SCRIPT_OUTPUT_AT_INDEX` | `0x00E` | Memory address of `t.outputs[$rB]` | -| `GTF_SCRIPT_WITNESS_AT_INDEX` | `0x00F` | Memory address of `tx.witnesses[$rB]` | -| `GTF_CREATE_GAS_PRICE` | `0x010` | `tx.gasPrice` | -| `GTF_CREATE_GAS_LIMIT` | `0x011` | `tx.gasLimit` | -| `GTF_CREATE_MATURITY` | `0x012` | `tx.maturity` | -| `GTF_CREATE_BYTECODE_LENGTH` | `0x013` | `tx.bytecodeLength` | -| `GTF_CREATE_BYTECODE_WITNESS_INDEX` | `0x014` | `tx.bytecodeWitnessIndex` | -| `GTF_CREATE_STORAGE_SLOTS_COUNT` | `0x015` | `tx.storageSlotsCount` | -| `GTF_CREATE_INPUTS_COUNT` | `0x016` | `tx.inputsCount` | -| `GTF_CREATE_OUTPUTS_COUNT` | `0x017` | `tx.outputsCount` | -| `GTF_CREATE_WITNESSES_COUNT` | `0x018` | `tx.witnessesCount` | -| `GTF_CREATE_SALT` | `0x019` | Memory address of `tx.salt` | -| `GTF_CREATE_STORAGE_SLOT_AT_INDEX` | `0x01A` | Memory address of `tx.storageSlots[$rB]` | -| `GTF_CREATE_INPUT_AT_INDEX` | `0x01B` | Memory address of `tx.inputs[$rB]` | -| `GTF_CREATE_OUTPUT_AT_INDEX` | `0x01C` | Memory address of `t.outputs[$rB]` | -| `GTF_CREATE_WITNESS_AT_INDEX` | `0x01D` | Memory address of `tx.witnesses[$rB]` | -| `GTF_INPUT_TYPE` | `0x101` | `tx.inputs[$rB].type` | -| `GTF_INPUT_COIN_TX_ID` | `0x102` | Memory address of `tx.inputs[$rB].txID` | -| `GTF_INPUT_COIN_OUTPUT_INDEX` | `0x103` | `tx.inputs[$rB].outputIndex` | -| `GTF_INPUT_COIN_OWNER` | `0x104` | Memory address of `tx.inputs[$rB].owner` | -| `GTF_INPUT_COIN_AMOUNT` | `0x105` | `tx.inputs[$rB].amount` | -| `GTF_INPUT_COIN_ASSET_ID` | `0x106` | Memory address of `tx.inputs[$rB].asset_id` | -| `GTF_INPUT_COIN_TX_POINTER` | `0x107` | Memory address of `tx.inputs[$rB].txPointer` | -| `GTF_INPUT_COIN_WITNESS_INDEX` | `0x108` | `tx.inputs[$rB].witnessIndex` | -| `GTF_INPUT_COIN_MATURITY` | `0x109` | `tx.inputs[$rB].maturity` | -| `GTF_INPUT_COIN_PREDICATE_LENGTH` | `0x10A` | `tx.inputs[$rB].predicateLength` | -| `GTF_INPUT_COIN_PREDICATE_DATA_LENGTH` | `0x10B` | `tx.inputs[$rB].predicateDataLength` | -| `GTF_INPUT_COIN_PREDICATE` | `0x10C` | Memory address of `tx.inputs[$rB].predicate` | -| `GTF_INPUT_COIN_PREDICATE_DATA` | `0x10D` | Memory address of `tx.inputs[$rB].predicateData` | -| `GTF_INPUT_COIN_PREDICATE_GAS_USED` | `0x10E` | `tx.inputs[$rB].predicateGasUsed` | -| `GTF_INPUT_CONTRACT_TX_ID` | `0x10F` | Memory address of `tx.inputs[$rB].txID` | -| `GTF_INPUT_CONTRACT_OUTPUT_INDEX` | `0x110` | `tx.inputs[$rB].outputIndex` | -| `GTF_INPUT_CONTRACT_BALANCE_ROOT` | `0x111` | Memory address of `tx.inputs[$rB].balanceRoot` | -| `GTF_INPUT_CONTRACT_STATE_ROOT` | `0x112` | Memory address of `tx.inputs[$rB].stateRoot` | -| `GTF_INPUT_CONTRACT_TX_POINTER` | `0x113` | Memory address of `tx.inputs[$rB].txPointer` | -| `GTF_INPUT_CONTRACT_CONTRACT_ID` | `0x114` | Memory address of `tx.inputs[$rB].contractID` | -| `GTF_INPUT_MESSAGE_SENDER` | `0x115` | Memory address of `tx.inputs[$rB].sender` | -| `GTF_INPUT_MESSAGE_RECIPIENT` | `0x116` | Memory address of `tx.inputs[$rB].recipient` | -| `GTF_INPUT_MESSAGE_AMOUNT` | `0x117` | `tx.inputs[$rB].amount` | -| `GTF_INPUT_MESSAGE_NONCE` | `0x118` | Memory address of `tx.inputs[$rB].nonce` | -| `GTF_INPUT_MESSAGE_WITNESS_INDEX` | `0x119` | `tx.inputs[$rB].witnessIndex` | -| `GTF_INPUT_MESSAGE_DATA_LENGTH` | `0x11A` | `tx.inputs[$rB].dataLength` | -| `GTF_INPUT_MESSAGE_PREDICATE_LENGTH` | `0x11B` | `tx.inputs[$rB].predicateLength` | -| `GTF_INPUT_MESSAGE_PREDICATE_DATA_LENGTH` | `0x11C` | `tx.inputs[$rB].predicateDataLength` | -| `GTF_INPUT_MESSAGE_DATA` | `0x11D` | Memory address of `tx.inputs[$rB].data` | -| `GTF_INPUT_MESSAGE_PREDICATE` | `0x11E` | Memory address of `tx.inputs[$rB].predicate` | -| `GTF_INPUT_MESSAGE_PREDICATE_DATA` | `0x11F` | Memory address of `tx.inputs[$rB].predicateData` | -| `GTF_INPUT_MESSAGE_PREDICATE_GAS_USED` | `0x120` | `tx.inputs[$rB].predicateGasUsed` | -| `GTF_OUTPUT_TYPE` | `0x201` | `tx.outputs[$rB].type` | -| `GTF_OUTPUT_COIN_TO` | `0x202` | Memory address of `tx.outputs[$rB].to` | -| `GTF_OUTPUT_COIN_AMOUNT` | `0x203` | `tx.outputs[$rB].amount` | -| `GTF_OUTPUT_COIN_ASSET_ID` | `0x204` | Memory address of `tx.outputs[$rB].asset_id` | -| `GTF_OUTPUT_CONTRACT_INPUT_INDEX` | `0x205` | `tx.outputs[$rB].inputIndex` | -| `GTF_OUTPUT_CONTRACT_BALANCE_ROOT` | `0x206` | Memory address of `tx.outputs[$rB].balanceRoot` | -| `GTF_OUTPUT_CONTRACT_STATE_ROOT` | `0x207` | Memory address of `tx.outputs[$rB].stateRoot` | -| `GTF_OUTPUT_CONTRACT_CREATED_CONTRACT_ID` | `0x208` | Memory address of `tx.outputs[$rB].contractID` | -| `GTF_OUTPUT_CONTRACT_CREATED_STATE_ROOT` | `0x209` | Memory address of `tx.outputs[$rB].stateRoot` | -| `GTF_WITNESS_DATA_LENGTH` | `0x301` | `tx.witnesses[$rB].dataLength` | -| `GTF_WITNESS_DATA` | `0x302` | Memory address of `tx.witnesses[$rB].data` | +| `GTF_SCRIPT_MATURITY` | `0x002` | `tx.maturity` | +| `GTF_SCRIPT_SCRIPT_LENGTH` | `0x003` | `tx.scriptLength` | +| `GTF_SCRIPT_SCRIPT_DATA_LENGTH` | `0x004` | `tx.scriptDataLength` | +| `GTF_SCRIPT_INPUTS_COUNT` | `0x005` | `tx.inputsCount` | +| `GTF_SCRIPT_OUTPUTS_COUNT` | `0x006` | `tx.outputsCount` | +| `GTF_SCRIPT_WITNESSES_COUNT` | `0x007` | `tx.witnessesCount` | +| `GTF_SCRIPT_RECEIPTS_ROOT` | `0x008` | Memory address of `tx.receiptsRoot` | +| `GTF_SCRIPT_SCRIPT` | `0x009` | Memory address of `tx.script` | +| `GTF_SCRIPT_SCRIPT_DATA` | `0x00A` | Memory address of `tx.scriptData` | +| `GTF_SCRIPT_INPUT_AT_INDEX` | `0x00B` | Memory address of `tx.inputs[$rB]` | +| `GTF_SCRIPT_OUTPUT_AT_INDEX` | `0x00C` | Memory address of `t.outputs[$rB]` | +| `GTF_SCRIPT_WITNESS_AT_INDEX` | `0x00D` | Memory address of `tx.witnesses[$rB]` | +| `GTF_CREATE_MATURITY` | `0x100` | `tx.maturity` | +| `GTF_CREATE_BYTECODE_LENGTH` | `0x101` | `tx.bytecodeLength` | +| `GTF_CREATE_BYTECODE_WITNESS_INDEX` | `0x102` | `tx.bytecodeWitnessIndex` | +| `GTF_CREATE_STORAGE_SLOTS_COUNT` | `0x103` | `tx.storageSlotsCount` | +| `GTF_CREATE_INPUTS_COUNT` | `0x104` | `tx.inputsCount` | +| `GTF_CREATE_OUTPUTS_COUNT` | `0x105` | `tx.outputsCount` | +| `GTF_CREATE_WITNESSES_COUNT` | `0x106` | `tx.witnessesCount` | +| `GTF_CREATE_SALT` | `0x107` | Memory address of `tx.salt` | +| `GTF_CREATE_STORAGE_SLOT_AT_INDEX` | `0x108` | Memory address of `tx.storageSlots[$rB]` | +| `GTF_CREATE_INPUT_AT_INDEX` | `0x109` | Memory address of `tx.inputs[$rB]` | +| `GTF_CREATE_OUTPUT_AT_INDEX` | `0x10A` | Memory address of `t.outputs[$rB]` | +| `GTF_CREATE_WITNESS_AT_INDEX` | `0x10D` | Memory address of `tx.witnesses[$rB]` | +| `GTF_INPUT_TYPE` | `0x200` | `tx.inputs[$rB].type` | +| `GTF_INPUT_COIN_TX_ID` | `0x201` | Memory address of `tx.inputs[$rB].txID` | +| `GTF_INPUT_COIN_OUTPUT_INDEX` | `0x202` | `tx.inputs[$rB].outputIndex` | +| `GTF_INPUT_COIN_OWNER` | `0x203` | Memory address of `tx.inputs[$rB].owner` | +| `GTF_INPUT_COIN_AMOUNT` | `0x204` | `tx.inputs[$rB].amount` | +| `GTF_INPUT_COIN_ASSET_ID` | `0x205` | Memory address of `tx.inputs[$rB].asset_id` | +| `GTF_INPUT_COIN_TX_POINTER` | `0x206` | Memory address of `tx.inputs[$rB].txPointer` | +| `GTF_INPUT_COIN_WITNESS_INDEX` | `0x207` | `tx.inputs[$rB].witnessIndex` | +| `GTF_INPUT_COIN_MATURITY` | `0x208` | `tx.inputs[$rB].maturity` | +| `GTF_INPUT_COIN_PREDICATE_LENGTH` | `0x209` | `tx.inputs[$rB].predicateLength` | +| `GTF_INPUT_COIN_PREDICATE_DATA_LENGTH` | `0x20A` | `tx.inputs[$rB].predicateDataLength` | +| `GTF_INPUT_COIN_PREDICATE` | `0x20B` | Memory address of `tx.inputs[$rB].predicate` | +| `GTF_INPUT_COIN_PREDICATE_DATA` | `0x20C` | Memory address of `tx.inputs[$rB].predicateData` | +| `GTF_INPUT_COIN_PREDICATE_GAS_USED` | `0x20D` | `tx.inputs[$rB].predicateGasUsed` | +| `GTF_INPUT_CONTRACT_TX_ID` | `0x220` | Memory address of `tx.inputs[$rB].txID` | +| `GTF_INPUT_CONTRACT_OUTPUT_INDEX` | `0x221` | `tx.inputs[$rB].outputIndex` | +| `GTF_INPUT_CONTRACT_BALANCE_ROOT` | `0x222` | Memory address of `tx.inputs[$rB].balanceRoot` | +| `GTF_INPUT_CONTRACT_STATE_ROOT` | `0x223` | Memory address of `tx.inputs[$rB].stateRoot` | +| `GTF_INPUT_CONTRACT_TX_POINTER` | `0x224` | Memory address of `tx.inputs[$rB].txPointer` | +| `GTF_INPUT_CONTRACT_CONTRACT_ID` | `0x225` | Memory address of `tx.inputs[$rB].contractID` | +| `GTF_INPUT_MESSAGE_SENDER` | `0x240` | Memory address of `tx.inputs[$rB].sender` | +| `GTF_INPUT_MESSAGE_RECIPIENT` | `0x241` | Memory address of `tx.inputs[$rB].recipient` | +| `GTF_INPUT_MESSAGE_AMOUNT` | `0x242` | `tx.inputs[$rB].amount` | +| `GTF_INPUT_MESSAGE_NONCE` | `0x243` | Memory address of `tx.inputs[$rB].nonce` | +| `GTF_INPUT_MESSAGE_WITNESS_INDEX` | `0x244` | `tx.inputs[$rB].witnessIndex` | +| `GTF_INPUT_MESSAGE_DATA_LENGTH` | `0x245` | `tx.inputs[$rB].dataLength` | +| `GTF_INPUT_MESSAGE_PREDICATE_LENGTH` | `0x246` | `tx.inputs[$rB].predicateLength` | +| `GTF_INPUT_MESSAGE_PREDICATE_DATA_LENGTH` | `0x247` | `tx.inputs[$rB].predicateDataLength` | +| `GTF_INPUT_MESSAGE_DATA` | `0x248` | Memory address of `tx.inputs[$rB].data` | +| `GTF_INPUT_MESSAGE_PREDICATE` | `0x249` | Memory address of `tx.inputs[$rB].predicate` | +| `GTF_INPUT_MESSAGE_PREDICATE_DATA` | `0x24A` | Memory address of `tx.inputs[$rB].predicateData` | +| `GTF_INPUT_MESSAGE_PREDICATE_GAS_USED` | `0x24B` | `tx.inputs[$rB].predicateGasUsed` | +| `GTF_OUTPUT_TYPE` | `0x300` | `tx.outputs[$rB].type` | +| `GTF_OUTPUT_COIN_TO` | `0x301` | Memory address of `tx.outputs[$rB].to` | +| `GTF_OUTPUT_COIN_AMOUNT` | `0x302` | `tx.outputs[$rB].amount` | +| `GTF_OUTPUT_COIN_ASSET_ID` | `0x303` | Memory address of `tx.outputs[$rB].asset_id` | +| `GTF_OUTPUT_CONTRACT_INPUT_INDEX` | `0x304` | `tx.outputs[$rB].inputIndex` | +| `GTF_OUTPUT_CONTRACT_BALANCE_ROOT` | `0x305` | Memory address of `tx.outputs[$rB].balanceRoot` | +| `GTF_OUTPUT_CONTRACT_STATE_ROOT` | `0x306` | Memory address of `tx.outputs[$rB].stateRoot` | +| `GTF_OUTPUT_CONTRACT_CREATED_CONTRACT_ID` | `0x307` | Memory address of `tx.outputs[$rB].contractID` | +| `GTF_OUTPUT_CONTRACT_CREATED_STATE_ROOT` | `0x308` | Memory address of `tx.outputs[$rB].stateRoot` | +| `GTF_WITNESS_DATA_LENGTH` | `0x400` | `tx.witnesses[$rB].dataLength` | +| `GTF_WITNESS_DATA` | `0x401` | Memory address of `tx.witnesses[$rB].data` | +| `GTF_POLICY_GAS_PRICE` | `0x500` | `tx.policies[0x00].gasPrice` | +| `GTF_POLICY_GAS_LIMIT` | `0x501` | `tx.policies[0x01].witnessLimit` | +| `GTF_POLICY_WITNESS_LIMIT` | `0x502` | `tx.policies[0x02].witnessLimit` | +| `GTF_POLICY_MATURITY` | `0x503` | `tx.policies[0x03].maturity` | Panic if: diff --git a/src/tx-format/policy.md b/src/tx-format/policy.md index 7205c9fa..d49ebea2 100644 --- a/src/tx-format/policy.md +++ b/src/tx-format/policy.md @@ -2,9 +2,10 @@ ```c++ enum PolicyType : uint8 { - GasLimit = 0, - WitnessLimit = 1, - Maturity = 2, + GasPrice = 0, + GasLimit = 1, + WitnessLimit = 2, + Maturity = 3, } ``` @@ -17,6 +18,13 @@ Transaction is invalid if: - `type > PolicyType.Maturity` + +## GasPrice + +| name | type | description | +|------------|----------|---------------------------| +| `gasPrice` | `uint64` | Gas price for transaction | + ## GasLimit | name | type | description | diff --git a/src/tx-format/transaction.md b/src/tx-format/transaction.md index 5c708ff7..98eed085 100644 --- a/src/tx-format/transaction.md +++ b/src/tx-format/transaction.md @@ -80,6 +80,8 @@ Transaction is invalid if: - `scriptDataLength > MAX_SCRIPT_DATA_LENGTH` - `scriptLength * 4 != len(script)` - `scriptDataLength != len(scriptData)` +- No policy of type `PolicyType.GasPrice` +- No policy of type `PolicyType.GasLimit` > **Note:** when signing a transaction, `receiptsRoot` is set to zero. > @@ -122,6 +124,7 @@ Transaction is invalid if: - The computed contract ID (see below) is not equal to the `contractID` of the one `OutputType.ContractCreated` output - `storageSlotsCount > MAX_STORAGE_SLOTS` - The [Sparse Merkle tree](../protocol/cryptographic-primitives.md#sparse-merkle-tree) root of `storageSlots` is not equal to the `stateRoot` of the one `OutputType.ContractCreated` output +- No policy of type `PolicyType.GasPrice` Creates a contract with contract ID as computed [here](../identifiers/contract-id.md). From ff4fca44e8a86a566f7b2f26454d1a1c0a2858b9 Mon Sep 17 00:00:00 2001 From: Voxelot Date: Wed, 11 Oct 2023 14:21:16 -0700 Subject: [PATCH 06/40] lint --- src/protocol/tx-validity.md | 2 +- src/tx-format/policy.md | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/protocol/tx-validity.md b/src/protocol/tx-validity.md index e3b33bce..48b067db 100644 --- a/src/protocol/tx-validity.md +++ b/src/protocol/tx-validity.md @@ -180,7 +180,7 @@ Once the free balances are computed, the [script is executed](../fuel-vm/index.m The fees incurred for a transaction are `ceiling(((size(tx) * GAS_PER_BYTE) + (tx.gasLimit - unspentGas) + sum(tx.inputs[i].predicateGasUsed)) * tx.gasPrice / GAS_PRICE_FACTOR)`. -`size(tx)` includes the entire transaction serialized according to the transaction format, including witness data. +`size(tx)` includes the entire transaction serialized according to the transaction format, including witness data. This ensures every byte of block space either on Fuel or corresponding DA layer can be accounted for. If the transaction as included in a block does not match this final transaction, the block is invalid. diff --git a/src/tx-format/policy.md b/src/tx-format/policy.md index d49ebea2..71e96cc9 100644 --- a/src/tx-format/policy.md +++ b/src/tx-format/policy.md @@ -18,7 +18,6 @@ Transaction is invalid if: - `type > PolicyType.Maturity` - ## GasPrice | name | type | description | From da1d6217683c4a31997121375e60e139db5afe6d Mon Sep 17 00:00:00 2001 From: Brandon Kite Date: Thu, 12 Oct 2023 16:21:14 -0700 Subject: [PATCH 07/40] Update src/tx-format/policy.md Co-authored-by: Green Baneling --- src/tx-format/policy.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tx-format/policy.md b/src/tx-format/policy.md index 71e96cc9..e9780b84 100644 --- a/src/tx-format/policy.md +++ b/src/tx-format/policy.md @@ -44,7 +44,7 @@ Given helper `len()` that returns the number of bytes of a field. Transaction is invalid if: -- `witnessLimit > len(tx.witnesses)` +- `len(tx.witnesses) > witnessLimit` ## Maturity From 29a5b71f69827f95c8538aa744478db49a6cb0e1 Mon Sep 17 00:00:00 2001 From: Voxelot Date: Thu, 12 Oct 2023 16:26:42 -0700 Subject: [PATCH 08/40] fixes --- src/fuel-vm/instruction-set.md | 46 ++++++++++++++++------------------ src/tx-format/transaction.md | 2 -- 2 files changed, 22 insertions(+), 26 deletions(-) diff --git a/src/fuel-vm/instruction-set.md b/src/fuel-vm/instruction-set.md index 31ff1748..b4f48050 100644 --- a/src/fuel-vm/instruction-set.md +++ b/src/fuel-vm/instruction-set.md @@ -2425,30 +2425,28 @@ Get [fields from the transaction](../tx-format/transaction.md). | name | `imm` | set `$rA` to | |-------------------------------------------|---------|--------------------------------------------------| | `GTF_TYPE` | `0x001` | `tx.type` | -| `GTF_SCRIPT_MATURITY` | `0x002` | `tx.maturity` | -| `GTF_SCRIPT_SCRIPT_LENGTH` | `0x003` | `tx.scriptLength` | -| `GTF_SCRIPT_SCRIPT_DATA_LENGTH` | `0x004` | `tx.scriptDataLength` | -| `GTF_SCRIPT_INPUTS_COUNT` | `0x005` | `tx.inputsCount` | -| `GTF_SCRIPT_OUTPUTS_COUNT` | `0x006` | `tx.outputsCount` | -| `GTF_SCRIPT_WITNESSES_COUNT` | `0x007` | `tx.witnessesCount` | -| `GTF_SCRIPT_RECEIPTS_ROOT` | `0x008` | Memory address of `tx.receiptsRoot` | -| `GTF_SCRIPT_SCRIPT` | `0x009` | Memory address of `tx.script` | -| `GTF_SCRIPT_SCRIPT_DATA` | `0x00A` | Memory address of `tx.scriptData` | -| `GTF_SCRIPT_INPUT_AT_INDEX` | `0x00B` | Memory address of `tx.inputs[$rB]` | -| `GTF_SCRIPT_OUTPUT_AT_INDEX` | `0x00C` | Memory address of `t.outputs[$rB]` | -| `GTF_SCRIPT_WITNESS_AT_INDEX` | `0x00D` | Memory address of `tx.witnesses[$rB]` | -| `GTF_CREATE_MATURITY` | `0x100` | `tx.maturity` | -| `GTF_CREATE_BYTECODE_LENGTH` | `0x101` | `tx.bytecodeLength` | -| `GTF_CREATE_BYTECODE_WITNESS_INDEX` | `0x102` | `tx.bytecodeWitnessIndex` | -| `GTF_CREATE_STORAGE_SLOTS_COUNT` | `0x103` | `tx.storageSlotsCount` | -| `GTF_CREATE_INPUTS_COUNT` | `0x104` | `tx.inputsCount` | -| `GTF_CREATE_OUTPUTS_COUNT` | `0x105` | `tx.outputsCount` | -| `GTF_CREATE_WITNESSES_COUNT` | `0x106` | `tx.witnessesCount` | -| `GTF_CREATE_SALT` | `0x107` | Memory address of `tx.salt` | -| `GTF_CREATE_STORAGE_SLOT_AT_INDEX` | `0x108` | Memory address of `tx.storageSlots[$rB]` | -| `GTF_CREATE_INPUT_AT_INDEX` | `0x109` | Memory address of `tx.inputs[$rB]` | -| `GTF_CREATE_OUTPUT_AT_INDEX` | `0x10A` | Memory address of `t.outputs[$rB]` | -| `GTF_CREATE_WITNESS_AT_INDEX` | `0x10D` | Memory address of `tx.witnesses[$rB]` | +| `GTF_SCRIPT_SCRIPT_LENGTH` | `0x002` | `tx.scriptLength` | +| `GTF_SCRIPT_SCRIPT_DATA_LENGTH` | `0x003` | `tx.scriptDataLength` | +| `GTF_SCRIPT_INPUTS_COUNT` | `0x004` | `tx.inputsCount` | +| `GTF_SCRIPT_OUTPUTS_COUNT` | `0x005` | `tx.outputsCount` | +| `GTF_SCRIPT_WITNESSES_COUNT` | `0x006` | `tx.witnessesCount` | +| `GTF_SCRIPT_RECEIPTS_ROOT` | `0x007` | Memory address of `tx.receiptsRoot` | +| `GTF_SCRIPT_SCRIPT` | `0x008` | Memory address of `tx.script` | +| `GTF_SCRIPT_SCRIPT_DATA` | `0x009` | Memory address of `tx.scriptData` | +| `GTF_SCRIPT_INPUT_AT_INDEX` | `0x00A` | Memory address of `tx.inputs[$rB]` | +| `GTF_SCRIPT_OUTPUT_AT_INDEX` | `0x00B` | Memory address of `t.outputs[$rB]` | +| `GTF_SCRIPT_WITNESS_AT_INDEX` | `0x00C` | Memory address of `tx.witnesses[$rB]` | +| `GTF_CREATE_BYTECODE_LENGTH` | `0x100` | `tx.bytecodeLength` | +| `GTF_CREATE_BYTECODE_WITNESS_INDEX` | `0x101` | `tx.bytecodeWitnessIndex` | +| `GTF_CREATE_STORAGE_SLOTS_COUNT` | `0x102` | `tx.storageSlotsCount` | +| `GTF_CREATE_INPUTS_COUNT` | `0x103` | `tx.inputsCount` | +| `GTF_CREATE_OUTPUTS_COUNT` | `0x104` | `tx.outputsCount` | +| `GTF_CREATE_WITNESSES_COUNT` | `0x105` | `tx.witnessesCount` | +| `GTF_CREATE_SALT` | `0x106` | Memory address of `tx.salt` | +| `GTF_CREATE_STORAGE_SLOT_AT_INDEX` | `0x107` | Memory address of `tx.storageSlots[$rB]` | +| `GTF_CREATE_INPUT_AT_INDEX` | `0x108` | Memory address of `tx.inputs[$rB]` | +| `GTF_CREATE_OUTPUT_AT_INDEX` | `0x109` | Memory address of `t.outputs[$rB]` | +| `GTF_CREATE_WITNESS_AT_INDEX` | `0x10A` | Memory address of `tx.witnesses[$rB]` | | `GTF_INPUT_TYPE` | `0x200` | `tx.inputs[$rB].type` | | `GTF_INPUT_COIN_TX_ID` | `0x201` | Memory address of `tx.inputs[$rB].txID` | | `GTF_INPUT_COIN_OUTPUT_INDEX` | `0x202` | `tx.inputs[$rB].outputIndex` | diff --git a/src/tx-format/transaction.md b/src/tx-format/transaction.md index 499ce319..63c37f5c 100644 --- a/src/tx-format/transaction.md +++ b/src/tx-format/transaction.md @@ -56,7 +56,6 @@ enum ReceiptType : uint8 { | name | type | description | |--------------------|-----------------------------|----------------------------------------| -| `gasPrice` | `uint64` | Gas price for transaction. | | `scriptLength` | `uint16` | Script length, in instructions. | | `scriptDataLength` | `uint16` | Length of script input data, in bytes. | | `policyCount` | `uint8` | Number of policies. | @@ -95,7 +94,6 @@ The receipts root `receiptsRoot` is the root of the [binary Merkle tree](../prot | name | type | description | |------------------------|-----------------------------|---------------------------------------------------| -| `gasPrice` | `uint64` | Gas price for transaction. | | `bytecodeLength` | `uint16` | Contract bytecode length, in instructions. | | `bytecodeWitnessIndex` | `uint8` | Witness index of contract bytecode to create. | | `policyCount` | `uint8` | Number of policies. | From 389fb666093d62bd2ba91e25f43495eced996109 Mon Sep 17 00:00:00 2001 From: Voxelot Date: Thu, 12 Oct 2023 16:54:35 -0700 Subject: [PATCH 09/40] add additional GTF args for policy metadata --- src/fuel-vm/instruction-set.md | 147 +++++++++++++++++---------------- src/tx-format/policy.md | 8 +- 2 files changed, 79 insertions(+), 76 deletions(-) diff --git a/src/fuel-vm/instruction-set.md b/src/fuel-vm/instruction-set.md index b4f48050..7297620c 100644 --- a/src/fuel-vm/instruction-set.md +++ b/src/fuel-vm/instruction-set.md @@ -2422,78 +2422,80 @@ Set `$rA` to the index of the currently-verifying predicate. Get [fields from the transaction](../tx-format/transaction.md). -| name | `imm` | set `$rA` to | -|-------------------------------------------|---------|--------------------------------------------------| -| `GTF_TYPE` | `0x001` | `tx.type` | -| `GTF_SCRIPT_SCRIPT_LENGTH` | `0x002` | `tx.scriptLength` | -| `GTF_SCRIPT_SCRIPT_DATA_LENGTH` | `0x003` | `tx.scriptDataLength` | -| `GTF_SCRIPT_INPUTS_COUNT` | `0x004` | `tx.inputsCount` | -| `GTF_SCRIPT_OUTPUTS_COUNT` | `0x005` | `tx.outputsCount` | -| `GTF_SCRIPT_WITNESSES_COUNT` | `0x006` | `tx.witnessesCount` | -| `GTF_SCRIPT_RECEIPTS_ROOT` | `0x007` | Memory address of `tx.receiptsRoot` | -| `GTF_SCRIPT_SCRIPT` | `0x008` | Memory address of `tx.script` | -| `GTF_SCRIPT_SCRIPT_DATA` | `0x009` | Memory address of `tx.scriptData` | -| `GTF_SCRIPT_INPUT_AT_INDEX` | `0x00A` | Memory address of `tx.inputs[$rB]` | -| `GTF_SCRIPT_OUTPUT_AT_INDEX` | `0x00B` | Memory address of `t.outputs[$rB]` | -| `GTF_SCRIPT_WITNESS_AT_INDEX` | `0x00C` | Memory address of `tx.witnesses[$rB]` | -| `GTF_CREATE_BYTECODE_LENGTH` | `0x100` | `tx.bytecodeLength` | -| `GTF_CREATE_BYTECODE_WITNESS_INDEX` | `0x101` | `tx.bytecodeWitnessIndex` | -| `GTF_CREATE_STORAGE_SLOTS_COUNT` | `0x102` | `tx.storageSlotsCount` | -| `GTF_CREATE_INPUTS_COUNT` | `0x103` | `tx.inputsCount` | -| `GTF_CREATE_OUTPUTS_COUNT` | `0x104` | `tx.outputsCount` | -| `GTF_CREATE_WITNESSES_COUNT` | `0x105` | `tx.witnessesCount` | -| `GTF_CREATE_SALT` | `0x106` | Memory address of `tx.salt` | -| `GTF_CREATE_STORAGE_SLOT_AT_INDEX` | `0x107` | Memory address of `tx.storageSlots[$rB]` | -| `GTF_CREATE_INPUT_AT_INDEX` | `0x108` | Memory address of `tx.inputs[$rB]` | -| `GTF_CREATE_OUTPUT_AT_INDEX` | `0x109` | Memory address of `t.outputs[$rB]` | -| `GTF_CREATE_WITNESS_AT_INDEX` | `0x10A` | Memory address of `tx.witnesses[$rB]` | -| `GTF_INPUT_TYPE` | `0x200` | `tx.inputs[$rB].type` | -| `GTF_INPUT_COIN_TX_ID` | `0x201` | Memory address of `tx.inputs[$rB].txID` | -| `GTF_INPUT_COIN_OUTPUT_INDEX` | `0x202` | `tx.inputs[$rB].outputIndex` | -| `GTF_INPUT_COIN_OWNER` | `0x203` | Memory address of `tx.inputs[$rB].owner` | -| `GTF_INPUT_COIN_AMOUNT` | `0x204` | `tx.inputs[$rB].amount` | -| `GTF_INPUT_COIN_ASSET_ID` | `0x205` | Memory address of `tx.inputs[$rB].asset_id` | -| `GTF_INPUT_COIN_TX_POINTER` | `0x206` | Memory address of `tx.inputs[$rB].txPointer` | -| `GTF_INPUT_COIN_WITNESS_INDEX` | `0x207` | `tx.inputs[$rB].witnessIndex` | -| `GTF_INPUT_COIN_MATURITY` | `0x208` | `tx.inputs[$rB].maturity` | -| `GTF_INPUT_COIN_PREDICATE_LENGTH` | `0x209` | `tx.inputs[$rB].predicateLength` | -| `GTF_INPUT_COIN_PREDICATE_DATA_LENGTH` | `0x20A` | `tx.inputs[$rB].predicateDataLength` | -| `GTF_INPUT_COIN_PREDICATE` | `0x20B` | Memory address of `tx.inputs[$rB].predicate` | -| `GTF_INPUT_COIN_PREDICATE_DATA` | `0x20C` | Memory address of `tx.inputs[$rB].predicateData` | -| `GTF_INPUT_COIN_PREDICATE_GAS_USED` | `0x20D` | `tx.inputs[$rB].predicateGasUsed` | -| `GTF_INPUT_CONTRACT_TX_ID` | `0x220` | Memory address of `tx.inputs[$rB].txID` | -| `GTF_INPUT_CONTRACT_OUTPUT_INDEX` | `0x221` | `tx.inputs[$rB].outputIndex` | -| `GTF_INPUT_CONTRACT_BALANCE_ROOT` | `0x222` | Memory address of `tx.inputs[$rB].balanceRoot` | -| `GTF_INPUT_CONTRACT_STATE_ROOT` | `0x223` | Memory address of `tx.inputs[$rB].stateRoot` | -| `GTF_INPUT_CONTRACT_TX_POINTER` | `0x224` | Memory address of `tx.inputs[$rB].txPointer` | -| `GTF_INPUT_CONTRACT_CONTRACT_ID` | `0x225` | Memory address of `tx.inputs[$rB].contractID` | -| `GTF_INPUT_MESSAGE_SENDER` | `0x240` | Memory address of `tx.inputs[$rB].sender` | -| `GTF_INPUT_MESSAGE_RECIPIENT` | `0x241` | Memory address of `tx.inputs[$rB].recipient` | -| `GTF_INPUT_MESSAGE_AMOUNT` | `0x242` | `tx.inputs[$rB].amount` | -| `GTF_INPUT_MESSAGE_NONCE` | `0x243` | Memory address of `tx.inputs[$rB].nonce` | -| `GTF_INPUT_MESSAGE_WITNESS_INDEX` | `0x244` | `tx.inputs[$rB].witnessIndex` | -| `GTF_INPUT_MESSAGE_DATA_LENGTH` | `0x245` | `tx.inputs[$rB].dataLength` | -| `GTF_INPUT_MESSAGE_PREDICATE_LENGTH` | `0x246` | `tx.inputs[$rB].predicateLength` | -| `GTF_INPUT_MESSAGE_PREDICATE_DATA_LENGTH` | `0x247` | `tx.inputs[$rB].predicateDataLength` | -| `GTF_INPUT_MESSAGE_DATA` | `0x248` | Memory address of `tx.inputs[$rB].data` | -| `GTF_INPUT_MESSAGE_PREDICATE` | `0x249` | Memory address of `tx.inputs[$rB].predicate` | -| `GTF_INPUT_MESSAGE_PREDICATE_DATA` | `0x24A` | Memory address of `tx.inputs[$rB].predicateData` | -| `GTF_INPUT_MESSAGE_PREDICATE_GAS_USED` | `0x24B` | `tx.inputs[$rB].predicateGasUsed` | -| `GTF_OUTPUT_TYPE` | `0x300` | `tx.outputs[$rB].type` | -| `GTF_OUTPUT_COIN_TO` | `0x301` | Memory address of `tx.outputs[$rB].to` | -| `GTF_OUTPUT_COIN_AMOUNT` | `0x302` | `tx.outputs[$rB].amount` | -| `GTF_OUTPUT_COIN_ASSET_ID` | `0x303` | Memory address of `tx.outputs[$rB].asset_id` | -| `GTF_OUTPUT_CONTRACT_INPUT_INDEX` | `0x304` | `tx.outputs[$rB].inputIndex` | -| `GTF_OUTPUT_CONTRACT_BALANCE_ROOT` | `0x305` | Memory address of `tx.outputs[$rB].balanceRoot` | -| `GTF_OUTPUT_CONTRACT_STATE_ROOT` | `0x306` | Memory address of `tx.outputs[$rB].stateRoot` | -| `GTF_OUTPUT_CONTRACT_CREATED_CONTRACT_ID` | `0x307` | Memory address of `tx.outputs[$rB].contractID` | -| `GTF_OUTPUT_CONTRACT_CREATED_STATE_ROOT` | `0x308` | Memory address of `tx.outputs[$rB].stateRoot` | -| `GTF_WITNESS_DATA_LENGTH` | `0x400` | `tx.witnesses[$rB].dataLength` | -| `GTF_WITNESS_DATA` | `0x401` | Memory address of `tx.witnesses[$rB].data` | -| `GTF_POLICY_GAS_PRICE` | `0x500` | `tx.policies[0x00].gasPrice` | -| `GTF_POLICY_GAS_LIMIT` | `0x501` | `tx.policies[0x01].witnessLimit` | -| `GTF_POLICY_WITNESS_LIMIT` | `0x502` | `tx.policies[0x02].witnessLimit` | -| `GTF_POLICY_MATURITY` | `0x503` | `tx.policies[0x03].maturity` | +| name | `imm` | set `$rA` to | +|-------------------------------------------|---------|--------------------------------------------------------| +| `GTF_TYPE` | `0x001` | `tx.type` | +| `GTF_SCRIPT_SCRIPT_LENGTH` | `0x002` | `tx.scriptLength` | +| `GTF_SCRIPT_SCRIPT_DATA_LENGTH` | `0x003` | `tx.scriptDataLength` | +| `GTF_SCRIPT_INPUTS_COUNT` | `0x004` | `tx.inputsCount` | +| `GTF_SCRIPT_OUTPUTS_COUNT` | `0x005` | `tx.outputsCount` | +| `GTF_SCRIPT_WITNESSES_COUNT` | `0x006` | `tx.witnessesCount` | +| `GTF_SCRIPT_RECEIPTS_ROOT` | `0x007` | Memory address of `tx.receiptsRoot` | +| `GTF_SCRIPT_SCRIPT` | `0x008` | Memory address of `tx.script` | +| `GTF_SCRIPT_SCRIPT_DATA` | `0x009` | Memory address of `tx.scriptData` | +| `GTF_SCRIPT_INPUT_AT_INDEX` | `0x00A` | Memory address of `tx.inputs[$rB]` | +| `GTF_SCRIPT_OUTPUT_AT_INDEX` | `0x00B` | Memory address of `t.outputs[$rB]` | +| `GTF_SCRIPT_WITNESS_AT_INDEX` | `0x00C` | Memory address of `tx.witnesses[$rB]` | +| `GTF_CREATE_BYTECODE_LENGTH` | `0x100` | `tx.bytecodeLength` | +| `GTF_CREATE_BYTECODE_WITNESS_INDEX` | `0x101` | `tx.bytecodeWitnessIndex` | +| `GTF_CREATE_STORAGE_SLOTS_COUNT` | `0x102` | `tx.storageSlotsCount` | +| `GTF_CREATE_INPUTS_COUNT` | `0x103` | `tx.inputsCount` | +| `GTF_CREATE_OUTPUTS_COUNT` | `0x104` | `tx.outputsCount` | +| `GTF_CREATE_WITNESSES_COUNT` | `0x105` | `tx.witnessesCount` | +| `GTF_CREATE_SALT` | `0x106` | Memory address of `tx.salt` | +| `GTF_CREATE_STORAGE_SLOT_AT_INDEX` | `0x107` | Memory address of `tx.storageSlots[$rB]` | +| `GTF_CREATE_INPUT_AT_INDEX` | `0x108` | Memory address of `tx.inputs[$rB]` | +| `GTF_CREATE_OUTPUT_AT_INDEX` | `0x109` | Memory address of `t.outputs[$rB]` | +| `GTF_CREATE_WITNESS_AT_INDEX` | `0x10A` | Memory address of `tx.witnesses[$rB]` | +| `GTF_INPUT_TYPE` | `0x200` | `tx.inputs[$rB].type` | +| `GTF_INPUT_COIN_TX_ID` | `0x201` | Memory address of `tx.inputs[$rB].txID` | +| `GTF_INPUT_COIN_OUTPUT_INDEX` | `0x202` | `tx.inputs[$rB].outputIndex` | +| `GTF_INPUT_COIN_OWNER` | `0x203` | Memory address of `tx.inputs[$rB].owner` | +| `GTF_INPUT_COIN_AMOUNT` | `0x204` | `tx.inputs[$rB].amount` | +| `GTF_INPUT_COIN_ASSET_ID` | `0x205` | Memory address of `tx.inputs[$rB].asset_id` | +| `GTF_INPUT_COIN_TX_POINTER` | `0x206` | Memory address of `tx.inputs[$rB].txPointer` | +| `GTF_INPUT_COIN_WITNESS_INDEX` | `0x207` | `tx.inputs[$rB].witnessIndex` | +| `GTF_INPUT_COIN_MATURITY` | `0x208` | `tx.inputs[$rB].maturity` | +| `GTF_INPUT_COIN_PREDICATE_LENGTH` | `0x209` | `tx.inputs[$rB].predicateLength` | +| `GTF_INPUT_COIN_PREDICATE_DATA_LENGTH` | `0x20A` | `tx.inputs[$rB].predicateDataLength` | +| `GTF_INPUT_COIN_PREDICATE` | `0x20B` | Memory address of `tx.inputs[$rB].predicate` | +| `GTF_INPUT_COIN_PREDICATE_DATA` | `0x20C` | Memory address of `tx.inputs[$rB].predicateData` | +| `GTF_INPUT_COIN_PREDICATE_GAS_USED` | `0x20D` | `tx.inputs[$rB].predicateGasUsed` | +| `GTF_INPUT_CONTRACT_TX_ID` | `0x220` | Memory address of `tx.inputs[$rB].txID` | +| `GTF_INPUT_CONTRACT_OUTPUT_INDEX` | `0x221` | `tx.inputs[$rB].outputIndex` | +| `GTF_INPUT_CONTRACT_BALANCE_ROOT` | `0x222` | Memory address of `tx.inputs[$rB].balanceRoot` | +| `GTF_INPUT_CONTRACT_STATE_ROOT` | `0x223` | Memory address of `tx.inputs[$rB].stateRoot` | +| `GTF_INPUT_CONTRACT_TX_POINTER` | `0x224` | Memory address of `tx.inputs[$rB].txPointer` | +| `GTF_INPUT_CONTRACT_CONTRACT_ID` | `0x225` | Memory address of `tx.inputs[$rB].contractID` | +| `GTF_INPUT_MESSAGE_SENDER` | `0x240` | Memory address of `tx.inputs[$rB].sender` | +| `GTF_INPUT_MESSAGE_RECIPIENT` | `0x241` | Memory address of `tx.inputs[$rB].recipient` | +| `GTF_INPUT_MESSAGE_AMOUNT` | `0x242` | `tx.inputs[$rB].amount` | +| `GTF_INPUT_MESSAGE_NONCE` | `0x243` | Memory address of `tx.inputs[$rB].nonce` | +| `GTF_INPUT_MESSAGE_WITNESS_INDEX` | `0x244` | `tx.inputs[$rB].witnessIndex` | +| `GTF_INPUT_MESSAGE_DATA_LENGTH` | `0x245` | `tx.inputs[$rB].dataLength` | +| `GTF_INPUT_MESSAGE_PREDICATE_LENGTH` | `0x246` | `tx.inputs[$rB].predicateLength` | +| `GTF_INPUT_MESSAGE_PREDICATE_DATA_LENGTH` | `0x247` | `tx.inputs[$rB].predicateDataLength` | +| `GTF_INPUT_MESSAGE_DATA` | `0x248` | Memory address of `tx.inputs[$rB].data` | +| `GTF_INPUT_MESSAGE_PREDICATE` | `0x249` | Memory address of `tx.inputs[$rB].predicate` | +| `GTF_INPUT_MESSAGE_PREDICATE_DATA` | `0x24A` | Memory address of `tx.inputs[$rB].predicateData` | +| `GTF_INPUT_MESSAGE_PREDICATE_GAS_USED` | `0x24B` | `tx.inputs[$rB].predicateGasUsed` | +| `GTF_OUTPUT_TYPE` | `0x300` | `tx.outputs[$rB].type` | +| `GTF_OUTPUT_COIN_TO` | `0x301` | Memory address of `tx.outputs[$rB].to` | +| `GTF_OUTPUT_COIN_AMOUNT` | `0x302` | `tx.outputs[$rB].amount` | +| `GTF_OUTPUT_COIN_ASSET_ID` | `0x303` | Memory address of `tx.outputs[$rB].asset_id` | +| `GTF_OUTPUT_CONTRACT_INPUT_INDEX` | `0x304` | `tx.outputs[$rB].inputIndex` | +| `GTF_OUTPUT_CONTRACT_BALANCE_ROOT` | `0x305` | Memory address of `tx.outputs[$rB].balanceRoot` | +| `GTF_OUTPUT_CONTRACT_STATE_ROOT` | `0x306` | Memory address of `tx.outputs[$rB].stateRoot` | +| `GTF_OUTPUT_CONTRACT_CREATED_CONTRACT_ID` | `0x307` | Memory address of `tx.outputs[$rB].contractID` | +| `GTF_OUTPUT_CONTRACT_CREATED_STATE_ROOT` | `0x308` | Memory address of `tx.outputs[$rB].stateRoot` | +| `GTF_WITNESS_DATA_LENGTH` | `0x400` | `tx.witnesses[$rB].dataLength` | +| `GTF_WITNESS_DATA` | `0x401` | Memory address of `tx.witnesses[$rB].data` | +| `GTF_POLICY_COUNT` | `0x500` | `len(tx.policies)` | +| `GTF_POLICY_TYPE` | `0x501` | `tx.policies[$rB].type` | +| `GTF_POLICY_GAS_PRICE` | `0x502` | `find(tx.policies.type == 0x00).gasPrice` | +| `GTF_POLICY_GAS_LIMIT` | `0x503` | `find(tx.policies.type == 0x01).gasLimit` | +| `GTF_POLICY_WITNESS_LIMIT` | `0x504` | `find(tx.policies.type == 0x02).witnessLimit` | +| `GTF_POLICY_MATURITY` | `0x505` | `find(tx.policies.type == 0x03).maturity` | Panic if: @@ -2501,5 +2503,6 @@ Panic if: - `imm` is not one of the values listed above - The value of `$rB` results in an out of bounds access for variable-length fields - The input or output type does not match (`OutputChange` and `OutputVariable` count as `OutputCoin`) +- The requested policy type is not set for this transaction. For fixed-length fields, the value of `$rB` is ignored. diff --git a/src/tx-format/policy.md b/src/tx-format/policy.md index e9780b84..9118ad0d 100644 --- a/src/tx-format/policy.md +++ b/src/tx-format/policy.md @@ -9,10 +9,10 @@ enum PolicyType : uint8 { } ``` -| name | type | description | -|--------|---------------------------------------------------------------------------------------|----------------| -| `type` | [PolicyType](#policy) | Type of input. | -| `data` | One of [GasLimit](#gaslimit), [WitnessLimit](#witnesslimit), or [Maturity](#maturity) | Input data. | +| name | type | description | +|--------|--------------------------------------------------------------------------------------------------------------|----------------| +| `type` | [PolicyType](#policy) | Type of input. | +| `data` | One of [GasPrice](#gasprice), [GasLimit](#gaslimit), [WitnessLimit](#witnesslimit), or [Maturity](#maturity) | Input data. | Transaction is invalid if: From 7fd36a67116448bcaaad0b616df4881f77646c1a Mon Sep 17 00:00:00 2001 From: Voxelot Date: Thu, 12 Oct 2023 17:13:12 -0700 Subject: [PATCH 10/40] experiment with bitmask approach for policy types to compress tx --- src/fuel-vm/instruction-set.md | 148 ++++++++++++++++----------------- src/tx-format/policy.md | 22 ++--- src/tx-format/transaction.md | 17 +++- 3 files changed, 98 insertions(+), 89 deletions(-) diff --git a/src/fuel-vm/instruction-set.md b/src/fuel-vm/instruction-set.md index 7297620c..1f9311c9 100644 --- a/src/fuel-vm/instruction-set.md +++ b/src/fuel-vm/instruction-set.md @@ -2422,80 +2422,80 @@ Set `$rA` to the index of the currently-verifying predicate. Get [fields from the transaction](../tx-format/transaction.md). -| name | `imm` | set `$rA` to | -|-------------------------------------------|---------|--------------------------------------------------------| -| `GTF_TYPE` | `0x001` | `tx.type` | -| `GTF_SCRIPT_SCRIPT_LENGTH` | `0x002` | `tx.scriptLength` | -| `GTF_SCRIPT_SCRIPT_DATA_LENGTH` | `0x003` | `tx.scriptDataLength` | -| `GTF_SCRIPT_INPUTS_COUNT` | `0x004` | `tx.inputsCount` | -| `GTF_SCRIPT_OUTPUTS_COUNT` | `0x005` | `tx.outputsCount` | -| `GTF_SCRIPT_WITNESSES_COUNT` | `0x006` | `tx.witnessesCount` | -| `GTF_SCRIPT_RECEIPTS_ROOT` | `0x007` | Memory address of `tx.receiptsRoot` | -| `GTF_SCRIPT_SCRIPT` | `0x008` | Memory address of `tx.script` | -| `GTF_SCRIPT_SCRIPT_DATA` | `0x009` | Memory address of `tx.scriptData` | -| `GTF_SCRIPT_INPUT_AT_INDEX` | `0x00A` | Memory address of `tx.inputs[$rB]` | -| `GTF_SCRIPT_OUTPUT_AT_INDEX` | `0x00B` | Memory address of `t.outputs[$rB]` | -| `GTF_SCRIPT_WITNESS_AT_INDEX` | `0x00C` | Memory address of `tx.witnesses[$rB]` | -| `GTF_CREATE_BYTECODE_LENGTH` | `0x100` | `tx.bytecodeLength` | -| `GTF_CREATE_BYTECODE_WITNESS_INDEX` | `0x101` | `tx.bytecodeWitnessIndex` | -| `GTF_CREATE_STORAGE_SLOTS_COUNT` | `0x102` | `tx.storageSlotsCount` | -| `GTF_CREATE_INPUTS_COUNT` | `0x103` | `tx.inputsCount` | -| `GTF_CREATE_OUTPUTS_COUNT` | `0x104` | `tx.outputsCount` | -| `GTF_CREATE_WITNESSES_COUNT` | `0x105` | `tx.witnessesCount` | -| `GTF_CREATE_SALT` | `0x106` | Memory address of `tx.salt` | -| `GTF_CREATE_STORAGE_SLOT_AT_INDEX` | `0x107` | Memory address of `tx.storageSlots[$rB]` | -| `GTF_CREATE_INPUT_AT_INDEX` | `0x108` | Memory address of `tx.inputs[$rB]` | -| `GTF_CREATE_OUTPUT_AT_INDEX` | `0x109` | Memory address of `t.outputs[$rB]` | -| `GTF_CREATE_WITNESS_AT_INDEX` | `0x10A` | Memory address of `tx.witnesses[$rB]` | -| `GTF_INPUT_TYPE` | `0x200` | `tx.inputs[$rB].type` | -| `GTF_INPUT_COIN_TX_ID` | `0x201` | Memory address of `tx.inputs[$rB].txID` | -| `GTF_INPUT_COIN_OUTPUT_INDEX` | `0x202` | `tx.inputs[$rB].outputIndex` | -| `GTF_INPUT_COIN_OWNER` | `0x203` | Memory address of `tx.inputs[$rB].owner` | -| `GTF_INPUT_COIN_AMOUNT` | `0x204` | `tx.inputs[$rB].amount` | -| `GTF_INPUT_COIN_ASSET_ID` | `0x205` | Memory address of `tx.inputs[$rB].asset_id` | -| `GTF_INPUT_COIN_TX_POINTER` | `0x206` | Memory address of `tx.inputs[$rB].txPointer` | -| `GTF_INPUT_COIN_WITNESS_INDEX` | `0x207` | `tx.inputs[$rB].witnessIndex` | -| `GTF_INPUT_COIN_MATURITY` | `0x208` | `tx.inputs[$rB].maturity` | -| `GTF_INPUT_COIN_PREDICATE_LENGTH` | `0x209` | `tx.inputs[$rB].predicateLength` | -| `GTF_INPUT_COIN_PREDICATE_DATA_LENGTH` | `0x20A` | `tx.inputs[$rB].predicateDataLength` | -| `GTF_INPUT_COIN_PREDICATE` | `0x20B` | Memory address of `tx.inputs[$rB].predicate` | -| `GTF_INPUT_COIN_PREDICATE_DATA` | `0x20C` | Memory address of `tx.inputs[$rB].predicateData` | -| `GTF_INPUT_COIN_PREDICATE_GAS_USED` | `0x20D` | `tx.inputs[$rB].predicateGasUsed` | -| `GTF_INPUT_CONTRACT_TX_ID` | `0x220` | Memory address of `tx.inputs[$rB].txID` | -| `GTF_INPUT_CONTRACT_OUTPUT_INDEX` | `0x221` | `tx.inputs[$rB].outputIndex` | -| `GTF_INPUT_CONTRACT_BALANCE_ROOT` | `0x222` | Memory address of `tx.inputs[$rB].balanceRoot` | -| `GTF_INPUT_CONTRACT_STATE_ROOT` | `0x223` | Memory address of `tx.inputs[$rB].stateRoot` | -| `GTF_INPUT_CONTRACT_TX_POINTER` | `0x224` | Memory address of `tx.inputs[$rB].txPointer` | -| `GTF_INPUT_CONTRACT_CONTRACT_ID` | `0x225` | Memory address of `tx.inputs[$rB].contractID` | -| `GTF_INPUT_MESSAGE_SENDER` | `0x240` | Memory address of `tx.inputs[$rB].sender` | -| `GTF_INPUT_MESSAGE_RECIPIENT` | `0x241` | Memory address of `tx.inputs[$rB].recipient` | -| `GTF_INPUT_MESSAGE_AMOUNT` | `0x242` | `tx.inputs[$rB].amount` | -| `GTF_INPUT_MESSAGE_NONCE` | `0x243` | Memory address of `tx.inputs[$rB].nonce` | -| `GTF_INPUT_MESSAGE_WITNESS_INDEX` | `0x244` | `tx.inputs[$rB].witnessIndex` | -| `GTF_INPUT_MESSAGE_DATA_LENGTH` | `0x245` | `tx.inputs[$rB].dataLength` | -| `GTF_INPUT_MESSAGE_PREDICATE_LENGTH` | `0x246` | `tx.inputs[$rB].predicateLength` | -| `GTF_INPUT_MESSAGE_PREDICATE_DATA_LENGTH` | `0x247` | `tx.inputs[$rB].predicateDataLength` | -| `GTF_INPUT_MESSAGE_DATA` | `0x248` | Memory address of `tx.inputs[$rB].data` | -| `GTF_INPUT_MESSAGE_PREDICATE` | `0x249` | Memory address of `tx.inputs[$rB].predicate` | -| `GTF_INPUT_MESSAGE_PREDICATE_DATA` | `0x24A` | Memory address of `tx.inputs[$rB].predicateData` | -| `GTF_INPUT_MESSAGE_PREDICATE_GAS_USED` | `0x24B` | `tx.inputs[$rB].predicateGasUsed` | -| `GTF_OUTPUT_TYPE` | `0x300` | `tx.outputs[$rB].type` | -| `GTF_OUTPUT_COIN_TO` | `0x301` | Memory address of `tx.outputs[$rB].to` | -| `GTF_OUTPUT_COIN_AMOUNT` | `0x302` | `tx.outputs[$rB].amount` | -| `GTF_OUTPUT_COIN_ASSET_ID` | `0x303` | Memory address of `tx.outputs[$rB].asset_id` | -| `GTF_OUTPUT_CONTRACT_INPUT_INDEX` | `0x304` | `tx.outputs[$rB].inputIndex` | -| `GTF_OUTPUT_CONTRACT_BALANCE_ROOT` | `0x305` | Memory address of `tx.outputs[$rB].balanceRoot` | -| `GTF_OUTPUT_CONTRACT_STATE_ROOT` | `0x306` | Memory address of `tx.outputs[$rB].stateRoot` | -| `GTF_OUTPUT_CONTRACT_CREATED_CONTRACT_ID` | `0x307` | Memory address of `tx.outputs[$rB].contractID` | -| `GTF_OUTPUT_CONTRACT_CREATED_STATE_ROOT` | `0x308` | Memory address of `tx.outputs[$rB].stateRoot` | -| `GTF_WITNESS_DATA_LENGTH` | `0x400` | `tx.witnesses[$rB].dataLength` | -| `GTF_WITNESS_DATA` | `0x401` | Memory address of `tx.witnesses[$rB].data` | -| `GTF_POLICY_COUNT` | `0x500` | `len(tx.policies)` | -| `GTF_POLICY_TYPE` | `0x501` | `tx.policies[$rB].type` | -| `GTF_POLICY_GAS_PRICE` | `0x502` | `find(tx.policies.type == 0x00).gasPrice` | -| `GTF_POLICY_GAS_LIMIT` | `0x503` | `find(tx.policies.type == 0x01).gasLimit` | -| `GTF_POLICY_WITNESS_LIMIT` | `0x504` | `find(tx.policies.type == 0x02).witnessLimit` | -| `GTF_POLICY_MATURITY` | `0x505` | `find(tx.policies.type == 0x03).maturity` | +| name | `imm` | set `$rA` to | +|-------------------------------------------|---------|--------------------------------------------------------------------| +| `GTF_TYPE` | `0x001` | `tx.type` | +| `GTF_SCRIPT_SCRIPT_LENGTH` | `0x002` | `tx.scriptLength` | +| `GTF_SCRIPT_SCRIPT_DATA_LENGTH` | `0x003` | `tx.scriptDataLength` | +| `GTF_SCRIPT_INPUTS_COUNT` | `0x004` | `tx.inputsCount` | +| `GTF_SCRIPT_OUTPUTS_COUNT` | `0x005` | `tx.outputsCount` | +| `GTF_SCRIPT_WITNESSES_COUNT` | `0x006` | `tx.witnessesCount` | +| `GTF_SCRIPT_RECEIPTS_ROOT` | `0x007` | Memory address of `tx.receiptsRoot` | +| `GTF_SCRIPT_SCRIPT` | `0x008` | Memory address of `tx.script` | +| `GTF_SCRIPT_SCRIPT_DATA` | `0x009` | Memory address of `tx.scriptData` | +| `GTF_SCRIPT_INPUT_AT_INDEX` | `0x00A` | Memory address of `tx.inputs[$rB]` | +| `GTF_SCRIPT_OUTPUT_AT_INDEX` | `0x00B` | Memory address of `t.outputs[$rB]` | +| `GTF_SCRIPT_WITNESS_AT_INDEX` | `0x00C` | Memory address of `tx.witnesses[$rB]` | +| `GTF_CREATE_BYTECODE_LENGTH` | `0x100` | `tx.bytecodeLength` | +| `GTF_CREATE_BYTECODE_WITNESS_INDEX` | `0x101` | `tx.bytecodeWitnessIndex` | +| `GTF_CREATE_STORAGE_SLOTS_COUNT` | `0x102` | `tx.storageSlotsCount` | +| `GTF_CREATE_INPUTS_COUNT` | `0x103` | `tx.inputsCount` | +| `GTF_CREATE_OUTPUTS_COUNT` | `0x104` | `tx.outputsCount` | +| `GTF_CREATE_WITNESSES_COUNT` | `0x105` | `tx.witnessesCount` | +| `GTF_CREATE_SALT` | `0x106` | Memory address of `tx.salt` | +| `GTF_CREATE_STORAGE_SLOT_AT_INDEX` | `0x107` | Memory address of `tx.storageSlots[$rB]` | +| `GTF_CREATE_INPUT_AT_INDEX` | `0x108` | Memory address of `tx.inputs[$rB]` | +| `GTF_CREATE_OUTPUT_AT_INDEX` | `0x109` | Memory address of `t.outputs[$rB]` | +| `GTF_CREATE_WITNESS_AT_INDEX` | `0x10A` | Memory address of `tx.witnesses[$rB]` | +| `GTF_INPUT_TYPE` | `0x200` | `tx.inputs[$rB].type` | +| `GTF_INPUT_COIN_TX_ID` | `0x201` | Memory address of `tx.inputs[$rB].txID` | +| `GTF_INPUT_COIN_OUTPUT_INDEX` | `0x202` | `tx.inputs[$rB].outputIndex` | +| `GTF_INPUT_COIN_OWNER` | `0x203` | Memory address of `tx.inputs[$rB].owner` | +| `GTF_INPUT_COIN_AMOUNT` | `0x204` | `tx.inputs[$rB].amount` | +| `GTF_INPUT_COIN_ASSET_ID` | `0x205` | Memory address of `tx.inputs[$rB].asset_id` | +| `GTF_INPUT_COIN_TX_POINTER` | `0x206` | Memory address of `tx.inputs[$rB].txPointer` | +| `GTF_INPUT_COIN_WITNESS_INDEX` | `0x207` | `tx.inputs[$rB].witnessIndex` | +| `GTF_INPUT_COIN_MATURITY` | `0x208` | `tx.inputs[$rB].maturity` | +| `GTF_INPUT_COIN_PREDICATE_LENGTH` | `0x209` | `tx.inputs[$rB].predicateLength` | +| `GTF_INPUT_COIN_PREDICATE_DATA_LENGTH` | `0x20A` | `tx.inputs[$rB].predicateDataLength` | +| `GTF_INPUT_COIN_PREDICATE` | `0x20B` | Memory address of `tx.inputs[$rB].predicate` | +| `GTF_INPUT_COIN_PREDICATE_DATA` | `0x20C` | Memory address of `tx.inputs[$rB].predicateData` | +| `GTF_INPUT_COIN_PREDICATE_GAS_USED` | `0x20D` | `tx.inputs[$rB].predicateGasUsed` | +| `GTF_INPUT_CONTRACT_TX_ID` | `0x220` | Memory address of `tx.inputs[$rB].txID` | +| `GTF_INPUT_CONTRACT_OUTPUT_INDEX` | `0x221` | `tx.inputs[$rB].outputIndex` | +| `GTF_INPUT_CONTRACT_BALANCE_ROOT` | `0x222` | Memory address of `tx.inputs[$rB].balanceRoot` | +| `GTF_INPUT_CONTRACT_STATE_ROOT` | `0x223` | Memory address of `tx.inputs[$rB].stateRoot` | +| `GTF_INPUT_CONTRACT_TX_POINTER` | `0x224` | Memory address of `tx.inputs[$rB].txPointer` | +| `GTF_INPUT_CONTRACT_CONTRACT_ID` | `0x225` | Memory address of `tx.inputs[$rB].contractID` | +| `GTF_INPUT_MESSAGE_SENDER` | `0x240` | Memory address of `tx.inputs[$rB].sender` | +| `GTF_INPUT_MESSAGE_RECIPIENT` | `0x241` | Memory address of `tx.inputs[$rB].recipient` | +| `GTF_INPUT_MESSAGE_AMOUNT` | `0x242` | `tx.inputs[$rB].amount` | +| `GTF_INPUT_MESSAGE_NONCE` | `0x243` | Memory address of `tx.inputs[$rB].nonce` | +| `GTF_INPUT_MESSAGE_WITNESS_INDEX` | `0x244` | `tx.inputs[$rB].witnessIndex` | +| `GTF_INPUT_MESSAGE_DATA_LENGTH` | `0x245` | `tx.inputs[$rB].dataLength` | +| `GTF_INPUT_MESSAGE_PREDICATE_LENGTH` | `0x246` | `tx.inputs[$rB].predicateLength` | +| `GTF_INPUT_MESSAGE_PREDICATE_DATA_LENGTH` | `0x247` | `tx.inputs[$rB].predicateDataLength` | +| `GTF_INPUT_MESSAGE_DATA` | `0x248` | Memory address of `tx.inputs[$rB].data` | +| `GTF_INPUT_MESSAGE_PREDICATE` | `0x249` | Memory address of `tx.inputs[$rB].predicate` | +| `GTF_INPUT_MESSAGE_PREDICATE_DATA` | `0x24A` | Memory address of `tx.inputs[$rB].predicateData` | +| `GTF_INPUT_MESSAGE_PREDICATE_GAS_USED` | `0x24B` | `tx.inputs[$rB].predicateGasUsed` | +| `GTF_OUTPUT_TYPE` | `0x300` | `tx.outputs[$rB].type` | +| `GTF_OUTPUT_COIN_TO` | `0x301` | Memory address of `tx.outputs[$rB].to` | +| `GTF_OUTPUT_COIN_AMOUNT` | `0x302` | `tx.outputs[$rB].amount` | +| `GTF_OUTPUT_COIN_ASSET_ID` | `0x303` | Memory address of `tx.outputs[$rB].asset_id` | +| `GTF_OUTPUT_CONTRACT_INPUT_INDEX` | `0x304` | `tx.outputs[$rB].inputIndex` | +| `GTF_OUTPUT_CONTRACT_BALANCE_ROOT` | `0x305` | Memory address of `tx.outputs[$rB].balanceRoot` | +| `GTF_OUTPUT_CONTRACT_STATE_ROOT` | `0x306` | Memory address of `tx.outputs[$rB].stateRoot` | +| `GTF_OUTPUT_CONTRACT_CREATED_CONTRACT_ID` | `0x307` | Memory address of `tx.outputs[$rB].contractID` | +| `GTF_OUTPUT_CONTRACT_CREATED_STATE_ROOT` | `0x308` | Memory address of `tx.outputs[$rB].stateRoot` | +| `GTF_WITNESS_DATA_LENGTH` | `0x400` | `tx.witnesses[$rB].dataLength` | +| `GTF_WITNESS_DATA` | `0x401` | Memory address of `tx.witnesses[$rB].data` | +| `GTF_POLICY_COUNT` | `0x500` | `len(tx.policies)` | +| `GTF_POLICY_TYPE` | `0x501` | `tx.policies[$rB].type` | +| `GTF_POLICY_GAS_PRICE` | `0x502` | `tx.policies[0x00].gasPrice` | +| `GTF_POLICY_GAS_LIMIT` | `0x503` | `tx.policies[count_ones(0x11 & tx.policyTypes) - 1].gasLimit` | +| `GTF_POLICY_WITNESS_LIMIT` | `0x504` | `tx.policies[count_ones(0x111 & tx.policyTypes) - 1].witnessLimit` | +| `GTF_POLICY_MATURITY` | `0x505` | `tx.policies[count_ones(0x1111 & tx.policyTypes) - 1].maturity` | Panic if: diff --git a/src/tx-format/policy.md b/src/tx-format/policy.md index 9118ad0d..b0e66ecc 100644 --- a/src/tx-format/policy.md +++ b/src/tx-format/policy.md @@ -1,22 +1,18 @@ # Policy ```c++ -enum PolicyType : uint8 { - GasPrice = 0, - GasLimit = 1, - WitnessLimit = 2, - Maturity = 3, +// index using power of 2's for efficient bitmasking +enum PolicyType : uint32 { + GasPrice = 1, + GasLimit = 2, + WitnessLimit = 4, + Maturity = 8, } ``` -| name | type | description | -|--------|--------------------------------------------------------------------------------------------------------------|----------------| -| `type` | [PolicyType](#policy) | Type of input. | -| `data` | One of [GasPrice](#gasprice), [GasLimit](#gaslimit), [WitnessLimit](#witnesslimit), or [Maturity](#maturity) | Input data. | - -Transaction is invalid if: - -- `type > PolicyType.Maturity` +| name | type | description | +|--------|--------------------------------------------------------------------------------------------------------------|-----------------| +| `data` | One of [GasPrice](#gasprice), [GasLimit](#gaslimit), [WitnessLimit](#witnesslimit), or [Maturity](#maturity) | Policy data. | ## GasPrice diff --git a/src/tx-format/transaction.md b/src/tx-format/transaction.md index 63c37f5c..88425171 100644 --- a/src/tx-format/transaction.md +++ b/src/tx-format/transaction.md @@ -58,19 +58,22 @@ enum ReceiptType : uint8 { |--------------------|-----------------------------|----------------------------------------| | `scriptLength` | `uint16` | Script length, in instructions. | | `scriptDataLength` | `uint16` | Length of script input data, in bytes. | -| `policyCount` | `uint8` | Number of policies. | +| `policyTypes` | `uint32` | Bitfield of used policy types. | | `inputsCount` | `uint8` | Number of inputs. | | `outputsCount` | `uint8` | Number of outputs. | | `witnessesCount` | `uint8` | Number of witnesses. | | `receiptsRoot` | `byte[32]` | Merkle root of receipts. | | `script` | `byte[]` | Script to execute. | | `scriptData` | `byte[]` | Script input data (parameters). | -| `policies` | [Policy](./policy.md)`[]` | List of policies. | +| `policies` | [Policy](./policy.md)`[]` | List of policies, sorted by PolicyType.| | `inputs` | [Input](./input.md)`[]` | List of inputs. | | `outputs` | [Output](./output.md)`[]` | List of outputs. | | `witnesses` | [Witness](./witness.md)`[]` | List of witnesses. | Given helper `len()` that returns the number of bytes of a field. +Given helper `count_ones()` that returns the number of ones in the binary representation of a field. +Given helper `count_variants()` that returns the number of variants in an enum. +Given helper `sum_variants()` that sums all variants of an enum. Transaction is invalid if: @@ -81,6 +84,9 @@ Transaction is invalid if: - `scriptDataLength != len(scriptData)` - No policy of type `PolicyType.GasPrice` - No policy of type `PolicyType.GasLimit` +- `count_ones(policyTypes) > count_variants(PolicyType)` +- `policyTypes > sum_variants(PolicyType)` +- `len(policies) > count_ones(policyTypes)` > **Note:** when signing a transaction, `receiptsRoot` is set to zero. > @@ -108,6 +114,10 @@ The receipts root `receiptsRoot` is the root of the [binary Merkle tree](../prot | `outputs` | [Output](./output.md)`[]` | List of outputs. | | `witnesses` | [Witness](./witness.md)`[]` | List of witnesses. | +Given helper `count_ones()` that returns the number of ones in the binary representation of a field. +Given helper `count_variants()` that returns the number of variants in an enum. +Given helper `sum_variants()` that sums all variants of an enum. + Transaction is invalid if: - Any input is of type `InputType.Contract` or `InputType.Message` where `input.dataLength > 0` @@ -123,6 +133,9 @@ Transaction is invalid if: - `storageSlotsCount > MAX_STORAGE_SLOTS` - The [Sparse Merkle tree](../protocol/cryptographic-primitives.md#sparse-merkle-tree) root of `storageSlots` is not equal to the `stateRoot` of the one `OutputType.ContractCreated` output - No policy of type `PolicyType.GasPrice` +- `count_ones(policyTypes) > count_variants(PolicyType)` +- `policyTypes > sum_variants(PolicyType)` +- `len(policies) > count_ones(policyTypes)` Creates a contract with contract ID as computed [here](../identifiers/contract-id.md). From 573be185ca7cf07cde4536c39da988463be3a19f Mon Sep 17 00:00:00 2001 From: Brandon Kite Date: Thu, 19 Oct 2023 23:00:17 -0700 Subject: [PATCH 11/40] add max_fee policy segregate predicateGasUsed from tx.gasLimit --- src/fuel-vm/index.md | 6 +++--- src/protocol/tx-validity.md | 25 ++++++++++++++++++++++--- src/tx-format/constants.md | 33 +++++++++++++++++---------------- src/tx-format/policy.md | 14 +++++++++++++- src/tx-format/transaction.md | 32 ++++++++++++++++---------------- 5 files changed, 71 insertions(+), 39 deletions(-) diff --git a/src/fuel-vm/index.md b/src/fuel-vm/index.md index 7775b425..1bf78c2a 100644 --- a/src/fuel-vm/index.md +++ b/src/fuel-vm/index.md @@ -109,7 +109,7 @@ For any input of type [`InputType.Coin`](../tx-format/index.md) or [`InputType.M 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 the minimum of `tx.gasLimit` or `MAX_GAS_PER_PREDICATE`. +1. `$ggas` and `$cgas` are set to `MAX_GAS_PER_PREDICATE`. Predicate estimation will fail if gas is exhausted during execution. @@ -121,7 +121,7 @@ In addition, during predicate mode if `$pc` is set to a value greater than the e 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`. +After successful execution, `predicateGasUsed` is set to `MAX_GAS_PER_PREDICATE - $ggas`. ## Predicate Verification @@ -149,7 +149,7 @@ If script bytecode is present, transaction validation requires execution. The VM is [initialized](#vm-initialization), then: 1. `$pc` and `$is` are set to the start of the transaction's script bytecode. -1. `$ggas` and `$cgas` are set to `tx.gasLimit` minus the sum of `predicateGasUsed` for all predicates. +1. `$ggas` and `$cgas` are set to `tx.gasLimit`. Following initialization, execution begins. diff --git a/src/protocol/tx-validity.md b/src/protocol/tx-validity.md index af4d4dbe..2ca48fab 100644 --- a/src/protocol/tx-validity.md +++ b/src/protocol/tx-validity.md @@ -103,6 +103,15 @@ def sum_inputs(tx, asset_id) -> int: total += input.amount return total +def sum_predicate_gas_used(tx) -> int: + total: int = 0 + for input in tx.inputs: + if input.type == InputType.Coin: + total += input.predicateGasUsed + elif input.type == InputType.Message: + total += input.predicateGasUsed + return total + """ Returns any minted amounts by the transaction """ @@ -127,15 +136,25 @@ def available_balance(tx, asset_id) -> int: def unavailable_balance(tx, asset_id) -> int: sentBalance = sum_outputs(tx, asset_id) - gasBalance = gasPrice * gasLimit / GAS_PRICE_FACTOR - bytesBalance = size(tx) * GAS_PER_BYTE * gasPrice / GAS_PRICE_FACTOR # Total fee balance - feeBalance = ceiling(gasBalance + bytesBalance) + feeBalance = fee_balance(tx, asset_id) # Only base asset can be used to pay for gas if asset_id == 0: return sentBalance + feeBalance return sentBalance +def fee_balance(tx, asset_id) -> int: + gas = tx.gasLimit + sum_predicate_gas_used(tx) + gasBalance = gasPrice * gas / GAS_PRICE_FACTOR + bytesBalance = size(tx) * GAS_PER_BYTE * gasPrice / GAS_PRICE_FACTOR + # Total fee balance + feeBalance = ceiling(gasBalance + bytesBalance) + # Only base asset can be used to pay for gas + if asset_id == 0: + return feeBalance + else: + return 0 + # The sum_data_messages total is not included in the unavailable_balance since it is spendable as long as there # is enough base asset amount to cover gas costs without using data messages. Messages containing data can't # cover gas costs since they are retryable. diff --git a/src/tx-format/constants.md b/src/tx-format/constants.md index f3c73619..212382a0 100644 --- a/src/tx-format/constants.md +++ b/src/tx-format/constants.md @@ -1,18 +1,19 @@ # Constants -| name | type | value | description | -|-----------------------------|----------|-----------------|-----------------------------------------------| -| `GAS_PER_BYTE` | `uint64` | | Gas charged per byte of the transaction. | -| `GAS_PRICE_FACTOR` | `uint64` | `1,000,000,000` | Unit factor for gas price. | -| `MAX_GAS_PER_TX` | `uint64` | | Maximum gas per transaction. | -| `MAX_INPUTS` | `uint64` | `8` | Maximum number of inputs. | -| `MAX_OUTPUTS` | `uint64` | `8` | Maximum number of outputs. | -| `MAX_PREDICATE_LENGTH` | `uint64` | | Maximum length of predicate, in instructions. | -| `MAX_GAS_PER_PREDICATE` | `uint64` | | Maximum gas per predicate. | -| `MAX_PREDICATE_DATA_LENGTH` | `uint64` | | Maximum length of predicate data, in bytes. | -| `MAX_SCRIPT_LENGTH` | `uint64` | | Maximum length of script, in instructions. | -| `MAX_SCRIPT_DATA_LENGTH` | `uint64` | | Maximum length of script data, in bytes. | -| `MAX_MESSAGE_DATA_LENGTH` | `uint16` | | Maximum length of message data, in bytes. | -| `MAX_STORAGE_SLOTS` | `uint16` | `255` | Maximum number of initial storage slots. | -| `MAX_WITNESSES` | `uint64` | `16` | Maximum number of witnesses. | -| `CHAIN_ID` | `uint64` | | A unique per-chain identifier. | +| name | type | value | description | +|-----------------------------|-----------|-----------------|-----------------------------------------------| +| `GAS_PER_BYTE` | `uint64` | | Gas charged per byte of the transaction. | +| `GAS_PRICE_FACTOR` | `uint64` | `1,000,000,000` | Unit factor for gas price. | +| `MAX_GAS_PER_TX` | `uint64` | | Maximum gas per transaction. | +| `MAX_INPUTS` | `uint64` | `8` | Maximum number of inputs. | +| `MAX_OUTPUTS` | `uint64` | `8` | Maximum number of outputs. | +| `MAX_PREDICATE_LENGTH` | `uint64` | | Maximum length of predicate, in instructions. | +| `MAX_GAS_PER_PREDICATE` | `uint64` | | Maximum gas per predicate. | +| `MAX_PREDICATE_DATA_LENGTH` | `uint64` | | Maximum length of predicate data, in bytes. | +| `MAX_SCRIPT_LENGTH` | `uint64` | | Maximum length of script, in instructions. | +| `MAX_SCRIPT_DATA_LENGTH` | `uint64` | | Maximum length of script data, in bytes. | +| `MAX_MESSAGE_DATA_LENGTH` | `uint16` | | Maximum length of message data, in bytes. | +| `MAX_STORAGE_SLOTS` | `uint16` | `255` | Maximum number of initial storage slots. | +| `MAX_WITNESSES` | `uint64` | `16` | Maximum number of witnesses. | +| `CHAIN_ID` | `uint64` | | A unique per-chain identifier. | +| `BASE_ASSET_ID` | `bytes32` | `0x00` | The base asset of the chain. | diff --git a/src/tx-format/policy.md b/src/tx-format/policy.md index b0e66ecc..e2aa22fc 100644 --- a/src/tx-format/policy.md +++ b/src/tx-format/policy.md @@ -1,12 +1,13 @@ # Policy ```c++ -// index using power of 2's for efficient bitmasking +// index using powers of 2 for efficient bitmasking enum PolicyType : uint32 { GasPrice = 1, GasLimit = 2, WitnessLimit = 4, Maturity = 8, + MaxFee = 16, } ``` @@ -51,3 +52,14 @@ Transaction is invalid if: Transaction is invalid if: - `blockheight() < maturity` + +## MaxFee + +| name | type | description | +|-----------|----------|----------------------------------------------| +| `max_fee` | `uint64` | The maximum fee payable by this transaction. | + +Transaction is invalid if: + +- `max_fee > sum_inputs(tx, BASE_ASSET_ID) - sum_outputs(tx, BASE_ASSET_ID)` +- `max_fee < fee_balance(tx, BASE_ASSET_ID)` diff --git a/src/tx-format/transaction.md b/src/tx-format/transaction.md index 88425171..dfe1c46f 100644 --- a/src/tx-format/transaction.md +++ b/src/tx-format/transaction.md @@ -54,21 +54,21 @@ enum ReceiptType : uint8 { } ``` -| name | type | description | -|--------------------|-----------------------------|----------------------------------------| -| `scriptLength` | `uint16` | Script length, in instructions. | -| `scriptDataLength` | `uint16` | Length of script input data, in bytes. | -| `policyTypes` | `uint32` | Bitfield of used policy types. | -| `inputsCount` | `uint8` | Number of inputs. | -| `outputsCount` | `uint8` | Number of outputs. | -| `witnessesCount` | `uint8` | Number of witnesses. | -| `receiptsRoot` | `byte[32]` | Merkle root of receipts. | -| `script` | `byte[]` | Script to execute. | -| `scriptData` | `byte[]` | Script input data (parameters). | -| `policies` | [Policy](./policy.md)`[]` | List of policies, sorted by PolicyType.| -| `inputs` | [Input](./input.md)`[]` | List of inputs. | -| `outputs` | [Output](./output.md)`[]` | List of outputs. | -| `witnesses` | [Witness](./witness.md)`[]` | List of witnesses. | +| name | type | description | +|--------------------|-----------------------------|-----------------------------------------| +| `scriptLength` | `uint16` | Script length, in instructions. | +| `scriptDataLength` | `uint16` | Length of script input data, in bytes. | +| `policyTypes` | `uint32` | Bitfield of used policy types. | +| `inputsCount` | `uint8` | Number of inputs. | +| `outputsCount` | `uint8` | Number of outputs. | +| `witnessesCount` | `uint8` | Number of witnesses. | +| `receiptsRoot` | `byte[32]` | Merkle root of receipts. | +| `script` | `byte[]` | Script to execute. | +| `scriptData` | `byte[]` | Script input data (parameters). | +| `policies` | [Policy](./policy.md)`[]` | List of policies, sorted by PolicyType. | +| `inputs` | [Input](./input.md)`[]` | List of inputs. | +| `outputs` | [Output](./output.md)`[]` | List of outputs. | +| `witnesses` | [Witness](./witness.md)`[]` | List of witnesses. | Given helper `len()` that returns the number of bytes of a field. Given helper `count_ones()` that returns the number of ones in the binary representation of a field. @@ -102,7 +102,7 @@ The receipts root `receiptsRoot` is the root of the [binary Merkle tree](../prot |------------------------|-----------------------------|---------------------------------------------------| | `bytecodeLength` | `uint16` | Contract bytecode length, in instructions. | | `bytecodeWitnessIndex` | `uint8` | Witness index of contract bytecode to create. | -| `policyCount` | `uint8` | Number of policies. | +| `policyTypes` | `uint32` | Bitfield of used policy types. | | `storageSlotsCount` | `uint16` | Number of storage slots to initialize. | | `inputsCount` | `uint8` | Number of inputs. | | `outputsCount` | `uint8` | Number of outputs. | From 6deff2acd55e82791ba1ec99c2c22df4356b8c59 Mon Sep 17 00:00:00 2001 From: Brandon Kite Date: Thu, 19 Oct 2023 23:02:28 -0700 Subject: [PATCH 12/40] merge with master --- src/tx-format/consensus_parameters.md | 35 ++++++++++++++------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/tx-format/consensus_parameters.md b/src/tx-format/consensus_parameters.md index 9d01c5fb..362d692e 100644 --- a/src/tx-format/consensus_parameters.md +++ b/src/tx-format/consensus_parameters.md @@ -1,19 +1,20 @@ # Consensus Parameters -| name | type | description | -|-----------------------------|----------|-----------------------------------------------| -| `GAS_PER_BYTE` | `uint64` | Gas charged per byte of the transaction. | -| `GAS_PRICE_FACTOR` | `uint64` | Unit factor for gas price. | -| `MAX_GAS_PER_TX` | `uint64` | Maximum gas per transaction. | -| `MAX_INPUTS` | `uint64` | Maximum number of inputs. | -| `MAX_OUTPUTS` | `uint64` | Maximum number of outputs. | -| `MAX_PREDICATE_LENGTH` | `uint64` | Maximum length of predicate, in instructions. | -| `MAX_GAS_PER_PREDICATE` | `uint64` | Maximum gas per predicate. | -| `MAX_PREDICATE_DATA_LENGTH` | `uint64` | Maximum length of predicate data, in bytes. | -| `MAX_SCRIPT_LENGTH` | `uint64` | Maximum length of script, in instructions. | -| `MAX_SCRIPT_DATA_LENGTH` | `uint64` | Maximum length of script data, in bytes. | -| `MAX_MESSAGE_DATA_LENGTH` | `uint16` | Maximum length of message data, in bytes. | -| `MAX_STORAGE_SLOTS` | `uint16` | Maximum number of initial storage slots. | -| `MAX_TRANSACTION_SIZE` | `uint64` | Maximum size of a transaction, in bytes. | -| `MAX_WITNESSES` | `uint64` | Maximum number of witnesses. | -| `CHAIN_ID` | `uint64` | A unique per-chain identifier. | +| name | type | description | +|-----------------------------|-----------|-----------------------------------------------| +| `GAS_PER_BYTE` | `uint64` | Gas charged per byte of the transaction. | +| `GAS_PRICE_FACTOR` | `uint64` | Unit factor for gas price. | +| `MAX_GAS_PER_TX` | `uint64` | Maximum gas per transaction. | +| `MAX_INPUTS` | `uint64` | Maximum number of inputs. | +| `MAX_OUTPUTS` | `uint64` | Maximum number of outputs. | +| `MAX_PREDICATE_LENGTH` | `uint64` | Maximum length of predicate, in instructions. | +| `MAX_GAS_PER_PREDICATE` | `uint64` | Maximum gas per predicate. | +| `MAX_PREDICATE_DATA_LENGTH` | `uint64` | Maximum length of predicate data, in bytes. | +| `MAX_SCRIPT_LENGTH` | `uint64` | Maximum length of script, in instructions. | +| `MAX_SCRIPT_DATA_LENGTH` | `uint64` | Maximum length of script data, in bytes. | +| `MAX_MESSAGE_DATA_LENGTH` | `uint16` | Maximum length of message data, in bytes. | +| `MAX_STORAGE_SLOTS` | `uint16` | Maximum number of initial storage slots. | +| `MAX_TRANSACTION_SIZE` | `uint64` | Maximum size of a transaction, in bytes. | +| `MAX_WITNESSES` | `uint64` | Maximum number of witnesses. | +| `CHAIN_ID` | `uint64` | A unique per-chain identifier. | +| `BASE_ASSET_ID` | `bytes32` | The base asset of the chain. | From 6cd617a46dfcb489630af8f26b7c03d312642e86 Mon Sep 17 00:00:00 2001 From: Brandon Kite Date: Mon, 23 Oct 2023 12:51:05 -0700 Subject: [PATCH 13/40] Update src/fuel-vm/instruction-set.md Co-authored-by: Green Baneling --- 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 38562a28..4d28832e 100644 --- a/src/fuel-vm/instruction-set.md +++ b/src/fuel-vm/instruction-set.md @@ -2513,6 +2513,7 @@ Get [fields from the transaction](../tx-format/transaction.md). | `GTF_POLICY_GAS_LIMIT` | `0x503` | `tx.policies[count_ones(0x11 & tx.policyTypes) - 1].gasLimit` | | `GTF_POLICY_WITNESS_LIMIT` | `0x504` | `tx.policies[count_ones(0x111 & tx.policyTypes) - 1].witnessLimit` | | `GTF_POLICY_MATURITY` | `0x505` | `tx.policies[count_ones(0x1111 & tx.policyTypes) - 1].maturity` | +| `GTF_POLICY_MAX_FEE` | `0x506` | `tx.policies[count_ones(0x11111 & tx.policyTypes) - 1].maxFee` | Panic if: From ca66b7ec43101b695f5d37203fef84ee2733df22 Mon Sep 17 00:00:00 2001 From: Voxelot Date: Wed, 25 Oct 2023 15:15:20 -0700 Subject: [PATCH 14/40] make gasLimit only applicable to script txs and remove it as a policy type. other misc feedback. --- src/fuel-vm/instruction-set.md | 571 ++++++++++++++++----------------- src/tx-format/policy.md | 29 +- src/tx-format/transaction.md | 35 +- 3 files changed, 312 insertions(+), 323 deletions(-) diff --git a/src/fuel-vm/instruction-set.md b/src/fuel-vm/instruction-set.md index 4d28832e..01202b3d 100644 --- a/src/fuel-vm/instruction-set.md +++ b/src/fuel-vm/instruction-set.md @@ -510,13 +510,13 @@ Panic if: ### MLDV: Fused multiply-divide -| | | -|-------------|---------------------------------------------------------------------------------------| -| Description | Multiplies two registers with arbitrary precision, then divides by a third register. | -| Operation | `a = (b * c) / d;` | -| Syntax | `mldv $rA, $rB, $rC, $rD` | -| Encoding | `0x00 rA rB rC rD` | -| Notes | Division by zero is treated as division by `1 << 64` instead. | +| | | +|-------------|--------------------------------------------------------------------------------------| +| Description | Multiplies two registers with arbitrary precision, then divides by a third register. | +| Operation | `a = (b * c) / d;` | +| Syntax | `mldv $rA, $rB, $rC, $rD` | +| Encoding | `0x00 rA rB rC rD` | +| Notes | Division by zero is treated as division by `1 << 64` instead. | If the divisor (`$rD`) is zero, then instead the value is divided by `1 << 64`. This returns the higher half of the 128-bit multiplication result. This operation never overflows. @@ -688,34 +688,34 @@ Panic if: ### WDCM: 128-bit integer comparison -| | | -|-------------|---------------------------------------------------------------------------------------| -| Description | Compare or examine two 128-bit integers using selected mode | -| Operation | `b = mem[$rB,16];`
`c = indirect?mem[$rC,16]:$rC;`
`$rA = cmp_op(b,c);` | -| Syntax | `wdcm $rA, $rB, $rC, imm` | -| Encoding | `0x00 rA rB rC i` | -| Notes | | +| | | +|-------------|-------------------------------------------------------------------------------| +| Description | Compare or examine two 128-bit integers using selected mode | +| Operation | `b = mem[$rB,16];`
`c = indirect?mem[$rC,16]:$rC;`
`$rA = cmp_op(b,c);` | +| Syntax | `wdcm $rA, $rB, $rC, imm` | +| Encoding | `0x00 rA rB rC i` | +| Notes | | The six-bit immediate value is used to select operating mode, as follows: Bits | Short name | Description ----------|------------|------------- +---------|------------|------------------------------------- `...XXX` | `mode` | Compare mode selection `.XX...` | `reserved` | Reserved and must be zero `X.....` | `indirect` | Is rhs operand ($rC) indirect or not Then the actual operation that's performed: -`mode`| Name | Description -------|------|------------ -0 | eq | Equality (`==`) -1 | ne | Inequality (`!=`) -2 | lt | Less than (`<`) -3 | gt | Greater than (`>`) -4 | lte | Less than or equals (`<=`) -5 | gte | Greater than or equals (`>=`) -6 | lzc | Leading zero count the lhs argument (`lzcnt`). Discards rhs. -7 | - | Reserved and must not be used +`mode` | Name | Description +-------|------|------------------------------------------------------------- +0 | eq | Equality (`==`) +1 | ne | Inequality (`!=`) +2 | lt | Less than (`<`) +3 | gt | Greater than (`>`) +4 | lte | Less than or equals (`<=`) +5 | gte | Greater than or equals (`>=`) +6 | lzc | Leading zero count the lhs argument (`lzcnt`). Discards rhs. +7 | - | Reserved and must not be used The leading zero count can be used to compute rounded-down log2 of a number using the following formula `TOTAL_BITS - 1 - lzc(n)`. Note that `log2(0)` is undefined, and will lead to integer overflow with this method. @@ -730,13 +730,13 @@ Panic if: ### WQCM: 256-bit integer comparison -| | | -|-------------|---------------------------------------------------------------------------------------| -| Description | Compare or examine two 256-bit integers using selected mode | -| Operation | `b = mem[$rB,32];`
`c = indirect?mem[$rC,32]:$rC;`
`$rA = cmp_op(b,c);` | -| Syntax | `wqcm $rA, $rB, $rC, imm` | -| Encoding | `0x00 rA rB rC i` | -| Notes | | +| | | +|-------------|-------------------------------------------------------------------------------| +| Description | Compare or examine two 256-bit integers using selected mode | +| Operation | `b = mem[$rB,32];`
`c = indirect?mem[$rC,32]:$rC;`
`$rA = cmp_op(b,c);` | +| Syntax | `wqcm $rA, $rB, $rC, imm` | +| Encoding | `0x00 rA rB rC i` | +| Notes | | The immediate value is interpreted identically to `WDCM`. @@ -751,18 +751,18 @@ Panic if: ### WDOP: Misc 128-bit integer operations -| | | -|-------------|---------------------------------------------------------------------------------------| -| Description | Perform an ALU operation on two 128-bit integers | -| Operation | `b = mem[$rB,16];`
`c = indirect?mem[$rC,16]:$rC;`
`mem[$rA,16] = op(b,c);` | -| Syntax | `wdop $rA, $rB, $rC, imm` | -| Encoding | `0x00 rA rB rC i` | -| Notes | | +| | | +|-------------|-----------------------------------------------------------------------------------| +| Description | Perform an ALU operation on two 128-bit integers | +| Operation | `b = mem[$rB,16];`
`c = indirect?mem[$rC,16]:$rC;`
`mem[$rA,16] = op(b,c);` | +| Syntax | `wdop $rA, $rB, $rC, imm` | +| Encoding | `0x00 rA rB rC i` | +| Notes | | The six-bit immediate value is used to select operating mode, as follows: Bits | Short name | Description ----------|------------|------------- +---------|------------|------------------------------------- `...XXX` | `op` | Operation selection, see below `.XX...` | `reserved` | Reserved and must be zero `X.....` | `indirect` | Is rhs operand ($rC) indirect or not @@ -770,7 +770,7 @@ Bits | Short name | Description Then the actual operation that's performed: `op` | Name | Description ------|------|------------ +-----|------|--------------------------- 0 | add | Add 1 | sub | Subtract 2 | not | Invert bits (discards rhs) @@ -791,13 +791,13 @@ Panic if: ### WQOP: Misc 256-bit integer operations -| | | -|-------------|---------------------------------------------------------------------------------------| -| Description | Perform an ALU operation on two 256-bit integers | -| Operation | `b = mem[$rB,32];`
`c = indirect?mem[$rC,32]:$rC;`
`mem[$rA,32] = op(b,c);` | -| Syntax | `wqop $rA, $rB, $rC, imm` | -| Encoding | `0x00 rA rB rC i` | -| Notes | | +| | | +|-------------|-----------------------------------------------------------------------------------| +| Description | Perform an ALU operation on two 256-bit integers | +| Operation | `b = mem[$rB,32];`
`c = indirect?mem[$rC,32]:$rC;`
`mem[$rA,32] = op(b,c);` | +| Syntax | `wqop $rA, $rB, $rC, imm` | +| Encoding | `0x00 rA rB rC i` | +| Notes | | The immediate value is interpreted identically to `WDOP`. @@ -812,21 +812,21 @@ Panic if: ### WDML: Multiply 128-bit integers -| | | -|-------------|---------------------------------------------------------------------------------------| -| Description | Perform integer multiplication operation on two 128-bit integers. | -| Operation | `b=indirect0?mem[$rB,16]:$rB;`
`c=indirect1?mem[$rC,16]:$rC;`
`mem[$rA,16]=b*c;`| -| Syntax | `wdml $rA, $rB, $rC, imm` | -| Encoding | `0x00 rA rB rC i` | -| Notes | | +| | | +|-------------|----------------------------------------------------------------------------------------| +| Description | Perform integer multiplication operation on two 128-bit integers. | +| Operation | `b=indirect0?mem[$rB,16]:$rB;`
`c=indirect1?mem[$rC,16]:$rC;`
`mem[$rA,16]=b*c;` | +| Syntax | `wdml $rA, $rB, $rC, imm` | +| Encoding | `0x00 rA rB rC i` | +| Notes | | The six-bit immediate value is used to select operating mode, as follows: -Bits | Short name | Description ----------|------------|------------- -`..XXXX` | `reserved` | Reserved and must be zero -`.X....` | `indirect0`| Is lhs operand ($rB) indirect or not -`X.....` | `indirect1`| Is rhs operand ($rC) indirect or not +Bits | Short name | Description +---------|-------------|------------------------------------- +`..XXXX` | `reserved` | Reserved and must be zero +`.X....` | `indirect0` | Is lhs operand ($rB) indirect or not +`X.....` | `indirect1` | Is rhs operand ($rC) indirect or not `$of` is set to `1` in case of overflow, and cleared otherwise. @@ -841,13 +841,13 @@ Panic if: ### WQML: Multiply 256-bit integers -| | | -|-------------|---------------------------------------------------------------------------------------| -| Description | Perform integer multiplication operation on two 256-bit integers. | -| Operation | `b=indirect0?mem[$rB,32]:$rB;`
`c=indirect1?mem[$rC,32]:$rC;`
`mem[$rA,32]=b*c;`| -| Syntax | `wqml $rA, $rB, $rC, imm` | -| Encoding | `0x00 rA rB rC i` | -| Notes | | +| | | +|-------------|----------------------------------------------------------------------------------------| +| Description | Perform integer multiplication operation on two 256-bit integers. | +| Operation | `b=indirect0?mem[$rB,32]:$rB;`
`c=indirect1?mem[$rC,32]:$rC;`
`mem[$rA,32]=b*c;` | +| Syntax | `wqml $rA, $rB, $rC, imm` | +| Encoding | `0x00 rA rB rC i` | +| Notes | | The immediate value is interpreted identically to `WDML`. @@ -864,18 +864,18 @@ Panic if: ### WDDV: 128-bit integer division -| | | -|-------------|---------------------------------------------------------------------------------------| -| Description | Divide a 128-bit integer by another. | -| Operation | `b = mem[$rB,16];`
`c = indirect?mem[$rC,16]:$rC;`
`mem[$rA,16] = b / c;` | -| Syntax | `wddv $rA, $rB, $rC, imm` | -| Encoding | `0x00 rA rB rC i` | -| Notes | | +| | | +|-------------|---------------------------------------------------------------------------------| +| Description | Divide a 128-bit integer by another. | +| Operation | `b = mem[$rB,16];`
`c = indirect?mem[$rC,16]:$rC;`
`mem[$rA,16] = b / c;` | +| Syntax | `wddv $rA, $rB, $rC, imm` | +| Encoding | `0x00 rA rB rC i` | +| Notes | | The six-bit immediate value is used to select operating mode, as follows: Bits | Short name | Description ----------|------------|------------- +---------|------------|------------------------------------- `.XXXXX` | `reserved` | Reserved and must be zero `X.....` | `indirect` | Is rhs operand ($rC) indirect or not @@ -892,13 +892,13 @@ Panic if: ### WQDV: 256-bit integer division -| | | -|-------------|---------------------------------------------------------------------------------------| -| Description | Divide a 256-bit integer by another. | -| Operation | `b = mem[$rB,32];`
`c = indirect?mem[$rC,32]:$rC;`
`mem[$rA,32] = b / c;` | -| Syntax | `wqdv $rA, $rB, $rC, imm` | -| Encoding | `0x00 rA rB rC i` | -| Notes | | +| | | +|-------------|---------------------------------------------------------------------------------| +| Description | Divide a 256-bit integer by another. | +| Operation | `b = mem[$rB,32];`
`c = indirect?mem[$rC,32]:$rC;`
`mem[$rA,32] = b / c;` | +| Syntax | `wqdv $rA, $rB, $rC, imm` | +| Encoding | `0x00 rA rB rC i` | +| Notes | | The immediate value is interpreted identically to `WDDV`. @@ -915,13 +915,13 @@ Panic if: ### WDMD: 128-bit integer fused multiply-divide -| | | -|-------------|---------------------------------------------------------------------------------------| -| Description | Combined multiply-divide of 128-bit integers with arbitrary precision. | -| Operation | `b=mem[$rB,16];`
`c=mem[$rC,16];`
`d=mem[$rD,16];`
`mem[$rA,16]=(b * c) / d;`| -| Syntax | `wddv $rA, $rB, $rC, $rD` | -| Encoding | `0x00 rA rB rC rD` | -| Notes | Division by zero is treated as division by `1 << 128` instead. | +| | | +|-------------|----------------------------------------------------------------------------------------| +| Description | Combined multiply-divide of 128-bit integers with arbitrary precision. | +| Operation | `b=mem[$rB,16];`
`c=mem[$rC,16];`
`d=mem[$rD,16];`
`mem[$rA,16]=(b * c) / d;` | +| Syntax | `wddv $rA, $rB, $rC, $rD` | +| Encoding | `0x00 rA rB rC rD` | +| Notes | Division by zero is treated as division by `1 << 128` instead. | If the divisor `MEM[$rA, 16]` is zero, then instead the value is divided by `1 << 128`. This returns the higher half of the 256-bit multiplication result. @@ -938,13 +938,13 @@ Panic if: ### WQMD: 256-bit integer fused multiply-divide -| | | -|-------------|---------------------------------------------------------------------------------------| -| Description | Combined multiply-divide of 256-bit integers with arbitrary precision. | -| Operation | `b=mem[$rB,32];`
`c=mem[$rC,32];`
`d=mem[$rD,32];`
`mem[$rA,32]=(b * c) / d;`| -| Syntax | `wqdv $rA, $rB, $rC, $rD` | -| Encoding | `0x00 rA rB rC rD` | -| Notes | Division by zero is treated as division by `1 << 256` instead. | +| | | +|-------------|----------------------------------------------------------------------------------------| +| Description | Combined multiply-divide of 256-bit integers with arbitrary precision. | +| Operation | `b=mem[$rB,32];`
`c=mem[$rC,32];`
`d=mem[$rD,32];`
`mem[$rA,32]=(b * c) / d;` | +| Syntax | `wqdv $rA, $rB, $rC, $rD` | +| Encoding | `0x00 rA rB rC rD` | +| Notes | Division by zero is treated as division by `1 << 256` instead. | If the divisor `MEM[$rA, 32]` is zero, then instead the value is divided by `1 << 256`. This returns the higher half of the 512-bit multiplication result. @@ -961,13 +961,13 @@ Panic if: ### WDAM: Modular 128-bit integer addition -| | | -|-------------|---------------------------------------------------------------------------------------| -| Description | Add two 128-bit integers and compute modulo remainder with arbitrary precision. | -| Operation | `b=mem[$rB,16];`
`c=mem[$rC,16];`
`d=mem[$rD,16];`
`mem[$rA,16] = (b+c)%d;` | -| Syntax | `wdam $rA, $rB, $rC, $rD` | -| Encoding | `0x00 rA rB rC rD` | -| Notes | | +| | | +|-------------|--------------------------------------------------------------------------------------| +| Description | Add two 128-bit integers and compute modulo remainder with arbitrary precision. | +| Operation | `b=mem[$rB,16];`
`c=mem[$rC,16];`
`d=mem[$rD,16];`
`mem[$rA,16] = (b+c)%d;` | +| Syntax | `wdam $rA, $rB, $rC, $rD` | +| Encoding | `0x00 rA rB rC rD` | +| Notes | | `$of` is cleared. @@ -982,13 +982,13 @@ Panic if: ### WQAM: Modular 256-bit integer addition -| | | -|-------------|---------------------------------------------------------------------------------------| -| Description | Add two 256-bit integers and compute modulo remainder with arbitrary precision. | -| Operation | `b=mem[$rB,32];`
`c=mem[$rC,32];`
`d=mem[$rD,32];`
`mem[$rA,32] = (b+c)%d;` | -| Syntax | `wdam $rA, $rB, $rC, $rD` | -| Encoding | `0x00 rA rB rC rD` | -| Notes | | +| | | +|-------------|--------------------------------------------------------------------------------------| +| Description | Add two 256-bit integers and compute modulo remainder with arbitrary precision. | +| Operation | `b=mem[$rB,32];`
`c=mem[$rC,32];`
`d=mem[$rD,32];`
`mem[$rA,32] = (b+c)%d;` | +| Syntax | `wdam $rA, $rB, $rC, $rD` | +| Encoding | `0x00 rA rB rC rD` | +| Notes | | `$of` is cleared. @@ -1003,13 +1003,13 @@ Panic if: ### WDMM: Modular 128-bit integer multiplication -| | | -|-------------|---------------------------------------------------------------------------------------| -| Description | Multiply two 128-bit integers and compute modulo remainder with arbitrary precision. | -| Operation | `b=mem[$rB,16];`
`c=mem[$rC,16];`
`d=mem[$rD,16];`
`mem[$rA,16] = (b*c)%d;` | -| Syntax | `wdmm $rA, $rB, $rC, $rD` | -| Encoding | `0x00 rA rB rC rD` | -| Notes | | +| | | +|-------------|--------------------------------------------------------------------------------------| +| Description | Multiply two 128-bit integers and compute modulo remainder with arbitrary precision. | +| Operation | `b=mem[$rB,16];`
`c=mem[$rC,16];`
`d=mem[$rD,16];`
`mem[$rA,16] = (b*c)%d;` | +| Syntax | `wdmm $rA, $rB, $rC, $rD` | +| Encoding | `0x00 rA rB rC rD` | +| Notes | | `$of` is cleared. @@ -1024,13 +1024,13 @@ Panic if: ### WQMM: Modular 256-bit integer multiplication -| | | -|-------------|---------------------------------------------------------------------------------------| -| Description | Multiply two 256-bit integers and compute modulo remainder with arbitrary precision. | -| Operation | `b=mem[$rB,32];`
`c=mem[$rC,32];`
`d=mem[$rD,32];`
`mem[$rA,32] = (b*c)%d;` | -| Syntax | `wqmm $rA, $rB, $rC, $rD` | -| Encoding | `0x00 rA rB rC rD` | -| Notes | | +| | | +|-------------|--------------------------------------------------------------------------------------| +| Description | Multiply two 256-bit integers and compute modulo remainder with arbitrary precision. | +| Operation | `b=mem[$rB,32];`
`c=mem[$rC,32];`
`d=mem[$rD,32];`
`mem[$rA,32] = (b*c)%d;` | +| Syntax | `wqmm $rA, $rB, $rC, $rD` | +| Encoding | `0x00 rA rB rC rD` | +| Notes | | `$of` is cleared. @@ -1149,13 +1149,13 @@ Panic if: ### JMPB: Jump relative backwards -| | | -|-------------|----------------------------------------------------------------------------------------| -| Description | Jump `$rA + imm` instructions backwards. | -| Operation | ```$pc -= ($rA + imm + 1) * 4;``` | -| Syntax | `jmpb $rA imm` | -| Encoding | `0x00 rA i i i` | -| Notes | | +| | | +|-------------|------------------------------------------| +| Description | Jump `$rA + imm` instructions backwards. | +| Operation | ```$pc -= ($rA + imm + 1) * 4;``` | +| Syntax | `jmpb $rA imm` | +| Encoding | `0x00 rA i i i` | +| Notes | | Panic if: @@ -1163,13 +1163,13 @@ Panic if: ### JMPF: Jump relative forwards -| | | -|-------------|----------------------------------------------------------------------------------------| -| Description | Jump `$rA + imm` instructions forwards | -| Operation | ```$pc += ($rA + imm + 1) * 4;``` | -| Syntax | `jmpf $rA imm` | -| Encoding | `0x00 rA i i i` | -| Notes | | +| | | +|-------------|----------------------------------------| +| Description | Jump `$rA + imm` instructions forwards | +| Operation | ```$pc += ($rA + imm + 1) * 4;``` | +| Syntax | `jmpf $rA imm` | +| Encoding | `0x00 rA i i i` | +| Notes | | Panic if: @@ -1177,13 +1177,13 @@ Panic if: ### JNZB: Jump if not zero relative backwards -| | | -|-------------|----------------------------------------------------------------------------------------| -| Description | Jump `$rB + imm` instructions backwards if `$rA != $zero`. | -| Operation | `if $rA != $zero:`
`$pc -= ($rB + imm + 1) * 4;`
`else:`
`$pc += 4;` | -| Syntax | `jnzb $rA $rB imm` | -| Encoding | `0x00 rA rB i i` | -| Notes | | +| | | +|-------------|-------------------------------------------------------------------------------| +| Description | Jump `$rB + imm` instructions backwards if `$rA != $zero`. | +| Operation | `if $rA != $zero:`
`$pc -= ($rB + imm + 1) * 4;`
`else:`
`$pc += 4;` | +| Syntax | `jnzb $rA $rB imm` | +| Encoding | `0x00 rA rB i i` | +| Notes | | Panic if: @@ -1191,13 +1191,13 @@ Panic if: ### JNZF: Jump if not zero relative forwards -| | | -|-------------|----------------------------------------------------------------------------------------| -| Description | Jump `$rB + imm` instructions forwards if `$rA != $zero`. | -| Operation | `if $rA != $zero:`
`$pc += ($rB + imm + 1) * 4;`
`else:`
`$pc += 4;` | -| Syntax | `jnzf $rA $rB imm` | -| Encoding | `0x00 rA rB i i` | -| Notes | | +| | | +|-------------|-------------------------------------------------------------------------------| +| Description | Jump `$rB + imm` instructions forwards if `$rA != $zero`. | +| Operation | `if $rA != $zero:`
`$pc += ($rB + imm + 1) * 4;`
`else:`
`$pc += 4;` | +| Syntax | `jnzf $rA $rB imm` | +| Encoding | `0x00 rA rB i i` | +| Notes | | Panic if: @@ -1205,13 +1205,13 @@ Panic if: ### JNEB: Jump if not equal relative backwards -| | | -|-------------|----------------------------------------------------------------------------------------| -| Description | Jump `$rC + imm` instructions backwards if `$rA != $rB`. | -| Operation | `if $rA != $rB:`
`$pc -= ($rC + imm + 1) * 4;`
`else:`
`$pc += 4;` | -| Syntax | `jneb $rA $rB $rC imm` | -| Encoding | `0x00 rA rB rC i` | -| Notes | | +| | | +|-------------|-----------------------------------------------------------------------------| +| Description | Jump `$rC + imm` instructions backwards if `$rA != $rB`. | +| Operation | `if $rA != $rB:`
`$pc -= ($rC + imm + 1) * 4;`
`else:`
`$pc += 4;` | +| Syntax | `jneb $rA $rB $rC imm` | +| Encoding | `0x00 rA rB rC i` | +| Notes | | Panic if: @@ -1219,13 +1219,13 @@ Panic if: ### JNEF: Jump if not equal relative forwards -| | | -|-------------|----------------------------------------------------------------------------------------| -| Description | Jump `$rC + imm` instructions forwards if `$rA != $rB`. | -| Operation | `if $rA != $rB:`
`$pc += ($rC + imm + 1) * 4;`
`else:`
`$pc += 4;` | -| Syntax | `jnef $rA $rB $rC imm` | -| Encoding | `0x00 rA rB rC i` | -| Notes | | +| | | +|-------------|-----------------------------------------------------------------------------| +| Description | Jump `$rC + imm` instructions forwards if `$rA != $rB`. | +| Operation | `if $rA != $rB:`
`$pc += ($rC + imm + 1) * 4;`
`else:`
`$pc += 4;` | +| Syntax | `jnef $rA $rB $rC imm` | +| Encoding | `0x00 rA rB rC i` | +| Notes | | Panic if: @@ -1477,13 +1477,13 @@ Panic if: ### PSHH: Push a set of high registers to stack -| | | -|-------------|----------------------------------------------------------------------------------------| -| Description | Push a set of registers from range 40..64 to the stack in order. | -| Operation | `tmp=$sp;`
`$sp+=popcnt(imm)*8;`
`MEM[tmp,$sp]=registers[40..64].mask(imm)` | -| Syntax | `pshh imm` | -| Encoding | `0x00 i i i i` | -| Notes | The immediate value is used as a bitmask for selecting the registers. | +| | | +|-------------|-----------------------------------------------------------------------------------| +| Description | Push a set of registers from range 40..64 to the stack in order. | +| Operation | `tmp=$sp;`
`$sp+=popcnt(imm)*8;`
`MEM[tmp,$sp]=registers[40..64].mask(imm)` | +| Syntax | `pshh imm` | +| Encoding | `0x00 i i i i` | +| Notes | The immediate value is used as a bitmask for selecting the registers. | Panic if: @@ -1492,13 +1492,13 @@ Panic if: ### PSHL: Push a set of low registers to stack -| | | -|-------------|----------------------------------------------------------------------------------------| -| Description | Push a set of registers from range 16..40 to the stack in order. | -| Operation | `tmp=$sp;`
`$sp+=popcnt(imm)*8;`
`MEM[tmp,$sp]=registers[16..40].mask(imm)` | -| Syntax | `pshl imm` | -| Encoding | `0x00 i i i i` | -| Notes | The immediate value is used as a bitmask for selecting the registers. | +| | | +|-------------|-----------------------------------------------------------------------------------| +| Description | Push a set of registers from range 16..40 to the stack in order. | +| Operation | `tmp=$sp;`
`$sp+=popcnt(imm)*8;`
`MEM[tmp,$sp]=registers[16..40].mask(imm)` | +| Syntax | `pshl imm` | +| Encoding | `0x00 i i i i` | +| Notes | The immediate value is used as a bitmask for selecting the registers. | Panic if: @@ -1507,13 +1507,13 @@ Panic if: ### POPH: Pop a set of high registers from stack -| | | -|-------------|----------------------------------------------------------------------------------------| -| Description | Pop to a set of registers from range 40..64 from the stack. | -| Operation |`tmp=$sp-popcnt(imm)*8;`
`registers[40..64].mask(imm)=MEM[tmp,$sp]`
`$sp-=tmp;` | -| Syntax | `poph imm` | -| Encoding | `0x00 i i i i` | -| Notes | The immediate value is used as a bitmask for selecting the registers. | +| | | +|-------------|---------------------------------------------------------------------------------------| +| Description | Pop to a set of registers from range 40..64 from the stack. | +| Operation | `tmp=$sp-popcnt(imm)*8;`
`registers[40..64].mask(imm)=MEM[tmp,$sp]`
`$sp-=tmp;` | +| Syntax | `poph imm` | +| Encoding | `0x00 i i i i` | +| Notes | The immediate value is used as a bitmask for selecting the registers. | Panic if: @@ -1522,13 +1522,13 @@ Panic if: ### POPL: Pop a set of low registers from stack -| | | -|-------------|----------------------------------------------------------------------------------------| -| Description | Pop to a set of registers from range 16..40 from the stack. | -| Operation |`tmp=$sp-popcnt(imm)*8;`
`registers[16..40].mask(imm)=MEM[tmp,$sp]`
`$sp-=tmp;` | -| Syntax | `poph imm` | -| Encoding | `0x00 i i i i` | -| Notes | The immediate value is used as a bitmask for selecting the registers. | +| | | +|-------------|---------------------------------------------------------------------------------------| +| Description | Pop to a set of registers from range 16..40 from the stack. | +| Operation | `tmp=$sp-popcnt(imm)*8;`
`registers[16..40].mask(imm)=MEM[tmp,$sp]`
`$sp-=tmp;` | +| Syntax | `poph imm` | +| Encoding | `0x00 i i i i` | +| Notes | The immediate value is used as a bitmask for selecting the registers. | Panic if: @@ -1649,14 +1649,14 @@ This modifies the `balanceRoot` field of the appropriate output. Append a receipt to the list of receipts, modifying tx.receiptsRoot: -| name | type | description | -|----------|---------------|---------------------------------------------------------------------------| -| `type` | `ReceiptType` | `ReceiptType.Burn` | -| `sub_id` | `byte[32]` | Asset sub identifier `MEM[$rB, $rB + 32]`. | -| `contract_id` | `byte[32]` | Contract ID of the current context. | -| `val` | `uint64` | Value of register `$rA`. | -| `pc` | `uint64` | Value of register `$pc`. | -| `is` | `uint64` | Value of register `$is`. | +| name | type | description | +|---------------|---------------|--------------------------------------------| +| `type` | `ReceiptType` | `ReceiptType.Burn` | +| `sub_id` | `byte[32]` | Asset sub identifier `MEM[$rB, $rB + 32]`. | +| `contract_id` | `byte[32]` | Contract ID of the current context. | +| `val` | `uint64` | Value of register `$rA`. | +| `pc` | `uint64` | Value of register `$pc`. | +| `is` | `uint64` | Value of register `$is`. | ### CALL: Call contract @@ -1699,7 +1699,7 @@ Append a receipt to the list of receipts, modifying `tx.receiptsRoot`: | `to` | `byte[32]` | Contract ID of called contract. | | `amount` | `uint64` | Amount of coins to forward, i.e. `$rB`. | | `asset_id` | `byte[32]` | Asset ID of coins to forward, i.e. `MEM[$rC, 32]`. | -| `gas` | `uint64` | Gas to forward, i.e. `min($rD, $cgas)`. | +| `gas` | `uint64` | Gas to forward, i.e. `min($rD, $cgas)`. | | `param1` | `uint64` | First parameter. | | `param2` | `uint64` | Second parameter. | | `pc` | `uint64` | Value of register `$pc`. | @@ -1895,14 +1895,14 @@ This modifies the `balanceRoot` field of the appropriate output. Append a receipt to the list of receipts, modifying tx.receiptsRoot: -| name | type | description | -|----------|---------------|---------------------------------------------------------------------------| -| `type` | `ReceiptType` | `ReceiptType.Mint` | -| `sub_id` | `byte[32]` | Asset sub identifier `MEM[$rB, $rB + 32]`. | -| `contract_id` | `byte[32]` | Contract ID of the current context. | -| `val` | `uint64` | Value of register `$rA`. | -| `pc` | `uint64` | Value of register `$pc`. | -| `is` | `uint64` | Value of register `$is`. | +| name | type | description | +|---------------|---------------|--------------------------------------------| +| `type` | `ReceiptType` | `ReceiptType.Mint` | +| `sub_id` | `byte[32]` | Asset sub identifier `MEM[$rB, $rB + 32]`. | +| `contract_id` | `byte[32]` | Contract ID of the current context. | +| `val` | `uint64` | Value of register `$rA`. | +| `pc` | `uint64` | Value of register `$pc`. | +| `is` | `uint64` | Value of register `$is`. | ### RETD: Return from context with data @@ -2439,81 +2439,80 @@ Set `$rA` to the index of the currently-verifying predicate. Get [fields from the transaction](../tx-format/transaction.md). -| name | `imm` | set `$rA` to | -|-------------------------------------------|---------|--------------------------------------------------------------------| -| `GTF_TYPE` | `0x001` | `tx.type` | -| `GTF_SCRIPT_SCRIPT_LENGTH` | `0x002` | `tx.scriptLength` | -| `GTF_SCRIPT_SCRIPT_DATA_LENGTH` | `0x003` | `tx.scriptDataLength` | -| `GTF_SCRIPT_INPUTS_COUNT` | `0x004` | `tx.inputsCount` | -| `GTF_SCRIPT_OUTPUTS_COUNT` | `0x005` | `tx.outputsCount` | -| `GTF_SCRIPT_WITNESSES_COUNT` | `0x006` | `tx.witnessesCount` | -| `GTF_SCRIPT_RECEIPTS_ROOT` | `0x007` | Memory address of `tx.receiptsRoot` | -| `GTF_SCRIPT_SCRIPT` | `0x008` | Memory address of `tx.script` | -| `GTF_SCRIPT_SCRIPT_DATA` | `0x009` | Memory address of `tx.scriptData` | -| `GTF_SCRIPT_INPUT_AT_INDEX` | `0x00A` | Memory address of `tx.inputs[$rB]` | -| `GTF_SCRIPT_OUTPUT_AT_INDEX` | `0x00B` | Memory address of `t.outputs[$rB]` | -| `GTF_SCRIPT_WITNESS_AT_INDEX` | `0x00C` | Memory address of `tx.witnesses[$rB]` | -| `GTF_CREATE_BYTECODE_LENGTH` | `0x100` | `tx.bytecodeLength` | -| `GTF_CREATE_BYTECODE_WITNESS_INDEX` | `0x101` | `tx.bytecodeWitnessIndex` | -| `GTF_CREATE_STORAGE_SLOTS_COUNT` | `0x102` | `tx.storageSlotsCount` | -| `GTF_CREATE_INPUTS_COUNT` | `0x103` | `tx.inputsCount` | -| `GTF_CREATE_OUTPUTS_COUNT` | `0x104` | `tx.outputsCount` | -| `GTF_CREATE_WITNESSES_COUNT` | `0x105` | `tx.witnessesCount` | -| `GTF_CREATE_SALT` | `0x106` | Memory address of `tx.salt` | -| `GTF_CREATE_STORAGE_SLOT_AT_INDEX` | `0x107` | Memory address of `tx.storageSlots[$rB]` | -| `GTF_CREATE_INPUT_AT_INDEX` | `0x108` | Memory address of `tx.inputs[$rB]` | -| `GTF_CREATE_OUTPUT_AT_INDEX` | `0x109` | Memory address of `t.outputs[$rB]` | -| `GTF_CREATE_WITNESS_AT_INDEX` | `0x10A` | Memory address of `tx.witnesses[$rB]` | -| `GTF_INPUT_TYPE` | `0x200` | `tx.inputs[$rB].type` | -| `GTF_INPUT_COIN_TX_ID` | `0x201` | Memory address of `tx.inputs[$rB].txID` | -| `GTF_INPUT_COIN_OUTPUT_INDEX` | `0x202` | `tx.inputs[$rB].outputIndex` | -| `GTF_INPUT_COIN_OWNER` | `0x203` | Memory address of `tx.inputs[$rB].owner` | -| `GTF_INPUT_COIN_AMOUNT` | `0x204` | `tx.inputs[$rB].amount` | -| `GTF_INPUT_COIN_ASSET_ID` | `0x205` | Memory address of `tx.inputs[$rB].asset_id` | -| `GTF_INPUT_COIN_TX_POINTER` | `0x206` | Memory address of `tx.inputs[$rB].txPointer` | -| `GTF_INPUT_COIN_WITNESS_INDEX` | `0x207` | `tx.inputs[$rB].witnessIndex` | -| `GTF_INPUT_COIN_MATURITY` | `0x208` | `tx.inputs[$rB].maturity` | -| `GTF_INPUT_COIN_PREDICATE_LENGTH` | `0x209` | `tx.inputs[$rB].predicateLength` | -| `GTF_INPUT_COIN_PREDICATE_DATA_LENGTH` | `0x20A` | `tx.inputs[$rB].predicateDataLength` | -| `GTF_INPUT_COIN_PREDICATE` | `0x20B` | Memory address of `tx.inputs[$rB].predicate` | -| `GTF_INPUT_COIN_PREDICATE_DATA` | `0x20C` | Memory address of `tx.inputs[$rB].predicateData` | -| `GTF_INPUT_COIN_PREDICATE_GAS_USED` | `0x20D` | `tx.inputs[$rB].predicateGasUsed` | -| `GTF_INPUT_CONTRACT_TX_ID` | `0x220` | Memory address of `tx.inputs[$rB].txID` | -| `GTF_INPUT_CONTRACT_OUTPUT_INDEX` | `0x221` | `tx.inputs[$rB].outputIndex` | -| `GTF_INPUT_CONTRACT_BALANCE_ROOT` | `0x222` | Memory address of `tx.inputs[$rB].balanceRoot` | -| `GTF_INPUT_CONTRACT_STATE_ROOT` | `0x223` | Memory address of `tx.inputs[$rB].stateRoot` | -| `GTF_INPUT_CONTRACT_TX_POINTER` | `0x224` | Memory address of `tx.inputs[$rB].txPointer` | -| `GTF_INPUT_CONTRACT_CONTRACT_ID` | `0x225` | Memory address of `tx.inputs[$rB].contractID` | -| `GTF_INPUT_MESSAGE_SENDER` | `0x240` | Memory address of `tx.inputs[$rB].sender` | -| `GTF_INPUT_MESSAGE_RECIPIENT` | `0x241` | Memory address of `tx.inputs[$rB].recipient` | -| `GTF_INPUT_MESSAGE_AMOUNT` | `0x242` | `tx.inputs[$rB].amount` | -| `GTF_INPUT_MESSAGE_NONCE` | `0x243` | Memory address of `tx.inputs[$rB].nonce` | -| `GTF_INPUT_MESSAGE_WITNESS_INDEX` | `0x244` | `tx.inputs[$rB].witnessIndex` | -| `GTF_INPUT_MESSAGE_DATA_LENGTH` | `0x245` | `tx.inputs[$rB].dataLength` | -| `GTF_INPUT_MESSAGE_PREDICATE_LENGTH` | `0x246` | `tx.inputs[$rB].predicateLength` | -| `GTF_INPUT_MESSAGE_PREDICATE_DATA_LENGTH` | `0x247` | `tx.inputs[$rB].predicateDataLength` | -| `GTF_INPUT_MESSAGE_DATA` | `0x248` | Memory address of `tx.inputs[$rB].data` | -| `GTF_INPUT_MESSAGE_PREDICATE` | `0x249` | Memory address of `tx.inputs[$rB].predicate` | -| `GTF_INPUT_MESSAGE_PREDICATE_DATA` | `0x24A` | Memory address of `tx.inputs[$rB].predicateData` | -| `GTF_INPUT_MESSAGE_PREDICATE_GAS_USED` | `0x24B` | `tx.inputs[$rB].predicateGasUsed` | -| `GTF_OUTPUT_TYPE` | `0x300` | `tx.outputs[$rB].type` | -| `GTF_OUTPUT_COIN_TO` | `0x301` | Memory address of `tx.outputs[$rB].to` | -| `GTF_OUTPUT_COIN_AMOUNT` | `0x302` | `tx.outputs[$rB].amount` | -| `GTF_OUTPUT_COIN_ASSET_ID` | `0x303` | Memory address of `tx.outputs[$rB].asset_id` | -| `GTF_OUTPUT_CONTRACT_INPUT_INDEX` | `0x304` | `tx.outputs[$rB].inputIndex` | -| `GTF_OUTPUT_CONTRACT_BALANCE_ROOT` | `0x305` | Memory address of `tx.outputs[$rB].balanceRoot` | -| `GTF_OUTPUT_CONTRACT_STATE_ROOT` | `0x306` | Memory address of `tx.outputs[$rB].stateRoot` | -| `GTF_OUTPUT_CONTRACT_CREATED_CONTRACT_ID` | `0x307` | Memory address of `tx.outputs[$rB].contractID` | -| `GTF_OUTPUT_CONTRACT_CREATED_STATE_ROOT` | `0x308` | Memory address of `tx.outputs[$rB].stateRoot` | -| `GTF_WITNESS_DATA_LENGTH` | `0x400` | `tx.witnesses[$rB].dataLength` | -| `GTF_WITNESS_DATA` | `0x401` | Memory address of `tx.witnesses[$rB].data` | -| `GTF_POLICY_COUNT` | `0x500` | `len(tx.policies)` | -| `GTF_POLICY_TYPE` | `0x501` | `tx.policies[$rB].type` | -| `GTF_POLICY_GAS_PRICE` | `0x502` | `tx.policies[0x00].gasPrice` | -| `GTF_POLICY_GAS_LIMIT` | `0x503` | `tx.policies[count_ones(0x11 & tx.policyTypes) - 1].gasLimit` | -| `GTF_POLICY_WITNESS_LIMIT` | `0x504` | `tx.policies[count_ones(0x111 & tx.policyTypes) - 1].witnessLimit` | -| `GTF_POLICY_MATURITY` | `0x505` | `tx.policies[count_ones(0x1111 & tx.policyTypes) - 1].maturity` | -| `GTF_POLICY_MAX_FEE` | `0x506` | `tx.policies[count_ones(0x11111 & tx.policyTypes) - 1].maxFee` | +| name | `imm` | set `$rA` to | +|-------------------------------------------|---------|-------------------------------------------------------------------| +| `GTF_TYPE` | `0x001` | `tx.type` | +| `GTF_SCRIPT_SCRIPT_LENGTH` | `0x002` | `tx.scriptLength` | +| `GTF_SCRIPT_SCRIPT_DATA_LENGTH` | `0x003` | `tx.scriptDataLength` | +| `GTF_SCRIPT_INPUTS_COUNT` | `0x004` | `tx.inputsCount` | +| `GTF_SCRIPT_OUTPUTS_COUNT` | `0x005` | `tx.outputsCount` | +| `GTF_SCRIPT_WITNESSES_COUNT` | `0x006` | `tx.witnessesCount` | +| `GTF_SCRIPT_RECEIPTS_ROOT` | `0x007` | Memory address of `tx.receiptsRoot` | +| `GTF_SCRIPT_SCRIPT` | `0x008` | Memory address of `tx.script` | +| `GTF_SCRIPT_SCRIPT_DATA` | `0x009` | Memory address of `tx.scriptData` | +| `GTF_SCRIPT_INPUT_AT_INDEX` | `0x00A` | Memory address of `tx.inputs[$rB]` | +| `GTF_SCRIPT_OUTPUT_AT_INDEX` | `0x00B` | Memory address of `t.outputs[$rB]` | +| `GTF_SCRIPT_WITNESS_AT_INDEX` | `0x00C` | Memory address of `tx.witnesses[$rB]` | +| `GTF_CREATE_BYTECODE_LENGTH` | `0x100` | `tx.bytecodeLength` | +| `GTF_CREATE_BYTECODE_WITNESS_INDEX` | `0x101` | `tx.bytecodeWitnessIndex` | +| `GTF_CREATE_STORAGE_SLOTS_COUNT` | `0x102` | `tx.storageSlotsCount` | +| `GTF_CREATE_INPUTS_COUNT` | `0x103` | `tx.inputsCount` | +| `GTF_CREATE_OUTPUTS_COUNT` | `0x104` | `tx.outputsCount` | +| `GTF_CREATE_WITNESSES_COUNT` | `0x105` | `tx.witnessesCount` | +| `GTF_CREATE_SALT` | `0x106` | Memory address of `tx.salt` | +| `GTF_CREATE_STORAGE_SLOT_AT_INDEX` | `0x107` | Memory address of `tx.storageSlots[$rB]` | +| `GTF_CREATE_INPUT_AT_INDEX` | `0x108` | Memory address of `tx.inputs[$rB]` | +| `GTF_CREATE_OUTPUT_AT_INDEX` | `0x109` | Memory address of `t.outputs[$rB]` | +| `GTF_CREATE_WITNESS_AT_INDEX` | `0x10A` | Memory address of `tx.witnesses[$rB]` | +| `GTF_INPUT_TYPE` | `0x200` | `tx.inputs[$rB].type` | +| `GTF_INPUT_COIN_TX_ID` | `0x201` | Memory address of `tx.inputs[$rB].txID` | +| `GTF_INPUT_COIN_OUTPUT_INDEX` | `0x202` | `tx.inputs[$rB].outputIndex` | +| `GTF_INPUT_COIN_OWNER` | `0x203` | Memory address of `tx.inputs[$rB].owner` | +| `GTF_INPUT_COIN_AMOUNT` | `0x204` | `tx.inputs[$rB].amount` | +| `GTF_INPUT_COIN_ASSET_ID` | `0x205` | Memory address of `tx.inputs[$rB].asset_id` | +| `GTF_INPUT_COIN_TX_POINTER` | `0x206` | Memory address of `tx.inputs[$rB].txPointer` | +| `GTF_INPUT_COIN_WITNESS_INDEX` | `0x207` | `tx.inputs[$rB].witnessIndex` | +| `GTF_INPUT_COIN_MATURITY` | `0x208` | `tx.inputs[$rB].maturity` | +| `GTF_INPUT_COIN_PREDICATE_LENGTH` | `0x209` | `tx.inputs[$rB].predicateLength` | +| `GTF_INPUT_COIN_PREDICATE_DATA_LENGTH` | `0x20A` | `tx.inputs[$rB].predicateDataLength` | +| `GTF_INPUT_COIN_PREDICATE` | `0x20B` | Memory address of `tx.inputs[$rB].predicate` | +| `GTF_INPUT_COIN_PREDICATE_DATA` | `0x20C` | Memory address of `tx.inputs[$rB].predicateData` | +| `GTF_INPUT_COIN_PREDICATE_GAS_USED` | `0x20D` | `tx.inputs[$rB].predicateGasUsed` | +| `GTF_INPUT_CONTRACT_TX_ID` | `0x220` | Memory address of `tx.inputs[$rB].txID` | +| `GTF_INPUT_CONTRACT_OUTPUT_INDEX` | `0x221` | `tx.inputs[$rB].outputIndex` | +| `GTF_INPUT_CONTRACT_BALANCE_ROOT` | `0x222` | Memory address of `tx.inputs[$rB].balanceRoot` | +| `GTF_INPUT_CONTRACT_STATE_ROOT` | `0x223` | Memory address of `tx.inputs[$rB].stateRoot` | +| `GTF_INPUT_CONTRACT_TX_POINTER` | `0x224` | Memory address of `tx.inputs[$rB].txPointer` | +| `GTF_INPUT_CONTRACT_CONTRACT_ID` | `0x225` | Memory address of `tx.inputs[$rB].contractID` | +| `GTF_INPUT_MESSAGE_SENDER` | `0x240` | Memory address of `tx.inputs[$rB].sender` | +| `GTF_INPUT_MESSAGE_RECIPIENT` | `0x241` | Memory address of `tx.inputs[$rB].recipient` | +| `GTF_INPUT_MESSAGE_AMOUNT` | `0x242` | `tx.inputs[$rB].amount` | +| `GTF_INPUT_MESSAGE_NONCE` | `0x243` | Memory address of `tx.inputs[$rB].nonce` | +| `GTF_INPUT_MESSAGE_WITNESS_INDEX` | `0x244` | `tx.inputs[$rB].witnessIndex` | +| `GTF_INPUT_MESSAGE_DATA_LENGTH` | `0x245` | `tx.inputs[$rB].dataLength` | +| `GTF_INPUT_MESSAGE_PREDICATE_LENGTH` | `0x246` | `tx.inputs[$rB].predicateLength` | +| `GTF_INPUT_MESSAGE_PREDICATE_DATA_LENGTH` | `0x247` | `tx.inputs[$rB].predicateDataLength` | +| `GTF_INPUT_MESSAGE_DATA` | `0x248` | Memory address of `tx.inputs[$rB].data` | +| `GTF_INPUT_MESSAGE_PREDICATE` | `0x249` | Memory address of `tx.inputs[$rB].predicate` | +| `GTF_INPUT_MESSAGE_PREDICATE_DATA` | `0x24A` | Memory address of `tx.inputs[$rB].predicateData` | +| `GTF_INPUT_MESSAGE_PREDICATE_GAS_USED` | `0x24B` | `tx.inputs[$rB].predicateGasUsed` | +| `GTF_OUTPUT_TYPE` | `0x300` | `tx.outputs[$rB].type` | +| `GTF_OUTPUT_COIN_TO` | `0x301` | Memory address of `tx.outputs[$rB].to` | +| `GTF_OUTPUT_COIN_AMOUNT` | `0x302` | `tx.outputs[$rB].amount` | +| `GTF_OUTPUT_COIN_ASSET_ID` | `0x303` | Memory address of `tx.outputs[$rB].asset_id` | +| `GTF_OUTPUT_CONTRACT_INPUT_INDEX` | `0x304` | `tx.outputs[$rB].inputIndex` | +| `GTF_OUTPUT_CONTRACT_BALANCE_ROOT` | `0x305` | Memory address of `tx.outputs[$rB].balanceRoot` | +| `GTF_OUTPUT_CONTRACT_STATE_ROOT` | `0x306` | Memory address of `tx.outputs[$rB].stateRoot` | +| `GTF_OUTPUT_CONTRACT_CREATED_CONTRACT_ID` | `0x307` | Memory address of `tx.outputs[$rB].contractID` | +| `GTF_OUTPUT_CONTRACT_CREATED_STATE_ROOT` | `0x308` | Memory address of `tx.outputs[$rB].stateRoot` | +| `GTF_WITNESS_DATA_LENGTH` | `0x400` | `tx.witnesses[$rB].dataLength` | +| `GTF_WITNESS_DATA` | `0x401` | Memory address of `tx.witnesses[$rB].data` | +| `GTF_POLICY_COUNT` | `0x500` | `len(tx.policies)` | +| `GTF_POLICY_TYPE` | `0x501` | `tx.policies[$rB].type` | +| `GTF_POLICY_GAS_PRICE` | `0x502` | `tx.policies[0x00].gasPrice` | +| `GTF_POLICY_WITNESS_LIMIT` | `0x504` | `tx.policies[count_ones(0b11 & tx.policyTypes) - 1].witnessLimit` | +| `GTF_POLICY_MATURITY` | `0x505` | `tx.policies[count_ones(0b111 & tx.policyTypes) - 1].maturity` | +| `GTF_POLICY_MAX_FEE` | `0x506` | `tx.policies[count_ones(0b1111 & tx.policyTypes) - 1].maxFee` | Panic if: diff --git a/src/tx-format/policy.md b/src/tx-format/policy.md index e2aa22fc..8ef8de67 100644 --- a/src/tx-format/policy.md +++ b/src/tx-format/policy.md @@ -4,16 +4,15 @@ // index using powers of 2 for efficient bitmasking enum PolicyType : uint32 { GasPrice = 1, - GasLimit = 2, - WitnessLimit = 4, - Maturity = 8, - MaxFee = 16, + WitnessLimit = 2, + Maturity = 4, + MaxFee = 8, } ``` -| name | type | description | -|--------|--------------------------------------------------------------------------------------------------------------|-----------------| -| `data` | One of [GasPrice](#gasprice), [GasLimit](#gaslimit), [WitnessLimit](#witnesslimit), or [Maturity](#maturity) | Policy data. | +| name | type | description | +|--------|---------------------------------------------------------------------------------------|--------------| +| `data` | One of [GasPrice](#gasprice), [WitnessLimit](#witnesslimit), or [Maturity](#maturity) | Policy data. | ## GasPrice @@ -21,16 +20,6 @@ enum PolicyType : uint32 { |------------|----------|---------------------------| | `gasPrice` | `uint64` | Gas price for transaction | -## GasLimit - -| name | type | description | -|------------|----------|------------------------------------------------------------| -| `gasLimit` | `uint64` | Gas limit for transaction (applicable to script execution) | - -Transaction is invalid if: - -- `gasLimit > MAX_GAS_PER_TX` - ## WitnessLimit | name | type | description | @@ -55,9 +44,9 @@ Transaction is invalid if: ## MaxFee -| name | type | description | -|-----------|----------|----------------------------------------------| -| `max_fee` | `uint64` | The maximum fee payable by this transaction. | +| name | type | description | +|-----------|----------|-----------------------------------------------------------------| +| `max_fee` | `uint64` | The maximum fee payable by this transaction using `BASE_ASSET`. | Transaction is invalid if: diff --git a/src/tx-format/transaction.md b/src/tx-format/transaction.md index 6476be1a..04acd77c 100644 --- a/src/tx-format/transaction.md +++ b/src/tx-format/transaction.md @@ -15,7 +15,7 @@ enum TransactionType : uint8 { Transaction is invalid if: -- `type > TransactionType.Create` +- `type > TransactionType.Mint` - `inputsCount > MAX_INPUTS` - `outputsCount > MAX_OUTPUTS` - `witnessesCount > MAX_WITNESSES` @@ -55,21 +55,22 @@ enum ReceiptType : uint8 { } ``` -| name | type | description | -|--------------------|-----------------------------|-----------------------------------------| -| `scriptLength` | `uint16` | Script length, in instructions. | -| `scriptDataLength` | `uint16` | Length of script input data, in bytes. | -| `policyTypes` | `uint32` | Bitfield of used policy types. | -| `inputsCount` | `uint8` | Number of inputs. | -| `outputsCount` | `uint8` | Number of outputs. | -| `witnessesCount` | `uint8` | Number of witnesses. | -| `receiptsRoot` | `byte[32]` | Merkle root of receipts. | -| `script` | `byte[]` | Script to execute. | -| `scriptData` | `byte[]` | Script input data (parameters). | -| `policies` | [Policy](./policy.md)`[]` | List of policies, sorted by PolicyType. | -| `inputs` | [Input](./input.md)`[]` | List of inputs. | -| `outputs` | [Output](./output.md)`[]` | List of outputs. | -| `witnesses` | [Witness](./witness.md)`[]` | List of witnesses. | +| name | type | description | +|--------------------|-----------------------------|------------------------------------------------------| +| `gasLimit` | `uint64` | Gas limit for transaction (including predicate gas). | +| `scriptLength` | `uint16` | Script length, in instructions. | +| `scriptDataLength` | `uint16` | Length of script input data, in bytes. | +| `policyTypes` | `uint32` | Bitfield of used policy types. | +| `inputsCount` | `uint8` | Number of inputs. | +| `outputsCount` | `uint8` | Number of outputs. | +| `witnessesCount` | `uint8` | Number of witnesses. | +| `receiptsRoot` | `byte[32]` | Merkle root of receipts. | +| `script` | `byte[]` | Script to execute. | +| `scriptData` | `byte[]` | Script input data (parameters). | +| `policies` | [Policy](./policy.md)`[]` | List of policies, sorted by PolicyType. | +| `inputs` | [Input](./input.md)`[]` | List of inputs. | +| `outputs` | [Output](./output.md)`[]` | List of outputs. | +| `witnesses` | [Witness](./witness.md)`[]` | List of witnesses. | Given helper `len()` that returns the number of bytes of a field. Given helper `count_ones()` that returns the number of ones in the binary representation of a field. @@ -83,8 +84,8 @@ Transaction is invalid if: - `scriptDataLength > MAX_SCRIPT_DATA_LENGTH` - `scriptLength * 4 != len(script)` - `scriptDataLength != len(scriptData)` +- `gasLimit > MAX_GAS_PER_TX` - No policy of type `PolicyType.GasPrice` -- No policy of type `PolicyType.GasLimit` - `count_ones(policyTypes) > count_variants(PolicyType)` - `policyTypes > sum_variants(PolicyType)` - `len(policies) > count_ones(policyTypes)` From a346591828875c317cf8c1dafa782147ff7c69da Mon Sep 17 00:00:00 2001 From: Voxelot Date: Wed, 25 Oct 2023 15:18:44 -0700 Subject: [PATCH 15/40] use count_ones() instead of len() for GTF_POLICY_COUNT --- 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 01202b3d..5862b921 100644 --- a/src/fuel-vm/instruction-set.md +++ b/src/fuel-vm/instruction-set.md @@ -2507,7 +2507,7 @@ Get [fields from the transaction](../tx-format/transaction.md). | `GTF_OUTPUT_CONTRACT_CREATED_STATE_ROOT` | `0x308` | Memory address of `tx.outputs[$rB].stateRoot` | | `GTF_WITNESS_DATA_LENGTH` | `0x400` | `tx.witnesses[$rB].dataLength` | | `GTF_WITNESS_DATA` | `0x401` | Memory address of `tx.witnesses[$rB].data` | -| `GTF_POLICY_COUNT` | `0x500` | `len(tx.policies)` | +| `GTF_POLICY_COUNT` | `0x500` | `count_ones(tx.policyTypes)` | | `GTF_POLICY_TYPE` | `0x501` | `tx.policies[$rB].type` | | `GTF_POLICY_GAS_PRICE` | `0x502` | `tx.policies[0x00].gasPrice` | | `GTF_POLICY_WITNESS_LIMIT` | `0x504` | `tx.policies[count_ones(0b11 & tx.policyTypes) - 1].witnessLimit` | From 505a23dcccdf3190982b13523902839be3249e60 Mon Sep 17 00:00:00 2001 From: Brandon Kite Date: Wed, 25 Oct 2023 17:05:04 -0700 Subject: [PATCH 16/40] add some intrinsic costs for transaction inputs --- src/protocol/tx-validity.md | 59 ++++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 21 deletions(-) diff --git a/src/protocol/tx-validity.md b/src/protocol/tx-validity.md index 2ca48fab..a008d214 100644 --- a/src/protocol/tx-validity.md +++ b/src/protocol/tx-validity.md @@ -103,15 +103,29 @@ def sum_inputs(tx, asset_id) -> int: total += input.amount return total -def sum_predicate_gas_used(tx) -> int: +def sum_inputs_intrinsic_gas(tx) -> int: + """ + Computes the intrinsic gas cost of verifying input utxos + """ total: int = 0 + witness_indices = set(()) for input in tx.inputs: - if input.type == InputType.Coin: - total += input.predicateGasUsed - elif input.type == InputType.Message: + # add gas required to read utxo from state + total += state_read_gas_cost() + if input.type == InputType.Coin or input.type == InputType.Message: + # add gas allocated for predicate execution total += input.predicateGasUsed + if input.predicateLength == 0: + # notate witness index if input is signed + witness_indices.add(input.witnessIndex) + else: + # add intrinsic cost of predicate merkleization + total += bmt_root_gas_cost(input.predicateLength) + # add intrinsic cost of vm initialization + total += vm_initialization_gas_cost() + # add intrinsic cost of verifying witness signatures + total += len(witness_indices) * eck1_recover_gas_cost() return total - """ Returns any minted amounts by the transaction """ @@ -127,6 +141,21 @@ def sum_outputs(tx, asset_id) -> int: total += output.amount return total +def reserved_fee_balance(tx, asset_id) -> int: + """ + Computes the maximum potential amount of fees that may need to be charged to process a transaction. + """ + gas = tx.gasLimit + sum_inputs_intrinsic_gas(tx) + gasBalance = tx.gasPrice * gas / GAS_PRICE_FACTOR + bytesBalance = size(tx) * GAS_PER_BYTE * tx.gasPrice / GAS_PRICE_FACTOR + # Total fee balance + feeBalance = ceiling(gasBalance + bytesBalance) + # Only base asset can be used to pay for gas + if asset_id == 0: + return feeBalance + else: + return 0 + def available_balance(tx, asset_id) -> int: """ Make the data message balance available to the script @@ -137,24 +166,12 @@ def available_balance(tx, asset_id) -> int: def unavailable_balance(tx, asset_id) -> int: sentBalance = sum_outputs(tx, asset_id) # Total fee balance - feeBalance = fee_balance(tx, asset_id) + feeBalance = reserved_fee_balance(tx, asset_id) # Only base asset can be used to pay for gas if asset_id == 0: return sentBalance + feeBalance return sentBalance -def fee_balance(tx, asset_id) -> int: - gas = tx.gasLimit + sum_predicate_gas_used(tx) - gasBalance = gasPrice * gas / GAS_PRICE_FACTOR - bytesBalance = size(tx) * GAS_PER_BYTE * gasPrice / GAS_PRICE_FACTOR - # Total fee balance - feeBalance = ceiling(gasBalance + bytesBalance) - # Only base asset can be used to pay for gas - if asset_id == 0: - return feeBalance - else: - return 0 - # The sum_data_messages total is not included in the unavailable_balance since it is spendable as long as there # is enough base asset amount to cover gas costs without using data messages. Messages containing data can't # cover gas costs since they are retryable. @@ -168,7 +185,7 @@ def address_from(pubkey: bytes) -> bytes: return sha256(pubkey)[0:32] for input in tx.inputs: - if (input.type == InputType.Coin || input.type == InputType.Message) and input.predicateLength == 0: + if (input.type == InputType.Coin or input.type == InputType.Message) and input.predicateLength == 0: # ECDSA signatures must be 64 bytes if tx.witnesses[input.witnessIndex].dataLength != 64: return False @@ -205,9 +222,9 @@ Once the free balances are computed, the [script is executed](../fuel-vm/index.m 1. The unspent free balance `unspentBalance` for each asset ID. 1. The unspent gas `unspentGas` from the `$ggas` register. -The fees incurred for a transaction are `ceiling(((size(tx) * GAS_PER_BYTE) + (tx.gasLimit - unspentGas) + sum(tx.inputs[i].predicateGasUsed)) * tx.gasPrice / GAS_PRICE_FACTOR)`. +The fees incurred for a transaction are `reserved_fee_balance(tx, BASE_ASSET) - floor(unspentGas * tx.gasPrice / GAS_PRICE_FACTOR)`. -`size(tx)` includes the entire transaction serialized according to the transaction format, including witness data. +`size(tx)` encompasses the entire transaction serialized according to the transaction format, including witness data. This ensures every byte of block space either on Fuel or corresponding DA layer can be accounted for. If the transaction as included in a block does not match this final transaction, the block is invalid. From 90687c6bb8f89f03c12392e81ef7a8cfcfbafd95 Mon Sep 17 00:00:00 2001 From: Brandon Kite Date: Wed, 25 Oct 2023 17:39:39 -0700 Subject: [PATCH 17/40] add some intrinsic costs for transaction processing --- src/protocol/tx-validity.md | 38 +++++++++++++++++++++++++++--------- src/tx-format/transaction.md | 1 + 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/src/protocol/tx-validity.md b/src/protocol/tx-validity.md index a008d214..b92820d1 100644 --- a/src/protocol/tx-validity.md +++ b/src/protocol/tx-validity.md @@ -103,7 +103,7 @@ def sum_inputs(tx, asset_id) -> int: total += input.amount return total -def sum_inputs_intrinsic_gas(tx) -> int: +def sum_input_intrinsic_fees(tx) -> int: """ Computes the intrinsic gas cost of verifying input utxos """ @@ -111,7 +111,7 @@ def sum_inputs_intrinsic_gas(tx) -> int: witness_indices = set(()) for input in tx.inputs: # add gas required to read utxo from state - total += state_read_gas_cost() + total += state_read_fee() if input.type == InputType.Coin or input.type == InputType.Message: # add gas allocated for predicate execution total += input.predicateGasUsed @@ -119,13 +119,34 @@ def sum_inputs_intrinsic_gas(tx) -> int: # notate witness index if input is signed witness_indices.add(input.witnessIndex) else: - # add intrinsic cost of predicate merkleization - total += bmt_root_gas_cost(input.predicateLength) + # add intrinsic cost of predicate merkleization based on number of predicate bytes + total += bmt_root_bytes_fee(input.predicateLength) # add intrinsic cost of vm initialization - total += vm_initialization_gas_cost() + total += vm_initialization_fee() # add intrinsic cost of verifying witness signatures - total += len(witness_indices) * eck1_recover_gas_cost() + total += len(witness_indices) * eck1_recover_fee() return total + +def sum_outputs_intrinsic_fees(tx) -> int: + """ + Computes the intrinsic gas cost of processing transaction outputs + """ + total: int = 0 + for output in tx.outputs: + total += state_write_fee() + if output.type == OutputType.OutputContractCreated: + # add intrinsic cost of verifying the contract root based on the size of the contract bytecode + total += bmt_root_bytes_fee(tx.witnesses[tx.bytecodeWitnessIndex].dataLength) + # add intrinsic cost of initializing contract storage + total += len(tx.storageSlotCount) * smt_insert_fee() + return total + +def intrinsic_fees(tx) -> int: + """ + Computes intrinsic costs for a transaction + """ + return sum_input_intrinsic_fees(tx) + sum_outputs_intrinsic_fees(tx) + """ Returns any minted amounts by the transaction """ @@ -145,11 +166,10 @@ def reserved_fee_balance(tx, asset_id) -> int: """ Computes the maximum potential amount of fees that may need to be charged to process a transaction. """ - gas = tx.gasLimit + sum_inputs_intrinsic_gas(tx) - gasBalance = tx.gasPrice * gas / GAS_PRICE_FACTOR + gasBalance = tx.gasLimit * tx.gasPrice / GAS_PRICE_FACTOR bytesBalance = size(tx) * GAS_PER_BYTE * tx.gasPrice / GAS_PRICE_FACTOR # Total fee balance - feeBalance = ceiling(gasBalance + bytesBalance) + feeBalance = ceiling(gasBalance + bytesBalance + intrinsic_fees(tx)) # Only base asset can be used to pay for gas if asset_id == 0: return feeBalance diff --git a/src/tx-format/transaction.md b/src/tx-format/transaction.md index 04acd77c..6dc1adb3 100644 --- a/src/tx-format/transaction.md +++ b/src/tx-format/transaction.md @@ -79,6 +79,7 @@ Given helper `sum_variants()` that sums all variants of an enum. Transaction is invalid if: +- More than one output is of type `OutputType.Change` with identical `asset_id` fields. - Any output is of type `OutputType.ContractCreated` - `scriptLength > MAX_SCRIPT_LENGTH` - `scriptDataLength > MAX_SCRIPT_DATA_LENGTH` From 7829f93bf85939778661ff24e71979e095af08de Mon Sep 17 00:00:00 2001 From: Brandon Kite Date: Wed, 25 Oct 2023 17:52:38 -0700 Subject: [PATCH 18/40] some minor corrections --- src/protocol/tx-validity.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/protocol/tx-validity.md b/src/protocol/tx-validity.md index b92820d1..0b587b07 100644 --- a/src/protocol/tx-validity.md +++ b/src/protocol/tx-validity.md @@ -110,11 +110,14 @@ def sum_input_intrinsic_fees(tx) -> int: total: int = 0 witness_indices = set(()) for input in tx.inputs: - # add gas required to read utxo from state + # add fees required to read utxo from state total += state_read_fee() if input.type == InputType.Coin or input.type == InputType.Message: - # add gas allocated for predicate execution - total += input.predicateGasUsed + # add fees required to remove coin or message from state + # note: this does not apply to contract inputs, as they are updated in place by the output + total += state_delete_fee() + # add fees allocated for predicate execution + total += input.predicateGasUsed * tx.gasPrice / GAS_PRICE_FACTOR if input.predicateLength == 0: # notate witness index if input is signed witness_indices.add(input.witnessIndex) @@ -133,12 +136,14 @@ def sum_outputs_intrinsic_fees(tx) -> int: """ total: int = 0 for output in tx.outputs: - total += state_write_fee() + # adds fees required to write new output to state + total += state_write_fee(size(output)) if output.type == OutputType.OutputContractCreated: # add intrinsic cost of verifying the contract root based on the size of the contract bytecode total += bmt_root_bytes_fee(tx.witnesses[tx.bytecodeWitnessIndex].dataLength) # add intrinsic cost of initializing contract storage - total += len(tx.storageSlotCount) * smt_insert_fee() + # note: smt_insert_fee may be dependent on the storage value if the protocol adopts dynamic sized slots + total += tx.storageSlotCount * smt_insert_fee() return total def intrinsic_fees(tx) -> int: From 3465f6f426dca4b354f281b62e1b0a54cd8cc41a Mon Sep 17 00:00:00 2001 From: Brandon Kite Date: Wed, 25 Oct 2023 17:58:53 -0700 Subject: [PATCH 19/40] cleanup --- src/protocol/tx-validity.md | 53 ++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/src/protocol/tx-validity.md b/src/protocol/tx-validity.md index 0b587b07..85dcdee6 100644 --- a/src/protocol/tx-validity.md +++ b/src/protocol/tx-validity.md @@ -83,6 +83,12 @@ If this check passes, the UTXO ID `(txID, outputIndex)` fields of each contract For each asset ID `asset_id` in the input and output set: ```py +def gas_to_fee(gas, gas_price) -> int: + """ + Converts gas units into a fee amount + """ + return gas * gas_price / GAS_PRICE_FACTOR + def sum_data_messages(tx, asset_id) -> int: """ Returns the total balance available from messages containing data @@ -102,6 +108,20 @@ def sum_inputs(tx, asset_id) -> int: elif input.type == InputType.Message and asset_id == 0 and input.dataLength == 0: total += input.amount return total +""" +Returns any minted amounts by the transaction +""" +def minted(tx, asset_id) -> int: + if tx.type != TransactionType.Mint or asset_id != tx.mint_asset_id: + return 0 + return tx.mint_amount + +def sum_outputs(tx, asset_id) -> int: + total: int = 0 + for output in tx.outputs: + if output.type == OutputType.Coin and output.asset_id == asset_id: + total += output.amount + return total def sum_input_intrinsic_fees(tx) -> int: """ @@ -117,7 +137,7 @@ def sum_input_intrinsic_fees(tx) -> int: # note: this does not apply to contract inputs, as they are updated in place by the output total += state_delete_fee() # add fees allocated for predicate execution - total += input.predicateGasUsed * tx.gasPrice / GAS_PRICE_FACTOR + total += gas_to_fee(input.predicateGasUsed, tx.gasPrice) if input.predicateLength == 0: # notate witness index if input is signed witness_indices.add(input.witnessIndex) @@ -147,39 +167,24 @@ def sum_outputs_intrinsic_fees(tx) -> int: return total def intrinsic_fees(tx) -> int: - """ - Computes intrinsic costs for a transaction - """ - return sum_input_intrinsic_fees(tx) + sum_outputs_intrinsic_fees(tx) - -""" -Returns any minted amounts by the transaction -""" -def minted(tx, asset_id) -> int: - if tx.type != TransactionType.Mint or asset_id != tx.mint_asset_id: - return 0 - return tx.mint_amount - -def sum_outputs(tx, asset_id) -> int: - total: int = 0 - for output in tx.outputs: - if output.type == OutputType.Coin and output.asset_id == asset_id: - total += output.amount - return total + """ + Computes intrinsic costs for a transaction + """ + return sum_input_intrinsic_fees(tx) + sum_outputs_intrinsic_fees(tx) def reserved_fee_balance(tx, asset_id) -> int: """ Computes the maximum potential amount of fees that may need to be charged to process a transaction. """ - gasBalance = tx.gasLimit * tx.gasPrice / GAS_PRICE_FACTOR - bytesBalance = size(tx) * GAS_PER_BYTE * tx.gasPrice / GAS_PRICE_FACTOR + gasBalance = gas_to_fee(tx.gasLimit, tx.gasPrice) + bytesBalance = gas_to_fee(size(tx) * GAS_PER_BYTE, tx.gasPrice) # Total fee balance feeBalance = ceiling(gasBalance + bytesBalance + intrinsic_fees(tx)) # Only base asset can be used to pay for gas if asset_id == 0: - return feeBalance + return feeBalance else: - return 0 + return 0 def available_balance(tx, asset_id) -> int: """ From 4fc24ab2b76c4598139456812e96983338cd2592 Mon Sep 17 00:00:00 2001 From: Brandon Kite Date: Wed, 25 Oct 2023 17:59:08 -0700 Subject: [PATCH 20/40] cleanup --- src/protocol/tx-validity.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/protocol/tx-validity.md b/src/protocol/tx-validity.md index 85dcdee6..76fc1a4e 100644 --- a/src/protocol/tx-validity.md +++ b/src/protocol/tx-validity.md @@ -84,10 +84,10 @@ For each asset ID `asset_id` in the input and output set: ```py def gas_to_fee(gas, gas_price) -> int: - """ - Converts gas units into a fee amount - """ - return gas * gas_price / GAS_PRICE_FACTOR + """ + Converts gas units into a fee amount + """ + return gas * gas_price / GAS_PRICE_FACTOR def sum_data_messages(tx, asset_id) -> int: """ From 46236b0bbb8770a6be301a0f9e1cb8009a9b2b29 Mon Sep 17 00:00:00 2001 From: Brandon Kite Date: Wed, 25 Oct 2023 18:08:10 -0700 Subject: [PATCH 21/40] add cost of script vm init --- src/protocol/tx-validity.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/protocol/tx-validity.md b/src/protocol/tx-validity.md index 76fc1a4e..399dc4e8 100644 --- a/src/protocol/tx-validity.md +++ b/src/protocol/tx-validity.md @@ -167,10 +167,15 @@ def sum_outputs_intrinsic_fees(tx) -> int: return total def intrinsic_fees(tx) -> int: - """ - Computes intrinsic costs for a transaction - """ - return sum_input_intrinsic_fees(tx) + sum_outputs_intrinsic_fees(tx) + """ + Computes intrinsic costs for a transaction + """ + fees: int = 0 + # add the cost of initializing a vm for the script + if tx.type == TransactionType.Script: + fees += vm_initialization_fee() + fees += sum_input_intrinsic_fees(tx) + sum_outputs_intrinsic_fees(tx) + return fees def reserved_fee_balance(tx, asset_id) -> int: """ From d013eb374e1976a2372d3589acb0a7a93edfd422 Mon Sep 17 00:00:00 2001 From: Brandon Kite Date: Wed, 25 Oct 2023 18:42:34 -0700 Subject: [PATCH 22/40] fix max_fee rule --- src/tx-format/policy.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tx-format/policy.md b/src/tx-format/policy.md index 8ef8de67..d3a28c89 100644 --- a/src/tx-format/policy.md +++ b/src/tx-format/policy.md @@ -51,4 +51,4 @@ Transaction is invalid if: Transaction is invalid if: - `max_fee > sum_inputs(tx, BASE_ASSET_ID) - sum_outputs(tx, BASE_ASSET_ID)` -- `max_fee < fee_balance(tx, BASE_ASSET_ID)` +- `max_fee < reserved_fee_balance(tx, BASE_ASSET_ID)` From 3a88d5bd3bc1555f51e2ec096301b2350a7a45a3 Mon Sep 17 00:00:00 2001 From: Brandon Vrooman Date: Wed, 29 Nov 2023 16:00:39 -0500 Subject: [PATCH 23/40] Fix merge --- src/protocol/tx-validity.md | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/src/protocol/tx-validity.md b/src/protocol/tx-validity.md index 1d26abf4..956678a1 100644 --- a/src/protocol/tx-validity.md +++ b/src/protocol/tx-validity.md @@ -187,20 +187,6 @@ def intrinsic_fees(tx) -> int: fees += sum_input_intrinsic_fees(tx) + sum_outputs_intrinsic_fees(tx) return fees -def reserved_fee_balance(tx, asset_id) -> int: - """ - Computes the maximum potential amount of fees that may need to be charged to process a transaction. - """ - gasBalance = gas_to_fee(tx.gasLimit, tx.gasPrice) - bytesBalance = gas_to_fee(size(tx) * GAS_PER_BYTE, tx.gasPrice) - # Total fee balance - feeBalance = ceiling(gasBalance + bytesBalance + intrinsic_fees(tx)) - # Only base asset can be used to pay for gas - if asset_id == 0: - return feeBalance - else: - return 0 - def available_balance(tx, asset_id) -> int: """ Make the data message balance available to the script @@ -217,12 +203,15 @@ def unavailable_balance(tx, asset_id) -> int: return sentBalance + feeBalance return sentBalance -def fee_balance(tx, asset_id) -> int: +def reserved_fee_balance(tx, asset_id) -> int: + """ + Computes the maximum potential amount of fees that may need to be charged to process a transaction. + """ gas = tx.gasLimit + sum_predicate_gas_used(tx) - gasBalance = gasPrice * gas / GAS_PRICE_FACTOR - bytesBalance = size(tx) * GAS_PER_BYTE * gasPrice / GAS_PRICE_FACTOR + gasBalance = gas_to_fee(gas, tx.gasPrice) + bytesBalance = gas_to_fee(size(tx) * GAS_PER_BYTE, tx.gasPrice) # Total fee balance - feeBalance = reserved_fee_balance(tx, asset_id) + feeBalance = ceiling(gasBalance + bytesBalance + intrinsic_fees(tx)) # Only base asset can be used to pay for gas if asset_id == 0: return feeBalance From 1d92526d2e5b17047f285f81178c2332acbcb755 Mon Sep 17 00:00:00 2001 From: Brandon Vrooman Date: Wed, 29 Nov 2023 16:20:33 -0500 Subject: [PATCH 24/40] Minor corrections to calculations --- src/protocol/tx-validity.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/protocol/tx-validity.md b/src/protocol/tx-validity.md index 956678a1..964cb0aa 100644 --- a/src/protocol/tx-validity.md +++ b/src/protocol/tx-validity.md @@ -153,14 +153,14 @@ def sum_input_intrinsic_fees(tx) -> int: witness_indices.add(input.witnessIndex) else: # add intrinsic cost of predicate merkleization based on number of predicate bytes - total += bmt_root_bytes_fee(input.predicateLength) + total += smt_root_bytes_fee(input.predicateLength) # add intrinsic cost of vm initialization total += vm_initialization_fee() # add intrinsic cost of verifying witness signatures total += len(witness_indices) * eck1_recover_fee() return total -def sum_outputs_intrinsic_fees(tx) -> int: +def sum_output_intrinsic_fees(tx) -> int: """ Computes the intrinsic gas cost of processing transaction outputs """ @@ -170,7 +170,7 @@ def sum_outputs_intrinsic_fees(tx) -> int: total += state_write_fee(size(output)) if output.type == OutputType.OutputContractCreated: # add intrinsic cost of verifying the contract root based on the size of the contract bytecode - total += bmt_root_bytes_fee(tx.witnesses[tx.bytecodeWitnessIndex].dataLength) + total += smt_root_bytes_fee(tx.witnesses[tx.bytecodeWitnessIndex].dataLength) # add intrinsic cost of initializing contract storage # note: smt_insert_fee may be dependent on the storage value if the protocol adopts dynamic sized slots total += tx.storageSlotCount * smt_insert_fee() @@ -184,7 +184,7 @@ def intrinsic_fees(tx) -> int: # add the cost of initializing a vm for the script if tx.type == TransactionType.Script: fees += vm_initialization_fee() - fees += sum_input_intrinsic_fees(tx) + sum_outputs_intrinsic_fees(tx) + fees += sum_input_intrinsic_fees(tx) + sum_output_intrinsic_fees(tx) return fees def available_balance(tx, asset_id) -> int: From 69c00c2f567da6648260314b60f04d4552cfe104 Mon Sep 17 00:00:00 2001 From: Brandon Vrooman Date: Wed, 29 Nov 2023 16:44:20 -0500 Subject: [PATCH 25/40] Fix python formatting --- src/protocol/tx-validity.md | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/src/protocol/tx-validity.md b/src/protocol/tx-validity.md index 964cb0aa..1361b7f8 100644 --- a/src/protocol/tx-validity.md +++ b/src/protocol/tx-validity.md @@ -89,6 +89,7 @@ def gas_to_fee(gas, gas_price) -> int: """ return gas * gas_price / GAS_PRICE_FACTOR + def sum_data_messages(tx, asset_id) -> int: """ Returns the total balance available from messages containing data @@ -100,6 +101,7 @@ def sum_data_messages(tx, asset_id) -> int: total += input.amount return total + def sum_inputs(tx, asset_id) -> int: total: int = 0 for input in tx.inputs: @@ -109,6 +111,7 @@ def sum_inputs(tx, asset_id) -> int: total += input.amount return total + def sum_predicate_gas_used(tx) -> int: total: int = 0 for input in tx.inputs: @@ -118,14 +121,23 @@ def sum_predicate_gas_used(tx) -> int: total += input.predicateGasUsed return total -""" -Returns any minted amounts by the transaction -""" + +def sum_tx_bytes_gas_used(tx) -> int: + """ + Computes the intrinsic gas cost of a transaction based on size in bytes + """ + size(tx) * GAS_PER_BYTE + + def minted(tx, asset_id) -> int: + """ + Returns any minted amounts by the transaction + """ if tx.type != TransactionType.Mint or asset_id != tx.mint_asset_id: return 0 return tx.mint_amount + def sum_outputs(tx, asset_id) -> int: total: int = 0 for output in tx.outputs: @@ -133,6 +145,7 @@ def sum_outputs(tx, asset_id) -> int: total += output.amount return total + def sum_input_intrinsic_fees(tx) -> int: """ Computes the intrinsic gas cost of verifying input utxos @@ -153,13 +166,14 @@ def sum_input_intrinsic_fees(tx) -> int: witness_indices.add(input.witnessIndex) else: # add intrinsic cost of predicate merkleization based on number of predicate bytes - total += smt_root_bytes_fee(input.predicateLength) + total += contract_root_bytes_fee(input.predicateLength) # add intrinsic cost of vm initialization total += vm_initialization_fee() # add intrinsic cost of verifying witness signatures total += len(witness_indices) * eck1_recover_fee() return total + def sum_output_intrinsic_fees(tx) -> int: """ Computes the intrinsic gas cost of processing transaction outputs @@ -170,12 +184,13 @@ def sum_output_intrinsic_fees(tx) -> int: total += state_write_fee(size(output)) if output.type == OutputType.OutputContractCreated: # add intrinsic cost of verifying the contract root based on the size of the contract bytecode - total += smt_root_bytes_fee(tx.witnesses[tx.bytecodeWitnessIndex].dataLength) + total += contract_root_bytes_fee(tx.witnesses[tx.bytecodeWitnessIndex].dataLength) # add intrinsic cost of initializing contract storage # note: smt_insert_fee may be dependent on the storage value if the protocol adopts dynamic sized slots total += tx.storageSlotCount * smt_insert_fee() return total + def intrinsic_fees(tx) -> int: """ Computes intrinsic costs for a transaction @@ -187,6 +202,7 @@ def intrinsic_fees(tx) -> int: fees += sum_input_intrinsic_fees(tx) + sum_output_intrinsic_fees(tx) return fees + def available_balance(tx, asset_id) -> int: """ Make the data message balance available to the script @@ -203,21 +219,22 @@ def unavailable_balance(tx, asset_id) -> int: return sentBalance + feeBalance return sentBalance + def reserved_fee_balance(tx, asset_id) -> int: """ Computes the maximum potential amount of fees that may need to be charged to process a transaction. """ - gas = tx.gasLimit + sum_predicate_gas_used(tx) - gasBalance = gas_to_fee(gas, tx.gasPrice) - bytesBalance = gas_to_fee(size(tx) * GAS_PER_BYTE, tx.gasPrice) + gas = tx.gasLimit + sum_predicate_gas_used(tx) + sum_tx_bytes_gas_used(tx) + intrinsic_fees(tx) # Total fee balance - feeBalance = ceiling(gasBalance + bytesBalance + intrinsic_fees(tx)) + fee_balance = gas_to_fee(gas, tx.gasPrice) + fee_balance = math.ceil(fee_balance) # Only base asset can be used to pay for gas if asset_id == 0: - return feeBalance + return fee_balance else: return 0 + # The sum_data_messages total is not included in the unavailable_balance since it is spendable as long as there # is enough base asset amount to cover gas costs without using data messages. Messages containing data can't # cover gas costs since they are retryable. From 8254b3d29a6ac79b155f5e3597361dd836846c26 Mon Sep 17 00:00:00 2001 From: Brandon Vrooman Date: Wed, 29 Nov 2023 17:31:03 -0500 Subject: [PATCH 26/40] Add section on fees --- src/protocol/tx-validity.md | 44 ++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/src/protocol/tx-validity.md b/src/protocol/tx-validity.md index 1361b7f8..f02fb792 100644 --- a/src/protocol/tx-validity.md +++ b/src/protocol/tx-validity.md @@ -220,13 +220,20 @@ def unavailable_balance(tx, asset_id) -> int: return sentBalance +def gas_balance(tx) -> int: + """ + Computes the maximum amount of gas required to process a transaction. + """ + gas = sum_predicate_gas_used(tx) + sum_tx_bytes_gas_used(tx) + intrinsic_fees(tx) + return gas + + def reserved_fee_balance(tx, asset_id) -> int: """ Computes the maximum potential amount of fees that may need to be charged to process a transaction. """ - gas = tx.gasLimit + sum_predicate_gas_used(tx) + sum_tx_bytes_gas_used(tx) + intrinsic_fees(tx) - # Total fee balance - fee_balance = gas_to_fee(gas, tx.gasPrice) + gas_balance = tx.gasLimit + gas_balance(tx) + fee_balance = gas_to_fee(gas_balance, tx.gasPrice) fee_balance = math.ceil(fee_balance) # Only base asset can be used to pay for gas if asset_id == 0: @@ -285,13 +292,40 @@ Once the free balances are computed, the [script is executed](../fuel-vm/index.m 1. The unspent free balance `unspentBalance` for each asset ID. 1. The unspent gas `unspentGas` from the `$ggas` register. -The fees incurred for a transaction are `reserved_fee_balance(tx, BASE_ASSET) - floor(unspentGas * tx.gasPrice / GAS_PRICE_FACTOR)`. - `size(tx)` encompasses the entire transaction serialized according to the transaction format, including witness data. This ensures every byte of block space either on Fuel or corresponding DA layer can be accounted for. If the transaction as included in a block does not match this final transaction, the block is invalid. +### Fees + +The fee incurred for a transaction can be calculated by: + +``` +fee = reserved_fee_balance(tx, BASE_ASSET) - floor(gas_to_fee(unspent_gas)) +``` + +where + +- `reserved_fee_balance(tx, BASE_ASSET)` is the maximum cost of the transaction, including fees for inputs, outputs, and the bytes of the transaction, collectively known as intrinsic fees, predicate execution, as well as the amount of gas the user is willing to spend on script execution. +- `unspent_gas` is the amount gas left over after intrinsic fees and execution of the transaction. Converting unspent gas to a fee describes how much "change" is left over from the user's payment; the block producer collects this unspent gas as reward. + +The cost of the transaction is the result of the calculation of this fee balance. Users wishing to submit transactions can incentivize block producers to include their transactions by providing a higher reward in the form of more unspent gas. This is achieved by specifying a higher gas limit on the transaction. + +``` +unspent_gas = tx.gas_limit - gas_balance(tx) +``` + +A natural result of the ability to vary the gas limit is the concept of minimum and maximum fees. The minimum fee is thus the exact fee required to pay the fee balance, while the maximum fee is, then, the minimum fee plus any gas remaining after execution paid as an incentive. + +``` +min_fee = reserved_fee_balance(tx, BASE_ASSET) +max_fee = min_fee + floor(gas_to_fee(unspent_gas)) +``` + +In the case that all gas was consumed during execution, the `min_fee` and `max_fee` will be equal. + + ## VM Postcondition Validity Rules This section defines _VM postcondition validity rules_ for transactions: the requirements for a transaction to be valid after it has been executed. From 8648ac4f55a1ab05d2edeed204618465db86c3d6 Mon Sep 17 00:00:00 2001 From: Brandon Vrooman Date: Wed, 29 Nov 2023 19:16:52 -0500 Subject: [PATCH 27/40] Update tx-validity.md --- src/protocol/tx-validity.md | 75 ++++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 35 deletions(-) diff --git a/src/protocol/tx-validity.md b/src/protocol/tx-validity.md index f02fb792..e8fdf51c 100644 --- a/src/protocol/tx-validity.md +++ b/src/protocol/tx-validity.md @@ -203,28 +203,11 @@ def intrinsic_fees(tx) -> int: return fees -def available_balance(tx, asset_id) -> int: - """ - Make the data message balance available to the script - """ - availableBalance = sum_inputs(tx, asset_id) + sum_data_messages(tx, asset_id) + minted(tx, asset_id) - return availableBalance - -def unavailable_balance(tx, asset_id) -> int: - sentBalance = sum_outputs(tx, asset_id) - # Total fee balance - feeBalance = fee_balance(tx, asset_id) - # Only base asset can be used to pay for gas - if asset_id == 0: - return sentBalance + feeBalance - return sentBalance - - def gas_balance(tx) -> int: """ Computes the maximum amount of gas required to process a transaction. """ - gas = sum_predicate_gas_used(tx) + sum_tx_bytes_gas_used(tx) + intrinsic_fees(tx) + gas = tx.gasLimit + sum_predicate_gas_used(tx) + sum_tx_bytes_gas_used(tx) + intrinsic_fees(tx) return gas @@ -232,9 +215,9 @@ def reserved_fee_balance(tx, asset_id) -> int: """ Computes the maximum potential amount of fees that may need to be charged to process a transaction. """ - gas_balance = tx.gasLimit + gas_balance(tx) + gas_balance = gas_balance(tx) fee_balance = gas_to_fee(gas_balance, tx.gasPrice) - fee_balance = math.ceil(fee_balance) + fee_balance = ceil(fee_balance) # Only base asset can be used to pay for gas if asset_id == 0: return fee_balance @@ -242,6 +225,24 @@ def reserved_fee_balance(tx, asset_id) -> int: return 0 +def available_balance(tx, asset_id) -> int: + """ + Make the data message balance available to the script + """ + availableBalance = sum_inputs(tx, asset_id) + sum_data_messages(tx, asset_id) + minted(tx, asset_id) + return availableBalance + + +def unavailable_balance(tx, asset_id) -> int: + sentBalance = sum_outputs(tx, asset_id) + # Total fee balance + feeBalance = reserved_fee_balance(tx, asset_id) + # Only base asset can be used to pay for gas + if asset_id == 0: + return sentBalance + feeBalance + return sentBalance + + # The sum_data_messages total is not included in the unavailable_balance since it is spendable as long as there # is enough base asset amount to cover gas costs without using data messages. Messages containing data can't # cover gas costs since they are retryable. @@ -299,31 +300,35 @@ If the transaction as included in a block does not match this final transaction, ### Fees -The fee incurred for a transaction can be calculated by: +The cost incurred by a transaction can be calculated by: -``` -fee = reserved_fee_balance(tx, BASE_ASSET) - floor(gas_to_fee(unspent_gas)) +```py +cost(tx) = gas_to_fee(gas_cost(tx) - unspentGas, tx.gasPrice) ``` where -- `reserved_fee_balance(tx, BASE_ASSET)` is the maximum cost of the transaction, including fees for inputs, outputs, and the bytes of the transaction, collectively known as intrinsic fees, predicate execution, as well as the amount of gas the user is willing to spend on script execution. -- `unspent_gas` is the amount gas left over after intrinsic fees and execution of the transaction. Converting unspent gas to a fee describes how much "change" is left over from the user's payment; the block producer collects this unspent gas as reward. - -The cost of the transaction is the result of the calculation of this fee balance. Users wishing to submit transactions can incentivize block producers to include their transactions by providing a higher reward in the form of more unspent gas. This is achieved by specifying a higher gas limit on the transaction. +- `gas_cost(tx)` is the final cost of the transaction in gas, including gas fees incurred from: + - The number of bytes comprising the transaction + - Processing inputs and outputs + - VM initialization + - Predicate and script execution +- `unspentGas` is the amount gas left over after intrinsic fees and execution of the transaction, extracted from the `$ggas` register. Converting unspent gas to a fee describes how much "change" is left over from the user's payment; the block producer collects this unspent gas as reward. +- `gas_to_fee` is a function that converts gas to a concrete fee based on a given gas price -``` -unspent_gas = tx.gas_limit - gas_balance(tx) -``` +Users wishing to submit transactions can incentivize block producers to include their transactions by providing a higher reward in the form of more unspent gas. This is achieved by specifying a higher gas limit on the transaction. -A natural result of the ability to vary the gas limit is the concept of minimum and maximum fees. The minimum fee is thus the exact fee required to pay the fee balance, while the maximum fee is, then, the minimum fee plus any gas remaining after execution paid as an incentive. +A naturally occurring result of a variable `gasLimit` is the concept of minimum and maximum fees. The minimum fee is thus the exact fee required to pay the fee balance, while the maximum fee is, then, the minimum fee plus any gas remaining after execution potentially paid to the producer as an incentive: +```py +min_gas = gas_cost(tx) +max_gas = min_gas + tx.gasLimit +min_fee = gas_to_fee(min_gas, tx.gasPrice) +max_fee = gas_to_fee(max_gas, tx.gasPrice) ``` -min_fee = reserved_fee_balance(tx, BASE_ASSET) -max_fee = min_fee + floor(gas_to_fee(unspent_gas)) -``` -In the case that all gas was consumed during execution, the `min_fee` and `max_fee` will be equal. +The cost of the transaction `cost(tx)` must lie within the range defined by [`min_fee`, `max_fee`]. The definition of `max_gas` illustrates that the delta between minimum fees and maximum fees is the user-defined `gasLimit`. Therefore, `min_fee` is the minimum reward the producer is guaranteed to collect, and `max_fee` is the maximum reward the producer is potentially eligible to collect. In practice, the user is always charged the cost of intrinsic fees. Calculating a conversion from `unspentGas` to an unspent fee describes the reward the producer will collect. + ## VM Postcondition Validity Rules From 664a49bded584c17b978ca0c4f87f38a969c827f Mon Sep 17 00:00:00 2001 From: Brandon Vrooman Date: Wed, 29 Nov 2023 19:21:39 -0500 Subject: [PATCH 28/40] Update tx-validity.md --- src/protocol/tx-validity.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/protocol/tx-validity.md b/src/protocol/tx-validity.md index e8fdf51c..238a6e43 100644 --- a/src/protocol/tx-validity.md +++ b/src/protocol/tx-validity.md @@ -300,13 +300,13 @@ If the transaction as included in a block does not match this final transaction, ### Fees -The cost incurred by a transaction can be calculated by: +The cost incurred by a transaction can be calculated by: ```py cost(tx) = gas_to_fee(gas_cost(tx) - unspentGas, tx.gasPrice) ``` -where +where: - `gas_cost(tx)` is the final cost of the transaction in gas, including gas fees incurred from: - The number of bytes comprising the transaction @@ -329,8 +329,6 @@ max_fee = gas_to_fee(max_gas, tx.gasPrice) The cost of the transaction `cost(tx)` must lie within the range defined by [`min_fee`, `max_fee`]. The definition of `max_gas` illustrates that the delta between minimum fees and maximum fees is the user-defined `gasLimit`. Therefore, `min_fee` is the minimum reward the producer is guaranteed to collect, and `max_fee` is the maximum reward the producer is potentially eligible to collect. In practice, the user is always charged the cost of intrinsic fees. Calculating a conversion from `unspentGas` to an unspent fee describes the reward the producer will collect. - - ## VM Postcondition Validity Rules This section defines _VM postcondition validity rules_ for transactions: the requirements for a transaction to be valid after it has been executed. From 797ad788ff2eea1da36795c4c8a21b2c82e10ddb Mon Sep 17 00:00:00 2001 From: Brandon Vrooman Date: Thu, 30 Nov 2023 12:44:25 -0500 Subject: [PATCH 29/40] Elaboration --- src/protocol/tx-validity.md | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/protocol/tx-validity.md b/src/protocol/tx-validity.md index 238a6e43..d0d6cc7a 100644 --- a/src/protocol/tx-validity.md +++ b/src/protocol/tx-validity.md @@ -300,7 +300,7 @@ If the transaction as included in a block does not match this final transaction, ### Fees -The cost incurred by a transaction can be calculated by: +The cost of a transaction can be described by: ```py cost(tx) = gas_to_fee(gas_cost(tx) - unspentGas, tx.gasPrice) @@ -309,25 +309,28 @@ cost(tx) = gas_to_fee(gas_cost(tx) - unspentGas, tx.gasPrice) where: - `gas_cost(tx)` is the final cost of the transaction in gas, including gas fees incurred from: - - The number of bytes comprising the transaction - - Processing inputs and outputs - - VM initialization + - Intrinsic fees: + - The number of bytes comprising the transaction + - Processing inputs and outputs + - VM initialization - Predicate and script execution - `unspentGas` is the amount gas left over after intrinsic fees and execution of the transaction, extracted from the `$ggas` register. Converting unspent gas to a fee describes how much "change" is left over from the user's payment; the block producer collects this unspent gas as reward. -- `gas_to_fee` is a function that converts gas to a concrete fee based on a given gas price +- `gas_to_fee` is a function that converts gas to a concrete fee based on a given gas price. -Users wishing to submit transactions can incentivize block producers to include their transactions by providing a higher reward in the form of more unspent gas. This is achieved by specifying a higher gas limit on the transaction. +Fees incurred by transaction processing outside the context of execution are collectively referred to as intrinsic fees. Intrinsic fees include the cost of storing the transaction, calculated on a per-byte basis, the cost of processing inputs and outputs, including signature verification, and initialization of the VM prior to any predicate or script execution. Because intrinsic fees are independent of execution, they can be determined _a priori_ and represent the bare minimum cost of the transaction. -A naturally occurring result of a variable `gasLimit` is the concept of minimum and maximum fees. The minimum fee is thus the exact fee required to pay the fee balance, while the maximum fee is, then, the minimum fee plus any gas remaining after execution potentially paid to the producer as an incentive: +Users wishing to submit transactions can incentivize block producers to include their transactions by providing a higher reward in the form of more unspent gas. This is achieved by specifying a higher [gas limit](../tx-format/transaction.md) on the transaction. + +A naturally occurring result of a variable gas limit is the concept of minimum and maximum fees. The minimum fee is, thus, the exact fee required to pay the fee balance, while the maximum fee is the minimum fee plus the gas limit: ```py -min_gas = gas_cost(tx) +min_gas = min_gas(tx) max_gas = min_gas + tx.gasLimit min_fee = gas_to_fee(min_gas, tx.gasPrice) max_fee = gas_to_fee(max_gas, tx.gasPrice) ``` -The cost of the transaction `cost(tx)` must lie within the range defined by [`min_fee`, `max_fee`]. The definition of `max_gas` illustrates that the delta between minimum fees and maximum fees is the user-defined `gasLimit`. Therefore, `min_fee` is the minimum reward the producer is guaranteed to collect, and `max_fee` is the maximum reward the producer is potentially eligible to collect. In practice, the user is always charged the cost of intrinsic fees. Calculating a conversion from `unspentGas` to an unspent fee describes the reward the producer will collect. +The cost of the transaction `cost(tx)` must lie within the range defined by [`min_fee`, `max_fee`]. `min_gas` is defined as the sum of all intrinsic costs of the transaction known prior to execution. The definition of `max_gas` illustrates that the delta between minimum gas and maximum gas is the user-defined `tx.gasLimit`. A transaction cost `cost(tx)`, in gas, greater than `max_gas` is invalid and must be rejected; this signifies that the user must provide a higher gas limit for the given transaction. `min_fee` is the minimum reward the producer is guaranteed to collect, and `max_fee` is the maximum reward the producer is potentially eligible to collect. In practice, the user is always charged intrinsic fees; thus, `unspentGas` is the remainder of `max_gas` after intrinsic fees and the variable cost of execution. Calculating a conversion from `unspentGas` to an unspent fee describes the reward the producer will collect. ## VM Postcondition Validity Rules From 7aa7556c04f169807f6f2eca1a2a39036f8fc66f Mon Sep 17 00:00:00 2001 From: Brandon Vrooman Date: Fri, 1 Dec 2023 20:12:30 -0500 Subject: [PATCH 30/40] Feedback --- src/protocol/tx-validity.md | 96 +++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 53 deletions(-) diff --git a/src/protocol/tx-validity.md b/src/protocol/tx-validity.md index d0d6cc7a..ae922037 100644 --- a/src/protocol/tx-validity.md +++ b/src/protocol/tx-validity.md @@ -33,9 +33,9 @@ Read-only access list: Write-destroy access list: - For each [input `InputType.Coin`](../tx-format/input.md#inputcoin) - - The [UTXO ID](../identifiers/utxo-id.md) `(txID, outputIndex)` + - The [UTXO ID](../identifiers/utxo-id.md) `(tx_id, output_index)` - For each [input `InputType.Contract`](../tx-format/input.md#inputcontract) - - The [UTXO ID](../identifiers/utxo-id.md) `(txID, outputIndex)` + - The [UTXO ID](../identifiers/utxo-id.md) `(tx_id, output_index)` - For each [input `InputType.Message`](../tx-format/input.md#inputmessage) - The [message ID](../identifiers/utxo-id.md#message-id) `messageID` @@ -46,7 +46,7 @@ Write-create access list: - For each output - The [created UTXO ID](../identifiers/utxo-id.md) -Note that block proposers use the contract ID `contractID` for inputs and outputs of type [`InputType.Contract`](../tx-format/input.md#inputcontract) and [`OutputType.Contract`](../tx-format/output.md#outputcontract) rather than the pair of `txID` and `outputIndex`. +Note that block proposers use the contract ID `contractID` for inputs and outputs of type [`InputType.Contract`](../tx-format/input.md#inputcontract) and [`OutputType.Contract`](../tx-format/output.md#outputcontract) rather than the pair of `tx_id` and `output_index`. ## VM Precondition Validity Rules @@ -54,7 +54,7 @@ This section defines _VM precondition validity rules_ for transactions: the bare For a transaction `tx`, UTXO set `state`, contract set `contracts`, and message set `messages`, the following checks must pass. -> **Note:** [InputMessages](../tx-format/input.md#inputmessage) where `input.dataLength > 0` are not dropped from the `messages` message set until they are included in a transaction of type `TransactionType.Script` with a `ScriptResult` receipt where `result` is equal to `0` indicating a successful script exit. +> **Note:** [InputMessages](../tx-format/input.md#inputmessage) where `input.data_length > 0` are not dropped from the `messages` message set until they are included in a transaction of type `TransactionType.Script` with a `ScriptResult` receipt where `result` is equal to `0` indicating a successful script exit. ### Base Sanity Checks @@ -71,12 +71,12 @@ for input in tx.inputs: if not input.nonce in messages: return False else: - if not (input.txID, input.outputIndex) in state: + if not (input.tx_id, input.output_index) in state: return False return True ``` -If this check passes, the UTXO ID `(txID, outputIndex)` fields of each contract input is set to the UTXO ID of the respective contract. The `txPointer` of each input is also set to the TX pointer of the UTXO with ID `utxoID`. +If this check passes, the UTXO ID `(tx_id, output_index)` fields of each contract input is set to the UTXO ID of the respective contract. The `txPointer` of each input is also set to the TX pointer of the UTXO with ID `utxoID`. ### Sufficient Balance @@ -87,7 +87,7 @@ def gas_to_fee(gas, gas_price) -> int: """ Converts gas units into a fee amount """ - return gas * gas_price / GAS_PRICE_FACTOR + return ceil(gas * gas_price / GAS_PRICE_FACTOR) def sum_data_messages(tx, asset_id) -> int: @@ -97,7 +97,7 @@ def sum_data_messages(tx, asset_id) -> int: total: int = 0 if asset_id == 0: for input in tx.inputs: - if input.type == InputType.Message and input.dataLength > 0: + if input.type == InputType.Message and input.data_length > 0: total += input.amount return total @@ -107,22 +107,12 @@ def sum_inputs(tx, asset_id) -> int: for input in tx.inputs: if input.type == InputType.Coin and input.asset_id == asset_id: total += input.amount - elif input.type == InputType.Message and asset_id == 0 and input.dataLength == 0: + elif input.type == InputType.Message and asset_id == 0 and input.data_length == 0: total += input.amount return total -def sum_predicate_gas_used(tx) -> int: - total: int = 0 - for input in tx.inputs: - if input.type == InputType.Coin: - total += input.predicateGasUsed - elif input.type == InputType.Message: - total += input.predicateGasUsed - return total - - -def sum_tx_bytes_gas_used(tx) -> int: +def transaction_size_gas_fees(tx) -> int: """ Computes the intrinsic gas cost of a transaction based on size in bytes """ @@ -146,60 +136,61 @@ def sum_outputs(tx, asset_id) -> int: return total -def sum_input_intrinsic_fees(tx) -> int: +def input_gas_fees(tx) -> int: """ Computes the intrinsic gas cost of verifying input utxos """ total: int = 0 witness_indices = set(()) for input in tx.inputs: - # add fees required to read utxo from state - total += state_read_fee() if input.type == InputType.Coin or input.type == InputType.Message: - # add fees required to remove coin or message from state - # note: this does not apply to contract inputs, as they are updated in place by the output - total += state_delete_fee() # add fees allocated for predicate execution - total += gas_to_fee(input.predicateGasUsed, tx.gasPrice) - if input.predicateLength == 0: + if input.predicate_length == 0: # notate witness index if input is signed - witness_indices.add(input.witnessIndex) + witness_indices.add(input.witness_index) else: - # add intrinsic cost of predicate merkleization based on number of predicate bytes - total += contract_root_bytes_fee(input.predicateLength) + # add intrinsic gas cost of predicate merkleization based on number of predicate bytes + total += contract_code_root_gas_fee(input.predicate_length) + total += input.predicate_gas_used # add intrinsic cost of vm initialization - total += vm_initialization_fee() + total += vm_initialization_gas_fee() # add intrinsic cost of verifying witness signatures - total += len(witness_indices) * eck1_recover_fee() + total += len(witness_indices) * eck1_recover_gas_fee() return total -def sum_output_intrinsic_fees(tx) -> int: +def metadata_gas_fees(tx) -> int: """ Computes the intrinsic gas cost of processing transaction outputs """ total: int = 0 - for output in tx.outputs: - # adds fees required to write new output to state - total += state_write_fee(size(output)) - if output.type == OutputType.OutputContractCreated: - # add intrinsic cost of verifying the contract root based on the size of the contract bytecode - total += contract_root_bytes_fee(tx.witnesses[tx.bytecodeWitnessIndex].dataLength) - # add intrinsic cost of initializing contract storage - # note: smt_insert_fee may be dependent on the storage value if the protocol adopts dynamic sized slots - total += tx.storageSlotCount * smt_insert_fee() + if tx.type == TransactionType.Create: + for output in tx.outputs: + if output.type == OutputType.OutputContractCreated: + # add intrinsic cost of verifying the contract root based on the size of the contract bytecode + total += contract_code_root_gas_fee(tx.witnesses[tx.bytecode_witness_index].data_length) + # add intrinsic cost of initializing contract storage + total += contract_state_root_gas_fee(tx.storage_slot_count) + # add intrinsic cost of calculating the contract id + total += sha256_gas_fee(96) + # add intrinsic cost of calculating the transaction id + total += sha256_gas_fee(size(tx)) + elif tx.type == TransactionType.Script: + # add intrinsic cost of calculating the transaction id + total += sha256(size(tx)) return total -def intrinsic_fees(tx) -> int: +def intrinsic_gas_fees(tx) -> int: """ Computes intrinsic costs for a transaction """ fees: int = 0 # add the cost of initializing a vm for the script - if tx.type == TransactionType.Script: - fees += vm_initialization_fee() - fees += sum_input_intrinsic_fees(tx) + sum_output_intrinsic_fees(tx) + if tx.type == TransactionType.Create or tx.type == TransactionType.Script: + fees += vm_initialization_gas_fee() + fees += metadata_gas_fees(tx) + fees += intrinsic_input_gas_fees(tx) return fees @@ -207,7 +198,7 @@ def gas_balance(tx) -> int: """ Computes the maximum amount of gas required to process a transaction. """ - gas = tx.gasLimit + sum_predicate_gas_used(tx) + sum_tx_bytes_gas_used(tx) + intrinsic_fees(tx) + gas = tx.gasLimit + transaction_size_gas_fees(tx) + intrinsic_gas_fees(tx) return gas @@ -217,7 +208,6 @@ def reserved_fee_balance(tx, asset_id) -> int: """ gas_balance = gas_balance(tx) fee_balance = gas_to_fee(gas_balance, tx.gasPrice) - fee_balance = ceil(fee_balance) # Only base asset can be used to pay for gas if asset_id == 0: return fee_balance @@ -256,12 +246,12 @@ def address_from(pubkey: bytes) -> bytes: return sha256(pubkey)[0:32] for input in tx.inputs: - if (input.type == InputType.Coin or input.type == InputType.Message) and input.predicateLength == 0: + if (input.type == InputType.Coin or input.type == InputType.Message) and input.predicate_length == 0: # ECDSA signatures must be 64 bytes - if tx.witnesses[input.witnessIndex].dataLength != 64: + if tx.witnesses[input.witness_index].data_length != 64: return False # Signature must be from owner - if address_from(ecrecover_k1(txhash(), tx.witnesses[input.witnessIndex].data)) != input.owner: + if address_from(ecrecover_k1(txhash(), tx.witnesses[input.witness_index].data)) != input.owner: return False return True ``` @@ -272,7 +262,7 @@ The transaction hash is computed as defined [here](../identifiers/transaction-id ## Predicate Verification -For each input of type `InputType.Coin` or `InputType.Message`, and `predicateLength > 0`, [verify its predicate](../fuel-vm/index.md#predicate-verification). +For each input of type `InputType.Coin` or `InputType.Message`, and `predicate_length > 0`, [verify its predicate](../fuel-vm/index.md#predicate-verification). ## Script Execution From a0c597778e74df66d801c84b96a1edfbef814350 Mon Sep 17 00:00:00 2001 From: Brandon Vrooman Date: Fri, 1 Dec 2023 20:30:05 -0500 Subject: [PATCH 31/40] Correct metadata calculation --- src/protocol/tx-validity.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/protocol/tx-validity.md b/src/protocol/tx-validity.md index ae922037..07cbcba7 100644 --- a/src/protocol/tx-validity.md +++ b/src/protocol/tx-validity.md @@ -167,17 +167,18 @@ def metadata_gas_fees(tx) -> int: if tx.type == TransactionType.Create: for output in tx.outputs: if output.type == OutputType.OutputContractCreated: - # add intrinsic cost of verifying the contract root based on the size of the contract bytecode + # add intrinsic cost of calculating the code root based on the size of the contract bytecode total += contract_code_root_gas_fee(tx.witnesses[tx.bytecode_witness_index].data_length) - # add intrinsic cost of initializing contract storage + # add intrinsic cost of calculating the state root based on the number of sotrage slots total += contract_state_root_gas_fee(tx.storage_slot_count) - # add intrinsic cost of calculating the contract id - total += sha256_gas_fee(96) + # add intrinsic cost of calculating the contract id + # size = 4 byte seed + 32 byte salt + 32 byte code root + 32 byte state root + total += sha256_gas_fee(100) # add intrinsic cost of calculating the transaction id total += sha256_gas_fee(size(tx)) elif tx.type == TransactionType.Script: # add intrinsic cost of calculating the transaction id - total += sha256(size(tx)) + total += sha256_gas_fee(size(tx)) return total From 1d8e25e78413078bf0c68b3eff4223be903701fa Mon Sep 17 00:00:00 2001 From: Brandon Vrooman Date: Wed, 6 Dec 2023 17:19:02 -0500 Subject: [PATCH 32/40] Apply suggestions from code review Co-authored-by: Hannes Karppila --- src/protocol/tx-validity.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/protocol/tx-validity.md b/src/protocol/tx-validity.md index 07cbcba7..a8545e9f 100644 --- a/src/protocol/tx-validity.md +++ b/src/protocol/tx-validity.md @@ -116,7 +116,7 @@ def transaction_size_gas_fees(tx) -> int: """ Computes the intrinsic gas cost of a transaction based on size in bytes """ - size(tx) * GAS_PER_BYTE + return size(tx) * GAS_PER_BYTE def minted(tx, asset_id) -> int: @@ -141,7 +141,7 @@ def input_gas_fees(tx) -> int: Computes the intrinsic gas cost of verifying input utxos """ total: int = 0 - witness_indices = set(()) + witness_indices = set() for input in tx.inputs: if input.type == InputType.Coin or input.type == InputType.Message: # add fees allocated for predicate execution From 40b9fc7ca65036c73b53bc52b7852cfa70fe058d Mon Sep 17 00:00:00 2001 From: Brandon Vrooman Date: Sun, 10 Dec 2023 19:13:34 -0500 Subject: [PATCH 33/40] Feedback --- src/protocol/tx-validity.md | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/src/protocol/tx-validity.md b/src/protocol/tx-validity.md index 07cbcba7..cb252baa 100644 --- a/src/protocol/tx-validity.md +++ b/src/protocol/tx-validity.md @@ -195,11 +195,19 @@ def intrinsic_gas_fees(tx) -> int: return fees -def gas_balance(tx) -> int: +def min_gas(tx) -> int: """ - Computes the maximum amount of gas required to process a transaction. + Comutes the minimum amount of gas required for a transaction to begin processing. """ - gas = tx.gasLimit + transaction_size_gas_fees(tx) + intrinsic_gas_fees(tx) + gas = transaction_size_gas_fees(tx) + intrinsic_gas_fees(tx) + return gas + + +def max_gas(tx) -> int: + """ + Computes the amount of gas required to process a transaction. + """ + gas = min_gas(tx) + tx.gasLimit return gas @@ -207,7 +215,7 @@ def reserved_fee_balance(tx, asset_id) -> int: """ Computes the maximum potential amount of fees that may need to be charged to process a transaction. """ - gas_balance = gas_balance(tx) + gas_balance = max_gas(tx) fee_balance = gas_to_fee(gas_balance, tx.gasPrice) # Only base asset can be used to pay for gas if asset_id == 0: @@ -294,23 +302,20 @@ If the transaction as included in a block does not match this final transaction, The cost of a transaction can be described by: ```py -cost(tx) = gas_to_fee(gas_cost(tx) - unspentGas, tx.gasPrice) +cost(tx) = gas_to_fee(min_gas(tx) + tx.gasLimit - unspentGas, tx.gasPrice) ``` where: -- `gas_cost(tx)` is the final cost of the transaction in gas, including gas fees incurred from: - - Intrinsic fees: - - The number of bytes comprising the transaction - - Processing inputs and outputs - - VM initialization - - Predicate and script execution +- `min_gas(tx)` is the minimum cost of the transaction in gas, including intrinsic gas fees incurred from: + - The number of bytes comprising the transaction + - Processing inputs, including predicates + - Processing outputs + - VM initialization - `unspentGas` is the amount gas left over after intrinsic fees and execution of the transaction, extracted from the `$ggas` register. Converting unspent gas to a fee describes how much "change" is left over from the user's payment; the block producer collects this unspent gas as reward. - `gas_to_fee` is a function that converts gas to a concrete fee based on a given gas price. -Fees incurred by transaction processing outside the context of execution are collectively referred to as intrinsic fees. Intrinsic fees include the cost of storing the transaction, calculated on a per-byte basis, the cost of processing inputs and outputs, including signature verification, and initialization of the VM prior to any predicate or script execution. Because intrinsic fees are independent of execution, they can be determined _a priori_ and represent the bare minimum cost of the transaction. - -Users wishing to submit transactions can incentivize block producers to include their transactions by providing a higher reward in the form of more unspent gas. This is achieved by specifying a higher [gas limit](../tx-format/transaction.md) on the transaction. +Fees incurred by transaction processing outside the context of execution are collectively referred to as intrinsic fees. Intrinsic fees include the cost of storing the transaction, calculated on a per-byte basis, the cost of processing inputs and outputs, including predicates and signature verification, and initialization of the VM prior to any predicate or script execution. Because intrinsic fees are independent of execution, they can be determined _a priori_ and represent the bare minimum cost of the transaction. A naturally occurring result of a variable gas limit is the concept of minimum and maximum fees. The minimum fee is, thus, the exact fee required to pay the fee balance, while the maximum fee is the minimum fee plus the gas limit: @@ -321,7 +326,7 @@ min_fee = gas_to_fee(min_gas, tx.gasPrice) max_fee = gas_to_fee(max_gas, tx.gasPrice) ``` -The cost of the transaction `cost(tx)` must lie within the range defined by [`min_fee`, `max_fee`]. `min_gas` is defined as the sum of all intrinsic costs of the transaction known prior to execution. The definition of `max_gas` illustrates that the delta between minimum gas and maximum gas is the user-defined `tx.gasLimit`. A transaction cost `cost(tx)`, in gas, greater than `max_gas` is invalid and must be rejected; this signifies that the user must provide a higher gas limit for the given transaction. `min_fee` is the minimum reward the producer is guaranteed to collect, and `max_fee` is the maximum reward the producer is potentially eligible to collect. In practice, the user is always charged intrinsic fees; thus, `unspentGas` is the remainder of `max_gas` after intrinsic fees and the variable cost of execution. Calculating a conversion from `unspentGas` to an unspent fee describes the reward the producer will collect. +The cost of the transaction `cost(tx)` must lie within the range defined by [`min_fee`, `max_fee`]. `min_gas` is defined as the sum of all intrinsic costs of the transaction known prior to execution. The definition of `max_gas` illustrates that the delta between minimum gas and maximum gas is the user-defined `tx.gasLimit`. A transaction cost `cost(tx)`, in gas, greater than `max_gas` is invalid and must be rejected; this signifies that the user must provide a higher gas limit for the given transaction. `min_fee` is the minimum reward the producer is guaranteed to collect, and `max_fee` is the maximum reward the producer is potentially eligible to collect. In practice, the user is always charged intrinsic fees; thus, `unspentGas` is the remainder of `max_gas` after intrinsic fees and the variable cost of execution. Calculating a conversion from `unspentGas` to an unspent fee describes the reward the producer will collect in addition to `min_fee`. ## VM Postcondition Validity Rules From 1328e2a478bddecd40748830e8682e5ff9727654 Mon Sep 17 00:00:00 2001 From: Brandon Vrooman Date: Sun, 10 Dec 2023 19:21:36 -0500 Subject: [PATCH 34/40] snake case to camel case for variables --- src/protocol/tx-validity.md | 90 ++++++++++++++++++------------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/src/protocol/tx-validity.md b/src/protocol/tx-validity.md index f9893d88..0d05532c 100644 --- a/src/protocol/tx-validity.md +++ b/src/protocol/tx-validity.md @@ -33,9 +33,9 @@ Read-only access list: Write-destroy access list: - For each [input `InputType.Coin`](../tx-format/input.md#inputcoin) - - The [UTXO ID](../identifiers/utxo-id.md) `(tx_id, output_index)` + - The [UTXO ID](../identifiers/utxo-id.md) `(txId, outputIndex)` - For each [input `InputType.Contract`](../tx-format/input.md#inputcontract) - - The [UTXO ID](../identifiers/utxo-id.md) `(tx_id, output_index)` + - The [UTXO ID](../identifiers/utxo-id.md) `(txId, outputIndex)` - For each [input `InputType.Message`](../tx-format/input.md#inputmessage) - The [message ID](../identifiers/utxo-id.md#message-id) `messageID` @@ -46,7 +46,7 @@ Write-create access list: - For each output - The [created UTXO ID](../identifiers/utxo-id.md) -Note that block proposers use the contract ID `contractID` for inputs and outputs of type [`InputType.Contract`](../tx-format/input.md#inputcontract) and [`OutputType.Contract`](../tx-format/output.md#outputcontract) rather than the pair of `tx_id` and `output_index`. +Note that block proposers use the contract ID `contractID` for inputs and outputs of type [`InputType.Contract`](../tx-format/input.md#inputcontract) and [`OutputType.Contract`](../tx-format/output.md#outputcontract) rather than the pair of `txId` and `outputIndex`. ## VM Precondition Validity Rules @@ -54,7 +54,7 @@ This section defines _VM precondition validity rules_ for transactions: the bare For a transaction `tx`, UTXO set `state`, contract set `contracts`, and message set `messages`, the following checks must pass. -> **Note:** [InputMessages](../tx-format/input.md#inputmessage) where `input.data_length > 0` are not dropped from the `messages` message set until they are included in a transaction of type `TransactionType.Script` with a `ScriptResult` receipt where `result` is equal to `0` indicating a successful script exit. +> **Note:** [InputMessages](../tx-format/input.md#inputmessage) where `input.dataLength > 0` are not dropped from the `messages` message set until they are included in a transaction of type `TransactionType.Script` with a `ScriptResult` receipt where `result` is equal to `0` indicating a successful script exit. ### Base Sanity Checks @@ -71,43 +71,43 @@ for input in tx.inputs: if not input.nonce in messages: return False else: - if not (input.tx_id, input.output_index) in state: + if not (input.txId, input.outputIndex) in state: return False return True ``` -If this check passes, the UTXO ID `(tx_id, output_index)` fields of each contract input is set to the UTXO ID of the respective contract. The `txPointer` of each input is also set to the TX pointer of the UTXO with ID `utxoID`. +If this check passes, the UTXO ID `(txId, outputIndex)` fields of each contract input is set to the UTXO ID of the respective contract. The `txPointer` of each input is also set to the TX pointer of the UTXO with ID `utxoID`. ### Sufficient Balance -For each asset ID `asset_id` in the input and output set: +For each asset ID `assetId` in the input and output set: ```py -def gas_to_fee(gas, gas_price) -> int: +def gas_to_fee(gas, gasPrice) -> int: """ Converts gas units into a fee amount """ - return ceil(gas * gas_price / GAS_PRICE_FACTOR) + return ceil(gas * gasPrice / GAS_PRICE_FACTOR) -def sum_data_messages(tx, asset_id) -> int: +def sum_data_messages(tx, assetId) -> int: """ Returns the total balance available from messages containing data """ total: int = 0 - if asset_id == 0: + if assetId == 0: for input in tx.inputs: - if input.type == InputType.Message and input.data_length > 0: + if input.type == InputType.Message and input.dataLength > 0: total += input.amount return total -def sum_inputs(tx, asset_id) -> int: +def sum_inputs(tx, assetId) -> int: total: int = 0 for input in tx.inputs: - if input.type == InputType.Coin and input.asset_id == asset_id: + if input.type == InputType.Coin and input.assetId == assetId: total += input.amount - elif input.type == InputType.Message and asset_id == 0 and input.data_length == 0: + elif input.type == InputType.Message and assetId == 0 and input.dataLength == 0: total += input.amount return total @@ -119,19 +119,19 @@ def transaction_size_gas_fees(tx) -> int: return size(tx) * GAS_PER_BYTE -def minted(tx, asset_id) -> int: +def minted(tx, assetId) -> int: """ Returns any minted amounts by the transaction """ - if tx.type != TransactionType.Mint or asset_id != tx.mint_asset_id: + if tx.type != TransactionType.Mint or assetId != tx.mintAssetId: return 0 return tx.mint_amount -def sum_outputs(tx, asset_id) -> int: +def sum_outputs(tx, assetId) -> int: total: int = 0 for output in tx.outputs: - if output.type == OutputType.Coin and output.asset_id == asset_id: + if output.type == OutputType.Coin and output.assetId == assetId: total += output.amount return total @@ -141,21 +141,21 @@ def input_gas_fees(tx) -> int: Computes the intrinsic gas cost of verifying input utxos """ total: int = 0 - witness_indices = set() + witnessIndices = set() for input in tx.inputs: if input.type == InputType.Coin or input.type == InputType.Message: # add fees allocated for predicate execution - if input.predicate_length == 0: + if input.predicateLength == 0: # notate witness index if input is signed - witness_indices.add(input.witness_index) + witnessIndices.add(input.witnessIndex) else: # add intrinsic gas cost of predicate merkleization based on number of predicate bytes - total += contract_code_root_gas_fee(input.predicate_length) + total += contract_code_root_gas_fee(input.predicateLength) total += input.predicate_gas_used # add intrinsic cost of vm initialization total += vm_initialization_gas_fee() # add intrinsic cost of verifying witness signatures - total += len(witness_indices) * eck1_recover_gas_fee() + total += len(witnessIndices) * eck1_recover_gas_fee() return total @@ -168,9 +168,9 @@ def metadata_gas_fees(tx) -> int: for output in tx.outputs: if output.type == OutputType.OutputContractCreated: # add intrinsic cost of calculating the code root based on the size of the contract bytecode - total += contract_code_root_gas_fee(tx.witnesses[tx.bytecode_witness_index].data_length) + total += contract_code_root_gas_fee(tx.witnesses[tx.bytecodeWitnessIndex].dataLength) # add intrinsic cost of calculating the state root based on the number of sotrage slots - total += contract_state_root_gas_fee(tx.storage_slot_count) + total += contract_state_root_gas_fee(tx.storageSlotCount) # add intrinsic cost of calculating the contract id # size = 4 byte seed + 32 byte salt + 32 byte code root + 32 byte state root total += sha256_gas_fee(100) @@ -211,33 +211,33 @@ def max_gas(tx) -> int: return gas -def reserved_fee_balance(tx, asset_id) -> int: +def reserved_feeBalance(tx, assetId) -> int: """ Computes the maximum potential amount of fees that may need to be charged to process a transaction. """ - gas_balance = max_gas(tx) - fee_balance = gas_to_fee(gas_balance, tx.gasPrice) + gasBalance = max_gas(tx) + feeBalance = gas_to_fee(gasBalance, tx.gasPrice) # Only base asset can be used to pay for gas - if asset_id == 0: - return fee_balance + if assetId == 0: + return feeBalance else: return 0 -def available_balance(tx, asset_id) -> int: +def available_balance(tx, assetId) -> int: """ Make the data message balance available to the script """ - availableBalance = sum_inputs(tx, asset_id) + sum_data_messages(tx, asset_id) + minted(tx, asset_id) + availableBalance = sum_inputs(tx, assetId) + sum_data_messages(tx, assetId) + minted(tx, assetId) return availableBalance -def unavailable_balance(tx, asset_id) -> int: - sentBalance = sum_outputs(tx, asset_id) +def unavailable_balance(tx, assetId) -> int: + sentBalance = sum_outputs(tx, assetId) # Total fee balance - feeBalance = reserved_fee_balance(tx, asset_id) + feeBalance = reserved_fee_balance(tx, assetId) # Only base asset can be used to pay for gas - if asset_id == 0: + if assetId == 0: return sentBalance + feeBalance return sentBalance @@ -245,7 +245,7 @@ def unavailable_balance(tx, asset_id) -> int: # The sum_data_messages total is not included in the unavailable_balance since it is spendable as long as there # is enough base asset amount to cover gas costs without using data messages. Messages containing data can't # cover gas costs since they are retryable. -return available_balance(tx, asset_id) >= (unavailable_balance(tx, asset_id) + sum_data_messages(tx, asset_id)) +return available_balance(tx, assetId) >= (unavailable_balance(tx, assetId) + sum_data_messages(tx, assetId)) ``` ### Valid Signatures @@ -255,12 +255,12 @@ def address_from(pubkey: bytes) -> bytes: return sha256(pubkey)[0:32] for input in tx.inputs: - if (input.type == InputType.Coin or input.type == InputType.Message) and input.predicate_length == 0: + if (input.type == InputType.Coin or input.type == InputType.Message) and input.predicateLength == 0: # ECDSA signatures must be 64 bytes - if tx.witnesses[input.witness_index].data_length != 64: + if tx.witnesses[input.witnessIndex].dataLength != 64: return False # Signature must be from owner - if address_from(ecrecover_k1(txhash(), tx.witnesses[input.witness_index].data)) != input.owner: + if address_from(ecrecover_k1(txhash(), tx.witnesses[input.witnessIndex].data)) != input.owner: return False return True ``` @@ -271,7 +271,7 @@ The transaction hash is computed as defined [here](../identifiers/transaction-id ## Predicate Verification -For each input of type `InputType.Coin` or `InputType.Message`, and `predicate_length > 0`, [verify its predicate](../fuel-vm/index.md#predicate-verification). +For each input of type `InputType.Coin` or `InputType.Message`, and `predicateLength > 0`, [verify its predicate](../fuel-vm/index.md#predicate-verification). ## Script Execution @@ -279,10 +279,10 @@ Given transaction `tx`, the following checks must pass: If `tx.scriptLength == 0`, there is no script and the transaction defines a simple balance transfer, so no further checks are required. -If `tx.scriptLength > 0`, the script must be executed. For each asset ID `asset_id` in the input set, the free balance available to be moved around by the script and called contracts is `freeBalance[asset_id]`. The initial message balance available to be moved around by the script and called contracts is `messageBalance`: +If `tx.scriptLength > 0`, the script must be executed. For each asset ID `assetId` in the input set, the free balance available to be moved around by the script and called contracts is `freeBalance[assetId]`. The initial message balance available to be moved around by the script and called contracts is `messageBalance`: ```py -freeBalance[asset_id] = available_balance(tx, asset_id) - unavailable_balance(tx, asset_id) +freeBalance[assetId] = available_balance(tx, assetId) - unavailable_balance(tx, assetId) messageBalance = sum_data_messages(tx, 0) ``` @@ -358,7 +358,7 @@ In order for a coinbase transaction to be valid: 1. It must be a [Mint](../tx-format/transaction.md#TransactionMint) transaction. 1. The coinbase transaction must be the last transaction within a block, even if there are no other transactions in the block and the fee is zero. 1. The `mintAmount` doesn't exceed the total amount of fees processed from all other transactions within the same block. -1. The `mintAssetId` matches the `asset_id` that fees are paid in (`asset_id == 0`). +1. The `mintAssetId` matches the `assetId` that fees are paid in (`assetId == 0`). The minted amount of the coinbase transaction intrinsically increases the balance corresponding to the `inputContract`. This means the balance of `mintAssetId` is directly increased by `mintAmount` on the input contract, From 1d8b1c92317fe4cc60ba72f78192f67aef432f32 Mon Sep 17 00:00:00 2001 From: Brandon Vrooman Date: Sun, 10 Dec 2023 19:22:46 -0500 Subject: [PATCH 35/40] Update snake case to camel case --- src/protocol/tx-validity.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/protocol/tx-validity.md b/src/protocol/tx-validity.md index 0d05532c..735899e6 100644 --- a/src/protocol/tx-validity.md +++ b/src/protocol/tx-validity.md @@ -151,7 +151,7 @@ def input_gas_fees(tx) -> int: else: # add intrinsic gas cost of predicate merkleization based on number of predicate bytes total += contract_code_root_gas_fee(input.predicateLength) - total += input.predicate_gas_used + total += input.predicateGasUsed # add intrinsic cost of vm initialization total += vm_initialization_gas_fee() # add intrinsic cost of verifying witness signatures From 29c0323f01f7be830687088cfa7fa2d61e50afb0 Mon Sep 17 00:00:00 2001 From: Brandon Vrooman Date: Mon, 11 Dec 2023 14:20:30 -0500 Subject: [PATCH 36/40] Apply suggestions from code review Co-authored-by: Green Baneling --- src/protocol/tx-validity.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/protocol/tx-validity.md b/src/protocol/tx-validity.md index 735899e6..35e207fa 100644 --- a/src/protocol/tx-validity.md +++ b/src/protocol/tx-validity.md @@ -215,8 +215,8 @@ def reserved_feeBalance(tx, assetId) -> int: """ Computes the maximum potential amount of fees that may need to be charged to process a transaction. """ - gasBalance = max_gas(tx) - feeBalance = gas_to_fee(gasBalance, tx.gasPrice) + maxGas = max_gas(tx) + feeBalance = gas_to_fee(maxGas, tx.gasPrice) # Only base asset can be used to pay for gas if assetId == 0: return feeBalance @@ -326,7 +326,7 @@ min_fee = gas_to_fee(min_gas, tx.gasPrice) max_fee = gas_to_fee(max_gas, tx.gasPrice) ``` -The cost of the transaction `cost(tx)` must lie within the range defined by [`min_fee`, `max_fee`]. `min_gas` is defined as the sum of all intrinsic costs of the transaction known prior to execution. The definition of `max_gas` illustrates that the delta between minimum gas and maximum gas is the user-defined `tx.gasLimit`. A transaction cost `cost(tx)`, in gas, greater than `max_gas` is invalid and must be rejected; this signifies that the user must provide a higher gas limit for the given transaction. `min_fee` is the minimum reward the producer is guaranteed to collect, and `max_fee` is the maximum reward the producer is potentially eligible to collect. In practice, the user is always charged intrinsic fees; thus, `unspentGas` is the remainder of `max_gas` after intrinsic fees and the variable cost of execution. Calculating a conversion from `unspentGas` to an unspent fee describes the reward the producer will collect in addition to `min_fee`. +The cost of the transaction `cost(tx)` must lie within the range defined by [`min_fee`, `max_fee`]. `min_gas` is defined as the sum of all intrinsic costs of the transaction known prior to execution. The definition of `max_gas` illustrates that the delta between minimum gas and maximum gas is the user-defined `tx.gasLimit` and `tx.witnessLimit`. A transaction cost `cost(tx)`, in gas, greater than `max_gas` is invalid and must be rejected; this signifies that the user must provide a higher gas limit for the given transaction. `min_fee` is the minimum reward the producer is guaranteed to collect, and `max_fee` is the maximum reward the producer is potentially eligible to collect. In practice, the user is always charged intrinsic fees; thus, `unspentGas` is the remainder of `max_gas` after intrinsic fees and the variable cost of execution. Calculating a conversion from `unspentGas` to an unspent fee describes the reward the producer will collect in addition to `min_fee`. ## VM Postcondition Validity Rules From f3d57ca96c82a818dc8dff46e2b3d91ed62d99a5 Mon Sep 17 00:00:00 2001 From: Brandon Vrooman Date: Mon, 11 Dec 2023 14:30:57 -0500 Subject: [PATCH 37/40] Update notes on max gas --- src/protocol/tx-validity.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/protocol/tx-validity.md b/src/protocol/tx-validity.md index 735899e6..d672f25f 100644 --- a/src/protocol/tx-validity.md +++ b/src/protocol/tx-validity.md @@ -321,12 +321,18 @@ A naturally occurring result of a variable gas limit is the concept of minimum a ```py min_gas = min_gas(tx) -max_gas = min_gas + tx.gasLimit +max_gas = min_gas + (WitnessBytesLimit - ActualWitnessBytes) * GAS_PER_BYTE + tx.gasLimit min_fee = gas_to_fee(min_gas, tx.gasPrice) max_fee = gas_to_fee(max_gas, tx.gasPrice) ``` -The cost of the transaction `cost(tx)` must lie within the range defined by [`min_fee`, `max_fee`]. `min_gas` is defined as the sum of all intrinsic costs of the transaction known prior to execution. The definition of `max_gas` illustrates that the delta between minimum gas and maximum gas is the user-defined `tx.gasLimit`. A transaction cost `cost(tx)`, in gas, greater than `max_gas` is invalid and must be rejected; this signifies that the user must provide a higher gas limit for the given transaction. `min_fee` is the minimum reward the producer is guaranteed to collect, and `max_fee` is the maximum reward the producer is potentially eligible to collect. In practice, the user is always charged intrinsic fees; thus, `unspentGas` is the remainder of `max_gas` after intrinsic fees and the variable cost of execution. Calculating a conversion from `unspentGas` to an unspent fee describes the reward the producer will collect in addition to `min_fee`. +The cost of the transaction `cost(tx)` must lie within the range defined by [`min_fee`, `max_fee`]. `min_gas` is defined as the sum of all intrinsic costs of the transaction known prior to execution. The definition of `max_gas` illustrates that the delta between minimum gas and maximum gas is the sum of: +- The remaining allocation of witness bytes, converted to gas +- The user-defined `tx.gasLimit` + +Note that `gasLimit` applies to transactions of type `Script`. `gas_limit` is not applicable for transactions of type `Create` and is defined to equal `0` in the above formula. + +A transaction cost `cost(tx)`, in gas, greater than `max_gas` is invalid and must be rejected; this signifies that the user must provide a higher gas limit for the given transaction. `min_fee` is the minimum reward the producer is guaranteed to collect, and `max_fee` is the maximum reward the producer is potentially eligible to collect. In practice, the user is always charged intrinsic fees; thus, `unspentGas` is the remainder of `max_gas` after intrinsic fees and the variable cost of execution. Calculating a conversion from `unspentGas` to an unspent fee describes the reward the producer will collect in addition to `min_fee`. ## VM Postcondition Validity Rules From 8393b7e517700c14cc1bc3dfa40a9afb62fd8883 Mon Sep 17 00:00:00 2001 From: Brandon Vrooman Date: Mon, 11 Dec 2023 14:32:32 -0500 Subject: [PATCH 38/40] fix typo --- src/protocol/tx-validity.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/protocol/tx-validity.md b/src/protocol/tx-validity.md index ce42e7f2..694f8eff 100644 --- a/src/protocol/tx-validity.md +++ b/src/protocol/tx-validity.md @@ -330,7 +330,7 @@ The cost of the transaction `cost(tx)` must lie within the range defined by [`mi - The remaining allocation of witness bytes, converted to gas - The user-defined `tx.gasLimit` -Note that `gasLimit` applies to transactions of type `Script`. `gas_limit` is not applicable for transactions of type `Create` and is defined to equal `0` in the above formula. +Note that `gasLimit` applies to transactions of type `Script`. `gasLimit` is not applicable for transactions of type `Create` and is defined to equal `0` in the above formula. A transaction cost `cost(tx)`, in gas, greater than `max_gas` is invalid and must be rejected; this signifies that the user must provide a higher gas limit for the given transaction. `min_fee` is the minimum reward the producer is guaranteed to collect, and `max_fee` is the maximum reward the producer is potentially eligible to collect. In practice, the user is always charged intrinsic fees; thus, `unspentGas` is the remainder of `max_gas` after intrinsic fees and the variable cost of execution. Calculating a conversion from `unspentGas` to an unspent fee describes the reward the producer will collect in addition to `min_fee`. From 954b22d22648ffc5f4e08a6ed363dff8d76440fd Mon Sep 17 00:00:00 2001 From: Brandon Vrooman Date: Mon, 11 Dec 2023 14:38:59 -0500 Subject: [PATCH 39/40] Fix max_gas fn --- src/protocol/tx-validity.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/protocol/tx-validity.md b/src/protocol/tx-validity.md index 694f8eff..bdf2cc71 100644 --- a/src/protocol/tx-validity.md +++ b/src/protocol/tx-validity.md @@ -207,7 +207,10 @@ def max_gas(tx) -> int: """ Computes the amount of gas required to process a transaction. """ - gas = min_gas(tx) + tx.gasLimit + gas = min_gas(tx) + gas = gas + (tx.witnessLimit - size(tx.witnesses)) * GAS_PER_BYTE + if tx.type == TransactionType.Script: + gas = gas + tx.gasLimit return gas From 1b6e806dea9dd1da55df44ac40800a1c63056ba6 Mon Sep 17 00:00:00 2001 From: Brandon Vrooman Date: Mon, 11 Dec 2023 14:50:04 -0500 Subject: [PATCH 40/40] Update tx-validity.md --- src/protocol/tx-validity.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/protocol/tx-validity.md b/src/protocol/tx-validity.md index bdf2cc71..393eea94 100644 --- a/src/protocol/tx-validity.md +++ b/src/protocol/tx-validity.md @@ -208,7 +208,7 @@ def max_gas(tx) -> int: Computes the amount of gas required to process a transaction. """ gas = min_gas(tx) - gas = gas + (tx.witnessLimit - size(tx.witnesses)) * GAS_PER_BYTE + gas = gas + (tx.witnessBytesLimit - tx.witnessBytes) * GAS_PER_BYTE if tx.type == TransactionType.Script: gas = gas + tx.gasLimit return gas @@ -324,7 +324,7 @@ A naturally occurring result of a variable gas limit is the concept of minimum a ```py min_gas = min_gas(tx) -max_gas = min_gas + (WitnessBytesLimit - ActualWitnessBytes) * GAS_PER_BYTE + tx.gasLimit +max_gas = min_gas + (tx.witnessBytesLimit - tx.witnessBytes) * GAS_PER_BYTE + tx.gasLimit min_fee = gas_to_fee(min_gas, tx.gasPrice) max_fee = gas_to_fee(max_gas, tx.gasPrice) ```