Skip to content

Commit

Permalink
Codec interface tests (#967)
Browse files Browse the repository at this point in the history
* Connect codec interface tests and refactor codec to interface like EVM one

* progress

* Fully implement Codec interface tests

* Run codec tests in loop

* Prettify codec and codec tests

* Refactor codec nil encoding handling

* Revert accidental changes to testIDL.json

* Add sonar exclusion for codec test utils

* Add sq exclusion for duplications in testutils, add decoder unit tests

* Add encoder unit test

* Fix lint and rename codec to solanacodec to avoid types name collision

* Solana codec entry improvements

* Fix Solana codec field casing

* minor err messages improvements

* Code improvements

* Fix encoder unit tests

* Fix sonar exclusions

* lint

* Reorder methods in Solana codec

* Fix CR integration tests config

* Revert TestNewIDLCodec_WithModifiers deletion

* Add comments for codec entry includeDiscriminator option

* Add discriminator value check in codec entry Decode

* Reuse utils from interface tests for Solana codec interface tests

* Fix comment

* Fix comment

* [Non-EVM-1062] Solana Codec events support, Hookup Fuzz tests and cleanup Codec init (#987)

* Add events IDL parsing to codec

* temp

* Add a basic codec test for event IDL parsing

* Cleanup Solana Codec init

* Hookup Codec fuzz tests

* delete an unnecessary comment

* lint

---------

Co-authored-by: Jonghyeon Park <[email protected]>
  • Loading branch information
2 people authored and dhaidashenko committed Dec 20, 2024
1 parent 972a01b commit 1976211
Show file tree
Hide file tree
Showing 25 changed files with 1,918 additions and 97 deletions.
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ require (
github.com/stretchr/testify v1.9.0
go.uber.org/zap v1.27.0
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0
golang.org/x/sync v0.8.0
golang.org/x/text v0.18.0
golang.org/x/sync v0.10.0
golang.org/x/text v0.21.0
)

require (
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -682,8 +682,8 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
Expand Down Expand Up @@ -746,8 +746,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
Expand Down
4 changes: 2 additions & 2 deletions integration-tests/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ require (
github.com/stretchr/testify v1.9.0
github.com/testcontainers/testcontainers-go v0.34.0
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c
golang.org/x/sync v0.8.0
golang.org/x/text v0.19.0
golang.org/x/sync v0.10.0
golang.org/x/text v0.21.0
gopkg.in/guregu/null.v4 v4.0.0
)

Expand Down
8 changes: 4 additions & 4 deletions integration-tests/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1692,8 +1692,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
Expand Down Expand Up @@ -1804,8 +1804,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
Expand Down
6 changes: 3 additions & 3 deletions integration-tests/relayinterface/chain_components_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ func (it *SolanaChainComponentsInterfaceTester[T]) Setup(t T) {
Procedure: config.ChainReaderProcedure{
IDLAccount: "DataAccount",
OutputModifications: codec.ModifiersConfig{
&codec.PropertyExtractorConfig{FieldName: "U64value"},
&codec.PropertyExtractorConfig{FieldName: "U64Value"},
},
},
},
Expand All @@ -142,7 +142,7 @@ func (it *SolanaChainComponentsInterfaceTester[T]) Setup(t T) {
Procedure: config.ChainReaderProcedure{
IDLAccount: "DataAccount",
OutputModifications: codec.ModifiersConfig{
&codec.PropertyExtractorConfig{FieldName: "U64slice"},
&codec.PropertyExtractorConfig{FieldName: "U64Slice"},
},
},
},
Expand All @@ -156,7 +156,7 @@ func (it *SolanaChainComponentsInterfaceTester[T]) Setup(t T) {
Procedure: config.ChainReaderProcedure{
IDLAccount: "DataAccount",
OutputModifications: codec.ModifiersConfig{
&codec.PropertyExtractorConfig{FieldName: "U64value"},
&codec.PropertyExtractorConfig{FieldName: "U64Value"},
},
},
},
Expand Down
192 changes: 192 additions & 0 deletions pkg/solana/codec/codec_entry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
package codec

import (
"bytes"
"fmt"
"reflect"

"github.com/smartcontractkit/chainlink-common/pkg/codec"
commonencodings "github.com/smartcontractkit/chainlink-common/pkg/codec/encodings"
commontypes "github.com/smartcontractkit/chainlink-common/pkg/types"
)

type Entry interface {
Encode(value any, into []byte) ([]byte, error)
Decode(encoded []byte) (any, []byte, error)
GetCodecType() commonencodings.TypeCodec
GetType() reflect.Type
Modifier() codec.Modifier
Size(numItems int) (int, error)
FixedSize() (int, error)
}

type entry struct {
// TODO this might not be needed in the end, it was handy to make tests simpler
offchainName string
onchainName string
reflectType reflect.Type
typeCodec commonencodings.TypeCodec
mod codec.Modifier
// includeDiscriminator during Encode adds a discriminator to the encoded bytes under an assumption that the provided value didn't have a discriminator.
// During Decode includeDiscriminator removes discriminator from bytes under an assumption that the provided struct doesn't need a discriminator.
includeDiscriminator bool
discriminator Discriminator
}

func NewAccountEntry(offchainName string, idlAccount IdlTypeDef, idlTypes IdlTypeDefSlice, includeDiscriminator bool, mod codec.Modifier, builder commonencodings.Builder) (Entry, error) {
_, accCodec, err := createCodecType(idlAccount, createRefs(idlTypes, builder), false)
if err != nil {
return nil, err
}

return newEntry(
offchainName,
idlAccount.Name,
accCodec,
includeDiscriminator,
mod,
), nil
}

func NewInstructionArgsEntry(offChainName string, instructions IdlInstruction, idlTypes IdlTypeDefSlice, mod codec.Modifier, builder commonencodings.Builder) (Entry, error) {
_, instructionCodecArgs, err := asStruct(instructions.Args, createRefs(idlTypes, builder), instructions.Name, false, true)
if err != nil {
return nil, err
}

return newEntry(
offChainName,
instructions.Name,
instructionCodecArgs,
// Instruction arguments don't need a discriminator by default
false,
mod,
), nil
}

func NewEventArgsEntry(offChainName string, event IdlEvent, idlTypes IdlTypeDefSlice, includeDiscriminator bool, mod codec.Modifier, builder commonencodings.Builder) (Entry, error) {
_, eventCodec, err := asStruct(eventFieldsToFields(event.Fields), createRefs(idlTypes, builder), event.Name, false, false)
if err != nil {
return nil, err
}

return newEntry(
offChainName,
event.Name,
eventCodec,
includeDiscriminator,
mod,
), nil
}

func newEntry(
offchainName, onchainName string,
typeCodec commonencodings.TypeCodec,
includeDiscriminator bool,
mod codec.Modifier,
) Entry {
return &entry{
offchainName: offchainName,
onchainName: onchainName,
reflectType: typeCodec.GetType(),
typeCodec: typeCodec,
mod: ensureModifier(mod),
includeDiscriminator: includeDiscriminator,
discriminator: *NewDiscriminator(onchainName),
}
}

func createRefs(idlTypes IdlTypeDefSlice, builder commonencodings.Builder) *codecRefs {
return &codecRefs{
builder: builder,
codecs: make(map[string]commonencodings.TypeCodec),
typeDefs: idlTypes,
dependencies: make(map[string][]string),
}
}

func (e *entry) Encode(value any, into []byte) ([]byte, error) {
// Special handling for encoding a nil pointer to an empty struct.
t := e.reflectType
if value == nil {
if t.Kind() == reflect.Pointer {
elem := t.Elem()
if elem.Kind() == reflect.Struct && elem.NumField() == 0 {
return []byte{}, nil
}
}
return nil, fmt.Errorf("%w: cannot encode nil value for offchainName: %q, onchainName: %q",
commontypes.ErrInvalidType, e.offchainName, e.onchainName)
}

encodedVal, err := e.typeCodec.Encode(value, into)
if err != nil {
return nil, err
}

if e.includeDiscriminator {
var byt []byte
encodedDisc, err := e.discriminator.Encode(&e.discriminator.hashPrefix, byt)
if err != nil {
return nil, err
}
return append(encodedDisc, encodedVal...), nil
}

return encodedVal, nil
}

func (e *entry) Decode(encoded []byte) (any, []byte, error) {
if e.includeDiscriminator {
if len(encoded) < discriminatorLength {
return nil, nil, fmt.Errorf("%w: encoded data too short to contain discriminator for offchainName: %q, onchainName: %q",
commontypes.ErrInvalidType, e.offchainName, e.onchainName)
}

if !bytes.Equal(e.discriminator.hashPrefix, encoded[:discriminatorLength]) {
return nil, nil, fmt.Errorf("%w: encoded data has a bad discriminator %v for offchainName: %q, onchainName: %q",
commontypes.ErrInvalidType, encoded[:discriminatorLength], e.offchainName, e.onchainName)
}

encoded = encoded[discriminatorLength:]
}
return e.typeCodec.Decode(encoded)
}

func (e *entry) GetCodecType() commonencodings.TypeCodec {
return e.typeCodec
}

func (e *entry) GetType() reflect.Type {
return e.reflectType
}

func (e *entry) Modifier() codec.Modifier {
return e.mod
}

func (e *entry) Size(numItems int) (int, error) {
return e.typeCodec.Size(numItems)
}

func (e *entry) FixedSize() (int, error) {
return e.typeCodec.FixedSize()
}

func ensureModifier(mod codec.Modifier) codec.Modifier {
if mod == nil {
return codec.MultiModifier{}
}
return mod
}

func eventFieldsToFields(evFields []IdlEventField) []IdlField {
var idlFields []IdlField
for _, evField := range evFields {
idlFields = append(idlFields, IdlField{
Name: evField.Name,
Type: evField.Type,
})
}
return idlFields
}
Loading

0 comments on commit 1976211

Please sign in to comment.