Skip to content

Commit

Permalink
Standardize var name 4 read mapping to genericName<>chainSpecificName
Browse files Browse the repository at this point in the history
  • Loading branch information
ilija42 committed Dec 24, 2024
1 parent c2d68d2 commit 809f1ad
Show file tree
Hide file tree
Showing 11 changed files with 113 additions and 111 deletions.
38 changes: 25 additions & 13 deletions integration-tests/relayinterface/chain_components_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package relayinterface
import (
"context"
"encoding/binary"
"encoding/json"
"io"
"os"
"path/filepath"
Expand All @@ -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"
Expand Down Expand Up @@ -105,15 +107,15 @@ 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)
}

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)
}
Expand All @@ -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
}

Expand All @@ -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"},
},
},
},
Expand Down Expand Up @@ -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
}
24 changes: 11 additions & 13 deletions pkg/solana/chainreader/account_read_binding.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
}

Expand All @@ -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))
}
44 changes: 20 additions & 24 deletions pkg/solana/chainreader/chain_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
Expand Down Expand Up @@ -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 {
Expand All @@ -262,50 +262,46 @@ 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)
}
}
}

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),
))

Expand Down
20 changes: 10 additions & 10 deletions pkg/solana/chainreader/chain_reader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}
38 changes: 17 additions & 21 deletions pkg/solana/chainreader/lookup.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
}
}
Expand All @@ -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)
}
Expand Down
Loading

0 comments on commit 809f1ad

Please sign in to comment.