Skip to content

Commit

Permalink
A new Upload transaction to upload the huge bytecode on the chain (#…
Browse files Browse the repository at this point in the history
…570)

Corresponding implementation:
FuelLabs/fuel-vm#720
The change adds a new `Upload` transaction that allows uploading huge
byte code on chain subsection by subsection.

The `Upload` transaction is chargeable and is twice as expensive as the
`Create` transaction. Anyone can submit this transaction.

The specification contains more description about the flow.
  • Loading branch information
xgreenx authored Apr 15, 2024
1 parent 1f50504 commit aa06d95
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 8 deletions.
16 changes: 15 additions & 1 deletion src/protocol/tx-validity.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,11 @@ def input_gas_fees(tx) -> int:
def metadata_gas_fees(tx) -> int:
"""
Computes the intrinsic gas cost of processing transaction outputs
The `contract_code_root_gas_fee`, `sha256_gas_fee`, and `contract_state_root_gas_fee`
are based on the benchmarked gas costs of these operations.
Consensus parameters contain definitions of gas costs for all operations and opcodes in the network.
"""
total: int = 0
if tx.type == TransactionType.Create:
Expand All @@ -178,6 +183,11 @@ def metadata_gas_fees(tx) -> int:
if tx.upgradePurpose.type == UpgradePurposeType.ConsensusParameters:
# add intrinsic cost of calculating the consensus parameters hash
total += sha256_gas_fee(size(tx.witnesses[tx.upgradePurpose.witnessIndex].data))
elif tx.type == TransactionType.Upload:
# add intrinsic cost of calculating the root based on the number of bytecode subsections
total += contract_state_root_gas_fee(tx.subsectionsNumber)
# add intrinsic cost of hashing the subsection for verification of the connection with Binary Merkle tree root
total += sha256_gas_fee(size(tx.witnesses[tx.witnessIndex]))

if tx.type != TransactionType.Mint:
# add intrinsic cost of calculating the transaction id
Expand All @@ -203,6 +213,10 @@ def min_gas(tx) -> int:
Comutes the minimum amount of gas required for a transaction to begin processing.
"""
gas = transaction_size_gas_fees(tx) + intrinsic_gas_fees(tx)
if tx.type == TransactionType.Upload
# charge additionally for storing bytecode on chain
gas += transaction_size_gas_fees(size(tx.witnesses[tx.witnessIndex]))

return gas


Expand All @@ -213,7 +227,7 @@ def max_gas(tx) -> int:
gas = min_gas(tx)
gas = gas + (tx.witnessBytesLimit - tx.witnessBytes) * GAS_PER_BYTE
if tx.type == TransactionType.Script:
gas = gas + tx.gasLimit
gas += tx.gasLimit
return gas


Expand Down
3 changes: 2 additions & 1 deletion src/tx-format/consensus_parameters.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
| `MAX_STORAGE_SLOTS` | `uint64` | Maximum number of initial storage slots. |
| `MAX_TRANSACTION_SIZE` | `uint64` | Maximum size of a transaction, in bytes. |
| `MAX_WITNESSES` | `uint64` | Maximum number of witnesses. |
| `MAX_BYTECODE_SUBSECTIONS` | `uint64` | Maximum number of bytecode subsections. |
| `CHAIN_ID` | `uint64` | A unique per-chain identifier. |
| `BASE_ASSET_ID` | `bytes32` | The base asset of the chain. |
| `PRIVELEGED_ADDRESS` | `bytes32` | The privileged address of the network who can perform upgrade. |
| `PRIVILEGED_ADDRESS` | `bytes32` | The privileged address of the network who can perform upgrade. |
49 changes: 44 additions & 5 deletions src/tx-format/transaction.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ enum TransactionType : uint8 {
Create = 1,
Mint = 2,
Upgrade = 3,
Upload = 4,
}
```

| name | type | description |
|--------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------|
| `type` | [`TransactionType`](#transaction) | Transaction type. |
| `data` | One of [`TransactionScript`](#transactionscript), [`TransactionCreate`](#transactioncreate), [`TransactionMint`](#transactionmint), or [`TransactionUpgrade`](#transactionupgrade) | Transaction data. |
| name | type | description |
|--------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------|
| `type` | [`TransactionType`](#transaction) | Transaction type. |
| `data` | One of [`TransactionScript`](#transactionscript), [`TransactionCreate`](#transactioncreate), [`TransactionMint`](#transactionmint), [`TransactionUpgrade`](#transactionupgrade), or [`TransactionUpload`](#transactionupload) | Transaction data. |

Given helper `max_gas()` returns the maximum gas that the transaction can use.
Given helper `count_ones()` that returns the number of ones in the binary representation of a field.
Expand Down Expand Up @@ -163,7 +164,7 @@ Only the privileged address from [`ConsensusParameters`](./consensus_parameters.

When the upgrade type is `UpgradePurposeType.ConsensusParameters` serialized consensus parameters are available in the witnesses and the `Upgrade` transaction is self-contained because it has all the required information.

When the upgrade type is `UpgradePurposeType.StateTransition`, the `bytecodeHash` field contains the hash of the new bytecode of the state transition function. The bytecode should already be available on the blockchain at the upgrade point; otherwise, the upgrade will fail. The bytecode can be part of the genesis block or can be uploaded via the `TransactionUpload` transaction.
When the upgrade type is `UpgradePurposeType.StateTransition`, the `bytecodeRoot` field contains the Merkle root of the new bytecode of the state transition function. The bytecode should already be available on the blockchain at the upgrade point; otherwise, the upgrade will fail. The bytecode can be part of the genesis block or can be uploaded via the `TransactionUpload` transaction.

The block header contains information about which versions of consensus parameters and state transition function are used to produce a block, and the `Upgrade` transaction defines behavior corresponding to the version. When the block executes the `Upgrade` transaction, it defines new behavior for either `BlockHeader.consensusParametersVersion + 1` or `BlockHeader.stateTransitionBytecodeVersion + 1`(it depends on the purpose of the upgrade).

Expand Down Expand Up @@ -193,3 +194,41 @@ Transaction is invalid if:
- Any output is of type `OutputType.Change` with non-base `asset_id`
- No input where `InputType.Message.owner == PRIVILEGED_ADDRESS` or `InputType.Coint.owner == PRIVILEGED_ADDRESS`
- The `UpgradePurpose` is invalid

## `TransactionUpload`

The `Upload` transaction allows the huge bytecode to be divided into subsections and uploaded slowly to the chain. The [Binary Merkle root](../protocol/cryptographic-primitives.md#binary-merkle-tree) built on top of subsections is an identifier of the bytecode.

Each transaction uploads a subsection of the code and must contain proof of connection to the root. All subsections should be uploaded sequentially, which allows the concatenation of previously uploaded subsections with new subsection. The bytecode is considered final when the last subsection is uploaded, and future `Upload` transactions with the same `root` fields should be rejected.

When the bytecode is completed it can be used to upgrade the network.

The size of each subsection can be arbitrary; the only limit is the maximum number of subsections allowed by the network. The combination of the transaction gas limit and the number of subsections limits the final maximum size of the bytecode.

| name | type | description |
|---------------------|-----------------------------|-----------------------------------------------------------------------------------------|
| `root` | `byte[32]` | The root of the Merkle tree is created over the bytecode. |
| `witnessIndex` | `uint16` | The witness index of the subsection of the bytecode. |
| `subsectionIndex` | `uint16` | The index of the subsection of the bytecode. |
| `subsectionsNumber` | `uint16` | The total number of subsections on which bytecode was divided. |
| `proofSetCount` | `uint16` | Number of Merkle nodes in the proof. |
| `policyTypes` | `uint32` | Bitfield of used policy types. |
| `inputsCount` | `uint16` | Number of inputs. |
| `outputsCount` | `uint16` | Number of outputs. |
| `witnessesCount` | `uint16` | Number of witnesses. |
| `proofSet` | `byte[32][]` | The proof set of Merkle nodes to verify the connection of the subsection to the `root`. |
| `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. |

Transaction is invalid if:

- Any input is of type `InputType.Contract` or `InputType.Message` where `input.dataLength > 0`
- Any input uses non-base asset.
- Any output is of type `OutputType.Contract` or `OutputType.Variable` or `OutputType.Message` or `OutputType.ContractCreated`
- Any output is of type `OutputType.Change` with non-base `asset_id`
- `witnessIndex >= tx.witnessesCount`
- `subsectionIndex` >= `subsectionsNumber`
- `subsectionsNumber > MAX_BYTECODE_SUBSECTIONS`
- The [Binary Merkle tree](../protocol/cryptographic-primitives.md#binary-merkle-tree) root calculated from `(witnesses[witnessIndex], subsectionIndex, subsectionsNumber, proofSet)` is not equal to the `root`. Root calculation is affected by all fields, so modification of one of them invalidates the proof.
2 changes: 1 addition & 1 deletion src/tx-format/upgrade_purpose.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,4 @@ Transaction is invalid if:

| name | type | description |
|----------------|------------|----------------------------------------------------------------|
| `bytecodeHash` | `byte[32]` | The hash of the new bytecode of the state transition function. |
| `bytecodeRoot` | `byte[32]` | The root of the new bytecode of the state transition function. |

0 comments on commit aa06d95

Please sign in to comment.