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

Remove special cases for Bytes, add arrays support #1587

Merged
merged 4 commits into from
Sep 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
36 changes: 19 additions & 17 deletions abi/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ This enables frontends to provide a verifiable display of what they are asking u
## Constraints
- Actions require an ID, other structs / types do not require one
- Multiple structs with the same name from different packages are not supported
- Maps are not supported; use slices (arrays) instead
- Built-in types include the special case type aliases: `codec.Address` and `codec.Bytes`
- Maps are not supported; use slices or arrays instead
- Built-in type `codec.Address` included as a special case

## Generating Golang Bindings
Use cmd/abigen to automatically generate Go bindings from an ABI's JSON.
Expand All @@ -78,19 +78,21 @@ This should generate the same code that is present in `./abi/mockabi_test.go`.

## Supported Primitive Types

| Type | Range/Description | JSON Serialization | Binary Serialization |
|----------|----------------------------------------------------------|--------------------|---------------------------------------|
| `bool` | true or false | boolean | 1 byte |
| `uint8` | numbers from 0 to 255 | number | 1 byte |
| `uint16` | numbers from 0 to 65535 | number | 2 bytes |
| `uint32` | numbers from 0 to 4294967295 | number | 4 bytes |
| `uint64` | numbers from 0 to 18446744073709551615 | number | 8 bytes |
| `int8` | numbers from -128 to 127 | number | 1 byte |
| `int16` | numbers from -32768 to 32767 | number | 2 bytes |
| `int32` | numbers from -2147483648 to 2147483647 | number | 4 bytes |
| `int64` | numbers from -9223372036854775808 to 9223372036854775807 | number | 8 bytes |
| `Address`| 33 byte array | base64 | 33 bytes |
| `Bytes` | byte array | base64 | uint32 length + bytes |
| `string` | string | string | uint16 length + bytes |
| `[]T` | for any `T` in the above list, serialized as an array | array | uint32 length + elements |
| Type | Range/Description | JSON Serialization | Binary Serialization |
|-----------|----------------------------------------------------------|--------------------|---------------------------------------|
| `bool` | true or false | boolean | 1 byte |
| `uint8` | numbers from 0 to 255 | number | 1 byte |
| `uint16` | numbers from 0 to 65535 | number | 2 bytes |
| `uint32` | numbers from 0 to 4294967295 | number | 4 bytes |
| `uint64` | numbers from 0 to 18446744073709551615 | number | 8 bytes |
| `int8` | numbers from -128 to 127 | number | 1 byte |
| `int16` | numbers from -32768 to 32767 | number | 2 bytes |
| `int32` | numbers from -2147483648 to 2147483647 | number | 4 bytes |
| `int64` | numbers from -9223372036854775808 to 9223372036854775807 | number | 8 bytes |
| `Address` | 33 byte array | base64 | 33 bytes |
| `string` | string | string | uint16 length + bytes |
| `[]T` | for any `T` in the above list, serialized as an array | array | uint32 length + elements |
| `[x]T` | for any `T` in the above list, serialized as an array | array | uint32 length + elements |
| `[]uint8` | byte slice | base64 | uint32 length + bytes |
| `[x]uint8`| byte array | array of numbers | x bytes |

13 changes: 9 additions & 4 deletions abi/abi.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,13 +168,18 @@ func describeStruct(t reflect.Type) ([]Field, []reflect.Type, error) {
} else {
arrayPrefix := ""

// Here we assume that all types without a name are slices.
// Here we assume that all types without a name are slices or arrays.
// We completely ignore the fact that maps exist as we don't support them.
// Types like `type Bytes = []byte` are slices technically, but they have a name
// Types like `type Address = [33]byte` are arrays technically, but they have a name
// and we need them to be named types instead of slices.
for fieldType.Name() == "" {
arrayPrefix += "[]"
fieldType = fieldType.Elem()
if fieldType.Kind() == reflect.Array {
arrayPrefix += fmt.Sprintf("[%d]", fieldType.Len())
fieldType = fieldType.Elem()
} else {
arrayPrefix += "[]"
fieldType = fieldType.Elem()
}
}

typeName := arrayPrefix + fieldType.Name()
Expand Down
1 change: 1 addition & 0 deletions abi/abi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ func TestNewABI(t *testing.T) {
MockActionWithTransferArray{},
Outer{},
ActionWithOutput{},
FixedBytes{},
}, []codec.Typed{
ActionOutput{},
})
Expand Down
1 change: 1 addition & 0 deletions abi/auto_marshal_abi_spec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ func TestMarshalSpecs(t *testing.T) {
{"strBytesEmpty", &MockObjectStringAndBytes{}},
{"strOnly", &MockObjectStringAndBytes{}},
{"outer", &Outer{}},
{"fixedBytes", &FixedBytes{}},
}

for _, tc := range testCases {
Expand Down
4 changes: 1 addition & 3 deletions abi/codegen.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func GenerateGoStructs(abi ABI, packageName string) (string, error) {
var sb strings.Builder

sb.WriteString(fmt.Sprintf("package %s\n\n", packageName))
sb.WriteString("import (\n\t\"github.com/ava-labs/hypersdk/codec\"\n)\n\n")
sb.WriteString("import \"github.com/ava-labs/hypersdk/codec\"\n\n")

processed := set.Set[string]{}

Expand Down Expand Up @@ -70,8 +70,6 @@ func convertToGoType(abiType string) string {
return abiType
case "Address":
return "codec.Address"
case "Bytes":
return "codec.Bytes"
default:
if strings.HasPrefix(abiType, "[]") {
return "[]" + convertToGoType(strings.TrimPrefix(abiType, "[]"))
Expand Down
15 changes: 11 additions & 4 deletions abi/mockabi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@

package abi

import (
"github.com/ava-labs/hypersdk/codec"
)
import "github.com/ava-labs/hypersdk/codec"

type MockObjectSingleNumber struct {
Field1 uint16 `serialize:"true"`
Expand All @@ -14,7 +12,7 @@ type MockObjectSingleNumber struct {
type MockActionTransfer struct {
To codec.Address `serialize:"true" json:"to"`
Value uint64 `serialize:"true" json:"value"`
Memo codec.Bytes `serialize:"true" json:"memo"`
Memo []uint8 `serialize:"true" json:"memo"`
}

type MockObjectAllNumbers struct {
Expand Down Expand Up @@ -67,6 +65,11 @@ type ActionWithOutput struct {
Field1 uint8 `serialize:"true" json:"field1"`
}

type FixedBytes struct {
TwoBytes [2]uint8 `serialize:"true" json:"twoBytes"`
ThirtyTwoBytes [32]uint8 `serialize:"true" json:"thirtyTwoBytes"`
}

type ActionOutput struct {
Field1 uint16 `serialize:"true" json:"field1"`
}
Expand Down Expand Up @@ -107,6 +110,10 @@ func (ActionWithOutput) GetTypeID() uint8 {
return 8
}

func (FixedBytes) GetTypeID() uint8 {
return 9
}

func (ActionOutput) GetTypeID() uint8 {
return 0
}
2 changes: 1 addition & 1 deletion abi/testdata/abi.abi.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,4 @@
]
}
]
}
}
2 changes: 1 addition & 1 deletion abi/testdata/abi.hash.hex
Original file line number Diff line number Diff line change
@@ -1 +1 @@
bffd5b2422e5c0fcdfff1b2c073efcac65ab0b5225461ca9a665638d41378e8e
075005590e3e3e39dffbf8e5a6d77559b8a33e621078782571695f60938126d3
21 changes: 19 additions & 2 deletions abi/testdata/abi.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@
{
"id": 8,
"name": "ActionWithOutput"
},
{
"id": 9,
"name": "FixedBytes"
}
],
"outputs": [
Expand Down Expand Up @@ -66,7 +70,7 @@
},
{
"name": "memo",
"type": "Bytes"
"type": "[]uint8"
}
]
},
Expand Down Expand Up @@ -214,6 +218,19 @@
],
"name": "ActionWithOutput"
},
{
"fields": [
{
"name": "twoBytes",
"type": "[2]uint8"
},
{
"name": "thirtyTwoBytes",
"type": "[32]uint8"
}
],
"name": "FixedBytes"
},
{
"fields": [
{
Expand All @@ -224,4 +241,4 @@
"name": "ActionOutput"
}
]
}
}
1 change: 1 addition & 0 deletions abi/testdata/fixedBytes.hex
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
01020100000000000000000002000000000000000000030000000000000000000400
40 changes: 40 additions & 0 deletions abi/testdata/fixedBytes.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"twoBytes": [
1,
2
],
"thirtyTwoBytes": [
1,
0,
0,
0,
0,
0,
0,
0,
0,
0,
2,
0,
0,
0,
0,
0,
0,
0,
0,
0,
3,
0,
0,
0,
0,
0,
0,
0,
0,
0,
4,
0
]
}
2 changes: 1 addition & 1 deletion api/jsonrpc/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ func (j *JSONRPCServer) GetABI(_ *http.Request, _ *GetABIArgs, reply *GetABIRepl

type ExecuteActionArgs struct {
Actor codec.Address `json:"actor"`
Action codec.Bytes `json:"action"`
Action []byte `json:"action"`
}

type ExecuteActionReply struct {
Expand Down
2 changes: 0 additions & 2 deletions codec/packer.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,5 +155,3 @@ func (p *Packer) addErr(err error) {
func (p *Packer) Offset() int {
return p.Packer.Offset
}

type Bytes []byte
4 changes: 2 additions & 2 deletions examples/morpheusvm/actions/transfer.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ type Transfer struct {
Value uint64 `serialize:"true" json:"value"`

// Optional message to accompany transaction.
Memo codec.Bytes `serialize:"true" json:"memo"`
Memo []byte `serialize:"true" json:"memo"`
}

func (*Transfer) GetTypeID() uint8 {
Expand Down Expand Up @@ -110,7 +110,7 @@ func UnmarshalTransfer(p *codec.Packer) (chain.Action, error) {
var transfer Transfer
p.UnpackAddress(&transfer.To)
transfer.Value = p.UnpackUint64(true)
p.UnpackBytes(MaxMemoSize, false, (*[]byte)(&transfer.Memo))
p.UnpackBytes(MaxMemoSize, false, &transfer.Memo)
return &transfer, p.Err()
}

Expand Down
Loading