Skip to content

Commit

Permalink
Fix most of the interface tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ilija42 committed Dec 23, 2024
1 parent fba2341 commit 22a3c5f
Show file tree
Hide file tree
Showing 8 changed files with 271 additions and 96 deletions.
30 changes: 18 additions & 12 deletions pkg/solana/chainreader/account_read_binding.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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))
}
9 changes: 9 additions & 0 deletions pkg/solana/chainreader/bindings.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -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)
}
}
}
2 changes: 2 additions & 0 deletions pkg/solana/chainreader/bindings_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
58 changes: 30 additions & 28 deletions pkg/solana/chainreader/chain_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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,
},
Expand Down Expand Up @@ -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
}

Expand All @@ -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:
Expand All @@ -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) {
Expand Down
12 changes: 5 additions & 7 deletions pkg/solana/chainreader/chain_reader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"},
Expand All @@ -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"},
},
Expand Down Expand Up @@ -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}, ","),
)
}

Expand Down
28 changes: 16 additions & 12 deletions pkg/solana/chainreader/lookup.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,53 +9,57 @@ 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
// 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][]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,
}
}
}
Expand All @@ -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)
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/solana/codec/solana.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down
Loading

0 comments on commit 22a3c5f

Please sign in to comment.