From 6ece045da6b31ba84ea7b618d98d47973d0bd1a4 Mon Sep 17 00:00:00 2001 From: ilija Date: Mon, 23 Dec 2024 19:46:31 +0100 Subject: [PATCH 01/11] Refactor CR init and cfg --- pkg/solana/chainreader/chain_reader.go | 119 ++++++++++++---- pkg/solana/chainreader/chain_reader_test.go | 132 +++++++++--------- pkg/solana/codec/codec_entry.go | 33 +++-- pkg/solana/codec/solana.go | 42 ++++-- pkg/solana/config/chain_reader.go | 99 ++++--------- pkg/solana/config/chain_reader_test.go | 71 +++------- .../config/testChainReader_invalid.json | 39 ++++-- pkg/solana/config/testChainReader_valid.json | 53 ++++--- 8 files changed, 315 insertions(+), 273 deletions(-) diff --git a/pkg/solana/chainreader/chain_reader.go b/pkg/solana/chainreader/chain_reader.go index d017eb25d..b7fda7346 100644 --- a/pkg/solana/chainreader/chain_reader.go +++ b/pkg/solana/chainreader/chain_reader.go @@ -10,7 +10,7 @@ import ( "github.com/gagliardetto/solana-go" "github.com/gagliardetto/solana-go/rpc" - codeccommon "github.com/smartcontractkit/chainlink-common/pkg/codec" + commoncodec "github.com/smartcontractkit/chainlink-common/pkg/codec" "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink-common/pkg/types" @@ -33,6 +33,8 @@ type SolanaChainReaderService struct { // internal values bindings namespaceBindings lookup *lookup + parsed *codec.ParsedTypes + codec types.RemoteCodec // service state management wg sync.WaitGroup @@ -45,19 +47,25 @@ var ( ) // NewChainReaderService is a constructor for a new ChainReaderService for Solana. Returns a nil service on error. -func NewChainReaderService(lggr logger.Logger, dataReader MultipleAccountGetter, cfg config.ChainReader) (*SolanaChainReaderService, error) { +func NewChainReaderService(lggr logger.Logger, dataReader MultipleAccountGetter, cfg config.ContractReader) (*SolanaChainReaderService, error) { svc := &SolanaChainReaderService{ lggr: logger.Named(lggr, ServiceName), client: dataReader, bindings: namespaceBindings{}, lookup: newLookup(), + parsed: &codec.ParsedTypes{EncoderDefs: map[string]codec.Entry{}, DecoderDefs: map[string]codec.Entry{}}, } if err := svc.init(cfg.Namespaces); err != nil { return nil, err } - return svc, nil + if svcCodec, err := svc.parsed.ToCodec(); err != nil { + return nil, err + } else { + svc.codec = svcCodec + return svc, nil + } } // Name implements the services.ServiceCtx interface and returns the logger service name. @@ -220,51 +228,106 @@ func (s *SolanaChainReaderService) CreateContractType(readIdentifier string, for return s.bindings.CreateType(values.contract, values.readName, forEncoding) } -func (s *SolanaChainReaderService) init(namespaces map[string]config.ChainReaderMethods) error { - for namespace, methods := range namespaces { - for methodName, method := range methods.Methods { +func (s *SolanaChainReaderService) addCodecDef(isInput bool, namespace, itemType string, readType codec.ChainConfigType, idl codec.IDL, idlDefinition interface{}, modCfg commoncodec.ModifiersConfig) error { + mod, err := modCfg.ToModifier(codec.DecoderHooks...) + if err != nil { + return err + } + + cEntry, err := codec.CreateCodecEntry(idlDefinition, itemType, idl, mod) + if err != nil { + return err + } + + s.parsed.EncoderDefs[codec.WrapItemType(isInput, namespace, itemType, readType)] = cEntry + return nil +} + +func (s *SolanaChainReaderService) init(namespaces map[string]config.ChainContractReader) error { + for namespace, namespaceReads := range namespaces { + for readName, read := range namespaceReads.Reads { var idl codec.IDL - if err := json.Unmarshal([]byte(method.AnchorIDL), &idl); err != nil { + if err := json.Unmarshal([]byte(namespaceReads.IDL), &idl); err != nil { return err } - idlCodec, err := codec.NewIDLAccountCodec(idl, config.BuilderForEncoding(method.Encoding)) + injectAddressModifier(read.InputModifications, read.OutputModifications) + idlDef, err := codec.FindDefinitionFromIDL(codec.ChainConfigTypeAccountDef, read.ChainSpecificName, idl) if err != nil { return err } - s.lookup.addReadNameForContract(namespace, methodName) - - procedure := method.Procedure + switch read.ReadType { + case config.Account: + accountIDLDef, isOk := idlDef.(codec.IdlTypeDef) + if !isOk { + return fmt.Errorf("unexpected type %T from IDL definition for account read: %q, with onchain name: %q, of type: %q", accountIDLDef, readName, read.ChainSpecificName, read.ReadType) + } + if err = s.addAccountRead(namespace, readName, idl, accountIDLTypes{ + Account: accountIDLDef, + Types: idl.Types, + }, read); err != nil { + return err + } + case config.Log: + eventIDlDef, isOk := idlDef.(codec.IdlEvent) + if !isOk { + return fmt.Errorf("unexpected type %T from IDL definition for log read: %q, with onchain name: %q, of type: %q", eventIDlDef, readName, read.ChainSpecificName, read.ReadType) + } + // TODO s.addLogRead() + default: + return fmt.Errorf("unexpected read type %q for: %q in namespace: %q", read.ReadType, readName, namespace) + } + } + } - injectAddressModifier(procedure.OutputModifications) + return nil +} - mod, err := procedure.OutputModifications.ToModifier(codec.DecoderHooks...) - if err != nil { - return err - } +type accountIDLTypes struct { + Account codec.IdlTypeDef + Types codec.IdlTypeDefSlice +} - codecWithModifiers, err := codec.NewNamedModifierCodec(idlCodec, procedure.IDLAccount, mod) - if err != nil { - return err - } +func (s *SolanaChainReaderService) addAccountRead(namespace string, itemType string, idl codec.IDL, idlType accountIDLTypes, readDefinition config.ReadDefinition) error { + // TODO if readDefinition.HasPDASeedInput{ + // if err := s.addCodecDef(false, namespace, itemType, codec.ChainConfigTypeAccountDef, idl,idlType, readDefinition.InputModifications,; err != nil { + // return err + // } + // } - s.bindings.AddReadBinding(namespace, methodName, newAccountReadBinding( - procedure.IDLAccount, - codecWithModifiers, - createRPCOpts(procedure.RPCOpts), - )) - } + if err := s.addCodecDef(false, namespace, itemType, codec.ChainConfigTypeAccountDef, idl, idlType, readDefinition.OutputModifications); err != nil { + return err } + s.lookup.addReadNameForContract(namespace, itemType) + s.bindings.AddReadBinding(namespace, itemType, newAccountReadBinding( + readDefinition.ChainSpecificName, + // TODO codec is not created at this point, set codec for all bindings after init + s.codec, + createRPCOpts(readDefinition.RPCOpts), + )) + + return nil +} + +func (s *SolanaChainReaderService) addLogRead() error { + // TODO: init codec types and modifiers, do address lookup here if needed? ... return nil } // injectAddressModifier injects AddressModifier into OutputModifications. // This is necessary because AddressModifier cannot be serialized and must be applied at runtime. -func injectAddressModifier(outputModifications codeccommon.ModifiersConfig) { +func injectAddressModifier(inputModifications, outputModifications commoncodec.ModifiersConfig) { + for i, modConfig := range inputModifications { + if addrModifierConfig, ok := modConfig.(*commoncodec.AddressBytesToStringModifierConfig); ok { + addrModifierConfig.Modifier = codec.SolanaAddressModifier{} + outputModifications[i] = addrModifierConfig + } + } + for i, modConfig := range outputModifications { - if addrModifierConfig, ok := modConfig.(*codeccommon.AddressBytesToStringModifierConfig); ok { + if addrModifierConfig, ok := modConfig.(*commoncodec.AddressBytesToStringModifierConfig); ok { 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 9f27ae0b7..63ce56061 100644 --- a/pkg/solana/chainreader/chain_reader_test.go +++ b/pkg/solana/chainreader/chain_reader_test.go @@ -64,7 +64,7 @@ func TestSolanaChainReaderService_ServiceCtx(t *testing.T) { t.Parallel() ctx := tests.Context(t) - svc, err := chainreader.NewChainReaderService(logger.Test(t), new(mockedRPCClient), config.ChainReader{}) + svc, err := chainreader.NewChainReaderService(logger.Test(t), new(mockedRPCClient), config.ContractReader{}) require.NoError(t, err) require.NotNil(t, svc) @@ -289,21 +289,19 @@ func newTestIDLAndCodec(t *testing.T) (string, codec.IDL, types.RemoteCodec) { return testutils.JSONIDLWithAllTypes, idl, entry } -func newTestConfAndCodec(t *testing.T) (types.RemoteCodec, config.ChainReader) { +func newTestConfAndCodec(t *testing.T) (types.RemoteCodec, config.ContractReader) { t.Helper() - rawIDL, _, testCodec := newTestIDLAndCodec(t) - conf := config.ChainReader{ - Namespaces: map[string]config.ChainReaderMethods{ + conf := config.ContractReader{ + Namespaces: map[string]config.ChainContractReader{ Namespace: { - Methods: map[string]config.ChainDataReader{ + IDL: rawIDL, + Reads: map[string]config.ReadDefinition{ NamedMethod: { - AnchorIDL: rawIDL, - Procedure: config.ChainReaderProcedure{ - IDLAccount: testutils.TestStructWithNestedStruct, - OutputModifications: codeccommon.ModifiersConfig{ - &codeccommon.RenameModifierConfig{Fields: map[string]string{"Value": "V"}}, - }, + ChainSpecificName: testutils.TestStructWithNestedStruct, + ReadType: config.Account, + OutputModifications: codeccommon.ModifiersConfig{ + &codeccommon.RenameModifierConfig{Fields: map[string]string{"Value": "V"}}, }, }, }, @@ -385,7 +383,7 @@ func (_m *mockedRPCClient) SetForAddress(pk ag_solana.PublicKey, bts []byte, err type chainReaderInterfaceTester struct { TestSelectionSupport - conf config.ChainReader + conf config.ContractReader address []string reader *wrappedTestChainReader } @@ -420,70 +418,55 @@ func (r *chainReaderInterfaceTester) Setup(t *testing.T) { offset := uint64(1) length := uint64(1) - r.conf = config.ChainReader{ - Namespaces: map[string]config.ChainReaderMethods{ + r.conf = config.ContractReader{ + Namespaces: map[string]config.ChainContractReader{ AnyContractName: { - Methods: map[string]config.ChainDataReader{ + IDL: fullTestIDL(t), + Reads: map[string]config.ReadDefinition{ MethodTakingLatestParamsReturningTestStruct: { - AnchorIDL: fullStructIDL(t), - Encoding: config.EncodingTypeBorsh, - Procedure: config.ChainReaderProcedure{ - IDLAccount: "TestStruct", - RPCOpts: &config.RPCOpts{ - Encoding: &encodingBase64, - Commitment: &commitment, - DataSlice: &rpc.DataSlice{ - Offset: &offset, - Length: &length, - }, + ReadType: config.Account, + ChainSpecificName: "TestStruct", + RPCOpts: &config.RPCOpts{ + Encoding: &encodingBase64, + Commitment: &commitment, + DataSlice: &rpc.DataSlice{ + Offset: &offset, + Length: &length, }, }, }, MethodReturningUint64: { - AnchorIDL: fmt.Sprintf(baseIDL, uint64BaseTypeIDL, ""), - Encoding: config.EncodingTypeBorsh, - Procedure: config.ChainReaderProcedure{ - IDLAccount: "SimpleUint64Value", - OutputModifications: codeccommon.ModifiersConfig{ - &codeccommon.PropertyExtractorConfig{FieldName: "I"}, - }, + ReadType: config.Account, + ChainSpecificName: "SimpleUint64Value", + + OutputModifications: codeccommon.ModifiersConfig{ + &codeccommon.PropertyExtractorConfig{FieldName: "I"}, }, }, MethodReturningUint64Slice: { - AnchorIDL: fmt.Sprintf(baseIDL, uint64SliceBaseTypeIDL, ""), - Encoding: config.EncodingTypeBincode, - Procedure: config.ChainReaderProcedure{ - IDLAccount: "Uint64Slice", - OutputModifications: codeccommon.ModifiersConfig{ - &codeccommon.PropertyExtractorConfig{FieldName: "Vals"}, - }, + ChainSpecificName: "Uint64Slice", + OutputModifications: codeccommon.ModifiersConfig{ + &codeccommon.PropertyExtractorConfig{FieldName: "Vals"}, }, }, MethodReturningSeenStruct: { - AnchorIDL: fullStructIDL(t), - Encoding: config.EncodingTypeBorsh, - Procedure: config.ChainReaderProcedure{ - IDLAccount: "TestStruct", - OutputModifications: codeccommon.ModifiersConfig{ - &codeccommon.AddressBytesToStringModifierConfig{ - Fields: []string{"Accountstruct.Accountstr"}, - }, - &codeccommon.HardCodeModifierConfig{OffChainValues: map[string]any{"ExtraField": AnyExtraValue}}, + ChainSpecificName: "TestStruct", + OutputModifications: codeccommon.ModifiersConfig{ + &codeccommon.AddressBytesToStringModifierConfig{ + Fields: []string{"Accountstruct.Accountstr"}, }, + &codeccommon.HardCodeModifierConfig{OffChainValues: map[string]any{"ExtraField": AnyExtraValue}}, }, }, }, }, AnySecondContractName: { - Methods: map[string]config.ChainDataReader{ + IDL: fmt.Sprintf(baseIDL, uint64BaseTypeIDL, ""), + Reads: map[string]config.ReadDefinition{ MethodReturningUint64: { - AnchorIDL: fmt.Sprintf(baseIDL, uint64BaseTypeIDL, ""), - Encoding: config.EncodingTypeBorsh, - Procedure: config.ChainReaderProcedure{ - IDLAccount: "SimpleUint64Value", - OutputModifications: codeccommon.ModifiersConfig{ - &codeccommon.PropertyExtractorConfig{FieldName: "I"}, - }, + ChainSpecificName: "SimpleUint64Value", + OutputModifications: codeccommon.ModifiersConfig{ + &codeccommon.PropertyExtractorConfig{FieldName: "I"}, }, }, }, @@ -574,7 +557,7 @@ func (r *wrappedTestChainReader) GetLatestValue(ctx context.Context, readIdentif case AnyContractName + EventName: r.test.Skip("Events are not yet supported in Solana") case AnyContractName + MethodReturningUint64: - cdc := makeTestCodec(r.test, fmt.Sprintf(baseIDL, uint64BaseTypeIDL, ""), config.EncodingTypeBorsh) + cdc := makeTestCodec(r.test, fmt.Sprintf(baseIDL, uint64BaseTypeIDL, "")) onChainStruct := struct { I uint64 }{ @@ -587,7 +570,7 @@ func (r *wrappedTestChainReader) GetLatestValue(ctx context.Context, readIdentif r.test.FailNow() } case AnyContractName + MethodReturningUint64Slice: - cdc := makeTestCodec(r.test, fmt.Sprintf(baseIDL, uint64SliceBaseTypeIDL, ""), config.EncodingTypeBincode) + cdc := makeTestCodec(r.test, fmt.Sprintf(baseIDL, uint64SliceBaseTypeIDL, "")) onChainStruct := struct { Vals []uint64 }{ @@ -599,7 +582,7 @@ func (r *wrappedTestChainReader) GetLatestValue(ctx context.Context, readIdentif r.test.FailNow() } case AnySecondContractName + MethodReturningUint64, AnyContractName: - cdc := makeTestCodec(r.test, fmt.Sprintf(baseIDL, uint64BaseTypeIDL, ""), config.EncodingTypeBorsh) + cdc := makeTestCodec(r.test, fmt.Sprintf(baseIDL, uint64BaseTypeIDL, "")) onChainStruct := struct { I uint64 }{ @@ -624,7 +607,7 @@ func (r *wrappedTestChainReader) GetLatestValue(ctx context.Context, readIdentif r.testStructQueue = r.testStructQueue[1:len(r.testStructQueue)] // split into two encoded parts to test the preloading function - cdc := makeTestCodec(r.test, fullStructIDL(r.test), config.EncodingTypeBorsh) + cdc := makeTestCodec(r.test, fullStructIDL(r.test)) if strings.Contains(r.test.Name(), "wraps_config_with_modifiers_using_its_own_mapstructure_overrides") { // TODO: This is a temporary solution. We are manually retyping this struct to avoid breaking unrelated tests. @@ -760,7 +743,7 @@ func (r *chainReaderInterfaceTester) MaxWaitTimeForEvents() time.Duration { return maxWaitTime } -func makeTestCodec(t *testing.T, rawIDL string, encoding config.EncodingType) types.RemoteCodec { +func makeTestCodec(t *testing.T, rawIDL string) types.RemoteCodec { t.Helper() var idl codec.IDL @@ -769,7 +752,7 @@ func makeTestCodec(t *testing.T, rawIDL string, encoding config.EncodingType) ty t.FailNow() } - testCodec, err := codec.NewIDLAccountCodec(idl, config.BuilderForEncoding(encoding)) + testCodec, err := codec.NewIDLAccountCodec(idl, binary.LittleEndian()) if err != nil { t.Logf("failed to create new codec from test IDL: %s", err.Error()) t.FailNow() @@ -788,6 +771,27 @@ func fullStructIDL(t *testing.T) string { ) } +func fullTestIDL(t *testing.T) string { + t.Helper() + + // Combine all of the type definitions into one comma-separated string. + allTypes := strings.Join([]string{ + midLevelDynamicStructIDL, + midLevelStaticStructIDL, + innerDynamicStructIDL, + innerStaticStructIDL, + accountStructIDL, + uint64BaseTypeIDL, + uint64SliceBaseTypeIDL, + }, ",") + + return fmt.Sprintf( + baseIDL, + testStructIDL, + allTypes, + ) +} + const ( baseIDL = `{ "version": "0.1.0", diff --git a/pkg/solana/codec/codec_entry.go b/pkg/solana/codec/codec_entry.go index bc42ae968..6342055b4 100644 --- a/pkg/solana/codec/codec_entry.go +++ b/pkg/solana/codec/codec_entry.go @@ -33,30 +33,40 @@ type entry struct { 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) +type AccountIDLTypes struct { + Account IdlTypeDef + Types IdlTypeDefSlice +} + +func NewAccountEntry(offchainName string, idlTypes AccountIDLTypes, includeDiscriminator bool, mod codec.Modifier, builder commonencodings.Builder) (Entry, error) { + _, accCodec, err := createCodecType(idlTypes.Account, createRefs(idlTypes.Types, builder), false) if err != nil { return nil, err } return newEntry( offchainName, - idlAccount.Name, + idlTypes.Account.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) +type InstructionArgsIDLTypes struct { + Instruction IdlInstruction + Types IdlTypeDefSlice +} + +func NewInstructionArgsEntry(offChainName string, idlTypes InstructionArgsIDLTypes, mod codec.Modifier, builder commonencodings.Builder) (Entry, error) { + _, instructionCodecArgs, err := asStruct(idlTypes.Instruction.Args, createRefs(idlTypes.Types, builder), idlTypes.Instruction.Name, false, true) if err != nil { return nil, err } return newEntry( offChainName, - instructions.Name, + idlTypes.Instruction.Name, instructionCodecArgs, // Instruction arguments don't need a discriminator by default false, @@ -64,15 +74,20 @@ func NewInstructionArgsEntry(offChainName string, instructions IdlInstruction, i ), 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) +type EventIDLTypes struct { + Event IdlEvent + Types IdlTypeDefSlice +} + +func NewEventArgsEntry(offChainName string, idlTypes EventIDLTypes, includeDiscriminator bool, mod codec.Modifier, builder commonencodings.Builder) (Entry, error) { + _, eventCodec, err := asStruct(eventFieldsToFields(idlTypes.Event.Fields), createRefs(idlTypes.Types, builder), idlTypes.Event.Name, false, false) if err != nil { return nil, err } return newEntry( offChainName, - event.Name, + idlTypes.Event.Name, eventCodec, includeDiscriminator, mod, diff --git a/pkg/solana/codec/solana.go b/pkg/solana/codec/solana.go index 19fe40d3e..3ecada671 100644 --- a/pkg/solana/codec/solana.go +++ b/pkg/solana/codec/solana.go @@ -73,22 +73,14 @@ func NewCodec(conf Config) (commontypes.RemoteCodec, error) { return nil, err } - definition, err := findDefinitionFromIDL(cfg.Type, cfg.OnChainName, idl) + definition, err := FindDefinitionFromIDL(cfg.Type, cfg.OnChainName, idl) if err != nil { return nil, err } - var cEntry Entry - switch v := definition.(type) { - case IdlTypeDef: - cEntry, err = NewAccountEntry(offChainName, v, idl.Types, true, mod, binary.LittleEndian()) - case IdlInstruction: - cEntry, err = NewInstructionArgsEntry(offChainName, v, idl.Types, mod, binary.LittleEndian()) - case IdlEvent: - cEntry, err = NewEventArgsEntry(offChainName, v, idl.Types, true, mod, binary.LittleEndian()) - } + cEntry, err := CreateCodecEntry(definition, offChainName, idl, mod) if err != nil { - return nil, fmt.Errorf("failed to create %q codec entry: %w", offChainName, err) + return nil, err } parsed.EncoderDefs[offChainName] = cEntry @@ -98,7 +90,25 @@ func NewCodec(conf Config) (commontypes.RemoteCodec, error) { return parsed.ToCodec() } -func findDefinitionFromIDL(cfgType ChainConfigType, onChainName string, idl IDL) (interface{}, error) { +func CreateCodecEntry(idlDefinition interface{}, offChainName string, idl IDL, mod commoncodec.Modifier) (entry Entry, err error) { + switch v := idlDefinition.(type) { + case IdlTypeDef: + entry, err = NewAccountEntry(offChainName, AccountIDLTypes{Account: v, Types: idl.Types}, true, mod, binary.LittleEndian()) + case IdlInstruction: + entry, err = NewInstructionArgsEntry(offChainName, InstructionArgsIDLTypes{Instruction: v, Types: idl.Types}, mod, binary.LittleEndian()) + case IdlEvent: + entry, err = NewEventArgsEntry(offChainName, EventIDLTypes{Event: v, Types: idl.Types}, true, mod, binary.LittleEndian()) + default: + return nil, fmt.Errorf("unknown codec IDL definition: %T", idlDefinition) + } + if err != nil { + return nil, fmt.Errorf("failed to create %q codec entry: %w", offChainName, err) + } + + return entry, nil +} + +func FindDefinitionFromIDL(cfgType ChainConfigType, onChainName string, idl IDL) (interface{}, error) { // not the most efficient way to do this, but these slices should always be very, very small switch cfgType { case ChainConfigTypeAccountDef: @@ -128,6 +138,14 @@ func findDefinitionFromIDL(cfgType ChainConfigType, onChainName string, idl IDL) return nil, fmt.Errorf("unknown type: %q", cfgType) } +func WrapItemType(isInput bool, contractName, itemType string, readType ChainConfigType) string { + if isInput { + return fmt.Sprintf("input.%s.%s", contractName, itemType) + } + + return fmt.Sprintf("output.%s.%s.%s", readType, contractName, itemType) +} + // NewIDLAccountCodec is for Anchor custom types func NewIDLAccountCodec(idl IDL, builder commonencodings.Builder) (commontypes.RemoteCodec, error) { return newIDLCoded(idl, builder, idl.Accounts, true) diff --git a/pkg/solana/config/chain_reader.go b/pkg/solana/config/chain_reader.go index 4251624fe..53ae30fe3 100644 --- a/pkg/solana/config/chain_reader.go +++ b/pkg/solana/config/chain_reader.go @@ -1,101 +1,58 @@ package config import ( - "encoding/json" "fmt" "github.com/gagliardetto/solana-go" "github.com/gagliardetto/solana-go/rpc" commoncodec "github.com/smartcontractkit/chainlink-common/pkg/codec" - "github.com/smartcontractkit/chainlink-common/pkg/codec/encodings" - "github.com/smartcontractkit/chainlink-common/pkg/codec/encodings/binary" - "github.com/smartcontractkit/chainlink-common/pkg/types" ) -type ChainReader struct { - Namespaces map[string]ChainReaderMethods `json:"namespaces" toml:"namespaces"` +type ContractReader struct { + Namespaces map[string]ChainContractReader `json:"namespaces" toml:"namespaces"` } -type ChainReaderMethods struct { - Methods map[string]ChainDataReader `json:"methods" toml:"methods"` +type ChainContractReader struct { + IDL string `json:"anchorIDL" toml:"anchorIDL"` + // Reads key is the off-chain name for this read. + Reads map[string]ReadDefinition + // TODO ContractPollingFilter same as EVM? } -type ChainDataReader struct { - AnchorIDL string `json:"anchorIDL" toml:"anchorIDL"` - // Encoding defines the type of encoding used for on-chain data. Currently supported - // are 'borsh' and 'bincode'. - Encoding EncodingType `json:"encoding" toml:"encoding"` - Procedure ChainReaderProcedure `json:"procedure" toml:"procedure"` +type ReadDefinition struct { + ChainSpecificName string `json:"chainSpecificName"` + ReadType ReadType `json:"readType,omitempty"` + InputModifications commoncodec.ModifiersConfig `json:"inputModifications,omitempty"` + OutputModifications commoncodec.ModifiersConfig `json:"outputModifications,omitempty"` + RPCOpts *RPCOpts `json:"rpcOpts,omitempty"` + + // TODO EventDefinitions *EventDefinitions similar to EVM? + // TODO Lookup details for PDAs and lookup tables to be merged with CW + //LookupTables *LookupTables + //Accounts *[]Lookup } -type EncodingType int +type ReadType int const ( - EncodingTypeBorsh EncodingType = iota - EncodingTypeBincode - - encodingTypeBorshStr = "borsh" - encodingTypeBincodeStr = "bincode" + Account ReadType = iota + Log ) -func (t EncodingType) MarshalJSON() ([]byte, error) { - switch t { - case EncodingTypeBorsh: - return json.Marshal(encodingTypeBorshStr) - case EncodingTypeBincode: - return json.Marshal(encodingTypeBincodeStr) +func (r ReadType) String() string { + switch r { + case Account: + return "Account" + case Log: + return "Log" default: - return nil, fmt.Errorf("%w: unrecognized encoding type: %d", types.ErrInvalidConfig, t) + return fmt.Sprintf("Unknown(%d)", r) } } -func (t *EncodingType) UnmarshalJSON(data []byte) error { - var str string - - if err := json.Unmarshal(data, &str); err != nil { - return fmt.Errorf("%w: %s", types.ErrInvalidConfig, err.Error()) - } - - switch str { - case encodingTypeBorshStr: - *t = EncodingTypeBorsh - case encodingTypeBincodeStr: - *t = EncodingTypeBincode - default: - return fmt.Errorf("%w: unrecognized encoding type: %s", types.ErrInvalidConfig, str) - } - - return nil -} - type RPCOpts struct { Encoding *solana.EncodingType `json:"encoding,omitempty"` Commitment *rpc.CommitmentType `json:"commitment,omitempty"` DataSlice *rpc.DataSlice `json:"dataSlice,omitempty"` } - -type ChainReaderProcedure chainDataProcedureFields - -type chainDataProcedureFields struct { - // IDLAccount refers to the account defined in the IDL. - IDLAccount string `json:"idlAccount,omitempty"` - // OutputModifications provides modifiers to convert chain data format to custom - // output formats. - OutputModifications commoncodec.ModifiersConfig `json:"outputModifications,omitempty"` - // RPCOpts provides optional configurations for commitment, encoding, and data - // slice offsets. - RPCOpts *RPCOpts `json:"rpcOpts,omitempty"` -} - -// BuilderForEncoding returns a builder for the encoding configuration. Defaults to little endian. -func BuilderForEncoding(eType EncodingType) encodings.Builder { - switch eType { - case EncodingTypeBorsh: - return binary.LittleEndian() - case EncodingTypeBincode: - return binary.BigEndian() - default: - return binary.LittleEndian() - } -} diff --git a/pkg/solana/config/chain_reader_test.go b/pkg/solana/config/chain_reader_test.go index 7d290b50c..876a0ca6a 100644 --- a/pkg/solana/config/chain_reader_test.go +++ b/pkg/solana/config/chain_reader_test.go @@ -11,7 +11,6 @@ import ( "github.com/stretchr/testify/require" codeccommon "github.com/smartcontractkit/chainlink-common/pkg/codec" - "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" @@ -30,7 +29,7 @@ func TestChainReaderConfig(t *testing.T) { t.Run("valid unmarshal", func(t *testing.T) { t.Parallel() - var result config.ChainReader + var result config.ContractReader require.NoError(t, json.Unmarshal([]byte(validJSON), &result)) assert.Equal(t, validChainReaderConfig, result) }) @@ -38,7 +37,7 @@ func TestChainReaderConfig(t *testing.T) { t.Run("invalid unmarshal", func(t *testing.T) { t.Parallel() - var result config.ChainReader + var result config.ContractReader require.ErrorIs(t, json.Unmarshal([]byte(invalidJSON), &result), types.ErrInvalidConfig) }) @@ -49,33 +48,13 @@ func TestChainReaderConfig(t *testing.T) { require.NoError(t, err) - var conf config.ChainReader + var conf config.ContractReader require.NoError(t, json.Unmarshal(result, &conf)) assert.Equal(t, validChainReaderConfig, conf) }) } -func TestEncodingType_Fail(t *testing.T) { - t.Parallel() - - _, err := json.Marshal(config.EncodingType(100)) - - require.NotNil(t, err) - - var tp config.EncodingType - - require.ErrorIs(t, json.Unmarshal([]byte(`42`), &tp), types.ErrInvalidConfig) - require.ErrorIs(t, json.Unmarshal([]byte(`"invalid"`), &tp), types.ErrInvalidConfig) -} - -func TestBuilderForEncoding_Default(t *testing.T) { - t.Parallel() - - builder := config.BuilderForEncoding(config.EncodingType(100)) - require.Equal(t, binary.LittleEndian(), builder) -} - var ( encodingBase64 = solana.EncodingBase64 commitment = rpc.CommitmentFinalized @@ -83,45 +62,33 @@ var ( length = uint64(10) ) -var validChainReaderConfig = config.ChainReader{ - Namespaces: map[string]config.ChainReaderMethods{ +var validChainReaderConfig = config.ContractReader{ + Namespaces: map[string]config.ChainContractReader{ "Contract": { - Methods: map[string]config.ChainDataReader{ + Reads: map[string]config.ReadDefinition{ "Method": { - AnchorIDL: "test idl 1", - Encoding: config.EncodingTypeBorsh, - Procedure: config.ChainReaderProcedure{ - IDLAccount: testutils.TestStructWithNestedStruct, - }, + ChainSpecificName: testutils.TestStructWithNestedStruct, }, "MethodWithOpts": { - AnchorIDL: "test idl 2", - Encoding: config.EncodingTypeBorsh, - Procedure: config.ChainReaderProcedure{ - IDLAccount: testutils.TestStructWithNestedStruct, - OutputModifications: codeccommon.ModifiersConfig{ - &codeccommon.PropertyExtractorConfig{FieldName: "DurationVal"}, - }, - RPCOpts: &config.RPCOpts{ - Encoding: &encodingBase64, - Commitment: &commitment, - DataSlice: &rpc.DataSlice{ - Offset: &offset, - Length: &length, - }, + ChainSpecificName: testutils.TestStructWithNestedStruct, + OutputModifications: codeccommon.ModifiersConfig{ + &codeccommon.PropertyExtractorConfig{FieldName: "DurationVal"}, + }, + RPCOpts: &config.RPCOpts{ + Encoding: &encodingBase64, + Commitment: &commitment, + DataSlice: &rpc.DataSlice{ + Offset: &offset, + Length: &length, }, }, }, }, }, "OtherContract": { - Methods: map[string]config.ChainDataReader{ + Reads: map[string]config.ReadDefinition{ "Method": { - AnchorIDL: "test idl 3", - Encoding: config.EncodingTypeBincode, - Procedure: config.ChainReaderProcedure{ - IDLAccount: testutils.TestStructWithNestedStruct, - }, + ChainSpecificName: testutils.TestStructWithNestedStruct, }, }, }, diff --git a/pkg/solana/config/testChainReader_invalid.json b/pkg/solana/config/testChainReader_invalid.json index 98caa8fcc..c7313f8d5 100644 --- a/pkg/solana/config/testChainReader_invalid.json +++ b/pkg/solana/config/testChainReader_invalid.json @@ -1,15 +1,38 @@ { "namespaces": { - "Contract": { - "methods": { - "Method": { - "anchorIDL": "test idl 1", - "encoding": "invalid", - "procedure": { - "idlAccount": "StructWithNestedStruct" + "example_namespace": { + "anchorIDL": { + "name": "example_invalid_idl_name", + "version": "1.0.0" + }, + "reads": { + "someRead": { + "chainSpecificName": "some_chain_specific_name", + "readType": 0, + "inputModifications": [ + { + "path": "someInputPath", + "action": "replace", + "value": "newValue" + } + ], + "outputModifications": [ + { + "path": "someOutputPath", + "action": "extract", + "value": "desiredField" + } + ], + "rpcOpts": { + "encoding": "base64", + "commitment": "processed", + "dataSlice": { + "offset": 0, + "length": 32 + } } } } } } -} \ No newline at end of file +} diff --git a/pkg/solana/config/testChainReader_valid.json b/pkg/solana/config/testChainReader_valid.json index ca75a936b..a690ba7f5 100644 --- a/pkg/solana/config/testChainReader_valid.json +++ b/pkg/solana/config/testChainReader_valid.json @@ -1,45 +1,40 @@ { "namespaces": { "Contract": { - "methods": { + "anchorIDL": "", + "reads": { "Method": { - "anchorIDL": "test idl 1", - "encoding": "borsh", - "procedure": { - "idlAccount": "StructWithNestedStruct" - } + "chainSpecificName": "StructWithNestedStruct", + "readType": 0 }, "MethodWithOpts": { - "anchorIDL": "test idl 2", - "encoding": "borsh", - "procedure": { - "idlAccount": "StructWithNestedStruct", - "outputModifications": [{ - "Type": "extract property", - "FieldName": "DurationVal" - }], - "rpcOpts": { - "encoding": "base64", - "commitment": "finalized", - "dataSlice": { - "offset": 10, - "length": 10 - } + "chainSpecificName": "StructWithNestedStruct", + "readType": 0, + "rpcOpts": { + "encoding": "base64", + "commitment": "finalized", + "dataSlice": { + "offset": 10, + "length": 10 } - } + }, + "outputModifications": [ + { + "fieldName": "DurationVal", + "type": "extract property" + } + ] } } }, "OtherContract": { - "methods": { + "anchorIDL": "", + "reads": { "Method": { - "anchorIDL": "test idl 3", - "encoding": "bincode", - "procedure": { - "idlAccount": "StructWithNestedStruct" - } + "chainSpecificName": "StructWithNestedStruct", + "readType": 0 } } } } -} \ No newline at end of file +} From 1ee55c5329bed543224937e99decace2014fd8c7 Mon Sep 17 00:00:00 2001 From: ilija Date: Mon, 23 Dec 2024 21:51:21 +0100 Subject: [PATCH 02/11] Fix most of the interface tests --- .../chainreader/account_read_binding.go | 30 ++- pkg/solana/chainreader/bindings.go | 9 + pkg/solana/chainreader/bindings_test.go | 2 + pkg/solana/chainreader/chain_reader.go | 58 ++--- pkg/solana/chainreader/chain_reader_test.go | 12 +- pkg/solana/chainreader/lookup.go | 28 ++- pkg/solana/codec/solana.go | 4 +- .../config/testChainReader_invalid.json | 224 +++++++++++++++--- 8 files changed, 271 insertions(+), 96 deletions(-) diff --git a/pkg/solana/chainreader/account_read_binding.go b/pkg/solana/chainreader/account_read_binding.go index 71ebb131b..4f14c0aa7 100644 --- a/pkg/solana/chainreader/account_read_binding.go +++ b/pkg/solana/chainreader/account_read_binding.go @@ -7,27 +7,33 @@ import ( "github.com/gagliardetto/solana-go/rpc" "github.com/smartcontractkit/chainlink-common/pkg/types" + "github.com/smartcontractkit/chainlink-solana/pkg/solana/codec" ) // accountReadBinding provides decoding and reading Solana Account data using a defined codec. The -// `idlAccount` refers to the account name in the IDL for which the codec has a type mapping. +// `idlAccount` refers to the account onChainName in the IDL for which the codec has a type mapping. type accountReadBinding struct { - idlAccount string - codec types.RemoteCodec - key solana.PublicKey - opts *rpc.GetAccountInfoOpts + namespace, onChainName, chainAgnosticName string + codec types.RemoteCodec + key solana.PublicKey + opts *rpc.GetAccountInfoOpts } -func newAccountReadBinding(acct string, codec types.RemoteCodec, opts *rpc.GetAccountInfoOpts) *accountReadBinding { +func newAccountReadBinding(namespace, onChainName, chainAgnosticName string, opts *rpc.GetAccountInfoOpts) *accountReadBinding { return &accountReadBinding{ - idlAccount: acct, - codec: codec, - opts: opts, + namespace: namespace, + onChainName: onChainName, + chainAgnosticName: chainAgnosticName, + opts: opts, } } var _ readBinding = &accountReadBinding{} +func (b *accountReadBinding) SetCodec(codec types.RemoteCodec) { + b.codec = codec +} + func (b *accountReadBinding) SetAddress(key solana.PublicKey) { b.key = key } @@ -36,10 +42,10 @@ func (b *accountReadBinding) GetAddress() solana.PublicKey { return b.key } -func (b *accountReadBinding) CreateType(_ bool) (any, error) { - return b.codec.CreateType(b.idlAccount, false) +func (b *accountReadBinding) CreateType(forEncoding bool) (any, error) { + return b.codec.CreateType(codec.WrapItemType(forEncoding, b.namespace, b.chainAgnosticName, codec.ChainConfigTypeAccountDef), forEncoding) } func (b *accountReadBinding) Decode(ctx context.Context, bts []byte, outVal any) error { - return b.codec.Decode(ctx, bts, outVal, b.idlAccount) + return b.codec.Decode(ctx, bts, outVal, codec.WrapItemType(false, b.namespace, b.chainAgnosticName, codec.ChainConfigTypeAccountDef)) } diff --git a/pkg/solana/chainreader/bindings.go b/pkg/solana/chainreader/bindings.go index 51cc8980a..751a58fdd 100644 --- a/pkg/solana/chainreader/bindings.go +++ b/pkg/solana/chainreader/bindings.go @@ -12,6 +12,7 @@ import ( type readBinding interface { SetAddress(solana.PublicKey) GetAddress() solana.PublicKey + SetCodec(types.RemoteCodec) CreateType(bool) (any, error) Decode(context.Context, []byte, any) error } @@ -70,3 +71,11 @@ func (b namespaceBindings) Bind(binding types.BoundContract) error { return nil } + +func (b namespaceBindings) SetCodec(codec types.RemoteCodec) { + for _, nbs := range b { + for _, rb := range nbs { + rb.SetCodec(codec) + } + } +} diff --git a/pkg/solana/chainreader/bindings_test.go b/pkg/solana/chainreader/bindings_test.go index d8b510648..e8dbea89a 100644 --- a/pkg/solana/chainreader/bindings_test.go +++ b/pkg/solana/chainreader/bindings_test.go @@ -46,6 +46,8 @@ type mockBinding struct { mock.Mock } +func (_m *mockBinding) SetCodec(_ types.RemoteCodec) {} + func (_m *mockBinding) SetAddress(_ solana.PublicKey) {} func (_m *mockBinding) GetAddress() solana.PublicKey { diff --git a/pkg/solana/chainreader/chain_reader.go b/pkg/solana/chainreader/chain_reader.go index b7fda7346..706db02b0 100644 --- a/pkg/solana/chainreader/chain_reader.go +++ b/pkg/solana/chainreader/chain_reader.go @@ -60,12 +60,15 @@ func NewChainReaderService(lggr logger.Logger, dataReader MultipleAccountGetter, return nil, err } - if svcCodec, err := svc.parsed.ToCodec(); err != nil { + svcCodec, err := svc.parsed.ToCodec() + if err != nil { return nil, err - } else { - svc.codec = svcCodec - return svc, nil } + + svc.codec = svcCodec + + svc.bindings.SetCodec(svcCodec) + return svc, nil } // Name implements the services.ServiceCtx interface and returns the logger service name. @@ -122,7 +125,7 @@ func (s *SolanaChainReaderService) GetLatestValue(ctx context.Context, readIdent batch := []call{ { ContractName: vals.contract, - ReadName: vals.readName, + ReadName: vals.name.onChainName, Params: params, ReturnVal: returnVal, }, @@ -225,21 +228,25 @@ func (s *SolanaChainReaderService) CreateContractType(readIdentifier string, for return nil, fmt.Errorf("%w: no contract for read identifier", types.ErrInvalidConfig) } - return s.bindings.CreateType(values.contract, values.readName, forEncoding) + return s.bindings.CreateType(values.contract, values.name.onChainName, forEncoding) } -func (s *SolanaChainReaderService) addCodecDef(isInput bool, namespace, itemType string, readType codec.ChainConfigType, idl codec.IDL, idlDefinition interface{}, modCfg commoncodec.ModifiersConfig) error { +func (s *SolanaChainReaderService) addCodecDef(forEncoding bool, namespace, chainAgnosticName string, readType codec.ChainConfigType, idl codec.IDL, idlDefinition interface{}, modCfg commoncodec.ModifiersConfig) error { mod, err := modCfg.ToModifier(codec.DecoderHooks...) if err != nil { return err } - cEntry, err := codec.CreateCodecEntry(idlDefinition, itemType, idl, mod) + cEntry, err := codec.CreateCodecEntry(idlDefinition, chainAgnosticName, idl, mod) if err != nil { return err } - s.parsed.EncoderDefs[codec.WrapItemType(isInput, namespace, itemType, readType)] = cEntry + if forEncoding { + s.parsed.EncoderDefs[codec.WrapItemType(forEncoding, namespace, chainAgnosticName, readType)] = cEntry + } else { + s.parsed.DecoderDefs[codec.WrapItemType(forEncoding, namespace, chainAgnosticName, readType)] = cEntry + } return nil } @@ -261,18 +268,15 @@ func (s *SolanaChainReaderService) init(namespaces map[string]config.ChainContra case config.Account: accountIDLDef, isOk := idlDef.(codec.IdlTypeDef) if !isOk { - return fmt.Errorf("unexpected type %T from IDL definition for account read: %q, with onchain name: %q, of type: %q", accountIDLDef, readName, read.ChainSpecificName, read.ReadType) + return fmt.Errorf("unexpected type %T from IDL definition for account read: %q, with onchain onChainName: %q, of type: %q", accountIDLDef, readName, read.ChainSpecificName, read.ReadType) } - if err = s.addAccountRead(namespace, readName, idl, accountIDLTypes{ - Account: accountIDLDef, - Types: idl.Types, - }, read); err != nil { + if err = s.addAccountRead(namespace, readName, idl, accountIDLDef, read); err != nil { return err } case config.Log: eventIDlDef, isOk := idlDef.(codec.IdlEvent) if !isOk { - return fmt.Errorf("unexpected type %T from IDL definition for log read: %q, with onchain name: %q, of type: %q", eventIDlDef, readName, read.ChainSpecificName, read.ReadType) + return fmt.Errorf("unexpected type %T from IDL definition for log read: %q, with onchain onChainName: %q, of type: %q", eventIDlDef, readName, read.ChainSpecificName, read.ReadType) } // TODO s.addLogRead() default: @@ -289,33 +293,31 @@ type accountIDLTypes struct { Types codec.IdlTypeDefSlice } -func (s *SolanaChainReaderService) addAccountRead(namespace string, itemType string, idl codec.IDL, idlType accountIDLTypes, readDefinition config.ReadDefinition) error { - // TODO if readDefinition.HasPDASeedInput{ - // if err := s.addCodecDef(false, namespace, itemType, codec.ChainConfigTypeAccountDef, idl,idlType, readDefinition.InputModifications,; err != nil { - // return err - // } - // } +func (s *SolanaChainReaderService) addAccountRead(namespace string, itemType string, idl codec.IDL, idlType codec.IdlTypeDef, readDefinition config.ReadDefinition) error { + if err := s.addCodecDef(true, namespace, itemType, codec.ChainConfigTypeAccountDef, idl, idlType, readDefinition.InputModifications); err != nil { + return err + } if err := s.addCodecDef(false, namespace, itemType, codec.ChainConfigTypeAccountDef, idl, idlType, readDefinition.OutputModifications); err != nil { return err } - s.lookup.addReadNameForContract(namespace, itemType) + s.lookup.addReadNameForContract(namespace, namePair{ + onChainName: itemType, + genericName: idlType.Name, + }) + s.bindings.AddReadBinding(namespace, itemType, newAccountReadBinding( + namespace, readDefinition.ChainSpecificName, + itemType, // TODO codec is not created at this point, set codec for all bindings after init - s.codec, createRPCOpts(readDefinition.RPCOpts), )) return nil } -func (s *SolanaChainReaderService) addLogRead() error { - // TODO: init codec types and modifiers, do address lookup here if needed? ... - return nil -} - // injectAddressModifier injects AddressModifier into OutputModifications. // This is necessary because AddressModifier cannot be serialized and must be applied at runtime. func injectAddressModifier(inputModifications, outputModifications commoncodec.ModifiersConfig) { diff --git a/pkg/solana/chainreader/chain_reader_test.go b/pkg/solana/chainreader/chain_reader_test.go index 63ce56061..804d7ed17 100644 --- a/pkg/solana/chainreader/chain_reader_test.go +++ b/pkg/solana/chainreader/chain_reader_test.go @@ -438,19 +438,20 @@ func (r *chainReaderInterfaceTester) Setup(t *testing.T) { MethodReturningUint64: { ReadType: config.Account, ChainSpecificName: "SimpleUint64Value", - OutputModifications: codeccommon.ModifiersConfig{ &codeccommon.PropertyExtractorConfig{FieldName: "I"}, }, }, MethodReturningUint64Slice: { ChainSpecificName: "Uint64Slice", + ReadType: config.Account, OutputModifications: codeccommon.ModifiersConfig{ &codeccommon.PropertyExtractorConfig{FieldName: "Vals"}, }, }, MethodReturningSeenStruct: { ChainSpecificName: "TestStruct", + ReadType: config.Account, OutputModifications: codeccommon.ModifiersConfig{ &codeccommon.AddressBytesToStringModifierConfig{ Fields: []string{"Accountstruct.Accountstr"}, @@ -465,6 +466,7 @@ func (r *chainReaderInterfaceTester) Setup(t *testing.T) { Reads: map[string]config.ReadDefinition{ MethodReturningUint64: { ChainSpecificName: "SimpleUint64Value", + ReadType: config.Account, OutputModifications: codeccommon.ModifiersConfig{ &codeccommon.PropertyExtractorConfig{FieldName: "I"}, }, @@ -776,19 +778,15 @@ func fullTestIDL(t *testing.T) string { // Combine all of the type definitions into one comma-separated string. allTypes := strings.Join([]string{ - midLevelDynamicStructIDL, - midLevelStaticStructIDL, - innerDynamicStructIDL, - innerStaticStructIDL, - accountStructIDL, + testStructIDL, uint64BaseTypeIDL, uint64SliceBaseTypeIDL, }, ",") return fmt.Sprintf( baseIDL, - testStructIDL, allTypes, + strings.Join([]string{midLevelDynamicStructIDL, midLevelStaticStructIDL, innerDynamicStructIDL, innerStaticStructIDL, accountStructIDL}, ","), ) } diff --git a/pkg/solana/chainreader/lookup.go b/pkg/solana/chainreader/lookup.go index b4295d20f..5c7d06241 100644 --- a/pkg/solana/chainreader/lookup.go +++ b/pkg/solana/chainreader/lookup.go @@ -9,7 +9,11 @@ import ( type readValues struct { address string contract string - readName string + name namePair +} + +type namePair struct { + onChainName, genericName string } // lookup provides basic utilities for mapping a complete readIdentifier to @@ -17,45 +21,45 @@ type readValues struct { type lookup struct { mu sync.RWMutex // contractReadNames maps a contract name to all available readNames (method, log, event, etc.) - contractReadNames map[string][]string + contractReadNames map[string][]namePair // readIdentifiers maps from a complete readIdentifier string to finite read data - // a readIdentifier is a combination of address, contract, and readName as a concatenated string + // a readIdentifier is a combination of address, contract, and namePair as a concatenated string readIdentifiers map[string]readValues } func newLookup() *lookup { return &lookup{ - contractReadNames: make(map[string][]string), + contractReadNames: make(map[string][]namePair), readIdentifiers: make(map[string]readValues), } } -func (l *lookup) addReadNameForContract(contract, readName string) { +func (l *lookup) addReadNameForContract(contract string, name namePair) { l.mu.Lock() defer l.mu.Unlock() readNames, exists := l.contractReadNames[contract] if !exists { - readNames = []string{} + readNames = []namePair{} } - l.contractReadNames[contract] = append(readNames, readName) + l.contractReadNames[contract] = append(readNames, name) } func (l *lookup) bindAddressForContract(contract, address string) { l.mu.Lock() defer l.mu.Unlock() - for _, readName := range l.contractReadNames[contract] { + for _, namePair := range l.contractReadNames[contract] { readIdentifier := types.BoundContract{ Address: address, Name: contract, - }.ReadIdentifier(readName) + }.ReadIdentifier(namePair.onChainName) l.readIdentifiers[readIdentifier] = readValues{ address: address, contract: contract, - readName: readName, + name: namePair, } } } @@ -64,11 +68,11 @@ func (l *lookup) unbindAddressForContract(contract, address string) { l.mu.Lock() defer l.mu.Unlock() - for _, readName := range l.contractReadNames[contract] { + for _, namePair := range l.contractReadNames[contract] { readIdentifier := types.BoundContract{ Address: address, Name: contract, - }.ReadIdentifier(readName) + }.ReadIdentifier(namePair.onChainName) delete(l.readIdentifiers, readIdentifier) } diff --git a/pkg/solana/codec/solana.go b/pkg/solana/codec/solana.go index 3ecada671..2a730ffa2 100644 --- a/pkg/solana/codec/solana.go +++ b/pkg/solana/codec/solana.go @@ -138,8 +138,8 @@ func FindDefinitionFromIDL(cfgType ChainConfigType, onChainName string, idl IDL) return nil, fmt.Errorf("unknown type: %q", cfgType) } -func WrapItemType(isInput bool, contractName, itemType string, readType ChainConfigType) string { - if isInput { +func WrapItemType(forEncoding bool, contractName, itemType string, readType ChainConfigType) string { + if forEncoding { return fmt.Sprintf("input.%s.%s", contractName, itemType) } diff --git a/pkg/solana/config/testChainReader_invalid.json b/pkg/solana/config/testChainReader_invalid.json index c7313f8d5..0572ee0b4 100644 --- a/pkg/solana/config/testChainReader_invalid.json +++ b/pkg/solana/config/testChainReader_invalid.json @@ -1,38 +1,192 @@ { - "namespaces": { - "example_namespace": { - "anchorIDL": { - "name": "example_invalid_idl_name", - "version": "1.0.0" - }, - "reads": { - "someRead": { - "chainSpecificName": "some_chain_specific_name", - "readType": 0, - "inputModifications": [ - { - "path": "someInputPath", - "action": "replace", - "value": "newValue" - } - ], - "outputModifications": [ - { - "path": "someOutputPath", - "action": "extract", - "value": "desiredField" - } - ], - "rpcOpts": { - "encoding": "base64", - "commitment": "processed", - "dataSlice": { - "offset": 0, - "length": 32 - } - } - } + "version": "0.1.0", + "name": "some_test_idl", + "accounts": [ + { + "name": "TestStruct", + "type": { + "kind": "struct", + "fields": [ + { + "name": "field", + "type": { + "option": "i32" + } + }, + { + "name": "differentField", + "type": "string" + }, + { + "name": "bigField", + "type": "i128" + }, + { + "name": "nestedDynamicStruct", + "type": { + "defined": "MidLevelDynamicStruct" + } + }, + { + "name": "nestedStaticStruct", + "type": { + "defined": "MidLevelStaticStruct" + } + }, + { + "name": "oracleID", + "type": "u8" + }, + { + "name": "oracleIDs", + "type": { + "array": [ + "u8", + 32 + ] + } + }, + { + "name": "accountstruct", + "type": { + "defined": "accountstruct" + } + }, + { + "name": "accounts", + "type": { + "vec": "bytes" + } + } + ] + } + } + ], + "types": [ + { + "name": "MidLevelDynamicStruct", + "type": { + "kind": "struct", + "fields": [ + { + "name": "fixedBytes", + "type": { + "array": [ + "u8", + 2 + ] + } + }, + { + "name": "inner", + "type": { + "defined": "InnerDynamicTestStruct" + } + } + ] + } + }, + { + "name": "MidLevelStaticStruct", + "type": { + "kind": "struct", + "fields": [ + { + "name": "fixedBytes", + "type": { + "array": [ + "u8", + 2 + ] + } + }, + { + "name": "inner", + "type": { + "defined": "InnerStaticTestStruct" + } + } + ] + } + }, + { + "name": "InnerDynamicTestStruct", + "type": { + "kind": "struct", + "fields": [ + { + "name": "i", + "type": "i32" + }, + { + "name": "s", + "type": "string" + } + ] + } + }, + { + "name": "InnerStaticTestStruct", + "type": { + "kind": "struct", + "fields": [ + { + "name": "i", + "type": "i32" + }, + { + "name": "a", + "type": "bytes" + } + ] + } + }, + { + "name": "accountstruct", + "type": { + "kind": "struct", + "fields": [ + { + "name": "account", + "type": "bytes" + }, + { + "name": "accountstr", + "type": { + "array": [ + "u8", + 32 + ] + } + } + ] + } + }, + { + "name": "SimpleUint64Value", + "type": { + "kind": "struct", + "fields": [ + { + "name": "i", + "type": "u64" + } + ] + } + }, + { + "name": "Uint64Slice", + "type": { + "kind": "struct", + "fields": [ + { + "name": "vals", + "type": { + "vec": "u64" + } + } + ] } } - } -} + ] +} \ No newline at end of file From 6d9e500c5391f7b657d6284c8500ef29236884d8 Mon Sep 17 00:00:00 2001 From: ilija Date: Mon, 23 Dec 2024 22:45:06 +0100 Subject: [PATCH 03/11] Fix nil idlType handling and update test IDL names --- pkg/solana/chainreader/batch.go | 1 - pkg/solana/chainreader/chain_reader.go | 7 ++++++- pkg/solana/chainreader/chain_reader_test.go | 8 ++++---- pkg/solana/codec/anchoridl.go | 5 +++++ 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/pkg/solana/chainreader/batch.go b/pkg/solana/chainreader/batch.go index 43e4971b9..d5990601d 100644 --- a/pkg/solana/chainreader/batch.go +++ b/pkg/solana/chainreader/batch.go @@ -77,7 +77,6 @@ func doMethodBatchCall(ctx context.Context, client MultipleAccountGetter, bindin results[idx].err, binding.Decode(ctx, data[idx], results[idx].returnVal), ) - continue } diff --git a/pkg/solana/chainreader/chain_reader.go b/pkg/solana/chainreader/chain_reader.go index 706db02b0..9b2c4bf36 100644 --- a/pkg/solana/chainreader/chain_reader.go +++ b/pkg/solana/chainreader/chain_reader.go @@ -294,7 +294,12 @@ type accountIDLTypes struct { } func (s *SolanaChainReaderService) addAccountRead(namespace string, itemType string, idl codec.IDL, idlType codec.IdlTypeDef, readDefinition config.ReadDefinition) error { - if err := s.addCodecDef(true, namespace, itemType, codec.ChainConfigTypeAccountDef, idl, idlType, readDefinition.InputModifications); err != nil { + inputAccountIDLDef := codec.NilIdlTypeDefTy + // TODO: + // if hasPDA{ + // inputAccountIDLDef = pdaType + // } + if err := s.addCodecDef(true, namespace, itemType, codec.ChainConfigTypeAccountDef, idl, inputAccountIDLDef, readDefinition.InputModifications); err != nil { return err } diff --git a/pkg/solana/chainreader/chain_reader_test.go b/pkg/solana/chainreader/chain_reader_test.go index 804d7ed17..eeb0289ec 100644 --- a/pkg/solana/chainreader/chain_reader_test.go +++ b/pkg/solana/chainreader/chain_reader_test.go @@ -454,7 +454,7 @@ func (r *chainReaderInterfaceTester) Setup(t *testing.T) { ReadType: config.Account, OutputModifications: codeccommon.ModifiersConfig{ &codeccommon.AddressBytesToStringModifierConfig{ - Fields: []string{"Accountstruct.Accountstr"}, + Fields: []string{"AccountStruct.AccountStr"}, }, &codeccommon.HardCodeModifierConfig{OffChainValues: map[string]any{"ExtraField": AnyExtraValue}}, }, @@ -810,7 +810,7 @@ const ( {"name": "nestedStaticStruct","type": {"defined": "MidLevelStaticStruct"}}, {"name": "oracleID","type": "u8"}, {"name": "oracleIDs","type": {"array": ["u8",32]}}, - {"name": "accountstruct","type": {"defined": "accountstruct"}}, + {"name": "accountStruct","type": {"defined": "accountStruct"}}, {"name": "accounts","type": {"vec": "bytes"}} ] @@ -818,12 +818,12 @@ const ( }` accountStructIDL = `{ - "name": "accountstruct", + "name": "accountStruct", "type": { "kind": "struct", "fields": [ {"name": "account", "type": "bytes"}, - {"name": "accountstr", "type": {"array": ["u8",32]}} + {"name": "accountStr", "type": {"array": ["u8",32]}} ] } }` diff --git a/pkg/solana/codec/anchoridl.go b/pkg/solana/codec/anchoridl.go index e54710c5c..3fc296e97 100644 --- a/pkg/solana/codec/anchoridl.go +++ b/pkg/solana/codec/anchoridl.go @@ -312,6 +312,11 @@ type IdlTypeDefTyEnum struct { Variants IdlEnumVariantSlice `json:"variants,omitempty"` } +var NilIdlTypeDefTy = IdlTypeDef{Type: IdlTypeDefTy{ + Kind: "struct", + Fields: &IdlTypeDefStruct{}, +}} + type IdlTypeDefTy struct { Kind IdlTypeDefTyKind `json:"kind"` From 382f4017f183bf68619a9c3b75e0dc007e87bbda Mon Sep 17 00:00:00 2001 From: ilija Date: Tue, 24 Dec 2024 12:36:09 +0100 Subject: [PATCH 04/11] lint and fix CR integration tests cfg --- .../relayinterface/chain_components_test.go | 45 ++++++++----------- .../chainreader/account_read_binding.go | 1 + pkg/solana/chainreader/chain_reader.go | 5 --- 3 files changed, 19 insertions(+), 32 deletions(-) diff --git a/integration-tests/relayinterface/chain_components_test.go b/integration-tests/relayinterface/chain_components_test.go index cd47d224e..95a7ceb24 100644 --- a/integration-tests/relayinterface/chain_components_test.go +++ b/integration-tests/relayinterface/chain_components_test.go @@ -131,48 +131,39 @@ type SolanaChainComponentsInterfaceTester[T TestingT[T]] struct { TestSelectionSupport Helper SolanaChainComponentsInterfaceTesterHelper[T] cr *chainreader.SolanaChainReaderService - chainReaderConfig config.ChainReader + contractReaderConfig config.ContractReader } func (it *SolanaChainComponentsInterfaceTester[T]) Setup(t T) { t.Cleanup(func() {}) - it.chainReaderConfig = config.ChainReader{ - Namespaces: map[string]config.ChainReaderMethods{ + it.contractReaderConfig = config.ContractReader{ + Namespaces: map[string]config.ChainContractReader{ AnyContractName: { - Methods: map[string]config.ChainDataReader{ + IDL: string(it.Helper.GetJSONEncodedIDL(t)), + Reads: map[string]config.ReadDefinition{ MethodReturningUint64: { - AnchorIDL: string(it.Helper.GetJSONEncodedIDL(t)), - Encoding: config.EncodingTypeBorsh, - Procedure: config.ChainReaderProcedure{ - IDLAccount: "DataAccount", - OutputModifications: codec.ModifiersConfig{ - &codec.PropertyExtractorConfig{FieldName: "U64Value"}, - }, + ChainSpecificName: "DataAccount", + ReadType: config.Account, + OutputModifications: codec.ModifiersConfig{ + &codec.PropertyExtractorConfig{FieldName: "U64Value"}, }, }, MethodReturningUint64Slice: { - AnchorIDL: string(it.Helper.GetJSONEncodedIDL(t)), - Encoding: config.EncodingTypeBorsh, - Procedure: config.ChainReaderProcedure{ - IDLAccount: "DataAccount", - OutputModifications: codec.ModifiersConfig{ - &codec.PropertyExtractorConfig{FieldName: "U64Slice"}, - }, + ChainSpecificName: "DataAccount", + OutputModifications: codec.ModifiersConfig{ + &codec.PropertyExtractorConfig{FieldName: "U64Slice"}, }, }, }, }, AnySecondContractName: { - Methods: map[string]config.ChainDataReader{ + IDL: string(it.Helper.GetJSONEncodedIDL(t)), + Reads: map[string]config.ReadDefinition{ MethodReturningUint64: { - AnchorIDL: string(it.Helper.GetJSONEncodedIDL(t)), - Encoding: config.EncodingTypeBorsh, - Procedure: config.ChainReaderProcedure{ - IDLAccount: "DataAccount", - OutputModifications: codec.ModifiersConfig{ - &codec.PropertyExtractorConfig{FieldName: "U64Value"}, - }, + ChainSpecificName: "DataAccount", + OutputModifications: codec.ModifiersConfig{ + &codec.PropertyExtractorConfig{FieldName: "U64Value"}, }, }, }, @@ -199,7 +190,7 @@ func (it *SolanaChainComponentsInterfaceTester[T]) GetContractReader(t T) types. return it.cr } - svc, err := chainreader.NewChainReaderService(it.Helper.Logger(t), it.Helper.RPCClient(), it.chainReaderConfig) + svc, err := chainreader.NewChainReaderService(it.Helper.Logger(t), it.Helper.RPCClient(), it.contractReaderConfig) require.NoError(t, err) require.NoError(t, svc.Start(ctx)) diff --git a/pkg/solana/chainreader/account_read_binding.go b/pkg/solana/chainreader/account_read_binding.go index 4f14c0aa7..c2cfaf6ff 100644 --- a/pkg/solana/chainreader/account_read_binding.go +++ b/pkg/solana/chainreader/account_read_binding.go @@ -7,6 +7,7 @@ import ( "github.com/gagliardetto/solana-go/rpc" "github.com/smartcontractkit/chainlink-common/pkg/types" + "github.com/smartcontractkit/chainlink-solana/pkg/solana/codec" ) diff --git a/pkg/solana/chainreader/chain_reader.go b/pkg/solana/chainreader/chain_reader.go index 9b2c4bf36..9b96ed319 100644 --- a/pkg/solana/chainreader/chain_reader.go +++ b/pkg/solana/chainreader/chain_reader.go @@ -288,11 +288,6 @@ func (s *SolanaChainReaderService) init(namespaces map[string]config.ChainContra return nil } -type accountIDLTypes struct { - Account codec.IdlTypeDef - Types codec.IdlTypeDefSlice -} - func (s *SolanaChainReaderService) addAccountRead(namespace string, itemType string, idl codec.IDL, idlType codec.IdlTypeDef, readDefinition config.ReadDefinition) error { inputAccountIDLDef := codec.NilIdlTypeDefTy // TODO: From c2d68d278b03d0b214c1d380cf47939ba68ddea0 Mon Sep 17 00:00:00 2001 From: ilija Date: Tue, 24 Dec 2024 15:47:17 +0100 Subject: [PATCH 05/11] Improve Contract Reader cfg handling --- pkg/solana/chainreader/chain_reader.go | 16 +- pkg/solana/chainreader/chain_reader_test.go | 24 ++- pkg/solana/config/chain_reader.go | 55 ++++- pkg/solana/config/chain_reader_test.go | 23 +- .../config/testChainReader_invalid.json | 200 ++---------------- pkg/solana/config/testChainReader_valid.json | 82 ++++--- ...tChainReader_valid_with_IDL_as_string.json | 65 ++++++ 7 files changed, 215 insertions(+), 250 deletions(-) create mode 100644 pkg/solana/config/testChainReader_valid_with_IDL_as_string.json diff --git a/pkg/solana/chainreader/chain_reader.go b/pkg/solana/chainreader/chain_reader.go index 9b96ed319..0345ccc9a 100644 --- a/pkg/solana/chainreader/chain_reader.go +++ b/pkg/solana/chainreader/chain_reader.go @@ -2,7 +2,6 @@ package chainreader import ( "context" - "encoding/json" "errors" "fmt" "sync" @@ -251,15 +250,10 @@ func (s *SolanaChainReaderService) addCodecDef(forEncoding bool, namespace, chai } func (s *SolanaChainReaderService) init(namespaces map[string]config.ChainContractReader) error { - for namespace, namespaceReads := range namespaces { - for readName, read := range namespaceReads.Reads { - var idl codec.IDL - if err := json.Unmarshal([]byte(namespaceReads.IDL), &idl); err != nil { - return err - } - + for namespace, nameSpaceDef := range namespaces { + for readName, read := range nameSpaceDef.Reads { injectAddressModifier(read.InputModifications, read.OutputModifications) - idlDef, err := codec.FindDefinitionFromIDL(codec.ChainConfigTypeAccountDef, read.ChainSpecificName, idl) + idlDef, err := codec.FindDefinitionFromIDL(codec.ChainConfigTypeAccountDef, read.ChainSpecificName, nameSpaceDef.IDL) if err != nil { return err } @@ -270,10 +264,10 @@ func (s *SolanaChainReaderService) init(namespaces map[string]config.ChainContra if !isOk { return fmt.Errorf("unexpected type %T from IDL definition for account read: %q, with onchain onChainName: %q, of type: %q", accountIDLDef, readName, read.ChainSpecificName, read.ReadType) } - if err = s.addAccountRead(namespace, readName, idl, accountIDLDef, read); err != nil { + if err = s.addAccountRead(namespace, readName, nameSpaceDef.IDL, accountIDLDef, read); err != nil { return err } - case config.Log: + case config.Event: eventIDlDef, isOk := idlDef.(codec.IdlEvent) if !isOk { return fmt.Errorf("unexpected type %T from IDL definition for log read: %q, with onchain onChainName: %q, of type: %q", eventIDlDef, readName, read.ChainSpecificName, read.ReadType) diff --git a/pkg/solana/chainreader/chain_reader_test.go b/pkg/solana/chainreader/chain_reader_test.go index eeb0289ec..6d04f6d15 100644 --- a/pkg/solana/chainreader/chain_reader_test.go +++ b/pkg/solana/chainreader/chain_reader_test.go @@ -295,7 +295,7 @@ func newTestConfAndCodec(t *testing.T) (types.RemoteCodec, config.ContractReader conf := config.ContractReader{ Namespaces: map[string]config.ChainContractReader{ Namespace: { - IDL: rawIDL, + IDL: mustUnmarshalIDL(t, rawIDL), Reads: map[string]config.ReadDefinition{ NamedMethod: { ChainSpecificName: testutils.TestStructWithNestedStruct, @@ -312,6 +312,16 @@ func newTestConfAndCodec(t *testing.T) (types.RemoteCodec, config.ContractReader return testCodec, conf } +func mustUnmarshalIDL(t *testing.T, rawIDL string) codec.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() + } + + return idl +} + type modifiedStructWithNestedStruct struct { V uint8 InnerStruct testutils.ObjectRef1 @@ -421,7 +431,7 @@ func (r *chainReaderInterfaceTester) Setup(t *testing.T) { r.conf = config.ContractReader{ Namespaces: map[string]config.ChainContractReader{ AnyContractName: { - IDL: fullTestIDL(t), + IDL: mustUnmarshalIDL(t, fullTestIDL(t)), Reads: map[string]config.ReadDefinition{ MethodTakingLatestParamsReturningTestStruct: { ReadType: config.Account, @@ -462,7 +472,7 @@ func (r *chainReaderInterfaceTester) Setup(t *testing.T) { }, }, AnySecondContractName: { - IDL: fmt.Sprintf(baseIDL, uint64BaseTypeIDL, ""), + IDL: mustUnmarshalIDL(t, fmt.Sprintf(baseIDL, uint64BaseTypeIDL, "")), Reads: map[string]config.ReadDefinition{ MethodReturningUint64: { ChainSpecificName: "SimpleUint64Value", @@ -748,13 +758,7 @@ func (r *chainReaderInterfaceTester) MaxWaitTimeForEvents() time.Duration { func makeTestCodec(t *testing.T, rawIDL string) types.RemoteCodec { t.Helper() - 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 := codec.NewIDLAccountCodec(idl, binary.LittleEndian()) + testCodec, err := codec.NewIDLAccountCodec(mustUnmarshalIDL(t, rawIDL), binary.LittleEndian()) if err != nil { t.Logf("failed to create new codec from test IDL: %s", err.Error()) t.FailNow() diff --git a/pkg/solana/config/chain_reader.go b/pkg/solana/config/chain_reader.go index 53ae30fe3..192ce3625 100644 --- a/pkg/solana/config/chain_reader.go +++ b/pkg/solana/config/chain_reader.go @@ -1,22 +1,25 @@ package config import ( + "encoding/json" "fmt" "github.com/gagliardetto/solana-go" "github.com/gagliardetto/solana-go/rpc" commoncodec "github.com/smartcontractkit/chainlink-common/pkg/codec" + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" + "github.com/smartcontractkit/chainlink-solana/pkg/solana/codec" ) type ContractReader struct { - Namespaces map[string]ChainContractReader `json:"namespaces" toml:"namespaces"` + Namespaces map[string]ChainContractReader `json:"namespaces"` } type ChainContractReader struct { - IDL string `json:"anchorIDL" toml:"anchorIDL"` + codec.IDL `json:"anchorIDL"` // Reads key is the off-chain name for this read. - Reads map[string]ReadDefinition + Reads map[string]ReadDefinition `json:"reads"` // TODO ContractPollingFilter same as EVM? } @@ -26,26 +29,21 @@ type ReadDefinition struct { InputModifications commoncodec.ModifiersConfig `json:"inputModifications,omitempty"` OutputModifications commoncodec.ModifiersConfig `json:"outputModifications,omitempty"` RPCOpts *RPCOpts `json:"rpcOpts,omitempty"` - - // TODO EventDefinitions *EventDefinitions similar to EVM? - // TODO Lookup details for PDAs and lookup tables to be merged with CW - //LookupTables *LookupTables - //Accounts *[]Lookup } type ReadType int const ( Account ReadType = iota - Log + Event ) func (r ReadType) String() string { switch r { case Account: return "Account" - case Log: - return "Log" + case Event: + return "Event" default: return fmt.Sprintf("Unknown(%d)", r) } @@ -56,3 +54,38 @@ type RPCOpts struct { Commitment *rpc.CommitmentType `json:"commitment,omitempty"` DataSlice *rpc.DataSlice `json:"dataSlice,omitempty"` } + +func (c *ChainContractReader) UnmarshalJSON(bytes []byte) error { + rawJson := make(map[string]json.RawMessage) + if err := json.Unmarshal(bytes, &rawJson); err != nil { + return err + } + + idlBytes := rawJson["anchorIDL"] + var rawString string + if err := json.Unmarshal(idlBytes, &rawString); err == nil { + if err = json.Unmarshal([]byte(rawString), &c.IDL); err != nil { + return fmt.Errorf("failed to parse anchorIDL string as IDL struct: %w", err) + } + return nil + } + + // If we didn't get a string, attempt to parse directly as an IDL object + if err := json.Unmarshal(idlBytes, &c.IDL); err != nil { + return fmt.Errorf("anchorIDL field is neither a valid JSON string nor a valid IDL object: %w", err) + } + + if len(c.Accounts) == 0 && len(c.Events) == 0 { + return fmt.Errorf("namespace idl must have at least one account or event: %w", commontypes.ErrInvalidConfig) + } + + if err := json.Unmarshal(rawJson["reads"], &c.Reads); err != nil { + return err + } + + if c.Reads == nil || len(c.Reads) == 0 { + return fmt.Errorf("namespace must have at least one read: %w", commontypes.ErrInvalidConfig) + } + + return nil +} diff --git a/pkg/solana/config/chain_reader_test.go b/pkg/solana/config/chain_reader_test.go index 876a0ca6a..48b3abb0d 100644 --- a/pkg/solana/config/chain_reader_test.go +++ b/pkg/solana/config/chain_reader_test.go @@ -12,6 +12,7 @@ import ( codeccommon "github.com/smartcontractkit/chainlink-common/pkg/codec" "github.com/smartcontractkit/chainlink-common/pkg/types" + "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" @@ -20,18 +21,28 @@ import ( //go:embed testChainReader_valid.json var validJSON string +//go:embed testChainReader_valid_with_IDL_as_string.json +var validJSONWithIDLAsString string + //go:embed testChainReader_invalid.json var invalidJSON string func TestChainReaderConfig(t *testing.T) { t.Parallel() - t.Run("valid unmarshal", func(t *testing.T) { + t.Run("valid unmarshal with idl as struct", func(t *testing.T) { t.Parallel() var result config.ContractReader require.NoError(t, json.Unmarshal([]byte(validJSON), &result)) assert.Equal(t, validChainReaderConfig, result) + + }) + + t.Run("valid unmarshal with idl as string", func(t *testing.T) { + var result config.ContractReader + require.NoError(t, json.Unmarshal([]byte(validJSONWithIDLAsString), &result)) + assert.Equal(t, validChainReaderConfig, result) }) t.Run("invalid unmarshal", func(t *testing.T) { @@ -62,9 +73,18 @@ var ( length = uint64(10) ) +var nilIDL = codec.IDL{ + Version: "0.1.0", + Name: "myProgram", + Accounts: codec.IdlTypeDefSlice{ + {Name: "NilType", Type: codec.IdlTypeDefTy{Kind: codec.IdlTypeDefTyKindStruct, Fields: &codec.IdlTypeDefStruct{}}}, + }, +} + var validChainReaderConfig = config.ContractReader{ Namespaces: map[string]config.ChainContractReader{ "Contract": { + IDL: nilIDL, Reads: map[string]config.ReadDefinition{ "Method": { ChainSpecificName: testutils.TestStructWithNestedStruct, @@ -86,6 +106,7 @@ var validChainReaderConfig = config.ContractReader{ }, }, "OtherContract": { + IDL: nilIDL, Reads: map[string]config.ReadDefinition{ "Method": { ChainSpecificName: testutils.TestStructWithNestedStruct, diff --git a/pkg/solana/config/testChainReader_invalid.json b/pkg/solana/config/testChainReader_invalid.json index 0572ee0b4..925f8eba0 100644 --- a/pkg/solana/config/testChainReader_invalid.json +++ b/pkg/solana/config/testChainReader_invalid.json @@ -1,192 +1,16 @@ { - "version": "0.1.0", - "name": "some_test_idl", - "accounts": [ - { - "name": "TestStruct", - "type": { - "kind": "struct", - "fields": [ - { - "name": "field", - "type": { - "option": "i32" - } - }, - { - "name": "differentField", - "type": "string" - }, - { - "name": "bigField", - "type": "i128" - }, - { - "name": "nestedDynamicStruct", - "type": { - "defined": "MidLevelDynamicStruct" - } - }, - { - "name": "nestedStaticStruct", - "type": { - "defined": "MidLevelStaticStruct" - } - }, - { - "name": "oracleID", - "type": "u8" - }, - { - "name": "oracleIDs", - "type": { - "array": [ - "u8", - 32 - ] - } - }, - { - "name": "accountstruct", - "type": { - "defined": "accountstruct" - } - }, - { - "name": "accounts", - "type": { - "vec": "bytes" - } - } - ] + "namespaces": { + "Contract": { + "anchorIDL": { + "version": "0.1.0", + "name": "myProgram" + }, + "OtherContract": { + "anchorIDL": { + "version": "0.1.0", + "name": "myProgram" + } } } - ], - "types": [ - { - "name": "MidLevelDynamicStruct", - "type": { - "kind": "struct", - "fields": [ - { - "name": "fixedBytes", - "type": { - "array": [ - "u8", - 2 - ] - } - }, - { - "name": "inner", - "type": { - "defined": "InnerDynamicTestStruct" - } - } - ] - } - }, - { - "name": "MidLevelStaticStruct", - "type": { - "kind": "struct", - "fields": [ - { - "name": "fixedBytes", - "type": { - "array": [ - "u8", - 2 - ] - } - }, - { - "name": "inner", - "type": { - "defined": "InnerStaticTestStruct" - } - } - ] - } - }, - { - "name": "InnerDynamicTestStruct", - "type": { - "kind": "struct", - "fields": [ - { - "name": "i", - "type": "i32" - }, - { - "name": "s", - "type": "string" - } - ] - } - }, - { - "name": "InnerStaticTestStruct", - "type": { - "kind": "struct", - "fields": [ - { - "name": "i", - "type": "i32" - }, - { - "name": "a", - "type": "bytes" - } - ] - } - }, - { - "name": "accountstruct", - "type": { - "kind": "struct", - "fields": [ - { - "name": "account", - "type": "bytes" - }, - { - "name": "accountstr", - "type": { - "array": [ - "u8", - 32 - ] - } - } - ] - } - }, - { - "name": "SimpleUint64Value", - "type": { - "kind": "struct", - "fields": [ - { - "name": "i", - "type": "u64" - } - ] - } - }, - { - "name": "Uint64Slice", - "type": { - "kind": "struct", - "fields": [ - { - "name": "vals", - "type": { - "vec": "u64" - } - } - ] - } - } - ] + } } \ No newline at end of file diff --git a/pkg/solana/config/testChainReader_valid.json b/pkg/solana/config/testChainReader_valid.json index a690ba7f5..cdd78eb34 100644 --- a/pkg/solana/config/testChainReader_valid.json +++ b/pkg/solana/config/testChainReader_valid.json @@ -1,40 +1,64 @@ { - "namespaces": { - "Contract": { - "anchorIDL": "", - "reads": { - "Method": { - "chainSpecificName": "StructWithNestedStruct", - "readType": 0 - }, - "MethodWithOpts": { - "chainSpecificName": "StructWithNestedStruct", - "readType": 0, - "rpcOpts": { - "encoding": "base64", - "commitment": "finalized", - "dataSlice": { - "offset": 10, - "length": 10 + "namespaces":{ + "Contract":{ + "anchorIDL":{ + "version":"0.1.0", + "name":"myProgram", + "accounts":[ + { + "name":"NilType", + "type":{ + "kind":"struct", + "fields":[ + + ] } - }, - "outputModifications": [ + } + ] + }, + "reads":{ + "Method":{ + "chainSpecificName":"StructWithNestedStruct" + }, + "MethodWithOpts":{ + "chainSpecificName":"StructWithNestedStruct", + "outputModifications":[ { - "fieldName": "DurationVal", - "type": "extract property" + "FieldName":"DurationVal", + "Type":"extract property" } - ] + ], + "rpcOpts":{ + "encoding":"base64", + "commitment":"finalized", + "dataSlice":{ + "offset":10, + "length":10 + } + } } } }, - "OtherContract": { - "anchorIDL": "", - "reads": { - "Method": { - "chainSpecificName": "StructWithNestedStruct", - "readType": 0 + "OtherContract":{ + "anchorIDL":{ + "version":"0.1.0", + "name":"myProgram", + "accounts":[ + { + "name":"NilType", + "type":{ + "kind":"struct", + "fields":[ + ] + } + } + ] + }, + "reads":{ + "Method":{ + "chainSpecificName":"StructWithNestedStruct" } } } } -} +} \ No newline at end of file diff --git a/pkg/solana/config/testChainReader_valid_with_IDL_as_string.json b/pkg/solana/config/testChainReader_valid_with_IDL_as_string.json new file mode 100644 index 000000000..43366e073 --- /dev/null +++ b/pkg/solana/config/testChainReader_valid_with_IDL_as_string.json @@ -0,0 +1,65 @@ +{ + "namespaces":{ + "Contract":{ + "anchorIDL":{ + "version":"0.1.0", + "name":"myProgram", + "accounts":[ + { + "name":"NilType", + "type":{ + "kind":"struct", + "fields":[ + + ] + } + } + ] + }, + "reads":{ + "Method":{ + "chainSpecificName":"StructWithNestedStruct" + }, + "MethodWithOpts":{ + "chainSpecificName":"StructWithNestedStruct", + "outputModifications":[ + { + "FieldName":"DurationVal", + "Type":"extract property" + } + ], + "rpcOpts":{ + "encoding":"base64", + "commitment":"finalized", + "dataSlice":{ + "offset":10, + "length":10 + } + } + } + } + }, + "OtherContract":{ + "anchorIDL":{ + "version":"0.1.0", + "name":"myProgram", + "accounts":[ + { + "name":"NilType", + "type":{ + "kind":"struct", + "fields":[ + + ] + } + } + ] + }, + "reads":{ + "Method":{ + "chainSpecificName":"StructWithNestedStruct" + } + } + } + } +} \ No newline at end of file From 809f1add1dd8ceeb7f628872d2c231a1aef01fea Mon Sep 17 00:00:00 2001 From: ilija Date: Tue, 24 Dec 2024 17:58:57 +0100 Subject: [PATCH 06/11] Standardize var name 4 read mapping to genericName<>chainSpecificName --- .../relayinterface/chain_components_test.go | 38 ++++++++++------ .../chainreader/account_read_binding.go | 24 +++++----- pkg/solana/chainreader/chain_reader.go | 44 +++++++++---------- pkg/solana/chainreader/chain_reader_test.go | 20 ++++----- pkg/solana/chainreader/lookup.go | 38 +++++++--------- pkg/solana/codec/codec_entry.go | 30 ++++++------- pkg/solana/codec/codec_test.go | 2 +- pkg/solana/codec/decoder.go | 4 +- pkg/solana/codec/encoder.go | 4 +- pkg/solana/codec/solana.go | 16 +++---- pkg/solana/codec/types.go | 4 +- 11 files changed, 113 insertions(+), 111 deletions(-) diff --git a/integration-tests/relayinterface/chain_components_test.go b/integration-tests/relayinterface/chain_components_test.go index 95a7ceb24..047706035 100644 --- a/integration-tests/relayinterface/chain_components_test.go +++ b/integration-tests/relayinterface/chain_components_test.go @@ -6,6 +6,7 @@ package relayinterface import ( "context" "encoding/binary" + "encoding/json" "io" "os" "path/filepath" @@ -19,13 +20,14 @@ import ( "github.com/gagliardetto/solana-go/text" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-common/pkg/codec" + commoncodec "github.com/smartcontractkit/chainlink-common/pkg/codec" "github.com/smartcontractkit/chainlink-common/pkg/logger" commontestutils "github.com/smartcontractkit/chainlink-common/pkg/loop/testutils" "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-common/pkg/types/query/primitives" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + "github.com/smartcontractkit/chainlink-solana/pkg/solana/codec" contract "github.com/smartcontractkit/chainlink-solana/contracts/generated/contract_reader_interface" "github.com/smartcontractkit/chainlink-solana/integration-tests/solclient" @@ -105,7 +107,7 @@ func RunChainComponentsInLoopSolanaTests[T TestingT[T]](t T, it ChainComponentsI func RunContractReaderSolanaTests[T TestingT[T]](t T, it *SolanaChainComponentsInterfaceTester[T]) { RunContractReaderInterfaceTests(t, it, false, true) - testCases := []Testcase[T]{} + var testCases []Testcase[T] RunTests(t, it, testCases) } @@ -113,7 +115,7 @@ func RunContractReaderSolanaTests[T TestingT[T]](t T, it *SolanaChainComponentsI func RunContractReaderInLoopTests[T TestingT[T]](t T, it ChainComponentsInterfaceTester[T]) { RunContractReaderInterfaceTests(t, it, false, true) - testCases := []Testcase[T]{} + var testCases []Testcase[T] RunTests(t, it, testCases) } @@ -129,8 +131,8 @@ type SolanaChainComponentsInterfaceTesterHelper[T TestingT[T]] interface { type SolanaChainComponentsInterfaceTester[T TestingT[T]] struct { TestSelectionSupport - Helper SolanaChainComponentsInterfaceTesterHelper[T] - cr *chainreader.SolanaChainReaderService + Helper SolanaChainComponentsInterfaceTesterHelper[T] + cr *chainreader.SolanaChainReaderService contractReaderConfig config.ContractReader } @@ -140,30 +142,30 @@ func (it *SolanaChainComponentsInterfaceTester[T]) Setup(t T) { it.contractReaderConfig = config.ContractReader{ Namespaces: map[string]config.ChainContractReader{ AnyContractName: { - IDL: string(it.Helper.GetJSONEncodedIDL(t)), + IDL: mustUnmarshalIDL(t, string(it.Helper.GetJSONEncodedIDL(t))), Reads: map[string]config.ReadDefinition{ MethodReturningUint64: { ChainSpecificName: "DataAccount", ReadType: config.Account, - OutputModifications: codec.ModifiersConfig{ - &codec.PropertyExtractorConfig{FieldName: "U64Value"}, + OutputModifications: commoncodec.ModifiersConfig{ + &commoncodec.PropertyExtractorConfig{FieldName: "U64Value"}, }, }, MethodReturningUint64Slice: { ChainSpecificName: "DataAccount", - OutputModifications: codec.ModifiersConfig{ - &codec.PropertyExtractorConfig{FieldName: "U64Slice"}, + OutputModifications: commoncodec.ModifiersConfig{ + &commoncodec.PropertyExtractorConfig{FieldName: "U64Slice"}, }, }, }, }, AnySecondContractName: { - IDL: string(it.Helper.GetJSONEncodedIDL(t)), + IDL: mustUnmarshalIDL(t, string(it.Helper.GetJSONEncodedIDL(t))), Reads: map[string]config.ReadDefinition{ MethodReturningUint64: { ChainSpecificName: "DataAccount", - OutputModifications: codec.ModifiersConfig{ - &codec.PropertyExtractorConfig{FieldName: "U64Value"}, + OutputModifications: commoncodec.ModifiersConfig{ + &commoncodec.PropertyExtractorConfig{FieldName: "U64Value"}, }, }, }, @@ -414,3 +416,13 @@ func setupTestValidator(t *testing.T, upgradeAuthority string) (string, string) return client.SetupLocalSolNodeWithFlags(t, flags...) } + +func mustUnmarshalIDL[T TestingT[T]](t T, rawIDL string) codec.IDL { + var idl codec.IDL + if err := json.Unmarshal([]byte(rawIDL), &idl); err != nil { + t.Errorf("failed to unmarshal test IDL", err) + t.FailNow() + } + + return idl +} diff --git a/pkg/solana/chainreader/account_read_binding.go b/pkg/solana/chainreader/account_read_binding.go index c2cfaf6ff..826897369 100644 --- a/pkg/solana/chainreader/account_read_binding.go +++ b/pkg/solana/chainreader/account_read_binding.go @@ -11,21 +11,19 @@ import ( "github.com/smartcontractkit/chainlink-solana/pkg/solana/codec" ) -// accountReadBinding provides decoding and reading Solana Account data using a defined codec. The -// `idlAccount` refers to the account onChainName in the IDL for which the codec has a type mapping. +// accountReadBinding provides decoding and reading Solana Account data using a defined codec. type accountReadBinding struct { - namespace, onChainName, chainAgnosticName string - codec types.RemoteCodec - key solana.PublicKey - opts *rpc.GetAccountInfoOpts + namespace, genericName string + codec types.RemoteCodec + key solana.PublicKey + opts *rpc.GetAccountInfoOpts } -func newAccountReadBinding(namespace, onChainName, chainAgnosticName string, opts *rpc.GetAccountInfoOpts) *accountReadBinding { +func newAccountReadBinding(namespace, genericName string, opts *rpc.GetAccountInfoOpts) *accountReadBinding { return &accountReadBinding{ - namespace: namespace, - onChainName: onChainName, - chainAgnosticName: chainAgnosticName, - opts: opts, + namespace: namespace, + genericName: genericName, + opts: opts, } } @@ -44,9 +42,9 @@ func (b *accountReadBinding) GetAddress() solana.PublicKey { } func (b *accountReadBinding) CreateType(forEncoding bool) (any, error) { - return b.codec.CreateType(codec.WrapItemType(forEncoding, b.namespace, b.chainAgnosticName, codec.ChainConfigTypeAccountDef), forEncoding) + return b.codec.CreateType(codec.WrapItemType(forEncoding, b.namespace, b.genericName, codec.ChainConfigTypeAccountDef), forEncoding) } func (b *accountReadBinding) Decode(ctx context.Context, bts []byte, outVal any) error { - return b.codec.Decode(ctx, bts, outVal, codec.WrapItemType(false, b.namespace, b.chainAgnosticName, codec.ChainConfigTypeAccountDef)) + return b.codec.Decode(ctx, bts, outVal, codec.WrapItemType(false, b.namespace, b.genericName, codec.ChainConfigTypeAccountDef)) } diff --git a/pkg/solana/chainreader/chain_reader.go b/pkg/solana/chainreader/chain_reader.go index 0345ccc9a..aad6bec31 100644 --- a/pkg/solana/chainreader/chain_reader.go +++ b/pkg/solana/chainreader/chain_reader.go @@ -116,15 +116,15 @@ func (s *SolanaChainReaderService) GetLatestValue(ctx context.Context, readIdent s.wg.Add(1) defer s.wg.Done() - vals, ok := s.lookup.getContractForReadIdentifiers(readIdentifier) + values, ok := s.lookup.getContractForReadIdentifiers(readIdentifier) if !ok { return fmt.Errorf("%w: no contract for read identifier %s", types.ErrInvalidType, readIdentifier) } batch := []call{ { - ContractName: vals.contract, - ReadName: vals.name.onChainName, + ContractName: values.contract, + ReadName: values.genericName, Params: params, ReturnVal: returnVal, }, @@ -227,31 +227,31 @@ func (s *SolanaChainReaderService) CreateContractType(readIdentifier string, for return nil, fmt.Errorf("%w: no contract for read identifier", types.ErrInvalidConfig) } - return s.bindings.CreateType(values.contract, values.name.onChainName, forEncoding) + return s.bindings.CreateType(values.contract, values.genericName, forEncoding) } -func (s *SolanaChainReaderService) addCodecDef(forEncoding bool, namespace, chainAgnosticName string, readType codec.ChainConfigType, idl codec.IDL, idlDefinition interface{}, modCfg commoncodec.ModifiersConfig) error { +func (s *SolanaChainReaderService) addCodecDef(forEncoding bool, namespace, genericName string, readType codec.ChainConfigType, idl codec.IDL, idlDefinition interface{}, modCfg commoncodec.ModifiersConfig) error { mod, err := modCfg.ToModifier(codec.DecoderHooks...) if err != nil { return err } - cEntry, err := codec.CreateCodecEntry(idlDefinition, chainAgnosticName, idl, mod) + cEntry, err := codec.CreateCodecEntry(idlDefinition, genericName, idl, mod) if err != nil { return err } if forEncoding { - s.parsed.EncoderDefs[codec.WrapItemType(forEncoding, namespace, chainAgnosticName, readType)] = cEntry + s.parsed.EncoderDefs[codec.WrapItemType(forEncoding, namespace, genericName, readType)] = cEntry } else { - s.parsed.DecoderDefs[codec.WrapItemType(forEncoding, namespace, chainAgnosticName, readType)] = cEntry + s.parsed.DecoderDefs[codec.WrapItemType(forEncoding, namespace, genericName, readType)] = cEntry } return nil } func (s *SolanaChainReaderService) init(namespaces map[string]config.ChainContractReader) error { for namespace, nameSpaceDef := range namespaces { - for readName, read := range nameSpaceDef.Reads { + for genericName, read := range nameSpaceDef.Reads { injectAddressModifier(read.InputModifications, read.OutputModifications) idlDef, err := codec.FindDefinitionFromIDL(codec.ChainConfigTypeAccountDef, read.ChainSpecificName, nameSpaceDef.IDL) if err != nil { @@ -262,19 +262,20 @@ func (s *SolanaChainReaderService) init(namespaces map[string]config.ChainContra case config.Account: accountIDLDef, isOk := idlDef.(codec.IdlTypeDef) if !isOk { - return fmt.Errorf("unexpected type %T from IDL definition for account read: %q, with onchain onChainName: %q, of type: %q", accountIDLDef, readName, read.ChainSpecificName, read.ReadType) + return fmt.Errorf("unexpected type %T from IDL definition for account read: %q, with chainSpecificName: %q, of type: %q", accountIDLDef, genericName, read.ChainSpecificName, read.ReadType) } - if err = s.addAccountRead(namespace, readName, nameSpaceDef.IDL, accountIDLDef, read); err != nil { + if err = s.addAccountRead(namespace, genericName, nameSpaceDef.IDL, accountIDLDef, read); err != nil { return err } case config.Event: eventIDlDef, isOk := idlDef.(codec.IdlEvent) if !isOk { - return fmt.Errorf("unexpected type %T from IDL definition for log read: %q, with onchain onChainName: %q, of type: %q", eventIDlDef, readName, read.ChainSpecificName, read.ReadType) + return fmt.Errorf("unexpected type %T from IDL definition for log read: %q, with chainSpecificName: %q, of type: %q", eventIDlDef, genericName, read.ChainSpecificName, read.ReadType) } // TODO s.addLogRead() + return fmt.Errorf("implement me") default: - return fmt.Errorf("unexpected read type %q for: %q in namespace: %q", read.ReadType, readName, namespace) + return fmt.Errorf("unexpected read type %q for: %q in namespace: %q", read.ReadType, genericName, namespace) } } } @@ -282,30 +283,25 @@ func (s *SolanaChainReaderService) init(namespaces map[string]config.ChainContra return nil } -func (s *SolanaChainReaderService) addAccountRead(namespace string, itemType string, idl codec.IDL, idlType codec.IdlTypeDef, readDefinition config.ReadDefinition) error { +func (s *SolanaChainReaderService) addAccountRead(namespace string, genericName string, idl codec.IDL, idlType codec.IdlTypeDef, readDefinition config.ReadDefinition) error { inputAccountIDLDef := codec.NilIdlTypeDefTy // TODO: // if hasPDA{ // inputAccountIDLDef = pdaType // } - if err := s.addCodecDef(true, namespace, itemType, codec.ChainConfigTypeAccountDef, idl, inputAccountIDLDef, readDefinition.InputModifications); err != nil { + if err := s.addCodecDef(true, namespace, genericName, codec.ChainConfigTypeAccountDef, idl, inputAccountIDLDef, readDefinition.InputModifications); err != nil { return err } - if err := s.addCodecDef(false, namespace, itemType, codec.ChainConfigTypeAccountDef, idl, idlType, readDefinition.OutputModifications); err != nil { + if err := s.addCodecDef(false, namespace, genericName, codec.ChainConfigTypeAccountDef, idl, idlType, readDefinition.OutputModifications); err != nil { return err } - s.lookup.addReadNameForContract(namespace, namePair{ - onChainName: itemType, - genericName: idlType.Name, - }) + s.lookup.addReadNameForContract(namespace, genericName) - s.bindings.AddReadBinding(namespace, itemType, newAccountReadBinding( + s.bindings.AddReadBinding(namespace, genericName, newAccountReadBinding( namespace, - readDefinition.ChainSpecificName, - itemType, - // TODO codec is not created at this point, set codec for all bindings after init + genericName, createRPCOpts(readDefinition.RPCOpts), )) diff --git a/pkg/solana/chainreader/chain_reader_test.go b/pkg/solana/chainreader/chain_reader_test.go index 6d04f6d15..8b24a9c82 100644 --- a/pkg/solana/chainreader/chain_reader_test.go +++ b/pkg/solana/chainreader/chain_reader_test.go @@ -312,16 +312,6 @@ func newTestConfAndCodec(t *testing.T) (types.RemoteCodec, config.ContractReader return testCodec, conf } -func mustUnmarshalIDL(t *testing.T, rawIDL string) codec.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() - } - - return idl -} - type modifiedStructWithNestedStruct struct { V uint8 InnerStruct testutils.ObjectRef1 @@ -939,3 +929,13 @@ func (s *skipEventsChainReader) QueryKey(_ context.Context, _ types.BoundContrac s.t.Skip("QueryKey is not yet supported in Solana") return nil, nil } + +func mustUnmarshalIDL(t *testing.T, rawIDL string) codec.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() + } + + return idl +} diff --git a/pkg/solana/chainreader/lookup.go b/pkg/solana/chainreader/lookup.go index 5c7d06241..ccd7b44db 100644 --- a/pkg/solana/chainreader/lookup.go +++ b/pkg/solana/chainreader/lookup.go @@ -7,59 +7,55 @@ import ( ) type readValues struct { - address string - contract string - name namePair -} - -type namePair struct { - onChainName, genericName string + address string + contract string + genericName string } // lookup provides basic utilities for mapping a complete readIdentifier to // finite contract read information type lookup struct { mu sync.RWMutex - // contractReadNames maps a contract name to all available readNames (method, log, event, etc.) - contractReadNames map[string][]namePair + // contractReadNames maps a contract name to all available namePairs (method, log, event, etc.) + contractReadNames map[string][]string // readIdentifiers maps from a complete readIdentifier string to finite read data - // a readIdentifier is a combination of address, contract, and namePair as a concatenated string + // a readIdentifier is a combination of address, contract, and chainSpecificName as a concatenated string readIdentifiers map[string]readValues } func newLookup() *lookup { return &lookup{ - contractReadNames: make(map[string][]namePair), + contractReadNames: make(map[string][]string), readIdentifiers: make(map[string]readValues), } } -func (l *lookup) addReadNameForContract(contract string, name namePair) { +func (l *lookup) addReadNameForContract(contract string, genericName string) { l.mu.Lock() defer l.mu.Unlock() readNames, exists := l.contractReadNames[contract] if !exists { - readNames = []namePair{} + readNames = []string{} } - l.contractReadNames[contract] = append(readNames, name) + l.contractReadNames[contract] = append(readNames, genericName) } func (l *lookup) bindAddressForContract(contract, address string) { l.mu.Lock() defer l.mu.Unlock() - for _, namePair := range l.contractReadNames[contract] { + for _, genericName := range l.contractReadNames[contract] { readIdentifier := types.BoundContract{ Address: address, Name: contract, - }.ReadIdentifier(namePair.onChainName) + }.ReadIdentifier(genericName) l.readIdentifiers[readIdentifier] = readValues{ - address: address, - contract: contract, - name: namePair, + address: address, + contract: contract, + genericName: genericName, } } } @@ -68,11 +64,11 @@ func (l *lookup) unbindAddressForContract(contract, address string) { l.mu.Lock() defer l.mu.Unlock() - for _, namePair := range l.contractReadNames[contract] { + for _, genericName := range l.contractReadNames[contract] { readIdentifier := types.BoundContract{ Address: address, Name: contract, - }.ReadIdentifier(namePair.onChainName) + }.ReadIdentifier(genericName) delete(l.readIdentifiers, readIdentifier) } diff --git a/pkg/solana/codec/codec_entry.go b/pkg/solana/codec/codec_entry.go index 6342055b4..f22b05984 100644 --- a/pkg/solana/codec/codec_entry.go +++ b/pkg/solana/codec/codec_entry.go @@ -22,11 +22,11 @@ type Entry interface { 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 + genericName string + chainSpecificName 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 @@ -95,19 +95,19 @@ func NewEventArgsEntry(offChainName string, idlTypes EventIDLTypes, includeDiscr } func newEntry( - offchainName, onchainName string, + genericName, chainSpecificName string, typeCodec commonencodings.TypeCodec, includeDiscriminator bool, mod codec.Modifier, ) Entry { return &entry{ - offchainName: offchainName, - onchainName: onchainName, + genericName: genericName, + chainSpecificName: chainSpecificName, reflectType: typeCodec.GetType(), typeCodec: typeCodec, mod: ensureModifier(mod), includeDiscriminator: includeDiscriminator, - discriminator: *NewDiscriminator(onchainName), + discriminator: *NewDiscriminator(chainSpecificName), } } @@ -130,8 +130,8 @@ func (e *entry) 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, e.offchainName, e.onchainName) + return nil, fmt.Errorf("%w: cannot encode nil value for genericName: %q, chainSpecificName: %q", + commontypes.ErrInvalidType, e.genericName, e.chainSpecificName) } encodedVal, err := e.typeCodec.Encode(value, into) @@ -154,13 +154,13 @@ func (e *entry) Encode(value any, into []byte) ([]byte, error) { 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) + return nil, nil, fmt.Errorf("%w: encoded data too short to contain discriminator for genericName: %q, chainSpecificName: %q", + commontypes.ErrInvalidType, e.genericName, e.chainSpecificName) } 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) + return nil, nil, fmt.Errorf("%w: encoded data has a bad discriminator %v for genericName: %q, chainSpecificName: %q", + commontypes.ErrInvalidType, encoded[:discriminatorLength], e.genericName, e.chainSpecificName) } encoded = encoded[discriminatorLength:] diff --git a/pkg/solana/codec/codec_test.go b/pkg/solana/codec/codec_test.go index 689d545ce..50b0366ac 100644 --- a/pkg/solana/codec/codec_test.go +++ b/pkg/solana/codec/codec_test.go @@ -122,7 +122,7 @@ func (it *codecInterfaceTester) GetCodec(t *testing.T) clcommontypes.Codec { codecEntryCfg := codecConfig.Configs[offChainName] codecEntryCfg.IDL = v.IDL codecEntryCfg.Type = v.ItemType - codecEntryCfg.OnChainName = v.IDLTypeName + codecEntryCfg.ChainSpecificName = v.IDLTypeName if offChainName != NilType { codecEntryCfg.ModifierConfigs = commoncodec.ModifiersConfig{ diff --git a/pkg/solana/codec/decoder.go b/pkg/solana/codec/decoder.go index 242dbc44f..e8af04563 100644 --- a/pkg/solana/codec/decoder.go +++ b/pkg/solana/codec/decoder.go @@ -10,14 +10,14 @@ import ( type Decoder struct { definitions map[string]Entry - codecFromTypeCodec encodings.CodecFromTypeCodec + codecFromTypeCodec encodings.LenientCodecFromTypeCodec } 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) + d.codecFromTypeCodec = make(encodings.LenientCodecFromTypeCodec) for k, v := range d.definitions { d.codecFromTypeCodec[k] = v } diff --git a/pkg/solana/codec/encoder.go b/pkg/solana/codec/encoder.go index 409fb0013..a33c5f5dd 100644 --- a/pkg/solana/codec/encoder.go +++ b/pkg/solana/codec/encoder.go @@ -10,14 +10,14 @@ import ( type Encoder struct { definitions map[string]Entry - codecFromTypeCodec encodings.CodecFromTypeCodec + codecFromTypeCodec encodings.LenientCodecFromTypeCodec } 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) + e.codecFromTypeCodec = make(encodings.LenientCodecFromTypeCodec) for k, v := range e.definitions { e.codecFromTypeCodec[k] = v } diff --git a/pkg/solana/codec/solana.go b/pkg/solana/codec/solana.go index 2a730ffa2..372e111aa 100644 --- a/pkg/solana/codec/solana.go +++ b/pkg/solana/codec/solana.go @@ -73,7 +73,7 @@ func NewCodec(conf Config) (commontypes.RemoteCodec, error) { return nil, err } - definition, err := FindDefinitionFromIDL(cfg.Type, cfg.OnChainName, idl) + definition, err := FindDefinitionFromIDL(cfg.Type, cfg.ChainSpecificName, idl) if err != nil { return nil, err } @@ -108,32 +108,32 @@ func CreateCodecEntry(idlDefinition interface{}, offChainName string, idl IDL, m return entry, nil } -func FindDefinitionFromIDL(cfgType ChainConfigType, onChainName string, idl IDL) (interface{}, error) { +func FindDefinitionFromIDL(cfgType ChainConfigType, chainSpecificName string, idl IDL) (interface{}, error) { // not the most efficient way to do this, but these slices should always be very, very small switch cfgType { case ChainConfigTypeAccountDef: for i := range idl.Accounts { - if idl.Accounts[i].Name == onChainName { + if idl.Accounts[i].Name == chainSpecificName { return idl.Accounts[i], nil } } - return nil, fmt.Errorf("failed to find account %q in IDL", onChainName) + return nil, fmt.Errorf("failed to find account %q in IDL", chainSpecificName) case ChainConfigTypeInstructionDef: for i := range idl.Instructions { - if idl.Instructions[i].Name == onChainName { + if idl.Instructions[i].Name == chainSpecificName { return idl.Instructions[i], nil } } - return nil, fmt.Errorf("failed to find instruction %q in IDL", onChainName) + return nil, fmt.Errorf("failed to find instruction %q in IDL", chainSpecificName) case ChainConfigTypeEventDef: for i := range idl.Events { - if idl.Events[i].Name == onChainName { + if idl.Events[i].Name == chainSpecificName { return idl.Events[i], nil } } - return nil, fmt.Errorf("failed to find event %q in IDL", onChainName) + return nil, fmt.Errorf("failed to find event %q in IDL", chainSpecificName) } return nil, fmt.Errorf("unknown type: %q", cfgType) } diff --git a/pkg/solana/codec/types.go b/pkg/solana/codec/types.go index e047b36ae..40852398e 100644 --- a/pkg/solana/codec/types.go +++ b/pkg/solana/codec/types.go @@ -16,8 +16,8 @@ type Config struct { } type ChainConfig struct { - IDL string `json:"typeIdl" toml:"typeIdl"` - OnChainName string `json:"onChainName" toml:"onChainName"` + IDL string `json:"typeIdl" toml:"typeIdl"` + ChainSpecificName string `json:"chainSpecificName" toml:"chainSpecificName"` // Type can be Solana Account, Instruction args, or TODO Event Type ChainConfigType `json:"type" toml:"type"` ModifierConfigs commoncodec.ModifiersConfig `json:"modifierConfigs,omitempty" toml:"modifierConfigs,omitempty"` From f2416c0693cc9c1dc6fd806b61b2a2e76a9a7d5d Mon Sep 17 00:00:00 2001 From: ilija Date: Tue, 24 Dec 2024 18:05:44 +0100 Subject: [PATCH 07/11] Revert Solana codec encoder from LenientCodecFromTypeCodec to strict --- pkg/solana/codec/encoder.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/solana/codec/encoder.go b/pkg/solana/codec/encoder.go index a33c5f5dd..409fb0013 100644 --- a/pkg/solana/codec/encoder.go +++ b/pkg/solana/codec/encoder.go @@ -10,14 +10,14 @@ import ( type Encoder struct { definitions map[string]Entry - codecFromTypeCodec encodings.LenientCodecFromTypeCodec + 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.LenientCodecFromTypeCodec) + e.codecFromTypeCodec = make(encodings.CodecFromTypeCodec) for k, v := range e.definitions { e.codecFromTypeCodec[k] = v } From 7bcaf47fc08b9511c398e2dba4260e60104f9ffe Mon Sep 17 00:00:00 2001 From: ilija Date: Tue, 24 Dec 2024 18:49:15 +0100 Subject: [PATCH 08/11] lint --- .../relayinterface/chain_components_test.go | 1 + pkg/solana/config/chain_reader.go | 11 ++++++----- pkg/solana/config/chain_reader_test.go | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/integration-tests/relayinterface/chain_components_test.go b/integration-tests/relayinterface/chain_components_test.go index 047706035..7512be361 100644 --- a/integration-tests/relayinterface/chain_components_test.go +++ b/integration-tests/relayinterface/chain_components_test.go @@ -27,6 +27,7 @@ import ( . "github.com/smartcontractkit/chainlink-common/pkg/types/interfacetests" //nolint common practice to import test mods with . "github.com/smartcontractkit/chainlink-common/pkg/types/query/primitives" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + "github.com/smartcontractkit/chainlink-solana/pkg/solana/codec" contract "github.com/smartcontractkit/chainlink-solana/contracts/generated/contract_reader_interface" diff --git a/pkg/solana/config/chain_reader.go b/pkg/solana/config/chain_reader.go index 192ce3625..31df7094d 100644 --- a/pkg/solana/config/chain_reader.go +++ b/pkg/solana/config/chain_reader.go @@ -9,6 +9,7 @@ import ( commoncodec "github.com/smartcontractkit/chainlink-common/pkg/codec" commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" + "github.com/smartcontractkit/chainlink-solana/pkg/solana/codec" ) @@ -56,12 +57,12 @@ type RPCOpts struct { } func (c *ChainContractReader) UnmarshalJSON(bytes []byte) error { - rawJson := make(map[string]json.RawMessage) - if err := json.Unmarshal(bytes, &rawJson); err != nil { + rawJSON := make(map[string]json.RawMessage) + if err := json.Unmarshal(bytes, &rawJSON); err != nil { return err } - idlBytes := rawJson["anchorIDL"] + idlBytes := rawJSON["anchorIDL"] var rawString string if err := json.Unmarshal(idlBytes, &rawString); err == nil { if err = json.Unmarshal([]byte(rawString), &c.IDL); err != nil { @@ -79,11 +80,11 @@ func (c *ChainContractReader) UnmarshalJSON(bytes []byte) error { return fmt.Errorf("namespace idl must have at least one account or event: %w", commontypes.ErrInvalidConfig) } - if err := json.Unmarshal(rawJson["reads"], &c.Reads); err != nil { + if err := json.Unmarshal(rawJSON["reads"], &c.Reads); err != nil { return err } - if c.Reads == nil || len(c.Reads) == 0 { + if len(c.Reads) == 0 { return fmt.Errorf("namespace must have at least one read: %w", commontypes.ErrInvalidConfig) } diff --git a/pkg/solana/config/chain_reader_test.go b/pkg/solana/config/chain_reader_test.go index 48b3abb0d..156017e8e 100644 --- a/pkg/solana/config/chain_reader_test.go +++ b/pkg/solana/config/chain_reader_test.go @@ -12,6 +12,7 @@ import ( codeccommon "github.com/smartcontractkit/chainlink-common/pkg/codec" "github.com/smartcontractkit/chainlink-common/pkg/types" + "github.com/smartcontractkit/chainlink-solana/pkg/solana/codec" "github.com/smartcontractkit/chainlink-solana/pkg/solana/codec/testutils" @@ -36,7 +37,6 @@ func TestChainReaderConfig(t *testing.T) { var result config.ContractReader require.NoError(t, json.Unmarshal([]byte(validJSON), &result)) assert.Equal(t, validChainReaderConfig, result) - }) t.Run("valid unmarshal with idl as string", func(t *testing.T) { From 2a74587c793e1555e41240a938ab4fed04d7e71e Mon Sep 17 00:00:00 2001 From: ilija Date: Tue, 24 Dec 2024 19:35:09 +0100 Subject: [PATCH 09/11] Improve panic handling and add a test for lenient decoding --- pkg/solana/codec/decoder.go | 18 ++++++++++++------ pkg/solana/codec/decoder_test.go | 6 +++--- pkg/solana/codec/encoder.go | 18 ++++++++++++------ 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/pkg/solana/codec/decoder.go b/pkg/solana/codec/decoder.go index e8af04563..ec734b36a 100644 --- a/pkg/solana/codec/decoder.go +++ b/pkg/solana/codec/decoder.go @@ -9,21 +9,27 @@ import ( ) type Decoder struct { - definitions map[string]Entry - codecFromTypeCodec encodings.LenientCodecFromTypeCodec + definitions map[string]Entry + lenientFromTypeCodec encodings.LenientCodecFromTypeCodec } 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.LenientCodecFromTypeCodec) + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("recovered from: %v, while decoding %q", r, itemType) + } + }() + + if d.lenientFromTypeCodec == nil { + d.lenientFromTypeCodec = make(encodings.LenientCodecFromTypeCodec) for k, v := range d.definitions { - d.codecFromTypeCodec[k] = v + d.lenientFromTypeCodec[k] = v } } - return d.codecFromTypeCodec.Decode(ctx, raw, into, itemType) + return d.lenientFromTypeCodec.Decode(ctx, raw, into, itemType) } func (d *Decoder) GetMaxDecodingSize(_ context.Context, n int, itemType string) (int, error) { diff --git a/pkg/solana/codec/decoder_test.go b/pkg/solana/codec/decoder_test.go index ceea9644f..0f21c43e8 100644 --- a/pkg/solana/codec/decoder_test.go +++ b/pkg/solana/codec/decoder_test.go @@ -24,7 +24,7 @@ type testErrDecodeRemainingBytes struct { } func (t *testErrDecodeRemainingBytes) Decode(_ []byte) (interface{}, []byte, error) { - return nil, []byte{1}, nil + return struct{}{}, []byte{1}, nil } func TestDecoder_Decode_Errors(t *testing.T) { @@ -45,10 +45,10 @@ func TestDecoder_Decode_Errors(t *testing.T) { require.Error(t, d.Decode(tests.Context(t), []byte{}, &into, someType)) }) - t.Run("error when remaining bytes exist after decode", func(t *testing.T) { + t.Run("remaining bytes exist after decode is ok", func(t *testing.T) { d := &Decoder{definitions: map[string]Entry{}} d.definitions[someType] = &testErrDecodeRemainingBytes{} - require.Error(t, d.Decode(tests.Context(t), []byte{}, &into, someType)) + require.NoError(t, d.Decode(tests.Context(t), []byte{}, &into, someType)) }) } diff --git a/pkg/solana/codec/encoder.go b/pkg/solana/codec/encoder.go index 409fb0013..e3f734768 100644 --- a/pkg/solana/codec/encoder.go +++ b/pkg/solana/codec/encoder.go @@ -9,21 +9,27 @@ import ( ) type Encoder struct { - definitions map[string]Entry - codecFromTypeCodec encodings.CodecFromTypeCodec + definitions map[string]Entry + lenientCodecFromTypeCodec encodings.LenientCodecFromTypeCodec } 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) + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("recovered from: %v, while encoding %q", r, itemType) + } + }() + + if e.lenientCodecFromTypeCodec == nil { + e.lenientCodecFromTypeCodec = make(encodings.LenientCodecFromTypeCodec) for k, v := range e.definitions { - e.codecFromTypeCodec[k] = v + e.lenientCodecFromTypeCodec[k] = v } } - return e.codecFromTypeCodec.Encode(ctx, item, itemType) + return e.lenientCodecFromTypeCodec.Encode(ctx, item, itemType) } func (e *Encoder) GetMaxEncodingSize(_ context.Context, n int, itemType string) (int, error) { From 2bdf245de607aa2f624bcaff7cd0342ef8b2b384 Mon Sep 17 00:00:00 2001 From: ilija Date: Mon, 30 Dec 2024 14:30:17 +0100 Subject: [PATCH 10/11] Remove account info opts from CR --- .../chainreader/account_read_binding.go | 3 +-- pkg/solana/chainreader/chain_reader.go | 21 ------------------- pkg/solana/chainreader/chain_reader_test.go | 14 ------------- pkg/solana/config/chain_reader.go | 10 --------- pkg/solana/config/chain_reader_test.go | 8 ------- pkg/solana/config/testChainReader_valid.json | 10 +-------- ...tChainReader_valid_with_IDL_as_string.json | 10 +-------- 7 files changed, 3 insertions(+), 73 deletions(-) diff --git a/pkg/solana/chainreader/account_read_binding.go b/pkg/solana/chainreader/account_read_binding.go index 826897369..9c028e341 100644 --- a/pkg/solana/chainreader/account_read_binding.go +++ b/pkg/solana/chainreader/account_read_binding.go @@ -19,11 +19,10 @@ type accountReadBinding struct { opts *rpc.GetAccountInfoOpts } -func newAccountReadBinding(namespace, genericName string, opts *rpc.GetAccountInfoOpts) *accountReadBinding { +func newAccountReadBinding(namespace, genericName string) *accountReadBinding { return &accountReadBinding{ namespace: namespace, genericName: genericName, - opts: opts, } } diff --git a/pkg/solana/chainreader/chain_reader.go b/pkg/solana/chainreader/chain_reader.go index aad6bec31..1edcb9b8e 100644 --- a/pkg/solana/chainreader/chain_reader.go +++ b/pkg/solana/chainreader/chain_reader.go @@ -302,7 +302,6 @@ func (s *SolanaChainReaderService) addAccountRead(namespace string, genericName s.bindings.AddReadBinding(namespace, genericName, newAccountReadBinding( namespace, genericName, - createRPCOpts(readDefinition.RPCOpts), )) return nil @@ -326,26 +325,6 @@ func injectAddressModifier(inputModifications, outputModifications commoncodec.M } } -func createRPCOpts(opts *config.RPCOpts) *rpc.GetAccountInfoOpts { - if opts == nil { - return nil - } - - result := &rpc.GetAccountInfoOpts{ - DataSlice: opts.DataSlice, - } - - if opts.Encoding != nil { - result.Encoding = *opts.Encoding - } - - if opts.Commitment != nil { - result.Commitment = *opts.Commitment - } - - return result -} - type accountDataReader struct { client *rpc.Client } diff --git a/pkg/solana/chainreader/chain_reader_test.go b/pkg/solana/chainreader/chain_reader_test.go index 8b24a9c82..de37567b6 100644 --- a/pkg/solana/chainreader/chain_reader_test.go +++ b/pkg/solana/chainreader/chain_reader_test.go @@ -14,7 +14,6 @@ import ( "github.com/gagliardetto/solana-go" ag_solana "github.com/gagliardetto/solana-go" - "github.com/gagliardetto/solana-go/rpc" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -413,11 +412,6 @@ func (r *chainReaderInterfaceTester) Setup(t *testing.T) { r.address[idx] = ag_solana.NewWallet().PublicKey().String() } - encodingBase64 := solana.EncodingBase64 - commitment := rpc.CommitmentConfirmed - offset := uint64(1) - length := uint64(1) - r.conf = config.ContractReader{ Namespaces: map[string]config.ChainContractReader{ AnyContractName: { @@ -426,14 +420,6 @@ func (r *chainReaderInterfaceTester) Setup(t *testing.T) { MethodTakingLatestParamsReturningTestStruct: { ReadType: config.Account, ChainSpecificName: "TestStruct", - RPCOpts: &config.RPCOpts{ - Encoding: &encodingBase64, - Commitment: &commitment, - DataSlice: &rpc.DataSlice{ - Offset: &offset, - Length: &length, - }, - }, }, MethodReturningUint64: { ReadType: config.Account, diff --git a/pkg/solana/config/chain_reader.go b/pkg/solana/config/chain_reader.go index 31df7094d..57ccb9040 100644 --- a/pkg/solana/config/chain_reader.go +++ b/pkg/solana/config/chain_reader.go @@ -4,9 +4,6 @@ import ( "encoding/json" "fmt" - "github.com/gagliardetto/solana-go" - "github.com/gagliardetto/solana-go/rpc" - commoncodec "github.com/smartcontractkit/chainlink-common/pkg/codec" commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" @@ -29,7 +26,6 @@ type ReadDefinition struct { ReadType ReadType `json:"readType,omitempty"` InputModifications commoncodec.ModifiersConfig `json:"inputModifications,omitempty"` OutputModifications commoncodec.ModifiersConfig `json:"outputModifications,omitempty"` - RPCOpts *RPCOpts `json:"rpcOpts,omitempty"` } type ReadType int @@ -50,12 +46,6 @@ func (r ReadType) String() string { } } -type RPCOpts struct { - Encoding *solana.EncodingType `json:"encoding,omitempty"` - Commitment *rpc.CommitmentType `json:"commitment,omitempty"` - DataSlice *rpc.DataSlice `json:"dataSlice,omitempty"` -} - func (c *ChainContractReader) UnmarshalJSON(bytes []byte) error { rawJSON := make(map[string]json.RawMessage) if err := json.Unmarshal(bytes, &rawJSON); err != nil { diff --git a/pkg/solana/config/chain_reader_test.go b/pkg/solana/config/chain_reader_test.go index 156017e8e..5749377d7 100644 --- a/pkg/solana/config/chain_reader_test.go +++ b/pkg/solana/config/chain_reader_test.go @@ -94,14 +94,6 @@ var validChainReaderConfig = config.ContractReader{ OutputModifications: codeccommon.ModifiersConfig{ &codeccommon.PropertyExtractorConfig{FieldName: "DurationVal"}, }, - RPCOpts: &config.RPCOpts{ - Encoding: &encodingBase64, - Commitment: &commitment, - DataSlice: &rpc.DataSlice{ - Offset: &offset, - Length: &length, - }, - }, }, }, }, diff --git a/pkg/solana/config/testChainReader_valid.json b/pkg/solana/config/testChainReader_valid.json index cdd78eb34..a813988c0 100644 --- a/pkg/solana/config/testChainReader_valid.json +++ b/pkg/solana/config/testChainReader_valid.json @@ -27,15 +27,7 @@ "FieldName":"DurationVal", "Type":"extract property" } - ], - "rpcOpts":{ - "encoding":"base64", - "commitment":"finalized", - "dataSlice":{ - "offset":10, - "length":10 - } - } + ] } } }, diff --git a/pkg/solana/config/testChainReader_valid_with_IDL_as_string.json b/pkg/solana/config/testChainReader_valid_with_IDL_as_string.json index 43366e073..700c7b768 100644 --- a/pkg/solana/config/testChainReader_valid_with_IDL_as_string.json +++ b/pkg/solana/config/testChainReader_valid_with_IDL_as_string.json @@ -27,15 +27,7 @@ "FieldName":"DurationVal", "Type":"extract property" } - ], - "rpcOpts":{ - "encoding":"base64", - "commitment":"finalized", - "dataSlice":{ - "offset":10, - "length":10 - } - } + ] } } }, From 1d69bbd1bc890b9d15a03eec9b43981cfc207864 Mon Sep 17 00:00:00 2001 From: ilija Date: Mon, 30 Dec 2024 14:46:55 +0100 Subject: [PATCH 11/11] lint --- pkg/solana/chainreader/account_read_binding.go | 2 -- pkg/solana/config/chain_reader_test.go | 9 --------- 2 files changed, 11 deletions(-) diff --git a/pkg/solana/chainreader/account_read_binding.go b/pkg/solana/chainreader/account_read_binding.go index 9c028e341..eacd45fad 100644 --- a/pkg/solana/chainreader/account_read_binding.go +++ b/pkg/solana/chainreader/account_read_binding.go @@ -4,7 +4,6 @@ import ( "context" "github.com/gagliardetto/solana-go" - "github.com/gagliardetto/solana-go/rpc" "github.com/smartcontractkit/chainlink-common/pkg/types" @@ -16,7 +15,6 @@ type accountReadBinding struct { namespace, genericName string codec types.RemoteCodec key solana.PublicKey - opts *rpc.GetAccountInfoOpts } func newAccountReadBinding(namespace, genericName string) *accountReadBinding { diff --git a/pkg/solana/config/chain_reader_test.go b/pkg/solana/config/chain_reader_test.go index 5749377d7..19bcedbe3 100644 --- a/pkg/solana/config/chain_reader_test.go +++ b/pkg/solana/config/chain_reader_test.go @@ -5,8 +5,6 @@ import ( "encoding/json" "testing" - "github.com/gagliardetto/solana-go" - "github.com/gagliardetto/solana-go/rpc" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -66,13 +64,6 @@ func TestChainReaderConfig(t *testing.T) { }) } -var ( - encodingBase64 = solana.EncodingBase64 - commitment = rpc.CommitmentFinalized - offset = uint64(10) - length = uint64(10) -) - var nilIDL = codec.IDL{ Version: "0.1.0", Name: "myProgram",