From f13427e55a10fea873171eece242cc51640db653 Mon Sep 17 00:00:00 2001 From: Cesar Date: Fri, 29 Sep 2023 02:05:40 -0400 Subject: [PATCH 1/7] Move warp/payload from Subnet-EVM Move warp/payload types to this repo so it can be easily reused from other subnets. --- vms/platformvm/warp/payload/README.md | 44 ++++++++ .../warp/payload/addressed_payload.go | 56 ++++++++++ .../warp/payload/block_hash_payload.go | 57 ++++++++++ vms/platformvm/warp/payload/codec.go | 43 +++++++ vms/platformvm/warp/payload/payload_test.go | 105 ++++++++++++++++++ 5 files changed, 305 insertions(+) create mode 100644 vms/platformvm/warp/payload/README.md create mode 100644 vms/platformvm/warp/payload/addressed_payload.go create mode 100644 vms/platformvm/warp/payload/block_hash_payload.go create mode 100644 vms/platformvm/warp/payload/codec.go create mode 100644 vms/platformvm/warp/payload/payload_test.go diff --git a/vms/platformvm/warp/payload/README.md b/vms/platformvm/warp/payload/README.md new file mode 100644 index 000000000000..f15179042bb3 --- /dev/null +++ b/vms/platformvm/warp/payload/README.md @@ -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. diff --git a/vms/platformvm/warp/payload/addressed_payload.go b/vms/platformvm/warp/payload/addressed_payload.go new file mode 100644 index 000000000000..c3678db688ab --- /dev/null +++ b/vms/platformvm/warp/payload/addressed_payload.go @@ -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 +} diff --git a/vms/platformvm/warp/payload/block_hash_payload.go b/vms/platformvm/warp/payload/block_hash_payload.go new file mode 100644 index 000000000000..8a11f81d6c0c --- /dev/null +++ b/vms/platformvm/warp/payload/block_hash_payload.go @@ -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 +} diff --git a/vms/platformvm/warp/payload/codec.go b/vms/platformvm/warp/payload/codec.go new file mode 100644 index 000000000000..c1accf16330d --- /dev/null +++ b/vms/platformvm/warp/payload/codec.go @@ -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) + } +} diff --git a/vms/platformvm/warp/payload/payload_test.go b/vms/platformvm/warp/payload/payload_test.go new file mode 100644 index 000000000000..b08322661a14 --- /dev/null +++ b/vms/platformvm/warp/payload/payload_test.go @@ -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) +} From 35724b994fef670e373069526d728eae3e742d4b Mon Sep 17 00:00:00 2001 From: Cesar <137245636+nytzuga@users.noreply.github.com> Date: Mon, 2 Oct 2023 10:35:01 -0300 Subject: [PATCH 2/7] Update vms/platformvm/warp/payload/README.md Co-authored-by: Stephen Buttolph Signed-off-by: Cesar <137245636+nytzuga@users.noreply.github.com> --- vms/platformvm/warp/payload/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vms/platformvm/warp/payload/README.md b/vms/platformvm/warp/payload/README.md index f15179042bb3..8bdd0421015e 100644 --- a/vms/platformvm/warp/payload/README.md +++ b/vms/platformvm/warp/payload/README.md @@ -21,7 +21,7 @@ AddressedPayload: - `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 +- `sourceAddress` is the address that sent this message from the source chain - `payload` is an arbitrary byte array payload ## BlockHashPayload From d74b0f44735182350a2defde06dcea8e1392b7f2 Mon Sep 17 00:00:00 2001 From: Cesar <137245636+nytzuga@users.noreply.github.com> Date: Mon, 2 Oct 2023 10:35:35 -0300 Subject: [PATCH 3/7] Update vms/platformvm/warp/payload/payload_test.go Co-authored-by: Stephen Buttolph Signed-off-by: Cesar <137245636+nytzuga@users.noreply.github.com> --- vms/platformvm/warp/payload/payload_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vms/platformvm/warp/payload/payload_test.go b/vms/platformvm/warp/payload/payload_test.go index b08322661a14..cacbff20a089 100644 --- a/vms/platformvm/warp/payload/payload_test.go +++ b/vms/platformvm/warp/payload/payload_test.go @@ -7,11 +7,11 @@ import ( "encoding/base64" "testing" + "github.com/stretchr/testify/require" + "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) { From a59e37326a17d4d89fa799dffde14e811eccf2e7 Mon Sep 17 00:00:00 2001 From: Aaron Buchwald Date: Wed, 4 Oct 2023 17:42:41 -0400 Subject: [PATCH 4/7] Address PR review comments --- vms/platformvm/warp/payload/README.md | 12 +-- .../warp/payload/addressed_payload.go | 33 ++++--- .../warp/payload/block_hash_payload.go | 30 +++--- vms/platformvm/warp/payload/codec.go | 25 ++++- vms/platformvm/warp/payload/payload_test.go | 94 +++++++++++++------ 5 files changed, 133 insertions(+), 61 deletions(-) diff --git a/vms/platformvm/warp/payload/README.md b/vms/platformvm/warp/payload/README.md index 8bdd0421015e..7f419c287881 100644 --- a/vms/platformvm/warp/payload/README.md +++ b/vms/platformvm/warp/payload/README.md @@ -2,9 +2,9 @@ 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 +## AddressedCall -AddressedPayload: +AddressedCall: ``` +---------------------+--------+----------------------------------+ | codecID : uint16 | 2 bytes | @@ -20,13 +20,13 @@ AddressedPayload: ``` - `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` +- `typeID` is the payload type identifier and is `0x00000000` for `AddressedCall` - `sourceAddress` is the address that sent this message from the source chain - `payload` is an arbitrary byte array payload -## BlockHashPayload +## BlockHash -BlockHashPayload: +BlockHash: ``` +-----------------+----------+-----------+ | codecID : uint16 | 2 bytes | @@ -40,5 +40,5 @@ BlockHashPayload: ``` - `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` +- `typeID` is the payload type identifier and is `0x00000001` for `BlockHash` - `blockHash` is a blockHash from the `sourceChainID`. A signed block hash payload indicates that the signer has accepted the block on the source chain. diff --git a/vms/platformvm/warp/payload/addressed_payload.go b/vms/platformvm/warp/payload/addressed_payload.go index c3678db688ab..f129d2430322 100644 --- a/vms/platformvm/warp/payload/addressed_payload.go +++ b/vms/platformvm/warp/payload/addressed_payload.go @@ -5,32 +5,35 @@ 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 { +var _ byteSetter = (*AddressedCall)(nil) + +// AddressedCall defines the format for delivering a call across VMs including a +// source address and a payload. +// Implementation of destinationAddress can be implemented on top of this payload. +type AddressedCall 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{ +// NewAddressedCall creates a new *AddressedCall and initializes it. +func NewAddressedCall(sourceAddress []byte, payload []byte) (*AddressedCall, error) { + ap := &AddressedCall{ SourceAddress: sourceAddress, Payload: payload, } return ap, ap.initialize() } -// ParseAddressedPayload converts a slice of bytes into an initialized -// AddressedPayload. -func ParseAddressedPayload(b []byte) (*AddressedPayload, error) { +// ParseAddressedCall converts a slice of bytes into an initialized +// AddressedCall. +func ParseAddressedCall(b []byte) (*AddressedCall, error) { var unmarshalledPayloadIntf any if _, err := c.Unmarshal(b, &unmarshalledPayloadIntf); err != nil { return nil, err } - payload, ok := unmarshalledPayloadIntf.(*AddressedPayload) + payload, ok := unmarshalledPayloadIntf.(*AddressedCall) if !ok { return nil, fmt.Errorf("%w: %T", errWrongType, unmarshalledPayloadIntf) } @@ -39,7 +42,7 @@ func ParseAddressedPayload(b []byte) (*AddressedPayload, error) { } // initialize recalculates the result of Bytes(). -func (a *AddressedPayload) initialize() error { +func (a *AddressedCall) initialize() error { payloadIntf := any(a) bytes, err := c.Marshal(codecVersion, &payloadIntf) if err != nil { @@ -49,8 +52,12 @@ func (a *AddressedPayload) initialize() error { return nil } +func (a *AddressedCall) setBytes(bytes []byte) { + a.bytes = bytes +} + // 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 { +// payload is initialized from either NewAddressedCall or ParseAddressedCall. +func (a *AddressedCall) Bytes() []byte { return a.bytes } diff --git a/vms/platformvm/warp/payload/block_hash_payload.go b/vms/platformvm/warp/payload/block_hash_payload.go index 8a11f81d6c0c..3832dbed79f0 100644 --- a/vms/platformvm/warp/payload/block_hash_payload.go +++ b/vms/platformvm/warp/payload/block_hash_payload.go @@ -9,29 +9,31 @@ import ( "github.com/ava-labs/avalanchego/ids" ) -// BlockHashPayload includes the block hash -type BlockHashPayload struct { +var _ byteSetter = (*BlockHash)(nil) + +// BlockHash includes the block hash +type BlockHash 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{ +// NewBlockHash creates a new *BlockHash and initializes it. +func NewBlockHash(blockHash ids.ID) (*BlockHash, error) { + bhp := &BlockHash{ BlockHash: blockHash, } return bhp, bhp.initialize() } -// ParseBlockHashPayload converts a slice of bytes into an initialized -// BlockHashPayload -func ParseBlockHashPayload(b []byte) (*BlockHashPayload, error) { +// ParseBlockHash converts a slice of bytes into an initialized +// BlockHash +func ParseBlockHash(b []byte) (*BlockHash, error) { var unmarshalledPayloadIntf any if _, err := c.Unmarshal(b, &unmarshalledPayloadIntf); err != nil { return nil, err } - payload, ok := unmarshalledPayloadIntf.(*BlockHashPayload) + payload, ok := unmarshalledPayloadIntf.(*BlockHash) if !ok { return nil, fmt.Errorf("%w: %T", errWrongType, unmarshalledPayloadIntf) } @@ -40,7 +42,7 @@ func ParseBlockHashPayload(b []byte) (*BlockHashPayload, error) { } // initialize recalculates the result of Bytes(). -func (b *BlockHashPayload) initialize() error { +func (b *BlockHash) initialize() error { payloadIntf := any(b) bytes, err := c.Marshal(codecVersion, &payloadIntf) if err != nil { @@ -50,8 +52,12 @@ func (b *BlockHashPayload) initialize() error { return nil } +func (b *BlockHash) setBytes(bytes []byte) { + b.bytes = bytes +} + // 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 { +// payload is initialized from either NewBlockHash or ParseBlockHash. +func (b *BlockHash) Bytes() []byte { return b.bytes } diff --git a/vms/platformvm/warp/payload/codec.go b/vms/platformvm/warp/payload/codec.go index c1accf16330d..c460a7fff970 100644 --- a/vms/platformvm/warp/payload/codec.go +++ b/vms/platformvm/warp/payload/codec.go @@ -5,6 +5,7 @@ package payload import ( "errors" + "fmt" "github.com/ava-labs/avalanchego/codec" "github.com/ava-labs/avalanchego/codec/linearcodec" @@ -33,11 +34,31 @@ func init() { errs := wrappers.Errs{} errs.Add( - lc.RegisterType(&AddressedPayload{}), - lc.RegisterType(&BlockHashPayload{}), + lc.RegisterType(&AddressedCall{}), + lc.RegisterType(&BlockHash{}), c.RegisterCodec(codecVersion, lc), ) if errs.Errored() { panic(errs.Err) } } + +// byteSetter provides an interface to set the bytes of an underlying type to [b] +// after unmarshalling into that type. +type byteSetter interface { + setBytes(b []byte) +} + +func Parse(bytes []byte) (interface{}, error) { + var intf interface{} + if _, err := c.Unmarshal(bytes, &intf); err != nil { + return nil, err + } + + payload, ok := intf.(byteSetter) + if !ok { + return nil, fmt.Errorf("%w: %T", errWrongType, intf) + } + payload.setBytes(bytes) + return payload, nil +} diff --git a/vms/platformvm/warp/payload/payload_test.go b/vms/platformvm/warp/payload/payload_test.go index cacbff20a089..379b8910d157 100644 --- a/vms/platformvm/warp/payload/payload_test.go +++ b/vms/platformvm/warp/payload/payload_test.go @@ -14,92 +14,130 @@ import ( "github.com/ava-labs/avalanchego/utils" ) -func TestAddressedPayload(t *testing.T) { +func TestAddressedCall(t *testing.T) { require := require.New(t) shortID := ids.GenerateTestShortID() - addressedPayload, err := NewAddressedPayload( + addressedPayload, err := NewAddressedCall( shortID[:], []byte{1, 2, 3}, ) require.NoError(err) addressedPayloadBytes := addressedPayload.Bytes() - addressedPayload2, err := ParseAddressedPayload(addressedPayloadBytes) + addressedPayload2, err := ParseAddressedCall(addressedPayloadBytes) require.NoError(err) require.Equal(addressedPayload, addressedPayload2) } -func TestParseAddressedPayloadJunk(t *testing.T) { +func TestParseAddressedCallJunk(t *testing.T) { require := require.New(t) - _, err := ParseAddressedPayload(utils.RandomBytes(1024)) + _, err := ParseAddressedCall(utils.RandomBytes(1024)) require.ErrorIs(err, codec.ErrUnknownVersion) } -func TestParseAddressedPayload(t *testing.T) { +func TestParseAddressedCall(t *testing.T) { + require := require.New(t) base64Payload := "AAAAAAAAAAAAEAECAwAAAAAAAAAAAAAAAAAAAAADCgsM" - payload := &AddressedPayload{ + payload := &AddressedCall{ 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.NoError(payload.initialize()) - require.Equal(t, base64Payload, base64.StdEncoding.EncodeToString(payload.Bytes())) + require.Equal(base64Payload, base64.StdEncoding.EncodeToString(payload.Bytes())) - parsedPayload, err := ParseAddressedPayload(payload.Bytes()) - require.NoError(t, err) - require.Equal(t, payload, parsedPayload) + parsedPayload, err := ParseAddressedCall(payload.Bytes()) + require.NoError(err) + require.Equal(payload, parsedPayload) } -func TestBlockHashPayload(t *testing.T) { +func TestBlockHash(t *testing.T) { require := require.New(t) - blockHashPayload, err := NewBlockHashPayload(ids.GenerateTestID()) + blockHashPayload, err := NewBlockHash(ids.GenerateTestID()) require.NoError(err) blockHashPayloadBytes := blockHashPayload.Bytes() - blockHashPayload2, err := ParseBlockHashPayload(blockHashPayloadBytes) + blockHashPayload2, err := ParseBlockHash(blockHashPayloadBytes) require.NoError(err) require.Equal(blockHashPayload, blockHashPayload2) } -func TestParseBlockHashPayloadJunk(t *testing.T) { +func TestParseBlockHashJunk(t *testing.T) { require := require.New(t) - _, err := ParseBlockHashPayload(utils.RandomBytes(1024)) + _, err := ParseBlockHash(utils.RandomBytes(1024)) require.ErrorIs(err, codec.ErrUnknownVersion) } -func TestParseBlockHashPayload(t *testing.T) { +func TestParseBlockHash(t *testing.T) { + require := require.New(t) base64Payload := "AAAAAAABBAUGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" - payload := &BlockHashPayload{ + payload := &BlockHash{ BlockHash: ids.ID{4, 5, 6}, } - require.NoError(t, payload.initialize()) + require.NoError(payload.initialize()) - require.Equal(t, base64Payload, base64.StdEncoding.EncodeToString(payload.Bytes())) + require.Equal(base64Payload, base64.StdEncoding.EncodeToString(payload.Bytes())) - parsedPayload, err := ParseBlockHashPayload(payload.Bytes()) - require.NoError(t, err) - require.Equal(t, payload, parsedPayload) + parsedPayload, err := ParseBlockHash(payload.Bytes()) + require.NoError(err) + require.Equal(payload, parsedPayload) } func TestParseWrongPayloadType(t *testing.T) { require := require.New(t) - blockHashPayload, err := NewBlockHashPayload(ids.GenerateTestID()) + blockHashPayload, err := NewBlockHash(ids.GenerateTestID()) require.NoError(err) shortID := ids.GenerateTestShortID() - addressedPayload, err := NewAddressedPayload( + addressedPayload, err := NewAddressedCall( shortID[:], []byte{1, 2, 3}, ) require.NoError(err) - _, err = ParseAddressedPayload(blockHashPayload.Bytes()) + _, err = ParseAddressedCall(blockHashPayload.Bytes()) require.ErrorIs(err, errWrongType) - _, err = ParseBlockHashPayload(addressedPayload.Bytes()) + _, err = ParseBlockHash(addressedPayload.Bytes()) require.ErrorIs(err, errWrongType) } + +func TestParseJunk(t *testing.T) { + require := require.New(t) + _, err := Parse(utils.RandomBytes(1024)) + require.ErrorIs(err, codec.ErrUnknownVersion) +} + +func TestParsePayload(t *testing.T) { + require := require.New(t) + base64BlockHashPayload := "AAAAAAABBAUGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" + blockHashPayload := &BlockHash{ + BlockHash: ids.ID{4, 5, 6}, + } + + require.NoError(blockHashPayload.initialize()) + + require.Equal(base64BlockHashPayload, base64.StdEncoding.EncodeToString(blockHashPayload.Bytes())) + + parsedBlockHashPayload, err := Parse(blockHashPayload.Bytes()) + require.NoError(err) + require.Equal(blockHashPayload, parsedBlockHashPayload) + + base64AddressedPayload := "AAAAAAAAAAAAEAECAwAAAAAAAAAAAAAAAAAAAAADCgsM" + addressedPayload := &AddressedCall{ + 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(addressedPayload.initialize()) + + require.Equal(base64AddressedPayload, base64.StdEncoding.EncodeToString(addressedPayload.Bytes())) + + parsedAddressedPayload, err := Parse(addressedPayload.Bytes()) + require.NoError(err) + require.Equal(addressedPayload, parsedAddressedPayload) +} From e4133aff4e9922e6aeef780dfd447167c83aef97 Mon Sep 17 00:00:00 2001 From: Aaron Buchwald Date: Thu, 5 Oct 2023 10:25:38 -0400 Subject: [PATCH 5/7] update warp Parse to return internal interface --- vms/platformvm/warp/payload/codec.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vms/platformvm/warp/payload/codec.go b/vms/platformvm/warp/payload/codec.go index c460a7fff970..00a590831df7 100644 --- a/vms/platformvm/warp/payload/codec.go +++ b/vms/platformvm/warp/payload/codec.go @@ -49,7 +49,7 @@ type byteSetter interface { setBytes(b []byte) } -func Parse(bytes []byte) (interface{}, error) { +func Parse(bytes []byte) (byteSetter, error) { var intf interface{} if _, err := c.Unmarshal(bytes, &intf); err != nil { return nil, err From fe767f0e430977e64046e90c64a21cf599e3ae35 Mon Sep 17 00:00:00 2001 From: Stephen Buttolph Date: Thu, 5 Oct 2023 12:27:16 -0400 Subject: [PATCH 6/7] Cleanup --- vms/platformvm/warp/payload/README.md | 4 +- ...addressed_payload.go => addressed_call.go} | 36 +++++++---------- .../{block_hash_payload.go => block_hash.go} | 36 ++++++----------- vms/platformvm/warp/payload/codec.go | 27 +------------ vms/platformvm/warp/payload/payload.go | 39 +++++++++++++++++++ vms/platformvm/warp/payload/payload_test.go | 8 ++-- 6 files changed, 70 insertions(+), 80 deletions(-) rename vms/platformvm/warp/payload/{addressed_payload.go => addressed_call.go} (59%) rename vms/platformvm/warp/payload/{block_hash_payload.go => block_hash.go} (51%) create mode 100644 vms/platformvm/warp/payload/payload.go diff --git a/vms/platformvm/warp/payload/README.md b/vms/platformvm/warp/payload/README.md index 7f419c287881..8a55b04bb127 100644 --- a/vms/platformvm/warp/payload/README.md +++ b/vms/platformvm/warp/payload/README.md @@ -1,6 +1,6 @@ # 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. +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. ## AddressedCall @@ -41,4 +41,4 @@ BlockHash: - `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 `BlockHash` -- `blockHash` is a blockHash from the `sourceChainID`. A signed block hash payload indicates that the signer has accepted the block on the source chain. +- `blockHash` is a blockHash from the `sourceChainID`. A signed block hash payload should indicate that the signers have accepted the block on the source chain diff --git a/vms/platformvm/warp/payload/addressed_payload.go b/vms/platformvm/warp/payload/addressed_call.go similarity index 59% rename from vms/platformvm/warp/payload/addressed_payload.go rename to vms/platformvm/warp/payload/addressed_call.go index f129d2430322..bd34444dd53c 100644 --- a/vms/platformvm/warp/payload/addressed_payload.go +++ b/vms/platformvm/warp/payload/addressed_call.go @@ -5,11 +5,13 @@ package payload import "fmt" -var _ byteSetter = (*AddressedCall)(nil) +var _ Payload = (*AddressedCall)(nil) // AddressedCall defines the format for delivering a call across VMs including a // source address and a payload. -// Implementation of destinationAddress can be implemented on top of this 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"` @@ -23,41 +25,29 @@ func NewAddressedCall(sourceAddress []byte, payload []byte) (*AddressedCall, err SourceAddress: sourceAddress, Payload: payload, } - return ap, ap.initialize() + return ap, initialize(ap) } // ParseAddressedCall converts a slice of bytes into an initialized // AddressedCall. func ParseAddressedCall(b []byte) (*AddressedCall, error) { - var unmarshalledPayloadIntf any - if _, err := c.Unmarshal(b, &unmarshalledPayloadIntf); err != nil { + payloadIntf, err := Parse(b) + if err != nil { return nil, err } - payload, ok := unmarshalledPayloadIntf.(*AddressedCall) + payload, ok := payloadIntf.(*AddressedCall) if !ok { - return nil, fmt.Errorf("%w: %T", errWrongType, unmarshalledPayloadIntf) + return nil, fmt.Errorf("%w: %T", errWrongType, payloadIntf) } - payload.bytes = b return payload, nil } -// initialize recalculates the result of Bytes(). -func (a *AddressedCall) 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 -} - -func (a *AddressedCall) setBytes(bytes []byte) { - a.bytes = bytes -} - // Bytes returns the binary representation of this payload. It assumes that the // payload is initialized from either NewAddressedCall or ParseAddressedCall. func (a *AddressedCall) Bytes() []byte { return a.bytes } + +func (a *AddressedCall) initialize(bytes []byte) { + a.bytes = bytes +} diff --git a/vms/platformvm/warp/payload/block_hash_payload.go b/vms/platformvm/warp/payload/block_hash.go similarity index 51% rename from vms/platformvm/warp/payload/block_hash_payload.go rename to vms/platformvm/warp/payload/block_hash.go index 3832dbed79f0..5e01c751fcf3 100644 --- a/vms/platformvm/warp/payload/block_hash_payload.go +++ b/vms/platformvm/warp/payload/block_hash.go @@ -9,9 +9,8 @@ import ( "github.com/ava-labs/avalanchego/ids" ) -var _ byteSetter = (*BlockHash)(nil) +var _ Payload = (*BlockHash)(nil) -// BlockHash includes the block hash type BlockHash struct { BlockHash ids.ID `serialize:"true"` @@ -23,41 +22,28 @@ func NewBlockHash(blockHash ids.ID) (*BlockHash, error) { bhp := &BlockHash{ BlockHash: blockHash, } - return bhp, bhp.initialize() + return bhp, initialize(bhp) } -// ParseBlockHash converts a slice of bytes into an initialized -// BlockHash +// ParseBlockHash converts a slice of bytes into an initialized BlockHash. func ParseBlockHash(b []byte) (*BlockHash, error) { - var unmarshalledPayloadIntf any - if _, err := c.Unmarshal(b, &unmarshalledPayloadIntf); err != nil { + payloadIntf, err := Parse(b) + if err != nil { return nil, err } - payload, ok := unmarshalledPayloadIntf.(*BlockHash) + payload, ok := payloadIntf.(*BlockHash) if !ok { - return nil, fmt.Errorf("%w: %T", errWrongType, unmarshalledPayloadIntf) + return nil, fmt.Errorf("%w: %T", errWrongType, payloadIntf) } - payload.bytes = b return payload, nil } -// initialize recalculates the result of Bytes(). -func (b *BlockHash) 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 -} - -func (b *BlockHash) setBytes(bytes []byte) { - b.bytes = bytes -} - // Bytes returns the binary representation of this payload. It assumes that the // payload is initialized from either NewBlockHash or ParseBlockHash. func (b *BlockHash) Bytes() []byte { return b.bytes } + +func (b *BlockHash) initialize(bytes []byte) { + b.bytes = bytes +} diff --git a/vms/platformvm/warp/payload/codec.go b/vms/platformvm/warp/payload/codec.go index 00a590831df7..e9d3a0e4e54e 100644 --- a/vms/platformvm/warp/payload/codec.go +++ b/vms/platformvm/warp/payload/codec.go @@ -4,17 +4,12 @@ package payload import ( - "errors" - "fmt" - "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 @@ -22,7 +17,7 @@ const ( // Note: Modifying this variable can have subtle implications on memory // usage when parsing malformed payloads. - MaxSliceLen = 24 * units.KiB + MaxSliceLen = 24 * 1024 ) // Codec does serialization and deserialization for Warp messages. @@ -42,23 +37,3 @@ func init() { panic(errs.Err) } } - -// byteSetter provides an interface to set the bytes of an underlying type to [b] -// after unmarshalling into that type. -type byteSetter interface { - setBytes(b []byte) -} - -func Parse(bytes []byte) (byteSetter, error) { - var intf interface{} - if _, err := c.Unmarshal(bytes, &intf); err != nil { - return nil, err - } - - payload, ok := intf.(byteSetter) - if !ok { - return nil, fmt.Errorf("%w: %T", errWrongType, intf) - } - payload.setBytes(bytes) - return payload, nil -} diff --git a/vms/platformvm/warp/payload/payload.go b/vms/platformvm/warp/payload/payload.go new file mode 100644 index 000000000000..e4601945be98 --- /dev/null +++ b/vms/platformvm/warp/payload/payload.go @@ -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 +} diff --git a/vms/platformvm/warp/payload/payload_test.go b/vms/platformvm/warp/payload/payload_test.go index 379b8910d157..79e4c6b03599 100644 --- a/vms/platformvm/warp/payload/payload_test.go +++ b/vms/platformvm/warp/payload/payload_test.go @@ -44,7 +44,7 @@ func TestParseAddressedCall(t *testing.T) { Payload: []byte{10, 11, 12}, } - require.NoError(payload.initialize()) + require.NoError(initialize(payload)) require.Equal(base64Payload, base64.StdEncoding.EncodeToString(payload.Bytes())) @@ -78,7 +78,7 @@ func TestParseBlockHash(t *testing.T) { BlockHash: ids.ID{4, 5, 6}, } - require.NoError(payload.initialize()) + require.NoError(initialize(payload)) require.Equal(base64Payload, base64.StdEncoding.EncodeToString(payload.Bytes())) @@ -119,7 +119,7 @@ func TestParsePayload(t *testing.T) { BlockHash: ids.ID{4, 5, 6}, } - require.NoError(blockHashPayload.initialize()) + require.NoError(initialize(blockHashPayload)) require.Equal(base64BlockHashPayload, base64.StdEncoding.EncodeToString(blockHashPayload.Bytes())) @@ -133,7 +133,7 @@ func TestParsePayload(t *testing.T) { Payload: []byte{10, 11, 12}, } - require.NoError(addressedPayload.initialize()) + require.NoError(initialize(addressedPayload)) require.Equal(base64AddressedPayload, base64.StdEncoding.EncodeToString(addressedPayload.Bytes())) From 9a08314e6e10a84903f0c38414b386161d8080aa Mon Sep 17 00:00:00 2001 From: Stephen Buttolph Date: Thu, 5 Oct 2023 13:02:55 -0400 Subject: [PATCH 7/7] BlockHash -> Hash --- vms/platformvm/warp/payload/README.md | 40 +++--- vms/platformvm/warp/payload/addressed_call.go | 2 +- .../warp/payload/addressed_call_test.go | 46 +++++++ vms/platformvm/warp/payload/block_hash.go | 49 -------- vms/platformvm/warp/payload/codec.go | 2 +- vms/platformvm/warp/payload/hash.go | 49 ++++++++ vms/platformvm/warp/payload/hash_test.go | 39 ++++++ vms/platformvm/warp/payload/payload_test.go | 115 +++--------------- 8 files changed, 172 insertions(+), 170 deletions(-) create mode 100644 vms/platformvm/warp/payload/addressed_call_test.go delete mode 100644 vms/platformvm/warp/payload/block_hash.go create mode 100644 vms/platformvm/warp/payload/hash.go create mode 100644 vms/platformvm/warp/payload/hash_test.go diff --git a/vms/platformvm/warp/payload/README.md b/vms/platformvm/warp/payload/README.md index 8a55b04bb127..d82a59831be1 100644 --- a/vms/platformvm/warp/payload/README.md +++ b/vms/platformvm/warp/payload/README.md @@ -2,6 +2,25 @@ 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: @@ -20,25 +39,6 @@ AddressedCall: ``` - `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 `AddressedCall` +- `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 - -## BlockHash - -BlockHash: -``` -+-----------------+----------+-----------+ -| 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 `BlockHash` -- `blockHash` is a blockHash from the `sourceChainID`. A signed block hash payload should indicate that the signers have accepted the block on the source chain diff --git a/vms/platformvm/warp/payload/addressed_call.go b/vms/platformvm/warp/payload/addressed_call.go index bd34444dd53c..afdecd9e9f01 100644 --- a/vms/platformvm/warp/payload/addressed_call.go +++ b/vms/platformvm/warp/payload/addressed_call.go @@ -43,7 +43,7 @@ func ParseAddressedCall(b []byte) (*AddressedCall, error) { } // Bytes returns the binary representation of this payload. It assumes that the -// payload is initialized from either NewAddressedCall or ParseAddressedCall. +// payload is initialized from either NewAddressedCall or Parse. func (a *AddressedCall) Bytes() []byte { return a.bytes } diff --git a/vms/platformvm/warp/payload/addressed_call_test.go b/vms/platformvm/warp/payload/addressed_call_test.go new file mode 100644 index 000000000000..0e60ef294c4b --- /dev/null +++ b/vms/platformvm/warp/payload/addressed_call_test.go @@ -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())) +} diff --git a/vms/platformvm/warp/payload/block_hash.go b/vms/platformvm/warp/payload/block_hash.go deleted file mode 100644 index 5e01c751fcf3..000000000000 --- a/vms/platformvm/warp/payload/block_hash.go +++ /dev/null @@ -1,49 +0,0 @@ -// 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 = (*BlockHash)(nil) - -type BlockHash struct { - BlockHash ids.ID `serialize:"true"` - - bytes []byte -} - -// NewBlockHash creates a new *BlockHash and initializes it. -func NewBlockHash(blockHash ids.ID) (*BlockHash, error) { - bhp := &BlockHash{ - BlockHash: blockHash, - } - return bhp, initialize(bhp) -} - -// ParseBlockHash converts a slice of bytes into an initialized BlockHash. -func ParseBlockHash(b []byte) (*BlockHash, error) { - payloadIntf, err := Parse(b) - if err != nil { - return nil, err - } - payload, ok := payloadIntf.(*BlockHash) - 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 NewBlockHash or ParseBlockHash. -func (b *BlockHash) Bytes() []byte { - return b.bytes -} - -func (b *BlockHash) initialize(bytes []byte) { - b.bytes = bytes -} diff --git a/vms/platformvm/warp/payload/codec.go b/vms/platformvm/warp/payload/codec.go index e9d3a0e4e54e..31d20f6777ac 100644 --- a/vms/platformvm/warp/payload/codec.go +++ b/vms/platformvm/warp/payload/codec.go @@ -29,8 +29,8 @@ func init() { errs := wrappers.Errs{} errs.Add( + lc.RegisterType(&Hash{}), lc.RegisterType(&AddressedCall{}), - lc.RegisterType(&BlockHash{}), c.RegisterCodec(codecVersion, lc), ) if errs.Errored() { diff --git a/vms/platformvm/warp/payload/hash.go b/vms/platformvm/warp/payload/hash.go new file mode 100644 index 000000000000..f3a0eb0c09d3 --- /dev/null +++ b/vms/platformvm/warp/payload/hash.go @@ -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 +} diff --git a/vms/platformvm/warp/payload/hash_test.go b/vms/platformvm/warp/payload/hash_test.go new file mode 100644 index 000000000000..1d890a8bd551 --- /dev/null +++ b/vms/platformvm/warp/payload/hash_test.go @@ -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())) +} diff --git a/vms/platformvm/warp/payload/payload_test.go b/vms/platformvm/warp/payload/payload_test.go index 79e4c6b03599..da14b8de0dbb 100644 --- a/vms/platformvm/warp/payload/payload_test.go +++ b/vms/platformvm/warp/payload/payload_test.go @@ -4,92 +4,25 @@ package payload import ( - "encoding/base64" "testing" "github.com/stretchr/testify/require" "github.com/ava-labs/avalanchego/codec" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/utils" ) -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() - addressedPayload2, err := ParseAddressedCall(addressedPayloadBytes) - require.NoError(err) - require.Equal(addressedPayload, addressedPayload2) -} - -func TestParseAddressedCallJunk(t *testing.T) { - require := require.New(t) - _, err := ParseAddressedCall(utils.RandomBytes(1024)) - require.ErrorIs(err, codec.ErrUnknownVersion) -} - -func TestParseAddressedCall(t *testing.T) { - require := require.New(t) - base64Payload := "AAAAAAAAAAAAEAECAwAAAAAAAAAAAAAAAAAAAAADCgsM" - payload := &AddressedCall{ - 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(initialize(payload)) - - require.Equal(base64Payload, base64.StdEncoding.EncodeToString(payload.Bytes())) - - parsedPayload, err := ParseAddressedCall(payload.Bytes()) - require.NoError(err) - require.Equal(payload, parsedPayload) -} - -func TestBlockHash(t *testing.T) { - require := require.New(t) - - blockHashPayload, err := NewBlockHash(ids.GenerateTestID()) - require.NoError(err) - - blockHashPayloadBytes := blockHashPayload.Bytes() - blockHashPayload2, err := ParseBlockHash(blockHashPayloadBytes) - require.NoError(err) - require.Equal(blockHashPayload, blockHashPayload2) -} +var junkBytes = []byte{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88} -func TestParseBlockHashJunk(t *testing.T) { +func TestParseJunk(t *testing.T) { require := require.New(t) - _, err := ParseBlockHash(utils.RandomBytes(1024)) + _, err := Parse(junkBytes) require.ErrorIs(err, codec.ErrUnknownVersion) } -func TestParseBlockHash(t *testing.T) { - require := require.New(t) - base64Payload := "AAAAAAABBAUGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" - payload := &BlockHash{ - BlockHash: ids.ID{4, 5, 6}, - } - - require.NoError(initialize(payload)) - - require.Equal(base64Payload, base64.StdEncoding.EncodeToString(payload.Bytes())) - - parsedPayload, err := ParseBlockHash(payload.Bytes()) - require.NoError(err) - require.Equal(payload, parsedPayload) -} - func TestParseWrongPayloadType(t *testing.T) { require := require.New(t) - blockHashPayload, err := NewBlockHash(ids.GenerateTestID()) + hashPayload, err := NewHash(ids.GenerateTestID()) require.NoError(err) shortID := ids.GenerateTestShortID() @@ -99,43 +32,27 @@ func TestParseWrongPayloadType(t *testing.T) { ) require.NoError(err) - _, err = ParseAddressedCall(blockHashPayload.Bytes()) + _, err = ParseAddressedCall(hashPayload.Bytes()) require.ErrorIs(err, errWrongType) - _, err = ParseBlockHash(addressedPayload.Bytes()) + _, err = ParseHash(addressedPayload.Bytes()) require.ErrorIs(err, errWrongType) } -func TestParseJunk(t *testing.T) { +func TestParse(t *testing.T) { require := require.New(t) - _, err := Parse(utils.RandomBytes(1024)) - require.ErrorIs(err, codec.ErrUnknownVersion) -} - -func TestParsePayload(t *testing.T) { - require := require.New(t) - base64BlockHashPayload := "AAAAAAABBAUGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" - blockHashPayload := &BlockHash{ - BlockHash: ids.ID{4, 5, 6}, - } - - require.NoError(initialize(blockHashPayload)) - - require.Equal(base64BlockHashPayload, base64.StdEncoding.EncodeToString(blockHashPayload.Bytes())) - - parsedBlockHashPayload, err := Parse(blockHashPayload.Bytes()) + hashPayload, err := NewHash(ids.ID{4, 5, 6}) require.NoError(err) - require.Equal(blockHashPayload, parsedBlockHashPayload) - - base64AddressedPayload := "AAAAAAAAAAAAEAECAwAAAAAAAAAAAAAAAAAAAAADCgsM" - addressedPayload := &AddressedCall{ - 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(initialize(addressedPayload)) + parsedHashPayload, err := Parse(hashPayload.Bytes()) + require.NoError(err) + require.Equal(hashPayload, parsedHashPayload) - require.Equal(base64AddressedPayload, base64.StdEncoding.EncodeToString(addressedPayload.Bytes())) + 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)