-
Notifications
You must be signed in to change notification settings - Fork 674
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Move warp/payload types to this repo so it can be easily reused from other subnets.
- Loading branch information
Showing
5 changed files
with
305 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 is parsed into one of the types included in this package to be further handled by the VM. | ||
|
||
## AddressedPayload | ||
|
||
AddressedPayload: | ||
``` | ||
+---------------------+--------+----------------------------------+ | ||
| 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 `0x00000000` for `AddressedPayload` | ||
- `sourceAddress` is the address that called `sendWarpPrecompile` on the source chain | ||
- `payload` is an arbitrary byte array payload | ||
|
||
## BlockHashPayload | ||
|
||
BlockHashPayload: | ||
``` | ||
+-----------------+----------+-----------+ | ||
| codecID : uint16 | 2 bytes | | ||
+-----------------+----------+-----------+ | ||
| typeID : uint32 | 4 bytes | | ||
+-----------------+----------+-----------+ | ||
| blockHash : [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 `0x00000001` for `BlockHashPayload` | ||
- `blockHash` is a blockHash from the `sourceChainID`. A signed block hash payload indicates that the signer has accepted the block on the source chain. |
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,56 @@ | ||
// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. | ||
// See the file LICENSE for licensing terms. | ||
|
||
package payload | ||
|
||
import "fmt" | ||
|
||
// AddressedPayload defines the format for delivering a point to point message across VMs | ||
// ie. (ChainA, AddressA) -> (ChainB, AddressB) | ||
type AddressedPayload struct { | ||
SourceAddress []byte `serialize:"true"` | ||
Payload []byte `serialize:"true"` | ||
|
||
bytes []byte | ||
} | ||
|
||
// NewAddressedPayload creates a new *AddressedPayload and initializes it. | ||
func NewAddressedPayload(sourceAddress []byte, payload []byte) (*AddressedPayload, error) { | ||
ap := &AddressedPayload{ | ||
SourceAddress: sourceAddress, | ||
Payload: payload, | ||
} | ||
return ap, ap.initialize() | ||
} | ||
|
||
// ParseAddressedPayload converts a slice of bytes into an initialized | ||
// AddressedPayload. | ||
func ParseAddressedPayload(b []byte) (*AddressedPayload, error) { | ||
var unmarshalledPayloadIntf any | ||
if _, err := c.Unmarshal(b, &unmarshalledPayloadIntf); err != nil { | ||
return nil, err | ||
} | ||
payload, ok := unmarshalledPayloadIntf.(*AddressedPayload) | ||
if !ok { | ||
return nil, fmt.Errorf("%w: %T", errWrongType, unmarshalledPayloadIntf) | ||
} | ||
payload.bytes = b | ||
return payload, nil | ||
} | ||
|
||
// initialize recalculates the result of Bytes(). | ||
func (a *AddressedPayload) initialize() error { | ||
payloadIntf := any(a) | ||
bytes, err := c.Marshal(codecVersion, &payloadIntf) | ||
if err != nil { | ||
return fmt.Errorf("couldn't marshal warp addressed payload: %w", err) | ||
} | ||
a.bytes = bytes | ||
return nil | ||
} | ||
|
||
// Bytes returns the binary representation of this payload. It assumes that the | ||
// payload is initialized from either NewAddressedPayload or ParseAddressedPayload. | ||
func (a *AddressedPayload) Bytes() []byte { | ||
return a.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,57 @@ | ||
// 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" | ||
) | ||
|
||
// BlockHashPayload includes the block hash | ||
type BlockHashPayload struct { | ||
BlockHash ids.ID `serialize:"true"` | ||
|
||
bytes []byte | ||
} | ||
|
||
// NewBlockHashPayload creates a new *BlockHashPayload and initializes it. | ||
func NewBlockHashPayload(blockHash ids.ID) (*BlockHashPayload, error) { | ||
bhp := &BlockHashPayload{ | ||
BlockHash: blockHash, | ||
} | ||
return bhp, bhp.initialize() | ||
} | ||
|
||
// ParseBlockHashPayload converts a slice of bytes into an initialized | ||
// BlockHashPayload | ||
func ParseBlockHashPayload(b []byte) (*BlockHashPayload, error) { | ||
var unmarshalledPayloadIntf any | ||
if _, err := c.Unmarshal(b, &unmarshalledPayloadIntf); err != nil { | ||
return nil, err | ||
} | ||
payload, ok := unmarshalledPayloadIntf.(*BlockHashPayload) | ||
if !ok { | ||
return nil, fmt.Errorf("%w: %T", errWrongType, unmarshalledPayloadIntf) | ||
} | ||
payload.bytes = b | ||
return payload, nil | ||
} | ||
|
||
// initialize recalculates the result of Bytes(). | ||
func (b *BlockHashPayload) initialize() error { | ||
payloadIntf := any(b) | ||
bytes, err := c.Marshal(codecVersion, &payloadIntf) | ||
if err != nil { | ||
return fmt.Errorf("couldn't marshal block hash payload: %w", err) | ||
} | ||
b.bytes = bytes | ||
return nil | ||
} | ||
|
||
// Bytes returns the binary representation of this payload. It assumes that the | ||
// payload is initialized from either NewBlockHashPayload or ParseBlockHashPayload. | ||
func (b *BlockHashPayload) Bytes() []byte { | ||
return b.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,43 @@ | ||
// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved. | ||
// See the file LICENSE for licensing terms. | ||
|
||
package payload | ||
|
||
import ( | ||
"errors" | ||
|
||
"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" | ||
) | ||
|
||
var errWrongType = errors.New("wrong payload type") | ||
|
||
const ( | ||
codecVersion = 0 | ||
|
||
MaxMessageSize = 24 * units.KiB | ||
|
||
// Note: Modifying this variable can have subtle implications on memory | ||
// usage when parsing malformed payloads. | ||
MaxSliceLen = 24 * units.KiB | ||
) | ||
|
||
// 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(&AddressedPayload{}), | ||
lc.RegisterType(&BlockHashPayload{}), | ||
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,105 @@ | ||
// 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/ava-labs/avalanchego/codec" | ||
"github.com/ava-labs/avalanchego/ids" | ||
"github.com/ava-labs/avalanchego/utils" | ||
|
||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestAddressedPayload(t *testing.T) { | ||
require := require.New(t) | ||
shortID := ids.GenerateTestShortID() | ||
|
||
addressedPayload, err := NewAddressedPayload( | ||
shortID[:], | ||
[]byte{1, 2, 3}, | ||
) | ||
require.NoError(err) | ||
|
||
addressedPayloadBytes := addressedPayload.Bytes() | ||
addressedPayload2, err := ParseAddressedPayload(addressedPayloadBytes) | ||
require.NoError(err) | ||
require.Equal(addressedPayload, addressedPayload2) | ||
} | ||
|
||
func TestParseAddressedPayloadJunk(t *testing.T) { | ||
require := require.New(t) | ||
_, err := ParseAddressedPayload(utils.RandomBytes(1024)) | ||
require.ErrorIs(err, codec.ErrUnknownVersion) | ||
} | ||
|
||
func TestParseAddressedPayload(t *testing.T) { | ||
base64Payload := "AAAAAAAAAAAAEAECAwAAAAAAAAAAAAAAAAAAAAADCgsM" | ||
payload := &AddressedPayload{ | ||
SourceAddress: []byte{1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, | ||
Payload: []byte{10, 11, 12}, | ||
} | ||
|
||
require.NoError(t, payload.initialize()) | ||
|
||
require.Equal(t, base64Payload, base64.StdEncoding.EncodeToString(payload.Bytes())) | ||
|
||
parsedPayload, err := ParseAddressedPayload(payload.Bytes()) | ||
require.NoError(t, err) | ||
require.Equal(t, payload, parsedPayload) | ||
} | ||
|
||
func TestBlockHashPayload(t *testing.T) { | ||
require := require.New(t) | ||
|
||
blockHashPayload, err := NewBlockHashPayload(ids.GenerateTestID()) | ||
require.NoError(err) | ||
|
||
blockHashPayloadBytes := blockHashPayload.Bytes() | ||
blockHashPayload2, err := ParseBlockHashPayload(blockHashPayloadBytes) | ||
require.NoError(err) | ||
require.Equal(blockHashPayload, blockHashPayload2) | ||
} | ||
|
||
func TestParseBlockHashPayloadJunk(t *testing.T) { | ||
require := require.New(t) | ||
_, err := ParseBlockHashPayload(utils.RandomBytes(1024)) | ||
require.ErrorIs(err, codec.ErrUnknownVersion) | ||
} | ||
|
||
func TestParseBlockHashPayload(t *testing.T) { | ||
base64Payload := "AAAAAAABBAUGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" | ||
payload := &BlockHashPayload{ | ||
BlockHash: ids.ID{4, 5, 6}, | ||
} | ||
|
||
require.NoError(t, payload.initialize()) | ||
|
||
require.Equal(t, base64Payload, base64.StdEncoding.EncodeToString(payload.Bytes())) | ||
|
||
parsedPayload, err := ParseBlockHashPayload(payload.Bytes()) | ||
require.NoError(t, err) | ||
require.Equal(t, payload, parsedPayload) | ||
} | ||
|
||
func TestParseWrongPayloadType(t *testing.T) { | ||
require := require.New(t) | ||
blockHashPayload, err := NewBlockHashPayload(ids.GenerateTestID()) | ||
require.NoError(err) | ||
|
||
shortID := ids.GenerateTestShortID() | ||
addressedPayload, err := NewAddressedPayload( | ||
shortID[:], | ||
[]byte{1, 2, 3}, | ||
) | ||
require.NoError(err) | ||
|
||
_, err = ParseAddressedPayload(blockHashPayload.Bytes()) | ||
require.ErrorIs(err, errWrongType) | ||
|
||
_, err = ParseBlockHashPayload(addressedPayload.Bytes()) | ||
require.ErrorIs(err, errWrongType) | ||
} |