diff --git a/pkg/solana/chainreader/chain_reader.go b/pkg/solana/chainreader/chain_reader.go index 2339af96c..d017eb25d 100644 --- a/pkg/solana/chainreader/chain_reader.go +++ b/pkg/solana/chainreader/chain_reader.go @@ -17,8 +17,8 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/types/query" "github.com/smartcontractkit/chainlink-common/pkg/types/query/primitives" + "github.com/smartcontractkit/chainlink-solana/pkg/solana/codec" "github.com/smartcontractkit/chainlink-solana/pkg/solana/config" - "github.com/smartcontractkit/chainlink-solana/pkg/solana/solanacodec" ) const ServiceName = "SolanaChainReader" @@ -223,12 +223,12 @@ func (s *SolanaChainReaderService) CreateContractType(readIdentifier string, for func (s *SolanaChainReaderService) init(namespaces map[string]config.ChainReaderMethods) error { for namespace, methods := range namespaces { for methodName, method := range methods.Methods { - var idl solanacodec.IDL + var idl codec.IDL if err := json.Unmarshal([]byte(method.AnchorIDL), &idl); err != nil { return err } - idlCodec, err := solanacodec.NewIDLAccountCodec(idl, config.BuilderForEncoding(method.Encoding)) + idlCodec, err := codec.NewIDLAccountCodec(idl, config.BuilderForEncoding(method.Encoding)) if err != nil { return err } @@ -239,12 +239,12 @@ func (s *SolanaChainReaderService) init(namespaces map[string]config.ChainReader injectAddressModifier(procedure.OutputModifications) - mod, err := procedure.OutputModifications.ToModifier(solanacodec.DecoderHooks...) + mod, err := procedure.OutputModifications.ToModifier(codec.DecoderHooks...) if err != nil { return err } - codecWithModifiers, err := solanacodec.NewNamedModifierCodec(idlCodec, procedure.IDLAccount, mod) + codecWithModifiers, err := codec.NewNamedModifierCodec(idlCodec, procedure.IDLAccount, mod) if err != nil { return err } @@ -265,7 +265,7 @@ func (s *SolanaChainReaderService) init(namespaces map[string]config.ChainReader func injectAddressModifier(outputModifications codeccommon.ModifiersConfig) { for i, modConfig := range outputModifications { if addrModifierConfig, ok := modConfig.(*codeccommon.AddressBytesToStringModifierConfig); ok { - addrModifierConfig.Modifier = solanacodec.SolanaAddressModifier{} + addrModifierConfig.Modifier = codec.SolanaAddressModifier{} outputModifications[i] = addrModifierConfig } } diff --git a/pkg/solana/chainreader/chain_reader_test.go b/pkg/solana/chainreader/chain_reader_test.go index a6caf5168..4652f5199 100644 --- a/pkg/solana/chainreader/chain_reader_test.go +++ b/pkg/solana/chainreader/chain_reader_test.go @@ -31,9 +31,9 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/smartcontractkit/chainlink-solana/pkg/solana/chainreader" + "github.com/smartcontractkit/chainlink-solana/pkg/solana/codec" + "github.com/smartcontractkit/chainlink-solana/pkg/solana/codec/testutils" "github.com/smartcontractkit/chainlink-solana/pkg/solana/config" - "github.com/smartcontractkit/chainlink-solana/pkg/solana/solanacodec" - "github.com/smartcontractkit/chainlink-solana/pkg/solana/solanacodec/testutils" ) const ( @@ -269,16 +269,16 @@ func TestSolanaChainReaderService_GetLatestValue(t *testing.T) { }) } -func newTestIDLAndCodec(t *testing.T) (string, solanacodec.IDL, types.RemoteCodec) { +func newTestIDLAndCodec(t *testing.T) (string, codec.IDL, types.RemoteCodec) { t.Helper() - var idl solanacodec.IDL + var idl codec.IDL if err := json.Unmarshal([]byte(testutils.JSONIDLWithAllTypes), &idl); err != nil { t.Logf("failed to unmarshal test IDL: %s", err.Error()) t.FailNow() } - entry, err := solanacodec.NewIDLAccountCodec(idl, binary.LittleEndian()) + entry, err := codec.NewIDLAccountCodec(idl, binary.LittleEndian()) if err != nil { t.Logf("failed to create new codec from test IDL: %s", err.Error()) t.FailNow() @@ -763,13 +763,13 @@ func (r *chainReaderInterfaceTester) MaxWaitTimeForEvents() time.Duration { func makeTestCodec(t *testing.T, rawIDL string, encoding config.EncodingType) types.RemoteCodec { t.Helper() - var idl solanacodec.IDL + var idl codec.IDL if err := json.Unmarshal([]byte(rawIDL), &idl); err != nil { t.Logf("failed to unmarshal test IDL: %s", err.Error()) t.FailNow() } - testCodec, err := solanacodec.NewIDLAccountCodec(idl, config.BuilderForEncoding(encoding)) + testCodec, err := codec.NewIDLAccountCodec(idl, config.BuilderForEncoding(encoding)) if err != nil { t.Logf("failed to create new codec from test IDL: %s", err.Error()) t.FailNow() diff --git a/pkg/solana/solanacodec/anchoridl.go b/pkg/solana/codec/anchoridl.go similarity index 99% rename from pkg/solana/solanacodec/anchoridl.go rename to pkg/solana/codec/anchoridl.go index fe35374d2..e54710c5c 100644 --- a/pkg/solana/solanacodec/anchoridl.go +++ b/pkg/solana/codec/anchoridl.go @@ -1,4 +1,4 @@ -package solanacodec +package codec /* copied from https://github.com/gagliardetto/anchor-go where the IDL definition is not importable due to being defined diff --git a/pkg/solana/solanacodec/byte_string_modifier.go b/pkg/solana/codec/byte_string_modifier.go similarity index 98% rename from pkg/solana/solanacodec/byte_string_modifier.go rename to pkg/solana/codec/byte_string_modifier.go index 984a1a34b..8a30b33d5 100644 --- a/pkg/solana/solanacodec/byte_string_modifier.go +++ b/pkg/solana/codec/byte_string_modifier.go @@ -1,4 +1,4 @@ -package solanacodec +package codec import ( "fmt" diff --git a/pkg/solana/solanacodec/byte_string_modifier_test.go b/pkg/solana/codec/byte_string_modifier_test.go similarity index 92% rename from pkg/solana/solanacodec/byte_string_modifier_test.go rename to pkg/solana/codec/byte_string_modifier_test.go index a2d27f00a..83d1bf189 100644 --- a/pkg/solana/solanacodec/byte_string_modifier_test.go +++ b/pkg/solana/codec/byte_string_modifier_test.go @@ -1,4 +1,4 @@ -package solanacodec_test +package codec_test import ( "testing" @@ -9,11 +9,11 @@ import ( commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" - "github.com/smartcontractkit/chainlink-solana/pkg/solana/solanacodec" + "github.com/smartcontractkit/chainlink-solana/pkg/solana/codec" ) func TestSolanaAddressModifier(t *testing.T) { - modifier := solanacodec.SolanaAddressModifier{} + modifier := codec.SolanaAddressModifier{} // Valid Solana address (32 bytes, Base58 encoded) validAddressStr := "9nQhQ7iCyY5SgAX2Zm4DtxNh9Ubc4vbiLkiYbX43SDXY" diff --git a/pkg/solana/solanacodec/codec_entry.go b/pkg/solana/codec/codec_entry.go similarity index 75% rename from pkg/solana/solanacodec/codec_entry.go rename to pkg/solana/codec/codec_entry.go index 0ebe20c30..6a47db06b 100644 --- a/pkg/solana/solanacodec/codec_entry.go +++ b/pkg/solana/codec/codec_entry.go @@ -1,4 +1,4 @@ -package solanacodec +package codec import ( "fmt" @@ -15,6 +15,8 @@ type Entry interface { GetCodecType() commonencodings.TypeCodec GetType() reflect.Type Modifier() codec.Modifier + Size(numItems int) (int, error) + FixedSize() (int, error) } func NewAccountEntry(offchainName string, idlAccount IdlTypeDef, idlTypes IdlTypeDefSlice, includeDiscriminator bool, mod codec.Modifier, builder commonencodings.Builder) (Entry, error) { @@ -30,15 +32,14 @@ func NewAccountEntry(offchainName string, idlAccount IdlTypeDef, idlTypes IdlTyp return nil, err } - entry := &CodecEntry{ + return &entry{ offchainName: offchainName, onchainName: idlAccount.Name, includeDiscriminator: includeDiscriminator, typeCodec: accCodec, reflectType: accCodec.GetType(), mod: ensureModifier(mod), - } - return entry, nil + }, nil } func NewInstructionArgsEntry(offChainName string, instructions IdlInstruction, idlTypes IdlTypeDefSlice, mod codec.Modifier, builder commonencodings.Builder) (Entry, error) { @@ -54,7 +55,7 @@ func NewInstructionArgsEntry(offChainName string, instructions IdlInstruction, i return nil, err } - return &CodecEntry{ + return &entry{ offchainName: offChainName, onchainName: instructions.Name, typeCodec: instructionCodecArgs, @@ -63,7 +64,7 @@ func NewInstructionArgsEntry(offChainName string, instructions IdlInstruction, i }, nil } -type CodecEntry struct { +type entry struct { // TODO this might not be needed in the end, it was handy to make tests simpler offchainName string onchainName string @@ -73,17 +74,17 @@ type CodecEntry struct { includeDiscriminator bool } -func (entry *CodecEntry) GetType() reflect.Type { - return entry.reflectType +func (e *entry) GetType() reflect.Type { + return e.reflectType } -func (entry *CodecEntry) GetCodecType() commonencodings.TypeCodec { - return entry.typeCodec +func (e *entry) GetCodecType() commonencodings.TypeCodec { + return e.typeCodec } -func (entry *CodecEntry) Encode(value any, into []byte) ([]byte, error) { +func (e *entry) Encode(value any, into []byte) ([]byte, error) { // Special handling for encoding a nil pointer to an empty struct. - t := entry.reflectType + t := e.reflectType if value == nil { if t.Kind() == reflect.Pointer { elem := t.Elem() @@ -91,17 +92,17 @@ func (entry *CodecEntry) Encode(value any, into []byte) ([]byte, error) { return []byte{}, nil } } - return nil, fmt.Errorf("%w: cannot encode nil value for offchainName: %q, onchainName: %q", commontypes.ErrInvalidType, entry.offchainName, entry.onchainName) + return nil, fmt.Errorf("%w: cannot encode nil value for offchainName: %q, onchainName: %q", commontypes.ErrInvalidType, e.offchainName, e.onchainName) } - encodedVal, err := entry.typeCodec.Encode(value, into) + encodedVal, err := e.typeCodec.Encode(value, into) if err != nil { return nil, err } - if entry.includeDiscriminator { + if e.includeDiscriminator { var byt []byte - disc := NewDiscriminator(entry.onchainName) + disc := NewDiscriminator(e.onchainName) encodedDisc, err := disc.Encode(&disc.hashPrefix, byt) if err != nil { return nil, err @@ -112,18 +113,18 @@ func (entry *CodecEntry) Encode(value any, into []byte) ([]byte, error) { return encodedVal, nil } -func (entry *CodecEntry) Decode(encoded []byte) (any, []byte, error) { - if entry.includeDiscriminator { +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, entry.offchainName, entry.onchainName) + return nil, nil, fmt.Errorf("%w: encoded data too short to contain discriminator for offchainName: %q, onchainName: %q", commontypes.ErrInvalidType, e.offchainName, e.onchainName) } encoded = encoded[discriminatorLength:] } - return entry.typeCodec.Decode(encoded) + return e.typeCodec.Decode(encoded) } -func (entry *CodecEntry) Modifier() codec.Modifier { - return entry.mod +func (e *entry) Modifier() codec.Modifier { + return e.mod } func ensureModifier(mod codec.Modifier) codec.Modifier { @@ -132,3 +133,11 @@ func ensureModifier(mod codec.Modifier) codec.Modifier { } return mod } + +func (e *entry) Size(numItems int) (int, error) { + return e.typeCodec.Size(numItems) +} + +func (e *entry) FixedSize() (int, error) { + return e.typeCodec.FixedSize() +} diff --git a/pkg/solana/solanacodec/codec_test.go b/pkg/solana/codec/codec_test.go similarity index 93% rename from pkg/solana/solanacodec/codec_test.go rename to pkg/solana/codec/codec_test.go index 4f9ad24fe..ce1e73c3c 100644 --- a/pkg/solana/solanacodec/codec_test.go +++ b/pkg/solana/codec/codec_test.go @@ -1,4 +1,4 @@ -package solanacodec_test +package codec_test import ( "bytes" @@ -15,8 +15,8 @@ import ( looptestutils "github.com/smartcontractkit/chainlink-common/pkg/loop/testutils" clcommontypes "github.com/smartcontractkit/chainlink-common/pkg/types" . "github.com/smartcontractkit/chainlink-common/pkg/types/interfacetests" //nolint common practice to import test mods with . - "github.com/smartcontractkit/chainlink-solana/pkg/solana/solanacodec" - "github.com/smartcontractkit/chainlink-solana/pkg/solana/solanacodec/testutils" + "github.com/smartcontractkit/chainlink-solana/pkg/solana/codec" + "github.com/smartcontractkit/chainlink-solana/pkg/solana/codec/testutils" ) const anyExtraValue = 3 @@ -83,7 +83,7 @@ func encodeFieldsOnSliceOrArray(t *testing.T, request *EncodeRequest) []byte { } func (it *codecInterfaceTester) GetCodec(t *testing.T) clcommontypes.Codec { - codecConfig := solanacodec.Config{Configs: map[string]solanacodec.ChainConfig{}} + codecConfig := codec.Config{Configs: map[string]codec.ChainConfig{}} TestItem := CreateTestStruct[*testing.T](0, it) for offChainName, v := range testutils.CodecDefs { codecEntryCfg := codecConfig.Configs[offChainName] @@ -101,7 +101,7 @@ func (it *codecInterfaceTester) GetCodec(t *testing.T) clcommontypes.Codec { if slices.Contains([]string{testutils.TestItemType, testutils.TestItemSliceType, testutils.TestItemArray1Type, testutils.TestItemArray2Type, testutils.TestItemWithConfigExtraType}, offChainName) { addressByteModifier := &commoncodec.AddressBytesToStringModifierConfig{ Fields: []string{"AccountStruct.AccountStr"}, - Modifier: solanacodec.SolanaAddressModifier{}, + Modifier: codec.SolanaAddressModifier{}, } codecEntryCfg.ModifierConfigs = append(codecEntryCfg.ModifierConfigs, addressByteModifier) } @@ -119,7 +119,7 @@ func (it *codecInterfaceTester) GetCodec(t *testing.T) clcommontypes.Codec { codecConfig.Configs[offChainName] = codecEntryCfg } - c, err := solanacodec.NewCodec(codecConfig) + c, err := codec.NewCodec(codecConfig) require.NoError(t, err) return c diff --git a/pkg/solana/codec/decoder.go b/pkg/solana/codec/decoder.go new file mode 100644 index 000000000..242dbc44f --- /dev/null +++ b/pkg/solana/codec/decoder.go @@ -0,0 +1,35 @@ +package codec + +import ( + "context" + "fmt" + + "github.com/smartcontractkit/chainlink-common/pkg/codec/encodings" + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" +) + +type Decoder struct { + definitions map[string]Entry + codecFromTypeCodec encodings.CodecFromTypeCodec +} + +var _ commontypes.Decoder = &Decoder{} + +func (d *Decoder) Decode(ctx context.Context, raw []byte, into any, itemType string) (err error) { + if d.codecFromTypeCodec == nil { + d.codecFromTypeCodec = make(encodings.CodecFromTypeCodec) + for k, v := range d.definitions { + d.codecFromTypeCodec[k] = v + } + } + + return d.codecFromTypeCodec.Decode(ctx, raw, into, itemType) +} + +func (d *Decoder) GetMaxDecodingSize(_ context.Context, n int, itemType string) (int, error) { + codecEntry, ok := d.definitions[itemType] + if !ok { + return 0, fmt.Errorf("%w: nil entry", commontypes.ErrInvalidType) + } + return codecEntry.GetCodecType().Size(n) +} diff --git a/pkg/solana/solanacodec/decoder_test.go b/pkg/solana/codec/decoder_test.go similarity index 73% rename from pkg/solana/solanacodec/decoder_test.go rename to pkg/solana/codec/decoder_test.go index 33c1dc65b..ceea9644f 100644 --- a/pkg/solana/solanacodec/decoder_test.go +++ b/pkg/solana/codec/decoder_test.go @@ -1,4 +1,4 @@ -package solanacodec_test +package codec import ( "fmt" @@ -9,12 +9,10 @@ import ( commonencodings "github.com/smartcontractkit/chainlink-common/pkg/codec/encodings" commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" - - "github.com/smartcontractkit/chainlink-solana/pkg/solana/solanacodec" ) type testErrDecodeEntry struct { - solanacodec.CodecEntry + entry } func (t *testErrDecodeEntry) Decode(_ []byte) (interface{}, []byte, error) { @@ -22,7 +20,7 @@ func (t *testErrDecodeEntry) Decode(_ []byte) (interface{}, []byte, error) { } type testErrDecodeRemainingBytes struct { - solanacodec.CodecEntry + entry } func (t *testErrDecodeRemainingBytes) Decode(_ []byte) (interface{}, []byte, error) { @@ -33,8 +31,8 @@ func TestDecoder_Decode_Errors(t *testing.T) { var into interface{} someType := "some-type" t.Run("error when item type not found", func(t *testing.T) { - d := &solanacodec.Decoder{Definitions: map[string]solanacodec.Entry{}} - d.Definitions[someType] = &solanacodec.CodecEntry{} + d := &Decoder{definitions: map[string]Entry{}} + d.definitions[someType] = &entry{} nonExistentType := "non-existent" err := d.Decode(tests.Context(t), []byte{}, &into, nonExistentType) @@ -42,20 +40,20 @@ func TestDecoder_Decode_Errors(t *testing.T) { }) t.Run("error when underlying entry decode fails", func(t *testing.T) { - d := &solanacodec.Decoder{Definitions: map[string]solanacodec.Entry{}} - d.Definitions[someType] = &testErrDecodeEntry{} + d := &Decoder{definitions: map[string]Entry{}} + d.definitions[someType] = &testErrDecodeEntry{} require.Error(t, d.Decode(tests.Context(t), []byte{}, &into, someType)) }) t.Run("error when remaining bytes exist after decode", func(t *testing.T) { - d := &solanacodec.Decoder{Definitions: map[string]solanacodec.Entry{}} - d.Definitions[someType] = &testErrDecodeRemainingBytes{} + d := &Decoder{definitions: map[string]Entry{}} + d.definitions[someType] = &testErrDecodeRemainingBytes{} require.Error(t, d.Decode(tests.Context(t), []byte{}, &into, someType)) }) } type testErrGetMaxDecodingSize struct { - solanacodec.CodecEntry + entry } type testErrGetMaxDecodingSizeCodecType struct { @@ -74,8 +72,8 @@ func TestDecoder_GetMaxDecodingSize_Errors(t *testing.T) { someType := "some-type" t.Run("error when entry for item type is missing", func(t *testing.T) { - d := &solanacodec.Decoder{Definitions: map[string]solanacodec.Entry{}} - d.Definitions[someType] = &solanacodec.CodecEntry{} + d := &Decoder{definitions: map[string]Entry{}} + d.definitions[someType] = &entry{} nonExistentType := "non-existent" _, err := d.GetMaxDecodingSize(tests.Context(t), 0, nonExistentType) @@ -83,8 +81,8 @@ func TestDecoder_GetMaxDecodingSize_Errors(t *testing.T) { }) t.Run("error when underlying entry decode fails", func(t *testing.T) { - d := &solanacodec.Decoder{Definitions: map[string]solanacodec.Entry{}} - d.Definitions[someType] = &testErrGetMaxDecodingSize{} + d := &Decoder{definitions: map[string]Entry{}} + d.definitions[someType] = &testErrGetMaxDecodingSize{} _, err := d.GetMaxDecodingSize(tests.Context(t), 0, someType) require.Error(t, err) diff --git a/pkg/solana/solanacodec/discriminator.go b/pkg/solana/codec/discriminator.go similarity index 98% rename from pkg/solana/solanacodec/discriminator.go rename to pkg/solana/codec/discriminator.go index 73bc88d76..9bc363ae7 100644 --- a/pkg/solana/solanacodec/discriminator.go +++ b/pkg/solana/codec/discriminator.go @@ -1,4 +1,4 @@ -package solanacodec +package codec import ( "bytes" diff --git a/pkg/solana/solanacodec/discriminator_test.go b/pkg/solana/codec/discriminator_test.go similarity index 82% rename from pkg/solana/solanacodec/discriminator_test.go rename to pkg/solana/codec/discriminator_test.go index a13205dbd..8a3ba95b9 100644 --- a/pkg/solana/solanacodec/discriminator_test.go +++ b/pkg/solana/codec/discriminator_test.go @@ -1,4 +1,4 @@ -package solanacodec_test +package codec_test import ( "crypto/sha256" @@ -10,14 +10,14 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/types" - "github.com/smartcontractkit/chainlink-solana/pkg/solana/solanacodec" + "github.com/smartcontractkit/chainlink-solana/pkg/solana/codec" ) func TestDiscriminator(t *testing.T) { t.Run("encode and decode return the discriminator", func(t *testing.T) { tmp := sha256.Sum256([]byte("account:Foo")) expected := tmp[:8] - c := solanacodec.NewDiscriminator("Foo") + c := codec.NewDiscriminator("Foo") encoded, err := c.Encode(&expected, nil) require.NoError(t, err) require.Equal(t, expected, encoded) @@ -28,7 +28,7 @@ func TestDiscriminator(t *testing.T) { }) t.Run("encode returns an error if the discriminator is invalid", func(t *testing.T) { - c := solanacodec.NewDiscriminator("Foo") + c := codec.NewDiscriminator("Foo") _, err := c.Encode(&[]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, nil) require.True(t, errors.Is(err, types.ErrInvalidType)) }) @@ -36,7 +36,7 @@ func TestDiscriminator(t *testing.T) { t.Run("encode injects the discriminator if it's not provided", func(t *testing.T) { tmp := sha256.Sum256([]byte("account:Foo")) expected := tmp[:8] - c := solanacodec.NewDiscriminator("Foo") + c := codec.NewDiscriminator("Foo") encoded, err := c.Encode(nil, nil) require.NoError(t, err) require.Equal(t, expected, encoded) @@ -46,37 +46,37 @@ func TestDiscriminator(t *testing.T) { }) t.Run("decode returns an error if the encoded value is too short", func(t *testing.T) { - c := solanacodec.NewDiscriminator("Foo") + c := codec.NewDiscriminator("Foo") _, _, err := c.Decode([]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06}) require.True(t, errors.Is(err, types.ErrInvalidEncoding)) }) t.Run("decode returns an error if the discriminator is invalid", func(t *testing.T) { - c := solanacodec.NewDiscriminator("Foo") + c := codec.NewDiscriminator("Foo") _, _, err := c.Decode([]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}) require.True(t, errors.Is(err, types.ErrInvalidEncoding)) }) t.Run("encode returns an error if the value is not a byte slice", func(t *testing.T) { - c := solanacodec.NewDiscriminator("Foo") + c := codec.NewDiscriminator("Foo") _, err := c.Encode(42, nil) require.True(t, errors.Is(err, types.ErrInvalidType)) }) t.Run("GetType returns the type of the discriminator", func(t *testing.T) { - c := solanacodec.NewDiscriminator("Foo") + c := codec.NewDiscriminator("Foo") require.Equal(t, reflect.TypeOf(&[]byte{}), c.GetType()) }) t.Run("Size returns the length of the discriminator", func(t *testing.T) { - c := solanacodec.NewDiscriminator("Foo") + c := codec.NewDiscriminator("Foo") size, err := c.Size(0) require.NoError(t, err) require.Equal(t, 8, size) }) t.Run("FixedSize returns the length of the discriminator", func(t *testing.T) { - c := solanacodec.NewDiscriminator("Foo") + c := codec.NewDiscriminator("Foo") size, err := c.FixedSize() require.NoError(t, err) require.Equal(t, 8, size) diff --git a/pkg/solana/solanacodec/duration.go b/pkg/solana/codec/duration.go similarity index 98% rename from pkg/solana/solanacodec/duration.go rename to pkg/solana/codec/duration.go index 2df336f0d..e6e540778 100644 --- a/pkg/solana/solanacodec/duration.go +++ b/pkg/solana/codec/duration.go @@ -1,4 +1,4 @@ -package solanacodec +package codec import ( "fmt" diff --git a/pkg/solana/codec/encoder.go b/pkg/solana/codec/encoder.go new file mode 100644 index 000000000..409fb0013 --- /dev/null +++ b/pkg/solana/codec/encoder.go @@ -0,0 +1,35 @@ +package codec + +import ( + "context" + "fmt" + + "github.com/smartcontractkit/chainlink-common/pkg/codec/encodings" + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" +) + +type Encoder struct { + definitions map[string]Entry + codecFromTypeCodec encodings.CodecFromTypeCodec +} + +var _ commontypes.Encoder = &Encoder{} + +func (e *Encoder) Encode(ctx context.Context, item any, itemType string) (res []byte, err error) { + if e.codecFromTypeCodec == nil { + e.codecFromTypeCodec = make(encodings.CodecFromTypeCodec) + for k, v := range e.definitions { + e.codecFromTypeCodec[k] = v + } + } + + return e.codecFromTypeCodec.Encode(ctx, item, itemType) +} + +func (e *Encoder) GetMaxEncodingSize(_ context.Context, n int, itemType string) (int, error) { + entry, ok := e.definitions[itemType] + if !ok { + return 0, fmt.Errorf("%w: nil entry", commontypes.ErrInvalidType) + } + return entry.GetCodecType().Size(n) +} diff --git a/pkg/solana/solanacodec/encoder_test.go b/pkg/solana/codec/encoder_test.go similarity index 82% rename from pkg/solana/solanacodec/encoder_test.go rename to pkg/solana/codec/encoder_test.go index d9313aaa6..4be7399ce 100644 --- a/pkg/solana/solanacodec/encoder_test.go +++ b/pkg/solana/codec/encoder_test.go @@ -1,4 +1,4 @@ -package solanacodec_test +package codec import ( "fmt" @@ -10,12 +10,10 @@ import ( commonencodings "github.com/smartcontractkit/chainlink-common/pkg/codec/encodings" commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" - - "github.com/smartcontractkit/chainlink-solana/pkg/solana/solanacodec" ) type testErrEncodeEntry struct { - solanacodec.CodecEntry + entry codecType commonencodings.TypeCodec } @@ -28,7 +26,7 @@ func (t *testErrEncodeEntry) GetCodecType() commonencodings.TypeCodec { } type testErrEncodeTypeEntry struct { - solanacodec.CodecEntry + entry tCodec commonencodings.TypeCodec } @@ -40,7 +38,7 @@ func TestEncoder_Encode_Errors(t *testing.T) { someType := "some-type" t.Run("error when item type not found", func(t *testing.T) { - e := &solanacodec.Encoder{Definitions: map[string]solanacodec.Entry{}} + e := &Encoder{definitions: map[string]Entry{}} _, err := e.Encode(tests.Context(t), nil, "non-existent-type") require.Error(t, err) require.ErrorIs(t, err, commontypes.ErrInvalidType) @@ -48,8 +46,8 @@ func TestEncoder_Encode_Errors(t *testing.T) { }) t.Run("error when convert fails because of unexpected type", func(t *testing.T) { - e := &solanacodec.Encoder{ - Definitions: map[string]solanacodec.Entry{ + e := &Encoder{ + definitions: map[string]Entry{ someType: &testErrEncodeEntry{}, }, } @@ -58,8 +56,8 @@ func TestEncoder_Encode_Errors(t *testing.T) { }) t.Run("error when entry encode fails", func(t *testing.T) { - e := &solanacodec.Encoder{ - Definitions: map[string]solanacodec.Entry{ + e := &Encoder{ + definitions: map[string]Entry{ someType: &testErrEncodeEntry{codecType: commonencodings.Empty{}}, }, } @@ -83,7 +81,7 @@ func (t testErrGetSize) Size(_ int) (int, error) { func TestEncoder_GetMaxEncodingSize_Errors(t *testing.T) { t.Run("error when entry for item type is missing", func(t *testing.T) { - e := &solanacodec.Encoder{Definitions: map[string]solanacodec.Entry{}} + e := &Encoder{definitions: map[string]Entry{}} _, err := e.GetMaxEncodingSize(tests.Context(t), 10, "no-entry-type") require.Error(t, err) require.ErrorIs(t, err, commontypes.ErrInvalidType) @@ -92,8 +90,8 @@ func TestEncoder_GetMaxEncodingSize_Errors(t *testing.T) { t.Run("error when size calculation fails", func(t *testing.T) { someType := "some-type" - e := &solanacodec.Encoder{ - Definitions: map[string]solanacodec.Entry{ + e := &Encoder{ + definitions: map[string]Entry{ someType: &testErrEncodeTypeEntry{tCodec: testErrGetSize{}}, }, } diff --git a/pkg/solana/solanacodec/parsed_types.go b/pkg/solana/codec/parsed_types.go similarity index 83% rename from pkg/solana/solanacodec/parsed_types.go rename to pkg/solana/codec/parsed_types.go index c9bfe9626..3144f6dd0 100644 --- a/pkg/solana/solanacodec/parsed_types.go +++ b/pkg/solana/codec/parsed_types.go @@ -1,4 +1,4 @@ -package solanacodec +package codec import ( "fmt" @@ -27,14 +27,14 @@ func (parsed *ParsedTypes) ToCodec() (commontypes.RemoteCodec, error) { return nil, err } underlying := &solanaCodec{ - Encoder: &Encoder{Definitions: parsed.EncoderDefs}, - Decoder: &Decoder{Definitions: parsed.DecoderDefs}, + Encoder: &Encoder{definitions: parsed.EncoderDefs}, + Decoder: &Decoder{definitions: parsed.DecoderDefs}, ParsedTypes: parsed, } return commoncodec.NewModifierCodec(underlying, mod, DecoderHooks...) } -// AddEntries extracts the mods from CodecEntry and adds them to modByTypeName use with codec.NewByItemTypeModifier +// AddEntries extracts the mods from entry and adds them to modByTypeName use with codec.NewByItemTypeModifier // Since each input/output can have its own modifications, we need to keep track of them by type name func AddEntries(defs map[string]Entry, modByTypeName map[string]commoncodec.Modifier) error { for k, def := range defs { diff --git a/pkg/solana/solanacodec/solana.go b/pkg/solana/codec/solana.go similarity index 98% rename from pkg/solana/solanacodec/solana.go rename to pkg/solana/codec/solana.go index 3ac2aee46..c2905e73e 100644 --- a/pkg/solana/solanacodec/solana.go +++ b/pkg/solana/codec/solana.go @@ -1,5 +1,5 @@ /* -Package solanacodec provides functions to create a codec from an Anchor IDL. All Anchor primitives map to the following native +Package codec provides functions to create a codec from an Anchor IDL. All Anchor primitives map to the following native Go values: bool -> bool @@ -17,7 +17,7 @@ supported at this time. Modifiers can be provided to assist in modifying property names, adding properties, etc. */ -package solanacodec +package codec import ( "encoding/json" diff --git a/pkg/solana/solanacodec/solana_test.go b/pkg/solana/codec/solana_test.go similarity index 80% rename from pkg/solana/solanacodec/solana_test.go rename to pkg/solana/codec/solana_test.go index fef855e18..ffefd5047 100644 --- a/pkg/solana/solanacodec/solana_test.go +++ b/pkg/solana/codec/solana_test.go @@ -1,4 +1,4 @@ -package solanacodec_test +package codec_test import ( "encoding/json" @@ -11,8 +11,8 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" - "github.com/smartcontractkit/chainlink-solana/pkg/solana/solanacodec" - "github.com/smartcontractkit/chainlink-solana/pkg/solana/solanacodec/testutils" + "github.com/smartcontractkit/chainlink-solana/pkg/solana/codec" + "github.com/smartcontractkit/chainlink-solana/pkg/solana/codec/testutils" ) func TestNewIDLAccountCodec(t *testing.T) { @@ -60,21 +60,21 @@ func TestNewIDLDefinedTypesCodecCodec(t *testing.T) { func TestNewIDLCodec_CircularDependency(t *testing.T) { t.Parallel() - var idl solanacodec.IDL + var idl codec.IDL if err := json.Unmarshal([]byte(testutils.CircularDepIDL), &idl); err != nil { t.Logf("failed to unmarshal test IDL: %s", err.Error()) t.FailNow() } - _, err := solanacodec.NewIDLAccountCodec(idl, binary.LittleEndian()) + _, err := codec.NewIDLAccountCodec(idl, binary.LittleEndian()) assert.ErrorIs(t, err, types.ErrInvalidConfig) } -func newTestIDLAndCodec(t *testing.T, account bool) (string, solanacodec.IDL, types.RemoteCodec) { +func newTestIDLAndCodec(t *testing.T, account bool) (string, codec.IDL, types.RemoteCodec) { t.Helper() - var idl solanacodec.IDL + var idl codec.IDL if err := json.Unmarshal([]byte(testutils.JSONIDLWithAllTypes), &idl); err != nil { t.Logf("failed to unmarshal test IDL: %s", err.Error()) t.FailNow() @@ -83,9 +83,9 @@ func newTestIDLAndCodec(t *testing.T, account bool) (string, solanacodec.IDL, ty var entry types.RemoteCodec var err error if account { - entry, err = solanacodec.NewIDLAccountCodec(idl, binary.LittleEndian()) + entry, err = codec.NewIDLAccountCodec(idl, binary.LittleEndian()) } else { - entry, err = solanacodec.NewIDLDefinedTypesCodec(idl, binary.LittleEndian()) + entry, err = codec.NewIDLDefinedTypesCodec(idl, binary.LittleEndian()) } if err != nil { diff --git a/pkg/solana/solanacodec/testutils/circularDepIDL.json b/pkg/solana/codec/testutils/circularDepIDL.json similarity index 100% rename from pkg/solana/solanacodec/testutils/circularDepIDL.json rename to pkg/solana/codec/testutils/circularDepIDL.json diff --git a/pkg/solana/solanacodec/testutils/itemArray1TypeIDL.json b/pkg/solana/codec/testutils/itemArray1TypeIDL.json similarity index 100% rename from pkg/solana/solanacodec/testutils/itemArray1TypeIDL.json rename to pkg/solana/codec/testutils/itemArray1TypeIDL.json diff --git a/pkg/solana/solanacodec/testutils/itemArray2TypeIDL.json b/pkg/solana/codec/testutils/itemArray2TypeIDL.json similarity index 100% rename from pkg/solana/solanacodec/testutils/itemArray2TypeIDL.json rename to pkg/solana/codec/testutils/itemArray2TypeIDL.json diff --git a/pkg/solana/solanacodec/testutils/itemIDL.json b/pkg/solana/codec/testutils/itemIDL.json similarity index 100% rename from pkg/solana/solanacodec/testutils/itemIDL.json rename to pkg/solana/codec/testutils/itemIDL.json diff --git a/pkg/solana/solanacodec/testutils/itemSliceTypeIDL.json b/pkg/solana/codec/testutils/itemSliceTypeIDL.json similarity index 100% rename from pkg/solana/solanacodec/testutils/itemSliceTypeIDL.json rename to pkg/solana/codec/testutils/itemSliceTypeIDL.json diff --git a/pkg/solana/solanacodec/testutils/nilTypeIDL.json b/pkg/solana/codec/testutils/nilTypeIDL.json similarity index 100% rename from pkg/solana/solanacodec/testutils/nilTypeIDL.json rename to pkg/solana/codec/testutils/nilTypeIDL.json diff --git a/pkg/solana/solanacodec/testutils/sizeItemTypeIDL.json b/pkg/solana/codec/testutils/sizeItemTypeIDL.json similarity index 100% rename from pkg/solana/solanacodec/testutils/sizeItemTypeIDL.json rename to pkg/solana/codec/testutils/sizeItemTypeIDL.json diff --git a/pkg/solana/solanacodec/testutils/testIDL.json b/pkg/solana/codec/testutils/testIDL.json similarity index 100% rename from pkg/solana/solanacodec/testutils/testIDL.json rename to pkg/solana/codec/testutils/testIDL.json diff --git a/pkg/solana/solanacodec/testutils/types.go b/pkg/solana/codec/testutils/types.go similarity index 97% rename from pkg/solana/solanacodec/testutils/types.go rename to pkg/solana/codec/testutils/types.go index 2d905549a..ddcc18eb0 100644 --- a/pkg/solana/solanacodec/testutils/types.go +++ b/pkg/solana/codec/testutils/types.go @@ -11,7 +11,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/types/interfacetests" - "github.com/smartcontractkit/chainlink-solana/pkg/solana/solanacodec" + "github.com/smartcontractkit/chainlink-solana/pkg/solana/codec" ) var ( @@ -117,7 +117,7 @@ var nilTypeJSONIDL string type CodecDef struct { IDL string IDLTypeName string - ItemType solanacodec.ChainConfigType + ItemType codec.ChainConfigType } // CodecDefs key is codec offchain type name @@ -125,32 +125,32 @@ var CodecDefs = map[string]CodecDef{ TestItemType: { IDL: itemTypeJSONIDL, IDLTypeName: TestItemType, - ItemType: solanacodec.ChainConfigTypeAccountDef, + ItemType: codec.ChainConfigTypeAccountDef, }, TestItemSliceType: { IDL: itemSliceTypeJSONIDL, IDLTypeName: TestItemSliceType, - ItemType: solanacodec.ChainConfigTypeInstructionDef, + ItemType: codec.ChainConfigTypeInstructionDef, }, TestItemArray1Type: { IDL: itemArray1TypeJSONIDL, IDLTypeName: TestItemArray1Type, - ItemType: solanacodec.ChainConfigTypeInstructionDef, + ItemType: codec.ChainConfigTypeInstructionDef, }, TestItemArray2Type: { IDL: itemArray2TypeJSONIDL, IDLTypeName: TestItemArray2Type, - ItemType: solanacodec.ChainConfigTypeInstructionDef, + ItemType: codec.ChainConfigTypeInstructionDef, }, TestItemWithConfigExtraType: { IDL: itemTypeJSONIDL, IDLTypeName: TestItemType, - ItemType: solanacodec.ChainConfigTypeAccountDef, + ItemType: codec.ChainConfigTypeAccountDef, }, NilType: { IDL: nilTypeJSONIDL, IDLTypeName: NilType, - ItemType: solanacodec.ChainConfigTypeAccountDef, + ItemType: codec.ChainConfigTypeAccountDef, }, } diff --git a/pkg/solana/solanacodec/types.go b/pkg/solana/codec/types.go similarity index 97% rename from pkg/solana/solanacodec/types.go rename to pkg/solana/codec/types.go index 8dd3b40eb..e047b36ae 100644 --- a/pkg/solana/solanacodec/types.go +++ b/pkg/solana/codec/types.go @@ -1,4 +1,4 @@ -package solanacodec +package codec import commoncodec "github.com/smartcontractkit/chainlink-common/pkg/codec" diff --git a/pkg/solana/config/chain_reader_test.go b/pkg/solana/config/chain_reader_test.go index b0dca9597..7d290b50c 100644 --- a/pkg/solana/config/chain_reader_test.go +++ b/pkg/solana/config/chain_reader_test.go @@ -14,8 +14,8 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/codec/encodings/binary" "github.com/smartcontractkit/chainlink-common/pkg/types" + "github.com/smartcontractkit/chainlink-solana/pkg/solana/codec/testutils" "github.com/smartcontractkit/chainlink-solana/pkg/solana/config" - "github.com/smartcontractkit/chainlink-solana/pkg/solana/solanacodec/testutils" ) //go:embed testChainReader_valid.json diff --git a/pkg/solana/solanacodec/decoder.go b/pkg/solana/solanacodec/decoder.go deleted file mode 100644 index 71b38668c..000000000 --- a/pkg/solana/solanacodec/decoder.go +++ /dev/null @@ -1,42 +0,0 @@ -package solanacodec - -import ( - "context" - "fmt" - "reflect" - - "github.com/smartcontractkit/chainlink-common/pkg/codec" - commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" -) - -type Decoder struct { - Definitions map[string]Entry -} - -var _ commontypes.Decoder = &Decoder{} - -func (m *Decoder) Decode(_ context.Context, raw []byte, into any, itemType string) (err error) { - item, ok := m.Definitions[itemType] - if !ok { - return fmt.Errorf("%w: cannot find type %s", commontypes.ErrInvalidType, itemType) - } - - val, remaining, err := item.Decode(raw) - if err != nil { - return err - } - - if len(remaining) != 0 { - return fmt.Errorf("%w: remaining bytes after decoding %s", commontypes.ErrInvalidEncoding, itemType) - } - - return codec.Convert(reflect.ValueOf(val), reflect.ValueOf(into), nil) -} - -func (m *Decoder) GetMaxDecodingSize(_ context.Context, n int, itemType string) (int, error) { - entry, ok := m.Definitions[itemType] - if !ok { - return 0, fmt.Errorf("%w: nil entry", commontypes.ErrInvalidType) - } - return entry.GetCodecType().Size(n) -} diff --git a/pkg/solana/solanacodec/encoder.go b/pkg/solana/solanacodec/encoder.go deleted file mode 100644 index 32adb15cf..000000000 --- a/pkg/solana/solanacodec/encoder.go +++ /dev/null @@ -1,51 +0,0 @@ -package solanacodec - -import ( - "context" - "fmt" - "reflect" - - "github.com/smartcontractkit/chainlink-common/pkg/codec" - commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" -) - -type Encoder struct { - Definitions map[string]Entry -} - -var _ commontypes.Encoder = &Encoder{} - -func (e *Encoder) Encode(_ context.Context, item any, itemType string) (res []byte, err error) { - info, ok := e.Definitions[itemType] - if !ok { - return nil, fmt.Errorf("%w: cannot find definition for %s", commontypes.ErrInvalidType, itemType) - } - - if item != nil { - rItem := reflect.ValueOf(item) - myType := info.GetCodecType().GetType() - if rItem.Kind() == reflect.Pointer && myType.Kind() != reflect.Pointer { - rItem = reflect.Indirect(rItem) - } - - if !rItem.IsZero() && rItem.Type() != myType { - tmp := reflect.New(myType) - if err := codec.Convert(rItem, tmp, nil); err != nil { - return nil, err - } - item = tmp.Elem().Interface() - } else { - item = rItem.Interface() - } - } - - return info.Encode(item, nil) -} - -func (e *Encoder) GetMaxEncodingSize(_ context.Context, n int, itemType string) (int, error) { - entry, ok := e.Definitions[itemType] - if !ok { - return 0, fmt.Errorf("%w: nil entry", commontypes.ErrInvalidType) - } - return entry.GetCodecType().Size(n) -}