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

ABI spec #194

Merged
merged 17 commits into from
Aug 28, 2021
Merged

ABI spec #194

merged 17 commits into from
Aug 28, 2021

Conversation

digorithm
Copy link
Member

@digorithm digorithm commented Aug 3, 2021

WIP

This is the initial draft for the Fuel's ABI specs. Heavily inspired by Solidity's ABI spec but directed toward Sway's canonical types.

This is tackling the following issue: #151

This is prerequisite to build an initial version of the Rust SDK for Sway, which is a prerequisite to enable users to write Sway tests in Rusts.

Having the ABI spec'd out will enable the tooling around the Sway compiler to craft ABI calls, enabling users to use other languages (such as Rust) to interact with Sway contracts.

This is a work in progress and we still have to discuss some implementation details, such as padding direction, encoding Sway custom types (structs, enums), and more. So feel free to discuss everything related to it in this space.

There's also an accompanying PR for this PR: FuelLabs/sway#131 where I'm writing some code to test the encoding of this ABI being spec'd here.

@digorithm digorithm changed the title Add initial specs for the ABI ABI spec Aug 3, 2021
@digorithm digorithm linked an issue Aug 3, 2021 that may be closed by this pull request
specs/protocol/abi.md Show resolved Hide resolved
specs/protocol/abi.md Outdated Show resolved Hide resolved
To select which function you want to call, first, this function must be a _public_ function in a _contract_ type of a Sway program. For instance:

```rust
contract;
Copy link
Contributor

Choose a reason for hiding this comment

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

This is actually not entirely true anymore, and this may be my fault for not making this clear. There are examples here. A function must be in an abi for it to be included here.

specs/protocol/abi.md Outdated Show resolved Hide resolved
specs/protocol/abi.md Outdated Show resolved Hide resolved
specs/protocol/abi.md Outdated Show resolved Hide resolved
specs/protocol/abi.md Outdated Show resolved Hide resolved
specs/protocol/abi.md Outdated Show resolved Hide resolved
specs/protocol/abi.md Outdated Show resolved Hide resolved
specs/protocol/abi.md Outdated Show resolved Hide resolved
## Function selector
To select which function you want to call, first, this function must be a _public_ function in a _contract_ type of a Sway program. For instance:

```rust
Copy link
Contributor

Choose a reason for hiding this comment

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

Is now a good time to migrate to mdBook and make use of FuelLabs/sway#129?

Copy link
Contributor

Choose a reason for hiding this comment

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

cc @leviathanbeak. Would a sway codeblock work with just the changes to highlight.js, or is it also required to modify mdBook somehow?

specs/protocol/abi.md Outdated Show resolved Hide resolved
specs/protocol/abi.md Outdated Show resolved Hide resolved
specs/protocol/abi.md Outdated Show resolved Hide resolved
@digorithm digorithm requested review from adlerjohn and sezna August 4, 2021 17:20
specs/protocol/abi.md Outdated Show resolved Hide resolved
specs/protocol/abi.md Show resolved Hide resolved
specs/protocol/abi.md Outdated Show resolved Hide resolved
specs/protocol/abi.md Outdated Show resolved Hide resolved
specs/protocol/abi.md Show resolved Hide resolved
specs/protocol/abi.md Outdated Show resolved Hide resolved
specs/protocol/abi.md Outdated Show resolved Hide resolved
specs/protocol/abi.md Outdated Show resolved Hide resolved
specs/protocol/abi.md Outdated Show resolved Hide resolved
@digorithm digorithm requested a review from adlerjohn August 5, 2021 16:51
specs/protocol/abi.md Outdated Show resolved Hide resolved
@digorithm digorithm requested a review from sezna August 12, 2021 19:44
specs/protocol/abi.md Outdated Show resolved Hide resolved
specs/protocol/abi.md Outdated Show resolved Hide resolved
specs/protocol/abi.md Outdated Show resolved Hide resolved
specs/protocol/abi.md Outdated Show resolved Hide resolved
specs/protocol/abi.md Outdated Show resolved Hide resolved
specs/protocol/abi.md Outdated Show resolved Hide resolved
specs/protocol/abi.md Outdated Show resolved Hide resolved
specs/protocol/abi.md Outdated Show resolved Hide resolved
specs/protocol/abi.md Outdated Show resolved Hide resolved
specs/protocol/abi.md Outdated Show resolved Hide resolved
000000000000002a // `42` encoded as u64
```

TODO: I'm assuming the encoder/decoder can infer which variant is being passed by the call arguments (e.g `MySumType::x(42)` being passed to the call, so it knows it's the variant `x`). We could also encode the enum variant's numeric representation (e.g 0, 1, 2, ...) in the ABI. Thoughts?
Copy link
Member Author

Choose a reason for hiding this comment

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

@adlerjohn @sezna Can I get some thoughts on this last TODO here?

Copy link
Contributor

Choose a reason for hiding this comment

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

ABI encoded data should be able to be decoded knowing only the ABI definition (and the encoded data ofc). So you shouldn't need access to how the encoding happened.

@digorithm digorithm marked this pull request as ready for review August 23, 2021 16:23
@digorithm digorithm requested a review from adlerjohn August 23, 2021 16:24
@sezna
Copy link
Contributor

sezna commented Aug 25, 2021

This is looking really good upon cursory skimming. I'll comb through it again soon.

specs/protocol/abi.md Outdated Show resolved Hide resolved
specs/protocol/abi.md Outdated Show resolved Hide resolved
specs/protocol/abi.md Outdated Show resolved Hide resolved
specs/protocol/abi.md Outdated Show resolved Hide resolved
specs/protocol/abi.md Outdated Show resolved Hide resolved
specs/protocol/abi.md Outdated Show resolved Hide resolved
specs/protocol/abi.md Outdated Show resolved Hide resolved
specs/protocol/abi.md Outdated Show resolved Hide resolved
specs/protocol/abi.md Outdated Show resolved Hide resolved
specs/protocol/abi.md Outdated Show resolved Hide resolved
@digorithm digorithm requested a review from adlerjohn August 26, 2021 17:20
specs/protocol/abi.md Outdated Show resolved Hide resolved
specs/protocol/abi.md Outdated Show resolved Hide resolved
@adlerjohn adlerjohn added the ABI Contract ABI label Aug 27, 2021
adlerjohn
adlerjohn previously approved these changes Aug 27, 2021

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](https://github.com/FuelLabs/fuel-specs/blob/master/specs/vm/opcodes.md).

_Important note:_ For the JSON representation of the receipts, we represent 64-bit unsigned integers as a JSON's `String` due to limitation around the type `Number` in the JSON specification, which only supports numbers up to `2^{53-1}`, whereas the FuelVM's registers hold values up to `2^64`.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
_Important note:_ For the JSON representation of the receipts, we represent 64-bit unsigned integers as a JSON's `String` due to limitation around the type `Number` in the JSON specification, which only supports numbers up to `2^{53-1}`, whereas the FuelVM's registers hold values up to `2^64`.
_Important note:_ For the JSON representation of receipts, we represent 64-bit unsigned integers as a JSON `String` due to limitations around the type `Number` in the JSON specification, which only supports numbers up to `2^{53-1}`, while the FuelVM's registers hold values up to `2^64`.

#### Panic receipt

- `type`: `Panic`.
- `id`: Hexadecimal string representation of the 256-bit (32 bytes) contract ID of the current context if in an internal context. `null` otherwise.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
- `id`: Hexadecimal string representation of the 256-bit (32 bytes) contract ID of the current context if in an internal context. `null` otherwise.
- `id`: Hexadecimal string representation of the 256-bit (32-byte) contract ID of the current context if in an internal context. `null` otherwise.

#### Return receipt

- `type`: `Return`.
- `id`: Hexadecimal string representation of the 256-bit (32 bytes) contract ID of the current context if in an internal context; `null` otherwise.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
- `id`: Hexadecimal string representation of the 256-bit (32 bytes) contract ID of the current context if in an internal context; `null` otherwise.
- `id`: Hexadecimal string representation of the 256-bit (32-byte) contract ID of the current context if in an internal context; `null` otherwise.

#### Call receipt

- `type`: `Call`.
- `from`: Hexadecimal string representation of the 256-bit (32 bytes) contract ID of the current context if in an internal context; `null` otherwise.
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not gonna do all of these but just find and replace 256-bit (32 bytes) with 256-bit (32-byte)

- `color`: Hexadecimal string representation of the 256-bit (32 bytes) color of coins to forward.
- `gas`: Decimal string representation of a 64-bit unsigned integer; amount gas to forward; value in register `$rD`.
- `param1`: Hexadecimal string representation of a 64-bit unsigned integer; first parameter, holds the function selector.
- `param2`: Hexadecimal string representation of a 64-bit unsigned integer; second parameter.
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we want to say something like "second parameter, typically used for the user-specified input to the ABI function being selected"?

Copy link
Contributor

Choose a reason for hiding this comment

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

@adlerjohn for your wordsmithing

Copy link
Contributor

Choose a reason for hiding this comment

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

Sure, sounds about right.


The signature is composed of the function name with the parenthesized list of comma-separated parameter types without spaces. All strings encoded with UTF-8.

For instance, in the case of the function `entry_one` above, we would pass the string `"entry_one(u64)"` to the `sha256()` hashing algorithm, the full digest would be:
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
For instance, in the case of the function `entry_one` above, we would pass the string `"entry_one(u64)"` to the `sha256()` hashing algorithm, the full digest would be:
For instance, in the case of the function `entry_one` above, we would pass the string `"entry_one(u64)"` to the `sha256()` hashing algorithm. The full digest would be:

0x0c36cb9cb766ff60422db243c4fff06d342949da3c64a3c6ac564941f84b6f06
```

Then we would get only the first 4 bytes of this digest and left pad it to 8 bytes:
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
Then we would get only the first 4 bytes of this digest and left pad it to 8 bytes:
Then we would get only the first 4 bytes of this digest and left-pad it to 8 bytes:


## Argument Encoding

When crafting a transaction script data, you must encode the arguments you wish to pass to the script.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
When crafting a transaction script data, you must encode the arguments you wish to pass to the script.
When crafting transaction script data, you must encode the arguments you wish to pass to the script.


### Address

A 256 bits (32 bytes) address, encoded in the same way as a `B256` argument: encoded as-is.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
A 256 bits (32 bytes) address, encoded in the same way as a `B256` argument: encoded as-is.
A 256-bit (32-byte) address, encoded in the same way as a `b256` argument: encoded as-is.


An array of a certain type `T`, `T[n]`, where `n` is the length of the array.

Arrays in Sway have fixed-length known at compile time, this means the ABI encoding for arrays also happens in-place.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
Arrays in Sway have fixed-length known at compile time, this means the ABI encoding for arrays also happens in-place.
Arrays in Sway have a fixed-length which is known at compile time. This means the ABI encoding for arrays also happens in-place, with no need to account for dynamic sizing.


### Structs

Encoding ABIs that contain custom types, such as structs, is similar to encoding a set of primitive types. The encoding will proceed in the order that the inner types of a custom type are declared and _recursively_ just like encoding any other type. This way you can encode structs with primitive types or structs with more complex types in it, such as other structs, arrays, strings, and enums.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
Encoding ABIs that contain custom types, such as structs, is similar to encoding a set of primitive types. The encoding will proceed in the order that the inner types of a custom type are declared and _recursively_ just like encoding any other type. This way you can encode structs with primitive types or structs with more complex types in it, such as other structs, arrays, strings, and enums.
Encoding ABIs that contain custom types, such as structs, is similar to encoding a set of primitive types. The encoding will proceed in the order that the inner types of a custom type are declared and _recursively_ just like encoding any other type. This way you can encode structs with primitive types or structs with more complex types in them such as other structs, arrays, strings, and enums.


### Sum types (Enums)

ABI calls containing sum types (enums) are encoded identically to structs: encode the offset first, then recursively encode the type of the enum variant being passed to the function being called.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
ABI calls containing sum types (enums) are encoded identically to structs: encode the offset first, then recursively encode the type of the enum variant being passed to the function being called.
ABI calls containing sum types (enums) are encoded similarly to structs: encode the discriminant first, then recursively encode the type of the enum variant being passed to the function being called.

I don't think it is fair to say identically, because we encode a discriminant where for structs do not.

Copy link
Member Author

Choose a reason for hiding this comment

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

Damn, that whole paragraph was actually outdated, it was when we were going with the dynamic encoding approach (encoding offset first and wasn't encoding the Enum's discriminant).

But this change you suggest makes it 100% correct 😁

0000000000000005 // `5` encoded as u8
```

More complex scenario
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
More complex scenario
A more complex scenario:

Copy link
Contributor

@sezna sezna left a comment

Choose a reason for hiding this comment

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

Copy link
Contributor

@sezna sezna left a comment

Choose a reason for hiding this comment

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

L
G
T
M
🎊

@digorithm digorithm merged commit 39be11b into master Aug 28, 2021
@digorithm digorithm deleted the contract-abi branch August 28, 2021 01:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ABI Contract ABI
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Contract ABI Specification Format
3 participants