Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

New messaging specification (replacing old deposit/withdrawal spec) #318

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
485e72f
Replaced deposit ID concept with message ID
pixelcircuits May 20, 2022
13324bf
Update message ID generation
pixelcircuits May 23, 2022
0a1eaa7
Added owner and amount to messageID
pixelcircuits May 24, 2022
8e5ebfb
Added InputMessage and OutputMessage
pixelcircuits May 24, 2022
5259dc5
Updated with MessageInput/Output
pixelcircuits May 24, 2022
e06bcb5
fixed markdown link
pixelcircuits May 24, 2022
53dead2
fixed typo
pixelcircuits May 24, 2022
54a63db
Added InputMessage and OutputMessage
pixelcircuits May 24, 2022
31610f9
Fixed markdown links
pixelcircuits May 24, 2022
8065d96
Added SMO opcode
pixelcircuits May 25, 2022
d473743
Added notes to OutputMessage
pixelcircuits May 25, 2022
1e83cf8
reworded abi to data
pixelcircuits Jun 3, 2022
addfe76
Reworded abi to data
pixelcircuits Jun 3, 2022
d6d896a
Added dataLength to messages
pixelcircuits Jun 3, 2022
0993005
Merge branch 'master' into pixelcircuits-refactor-depositwithdraw-to-…
pixelcircuits Jun 6, 2022
e2d4423
Apply suggestions from code review
pixelcircuits Jun 7, 2022
36108d0
Messaging feedback updates
pixelcircuits Jun 7, 2022
9ee74f2
Make message data easier to parse
pixelcircuits Jun 7, 2022
e528991
Updated orders
pixelcircuits Jun 8, 2022
76cba5f
Updated OutputMessage
pixelcircuits Jun 11, 2022
978df96
Fixed link
pixelcircuits Jun 11, 2022
3a5b96e
Spec review changes
pixelcircuits Jun 11, 2022
206635a
Spec changes from feedback
pixelcircuits Jun 12, 2022
142fe9f
Merge branch 'master' into pixelcircuits-refactor-depositwithdraw-to-…
pixelcircuits Jun 12, 2022
d895e1d
Added extra panic scenario
pixelcircuits Jun 14, 2022
d86b788
Apply suggestions from code review
pixelcircuits Jun 14, 2022
64e1801
Added message nonce definition
pixelcircuits Jun 14, 2022
27c55a5
split message id to input and output
pixelcircuits Jun 15, 2022
8e83d05
updated message hash order
pixelcircuits Jun 15, 2022
eba0b42
Update specs/protocol/identifiers.md
pixelcircuits Jun 16, 2022
5ad5b9e
Corrected panic case
pixelcircuits Jun 16, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions specs/protocol/abi.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ All receipts will have a `type` property:
- "Transfer"
- "TransferOut"
- "ScriptResult"
- "MessageOut"

Then, each receipt type will have its own properties. Some of these properties are related to the FuelVM's registers, as specified in more detail [here](../vm/instruction_set.md).

Expand Down Expand Up @@ -343,6 +344,32 @@ _Important note:_ For the JSON representation of receipts, we represent 64-bit u
}
```

#### MessageOut receipt

- `type`: `MessageOut`.
- `messageID`: Hexadecimal string representation of the 256-bit (32-byte) message ID as described [here](../protocol/identifiers.md#output-message-id).
- `sender`: Hexadecimal string representation of the 256-bit (32-byte) address of the message sender: `MEM[$fp, 32]`.
- `recipient`: Hexadecimal string representation of the 256-bit (32-byte) address of the message recipient: `MEM[$rA, 32]`.
- `amount`: Hexadecimal string representation of a 64-bit unsigned integer; value of register `$rD`.
- `nonce`: Hexadecimal string representation of the 256-bit (32-byte) message nonce as described [here](../protocol/identifiers.md#output-message-nonce).
- `len`: Decimal string representation of a 16-bit unsigned integer; value of register `$rB`.
- `digest`: Hexadecimal string representation of 256-bit (32-byte), hash of `MEM[$rA + 32, $rB]`.
- `data`: Hexadecimal string representation of the value of the memory range `MEM[$rA + 32, $rB]`.

```json
{
"type":"MessageOut",
"messageID":"0x39150017c9e38e5e280432d546fae345d6ce6d8fe4710162c2e3a95a6faff051",
"sender":"0x38e5e280432d546fae345d6ce6d8fe4710162c2e3a95a6faff05139150017c9e",
"recipient":"0x4710162c2e3a95a6faff05139150017c9e38e5e280432d546fae345d6ce6d8fe",
"amount":"0xe6d8fe4710162c2e",
"nonce":"0x47101017c9e38e5e280432d546fae345d6ce6d8fe4710162c2e3a95a6faff051",
"len":"65535",
"digest":"0xd28b78894e493c98a196aa51b432b674e4813253257ed9331054ee8d6813b3aa",
"data":"0xa7c5ac471b47"
}
```

## Function Selector Encoding

To select which function you want to call, first, this function must be in an ABI struct of a Sway program. For instance:
Expand Down
33 changes: 27 additions & 6 deletions specs/protocol/identifiers.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
- [Contract ID](#contract-id)
- [UTXO ID](#utxo-id)
- [Coin ID](#coin-id)
- [Deposit ID](#deposit-id)
- [Deposit Asset ID](#deposit-asset-id)
- [Input Message ID](#input-message-id)
- [Output Message ID](#output-message-id)
- [Output Message Nonce](#output-message-nonce)
- [Fee ID](#fee-id)

This document defines how to compute unique identifiers.
Expand All @@ -24,14 +25,34 @@ For a transaction of type `TransactionType.Create`, `tx`, the contract ID is `sh

Is represented as an _outpoint_: a pair of [transaction ID](#transaction-id) as `byte[32]` and output index as a `uint8`.

### Input Message ID

### Deposit ID
The ID of an input message is computed as the [hash](./cryptographic_primitives.md#hashing) of:

The ID of a deposit is computed as the [hash](./cryptographic_primitives.md#hashing) of the [Deposit Asset ID](#deposit-asset-id) `byte[32]`, recipient address `byte[32]`, amount `uint64` and deposit nonce `uint64`: `hash(byte[32] ++ byte[32] ++ uint64 ++ uint64)`. All value types are serialized according to the standard [transaction serialization](./tx_format.md#transaction).
1. the sender address as `byte[32]`,
1. recipient address as `byte[32]`,
1. the message owner (either address or predicate root) as `byte[32]`,
1. the message nonce as `uint64`,
1. the amount being sent with the message as `uint64`,
1. the message data as `byte[]`

#### Deposit Asset ID
`hash(byte[32] ++ byte[32] ++ byte[32] ++ uint64 ++ uint64 ++ byte[])`. The address values are serialized as a byte array of length 32 left-padded with zeroes, and all other value types are serialized according to the standard [transaction serialization](./tx_format.md#transaction). Note that the message data length is not included since there is only one dynamically sized field and can be implicitly determined by the hash preimage size.

The ID of a deposit asset is computed as the [hash](./cryptographic_primitives.md#hashing) of the token address `byte[32]` and token precision `uint8`: `hash(byte[32] ++ uint8)`. The address value is serialized as a byte array of length 32 left-padded with zeroes, and all other value types are serialized according to the standard [transaction serialization](./tx_format.md#transaction).
### Output Message ID

The ID of an input message is computed as the [hash](./cryptographic_primitives.md#hashing) of:

1. the sender address as `byte[32]`,
1. recipient address as `byte[32]`,
1. the [OutputMessage nonce](#output-message-nonce) as `byte[32]`,
1. the amount being sent with the message as `uint64`,
1. the message data as `byte[]`

`hash(byte[32] ++ byte[32] ++ byte[32] ++ uint64 ++ byte[])`. The address values are serialized as a byte array of length 32 left-padded with zeroes, and all other value types are serialized according to the standard [transaction serialization](./tx_format.md#transaction). Note that the message data length is not included since there is only one dynamically sized field and can be implicitly determined by the hash preimage size.

#### Output Message Nonce

The nonce value for `OutputMessage` is computed as the [hash](./cryptographic_primitives.md#hashing) of the [Transaction ID](#transaction-id) that the message is an output for and the index of the output as a `uint8`: `hash(byte[32] ++ uint8)`.

### Fee ID

Expand Down
59 changes: 47 additions & 12 deletions specs/protocol/tx_format.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@
- [Input](#input)
- [InputCoin](#inputcoin)
- [InputContract](#inputcontract)
- [InputMessage](#inputmessage)
- [OutputType](#outputtype)
- [Output](#output)
- [OutputCoin](#outputcoin)
- [OutputContract](#outputcontract)
- [OutputWithdrawal](#outputwithdrawal)
- [OutputMessage](#outputmessage)
pixelcircuits marked this conversation as resolved.
Show resolved Hide resolved
- [OutputChange](#outputchange)
- [OutputVariable](#outputvariable)
- [OutputContractCreated](#outputcontractcreated)
Expand All @@ -33,6 +34,7 @@
| `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. |

Expand Down Expand Up @@ -86,6 +88,7 @@ enum ReceiptType : uint8 {
Transfer = 7,
TransferOut = 8,
ScriptResult = 9,
MessageOut = 10,
}
```

Expand Down Expand Up @@ -162,6 +165,7 @@ Creates a contract with contract ID as computed [here](./identifiers.md#contract
enum InputType : uint8 {
Coin = 0,
Contract = 1,
Message = 2,
}
```

Expand All @@ -170,7 +174,7 @@ enum InputType : uint8 {
| name | type | description |
|--------|-------------------------------------------------------------------|----------------|
| `type` | [InputType](#inputtype) | Type of input. |
| `data` | One of [InputCoin](#inputcoin) or [InputContract](#inputcontract) | Input data. |
| `data` | One of [InputCoin](#inputcoin), [InputContract](#inputcontract), or [InputMessage](#inputmessage) | Input data. |

Transaction is invalid if:

Expand Down Expand Up @@ -231,13 +235,41 @@ Note: when verifying a predicate, `txID`, `outputIndex`, `balanceRoot`, `stateRo

Note: when executing a script, `txID`, `outputIndex`, `balanceRoot`, and `stateRoot` are initialized to the transaction ID, output index, amount, and state root of the contract with ID `contractID`, and `txPointer` is initialized to zero.

### InputMessage

| name | type | description |
|-----------------------|-------------|------------------------------------------------------------------------|
| `messageID` | `byte[32]` | The messageID as described [here](./identifiers.md#input-message-id). |
| `sender` | `byte[32]` | The address of the message sender. |
| `recipient` | `byte[32]` | The address of the message recipient. |
| `amount` | `uint64` | Amount of base asset coins sent with message. |
| `nonce` | `byte[32]` | The message nonce. |
| `owner` | `byte[32]` | Owning address or predicate root. |
| `witnessIndex` | `uint8` | Index of witness that authorizes spending the coin. |
| `dataLength` | `uint16` | Length of message data, in bytes. |
| `predicateLength` | `uint16` | Length of predicate, in instructions. |
| `predicateDataLength` | `uint16` | Length of predicate input data, in bytes. |
| `data` | `byte[]` | The message data. |
| `predicate` | `byte[]` | Predicate bytecode. |
| `predicateData` | `byte[]` | Predicate input data (parameters). |

Transaction is invalid if:
pixelcircuits marked this conversation as resolved.
Show resolved Hide resolved

- `witnessIndex >= tx.witnessesCount`
- `dataLength > MAX_MESSAGE_DATA_LENGTH`
- `predicateLength > MAX_PREDICATE_LENGTH`
- `predicateDataLength > MAX_PREDICATE_DATA_LENGTH`
- If `predicateLength > 0`; the computed predicate root (see below) is not equal `owner`

The predicate root is computed identically to the contract root, used to compute the contract ID, [here](./identifiers.md#contract-id).

## OutputType

```
enum OutputType : uint8 {
Coin = 0,
Contract = 1,
Withdrawal = 2,
Message = 2,
Change = 3,
Variable = 4,
ContractCreated = 5,
Expand All @@ -248,8 +280,8 @@ enum OutputType : uint8 {

| name | type | description |
|--------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------|
| `type` | [OutputType](#outputtype) | Type of output. |
| `data` | One of [OutputCoin](#outputcoin), [OutputContract](#outputcontract), [OutputWithdrawal](#outputwithdrawal) [OutputChange](#outputchange), [OutputVariable](#outputvariable), or [OutputContractCreated](#outputcontractcreated). | Output data. |
| `type` | [OutputType](#outputtype) | Type of output. |
| `data` | One of [OutputCoin](#outputcoin), [OutputContract](#outputcontract), [OutputMessage](#outputmessage) [OutputChange](#outputchange), [OutputVariable](#outputvariable), or [OutputContractCreated](#outputcontractcreated). | Output data. |

Transaction is invalid if:

Expand Down Expand Up @@ -286,15 +318,18 @@ The balance root `balanceRoot` is the root of the [SMT](./cryptographic_primitiv

The state root `stateRoot` is the root of the [SMT](./cryptographic_primitives.md#sparse-merkle-tree) of storage slots. Each storage slot is a `byte[32]`, keyed by a `byte[32]`.

### OutputWithdrawal
### OutputMessage

| name | type | description |
|-----------------------|-------------|-------------------------------------------------------------------------|
| `recipient` | `byte[32]` | The address of the message recipient. |
| `amount` | `uint64` | Amount of base asset coins sent with message. |

Note: when signing a transaction `recipient` and `amount` are set to zero.

| name | type | description |
|------------|------------|------------------------------|
| `to` | `byte[32]` | Receiving address. |
| `amount` | `uint64` | Amount of coins to withdraw. |
| `asset_id` | `byte[32]` | Asset ID of coins. |
Note: when verifying a predicate or executing a script, `recipient` and `amount` are initialized to zero.

This output type is unspendable and can be pruned form the UTXO set.
Note: this output type is unspendable and can be pruned from the UTXO set.

### OutputChange

Expand Down
4 changes: 2 additions & 2 deletions specs/protocol/tx_validity.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,14 @@ For each asset ID `asset_id` in the input and output set:
def sum_inputs(tx, col) -> int:
total: int = 0
for input in tx.inputs:
if input.type == InputType.Coin and input.asset_id == col:
if (input.type == InputType.Coin and input.asset_id == col) or (input.type == InputType.Message and col == 0):
adlerjohn marked this conversation as resolved.
Show resolved Hide resolved
total += input.amount
return total

def sum_outputs(tx, col) -> int:
total: int = 0
for output in tx.outputs:
if (output.type == OutputType.Coin or output.type == OutputType.Withdrawal) and output.asset_id == col:
if (output.type == OutputType.Coin and output.asset_id == col) or (output.type == OutputType.Message and col == 0):
Voxelot marked this conversation as resolved.
Show resolved Hide resolved
total += output.amount
return total

Expand Down
48 changes: 48 additions & 0 deletions specs/vm/instruction_set.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
- [MINT: Mint new coins](#mint-mint-new-coins)
- [RETD: Return from context with data](#retd-return-from-context-with-data)
- [RVRT: Revert](#rvrt-revert)
- [SMO: Send Message to output](#smo-send-message-to-output)
- [SRW: State read word](#srw-state-read-word)
- [SRWQ: State read 32 bytes](#srwq-state-read-32-bytes)
- [SWW: State write word](#sww-state-write-word)
Expand Down Expand Up @@ -1416,6 +1417,53 @@ Cease VM execution and revert script effects. After a revert:
1. All [OutputContract](../protocol/tx_format.md#outputcontract) outputs will have the same `balanceRoot` and `stateRoot` as on initialization.
1. All [OutputVariable](../protocol/tx_format.md#outputvariable) outputs will have `to`, `amount`, and `asset_id` of zero.

### SMO: Send message to output

| | |
|-------------|-------------------------------------------------------------------------------------|
| Description | Send a message to recipient address `MEM[$rA, 32]` with call abi `MEM[$rA + 32, $rB]` and `$rD` coins, with output `$rC`. |
| Operation | ```outputmessage(MEM[$fp, 32], MEM[$rA, 32], MEM[$rA + 32, $rB], $rD, $rC);``` |
| Syntax | `smo $rA, $rB, $rC, $rD` |
| Encoding | `0x00 rA rB rC rD` |
| Notes | |

Given helper `balanceOfStart(asset_id: byte[32]) -> uint32` which returns the memory address of the remaining free balance of `asset_id`, or panics if `asset_id` has no free balance remaining.

Panic if:

- `$rA + $rB + 32` overflows
- `$rA + $rB + 32 > VM_MAX_RAM`
pixelcircuits marked this conversation as resolved.
Show resolved Hide resolved
- `$rB > MEM_MAX_ACCESS_SIZE`
- `$rB > MESSAGE_MAX_DATA_SIZE`
- `$rC > tx.outputsCount`
- In an external context, if `$rD > MEM[balanceOfStart(0), 8]`
- In an internal context, if `$rD` is greater than the balance of asset ID 0 of output with contract ID `MEM[$fp, 32]`
- `tx.outputs[$rC].type != OutputType.Message`
- `tx.outputs[$rC].recipient != 0`
pixelcircuits marked this conversation as resolved.
Show resolved Hide resolved
- `MEM[$rA, 32] == 0`

Append a receipt to the list of receipts, modifying `tx.receiptsRoot`:
pixelcircuits marked this conversation as resolved.
Show resolved Hide resolved

| name | type | description |
|--------------|---------------|------------------------------------------------------------------------------------------|
| `type` | `ReceiptType` | `ReceiptType.MessageOut` |
| `messageID` | `byte[32]` | The messageID as described [here](../protocol/identifiers.md#output-message-id). |
| `sender` | `byte[32]` | The address of the message sender: `MEM[$fp, 32]`. |
| `recipient` | `byte[32]` | The address of the message recipient: `MEM[$rA, 32]`. |
| `amount` | `uint64` | Amount of base asset coins sent with message: `$rD`. |
| `nonce` | `byte[32]` | The message nonce as described [here](../protocol/identifiers.md#output-message-nonce). |
| `len` | `uint16` | Length of message data, in bytes: `$rB`. |
| `digest` | `byte[32]` | [Hash](#s256-sha-2-256) of `MEM[$rA + 32, $rB]`. |

In an external context, decrease `MEM[balanceOfStart(0), 8]` by `$rD`. In an internal context, decrease asset ID 0 balance of output with contract ID `MEM[$fp, 32]` by `$rD`. Then set:

- `tx.outputs[$rC].recipient = MEM[$rA, 32]`
- `tx.outputs[$rC].amount = $rD`

This modifies the `balanceRoot` field of the appropriate output.
`messageID` is added to the `OutputMessage` Merkle tree as part of block header.
TODO: document output messages merkle tree construction and maintenance and link here

### SRW: State read word

| | |
Expand Down
11 changes: 6 additions & 5 deletions specs/vm/main.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@ This document provides the specification for the Fuel Virtual Machine (FuelVM).

## Parameters

| name | type | value | note |
|-----------------------|----------|---------|---------------------------------------|
| `CONTRACT_MAX_SIZE` | `uint64` | | Maximum contract size, in bytes. |
| `MEM_MAX_ACCESS_SIZE` | `uint64` | | Maximum memory access size, in bytes. |
| `VM_MAX_RAM` | `uint64` | `2**26` | 64 MiB. |
| name | type | value | note |
|----------------------------|----------|---------|---------------------------------------------|
| `CONTRACT_MAX_SIZE` | `uint64` | | Maximum contract size, in bytes. |
| `MEM_MAX_ACCESS_SIZE` | `uint64` | | Maximum memory access size, in bytes. |
| `VM_MAX_RAM` | `uint64` | `2**26` | 64 MiB. |
| `MESSAGE_MAX_DATA_SIZE` | `uint16` | | Maximum size of message data, in bytes. |

## Semantics

Expand Down