-
Notifications
You must be signed in to change notification settings - Fork 721
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Cesar <[email protected]> Co-authored-by: Stephen Buttolph <[email protected]> Co-authored-by: aaronbuchwald <[email protected]>
- Loading branch information
1 parent
1157e2d
commit 1eaf40b
Showing
8 changed files
with
369 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
# Payload | ||
|
||
An Avalanche Unsigned Warp Message already includes a `networkID`, `sourceChainID`, and `payload` field. The `payload` field can be parsed into one of the types included in this package to be further handled by the VM. | ||
|
||
## Hash | ||
|
||
Hash: | ||
``` | ||
+-----------------+----------+-----------+ | ||
| codecID : uint16 | 2 bytes | | ||
+-----------------+----------+-----------+ | ||
| typeID : uint32 | 4 bytes | | ||
+-----------------+----------+-----------+ | ||
| hash : [32]byte | 32 bytes | | ||
+-----------------+----------+-----------+ | ||
| 38 bytes | | ||
+-----------+ | ||
``` | ||
|
||
- `codecID` is the codec version used to serialize the payload and is hardcoded to `0x0000` | ||
- `typeID` is the payload type identifier and is `0x00000000` for `Hash` | ||
- `hash` is a hash from the `sourceChainID`. As an example, this may be the hash of a block that was accepted on the source chain | ||
|
||
## AddressedCall | ||
|
||
AddressedCall: | ||
``` | ||
+---------------------+--------+----------------------------------+ | ||
| codecID : uint16 | 2 bytes | | ||
+---------------------+--------+----------------------------------+ | ||
| typeID : uint32 | 4 bytes | | ||
+---------------------+--------+----------------------------------+ | ||
| sourceAddress : []byte | 4 + len(address) | | ||
+---------------------+--------+----------------------------------+ | ||
| payload : []byte | 4 + len(payload) | | ||
+---------------------+--------+----------------------------------+ | ||
| 14 + len(payload) + len(address) | | ||
+----------------------------------+ | ||
``` | ||
|
||
- `codecID` is the codec version used to serialize the payload and is hardcoded to `0x0000` | ||
- `typeID` is the payload type identifier and is `0x00000001` for `AddressedCall` | ||
- `sourceAddress` is the address that sent this message from the source chain | ||
- `payload` is an arbitrary byte array payload |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. | ||
// See the file LICENSE for licensing terms. | ||
|
||
package payload | ||
|
||
import "fmt" | ||
|
||
var _ Payload = (*AddressedCall)(nil) | ||
|
||
// AddressedCall defines the format for delivering a call across VMs including a | ||
// source address and a payload. | ||
// | ||
// Note: If a destination address is expected, it should be encoded in the | ||
// payload. | ||
type AddressedCall struct { | ||
SourceAddress []byte `serialize:"true"` | ||
Payload []byte `serialize:"true"` | ||
|
||
bytes []byte | ||
} | ||
|
||
// NewAddressedCall creates a new *AddressedCall and initializes it. | ||
func NewAddressedCall(sourceAddress []byte, payload []byte) (*AddressedCall, error) { | ||
ap := &AddressedCall{ | ||
SourceAddress: sourceAddress, | ||
Payload: payload, | ||
} | ||
return ap, initialize(ap) | ||
} | ||
|
||
// ParseAddressedCall converts a slice of bytes into an initialized | ||
// AddressedCall. | ||
func ParseAddressedCall(b []byte) (*AddressedCall, error) { | ||
payloadIntf, err := Parse(b) | ||
if err != nil { | ||
return nil, err | ||
} | ||
payload, ok := payloadIntf.(*AddressedCall) | ||
if !ok { | ||
return nil, fmt.Errorf("%w: %T", errWrongType, payloadIntf) | ||
} | ||
return payload, nil | ||
} | ||
|
||
// Bytes returns the binary representation of this payload. It assumes that the | ||
// payload is initialized from either NewAddressedCall or Parse. | ||
func (a *AddressedCall) Bytes() []byte { | ||
return a.bytes | ||
} | ||
|
||
func (a *AddressedCall) initialize(bytes []byte) { | ||
a.bytes = bytes | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. | ||
// See the file LICENSE for licensing terms. | ||
|
||
package payload | ||
|
||
import ( | ||
"encoding/base64" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
|
||
"github.com/ava-labs/avalanchego/codec" | ||
"github.com/ava-labs/avalanchego/ids" | ||
) | ||
|
||
func TestAddressedCall(t *testing.T) { | ||
require := require.New(t) | ||
shortID := ids.GenerateTestShortID() | ||
|
||
addressedPayload, err := NewAddressedCall( | ||
shortID[:], | ||
[]byte{1, 2, 3}, | ||
) | ||
require.NoError(err) | ||
|
||
addressedPayloadBytes := addressedPayload.Bytes() | ||
parsedAddressedPayload, err := ParseAddressedCall(addressedPayloadBytes) | ||
require.NoError(err) | ||
require.Equal(addressedPayload, parsedAddressedPayload) | ||
} | ||
|
||
func TestParseAddressedCallJunk(t *testing.T) { | ||
_, err := ParseAddressedCall(junkBytes) | ||
require.ErrorIs(t, err, codec.ErrUnknownVersion) | ||
} | ||
|
||
func TestAddressedCallBytes(t *testing.T) { | ||
require := require.New(t) | ||
base64Payload := "AAAAAAABAAAAEAECAwAAAAAAAAAAAAAAAAAAAAADCgsM" | ||
addressedPayload, err := NewAddressedCall( | ||
[]byte{1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, | ||
[]byte{10, 11, 12}, | ||
) | ||
require.NoError(err) | ||
require.Equal(base64Payload, base64.StdEncoding.EncodeToString(addressedPayload.Bytes())) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. | ||
// See the file LICENSE for licensing terms. | ||
|
||
package payload | ||
|
||
import ( | ||
"github.com/ava-labs/avalanchego/codec" | ||
"github.com/ava-labs/avalanchego/codec/linearcodec" | ||
"github.com/ava-labs/avalanchego/utils/units" | ||
"github.com/ava-labs/avalanchego/utils/wrappers" | ||
) | ||
|
||
const ( | ||
codecVersion = 0 | ||
|
||
MaxMessageSize = 24 * units.KiB | ||
|
||
// Note: Modifying this variable can have subtle implications on memory | ||
// usage when parsing malformed payloads. | ||
MaxSliceLen = 24 * 1024 | ||
) | ||
|
||
// Codec does serialization and deserialization for Warp messages. | ||
var c codec.Manager | ||
|
||
func init() { | ||
c = codec.NewManager(MaxMessageSize) | ||
lc := linearcodec.NewCustomMaxLength(MaxSliceLen) | ||
|
||
errs := wrappers.Errs{} | ||
errs.Add( | ||
lc.RegisterType(&Hash{}), | ||
lc.RegisterType(&AddressedCall{}), | ||
c.RegisterCodec(codecVersion, lc), | ||
) | ||
if errs.Errored() { | ||
panic(errs.Err) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. | ||
// See the file LICENSE for licensing terms. | ||
|
||
package payload | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/ava-labs/avalanchego/ids" | ||
) | ||
|
||
var _ Payload = (*Hash)(nil) | ||
|
||
type Hash struct { | ||
Hash ids.ID `serialize:"true"` | ||
|
||
bytes []byte | ||
} | ||
|
||
// NewHash creates a new *Hash and initializes it. | ||
func NewHash(hash ids.ID) (*Hash, error) { | ||
bhp := &Hash{ | ||
Hash: hash, | ||
} | ||
return bhp, initialize(bhp) | ||
} | ||
|
||
// ParseHash converts a slice of bytes into an initialized Hash. | ||
func ParseHash(b []byte) (*Hash, error) { | ||
payloadIntf, err := Parse(b) | ||
if err != nil { | ||
return nil, err | ||
} | ||
payload, ok := payloadIntf.(*Hash) | ||
if !ok { | ||
return nil, fmt.Errorf("%w: %T", errWrongType, payloadIntf) | ||
} | ||
return payload, nil | ||
} | ||
|
||
// Bytes returns the binary representation of this payload. It assumes that the | ||
// payload is initialized from either NewHash or Parse. | ||
func (b *Hash) Bytes() []byte { | ||
return b.bytes | ||
} | ||
|
||
func (b *Hash) initialize(bytes []byte) { | ||
b.bytes = bytes | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. | ||
// See the file LICENSE for licensing terms. | ||
|
||
package payload | ||
|
||
import ( | ||
"encoding/base64" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
|
||
"github.com/ava-labs/avalanchego/codec" | ||
"github.com/ava-labs/avalanchego/ids" | ||
) | ||
|
||
func TestHash(t *testing.T) { | ||
require := require.New(t) | ||
|
||
hashPayload, err := NewHash(ids.GenerateTestID()) | ||
require.NoError(err) | ||
|
||
hashPayloadBytes := hashPayload.Bytes() | ||
parsedHashPayload, err := ParseHash(hashPayloadBytes) | ||
require.NoError(err) | ||
require.Equal(hashPayload, parsedHashPayload) | ||
} | ||
|
||
func TestParseHashJunk(t *testing.T) { | ||
_, err := ParseHash(junkBytes) | ||
require.ErrorIs(t, err, codec.ErrUnknownVersion) | ||
} | ||
|
||
func TestHashBytes(t *testing.T) { | ||
require := require.New(t) | ||
base64Payload := "AAAAAAAABAUGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" | ||
hashPayload, err := NewHash(ids.ID{4, 5, 6}) | ||
require.NoError(err) | ||
require.Equal(base64Payload, base64.StdEncoding.EncodeToString(hashPayload.Bytes())) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. | ||
// See the file LICENSE for licensing terms. | ||
|
||
package payload | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
) | ||
|
||
var errWrongType = errors.New("wrong payload type") | ||
|
||
// Payload provides a common interface for all payloads implemented by this | ||
// package. | ||
type Payload interface { | ||
// Bytes returns the binary representation of this payload. | ||
Bytes() []byte | ||
|
||
// initialize the payload with the provided binary representation. | ||
initialize(b []byte) | ||
} | ||
|
||
func Parse(bytes []byte) (Payload, error) { | ||
var payload Payload | ||
if _, err := c.Unmarshal(bytes, &payload); err != nil { | ||
return nil, err | ||
} | ||
payload.initialize(bytes) | ||
return payload, nil | ||
} | ||
|
||
func initialize(p Payload) error { | ||
bytes, err := c.Marshal(codecVersion, &p) | ||
if err != nil { | ||
return fmt.Errorf("couldn't marshal %T payload: %w", p, err) | ||
} | ||
p.initialize(bytes) | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. | ||
// See the file LICENSE for licensing terms. | ||
|
||
package payload | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
|
||
"github.com/ava-labs/avalanchego/codec" | ||
"github.com/ava-labs/avalanchego/ids" | ||
) | ||
|
||
var junkBytes = []byte{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88} | ||
|
||
func TestParseJunk(t *testing.T) { | ||
require := require.New(t) | ||
_, err := Parse(junkBytes) | ||
require.ErrorIs(err, codec.ErrUnknownVersion) | ||
} | ||
|
||
func TestParseWrongPayloadType(t *testing.T) { | ||
require := require.New(t) | ||
hashPayload, err := NewHash(ids.GenerateTestID()) | ||
require.NoError(err) | ||
|
||
shortID := ids.GenerateTestShortID() | ||
addressedPayload, err := NewAddressedCall( | ||
shortID[:], | ||
[]byte{1, 2, 3}, | ||
) | ||
require.NoError(err) | ||
|
||
_, err = ParseAddressedCall(hashPayload.Bytes()) | ||
require.ErrorIs(err, errWrongType) | ||
|
||
_, err = ParseHash(addressedPayload.Bytes()) | ||
require.ErrorIs(err, errWrongType) | ||
} | ||
|
||
func TestParse(t *testing.T) { | ||
require := require.New(t) | ||
hashPayload, err := NewHash(ids.ID{4, 5, 6}) | ||
require.NoError(err) | ||
|
||
parsedHashPayload, err := Parse(hashPayload.Bytes()) | ||
require.NoError(err) | ||
require.Equal(hashPayload, parsedHashPayload) | ||
|
||
addressedPayload, err := NewAddressedCall( | ||
[]byte{1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, | ||
[]byte{10, 11, 12}, | ||
) | ||
require.NoError(err) | ||
|
||
parsedAddressedPayload, err := Parse(addressedPayload.Bytes()) | ||
require.NoError(err) | ||
require.Equal(addressedPayload, parsedAddressedPayload) | ||
} |