Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Address sharing #1069

Merged
merged 2 commits into from
Feb 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 68 additions & 25 deletions integration-tests/relayinterface/chain_components_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,9 +198,10 @@ func RunContractReaderTests[T WrappedTestingT[T]](t T, it *SolanaChainComponents

// GetLatestValue method
const (
ContractReaderGetLatestValueUsingMultiReader = "Get latest value using multi reader"
ContractReaderGetLatestValueUsingMultiReaderWithParmsReuse = "Get latest value using multi reader with params reuse"
ContractReaderGetLatestValueGetTokenPrices = "Get latest value handles get token prices edge case"
ContractReaderGetLatestValueUsingMultiReader = "Get latest value using multi reader"
ContractReaderGetLatestValueWithAddressHardcodedIntoResponse = "Get latest value with AddressHardcoded into response"
ContractReaderGetLatestValueUsingMultiReaderWithParmsReuse = "Get latest value using multi reader with params reuse"
ContractReaderGetLatestValueGetTokenPrices = "Get latest value handles get token prices edge case"
)

type TimestampedUnixBig struct {
Expand All @@ -211,6 +212,33 @@ type TimestampedUnixBig struct {
func RunContractReaderInLoopTests[T WrappedTestingT[T]](t T, it ChainComponentsInterfaceTester[T]) {
//RunContractReaderInterfaceTests(t, it, false, true)
testCases := []Testcase[T]{
{
Name: ContractReaderGetLatestValueWithAddressHardcodedIntoResponse,
Test: func(t T) {
cr := it.GetContractReader(t)
bindings := it.GetBindings(t)
ctx := tests.Context(t)

bound := BindingsByName(bindings, AnyContractName)[0]
require.NoError(t, cr.Bind(ctx, bindings))

boundAddress, err := solana.PublicKeyFromBase58(bound.Address)
require.NoError(t, err)

type MultiReadResult struct {
A uint8
B int16
SharedAddress []byte
AddressToShare []byte
}

mRR := MultiReadResult{}
require.NoError(t, cr.GetLatestValue(ctx, bound.ReadIdentifier(ReadWithAddressHardCodedIntoResponse), primitives.Unconfirmed, nil, &mRR))

expectedMRR := MultiReadResult{A: 1, B: 2, SharedAddress: boundAddress.Bytes(), AddressToShare: boundAddress.Bytes()}
require.Equal(t, expectedMRR, mRR)
},
},
{
Name: ContractReaderGetLatestValueUsingMultiReader,
Test: func(t T) {
Expand Down Expand Up @@ -603,9 +631,10 @@ func (h *helper) runInitialize(
}

const (
MultiRead = "MultiRead"
MultiReadWithParamsReuse = "MultiReadWithParamsReuse"
GetTokenPrices = "GetTokenPrices"
MultiRead = "MultiRead"
ReadWithAddressHardCodedIntoResponse = "ReadWithAddressHardCodedIntoResponse"
MultiReadWithParamsReuse = "MultiReadWithParamsReuse"
GetTokenPrices = "GetTokenPrices"
)

func (it *SolanaChainComponentsInterfaceTester[T]) buildContractReaderConfig(t T) config.ContractReader {
Expand All @@ -631,11 +660,43 @@ func (it *SolanaChainComponentsInterfaceTester[T]) buildContractReaderConfig(t T
MethodReturningUint64: uint64ReadDef,
},
}

readWithAddressHardCodedIntoResponseDef := config.ReadDefinition{
ChainSpecificName: "MultiRead1",
ReadType: config.Account,
PDADefinition: codec.PDATypeDef{
Prefix: []byte("multi_read1"),
},
ResponseAddressHardCoder: &commoncodec.HardCodeModifierConfig{
// placeholder values, whatever is put as value gets replaced with a solana pub key anyway
OffChainValues: map[string]any{
"SharedAddress": solana.PublicKey{},
"AddressToShare": solana.PublicKey{},
},
},
OutputModifications: commoncodec.ModifiersConfig{
&commoncodec.HardCodeModifierConfig{
OffChainValues: map[string]any{"U": "", "V": false},
},
},
}

multiReadDef := readWithAddressHardCodedIntoResponseDef
multiReadDef.ResponseAddressHardCoder = nil
multiReadDef.MultiReader = &config.MultiReader{
Reads: []config.ReadDefinition{{
ChainSpecificName: "MultiRead2",
PDADefinition: codec.PDATypeDef{Prefix: []byte("multi_read2")},
ReadType: config.Account,
}},
}

return config.ContractReader{
Namespaces: map[string]config.ChainContractReader{
AnyContractName: {
IDL: mustUnmarshalIDL(t, string(it.Helper.GetPrimaryIDL(t))),
Reads: map[string]config.ReadDefinition{
ReadWithAddressHardCodedIntoResponse: readWithAddressHardCodedIntoResponseDef,
GetTokenPrices: {
ChainSpecificName: "BillingTokenConfigWrapper",
PDADefinition: codec.PDATypeDef{
Expand Down Expand Up @@ -664,25 +725,7 @@ func (it *SolanaChainComponentsInterfaceTester[T]) buildContractReaderConfig(t T
},
ReadType: config.Account,
},
MultiRead: {
ChainSpecificName: "MultiRead1",
PDADefinition: codec.PDATypeDef{
Prefix: []byte("multi_read1"),
},
OutputModifications: commoncodec.ModifiersConfig{
&commoncodec.HardCodeModifierConfig{
OffChainValues: map[string]any{"U": "", "V": false},
},
},
MultiReader: &config.MultiReader{Reads: []config.ReadDefinition{
{
ChainSpecificName: "MultiRead2",
PDADefinition: codec.PDATypeDef{Prefix: []byte("multi_read2")},
ReadType: config.Account,
},
}},
ReadType: config.Account,
},
MultiRead: multiReadDef,
MultiReadWithParamsReuse: {
ChainSpecificName: "MultiRead3",
PDADefinition: codec.PDATypeDef{
Expand Down
82 changes: 58 additions & 24 deletions pkg/solana/chainreader/account_read_binding.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,38 +11,46 @@ import (
"github.com/smartcontractkit/chainlink-common/pkg/types"
"github.com/smartcontractkit/chainlink-common/pkg/types/query"

"github.com/smartcontractkit/chainlink-solana/pkg/solana/config"

"github.com/smartcontractkit/chainlink-solana/pkg/solana/codec"
)

// accountReadBinding provides decoding and reading Solana Account data using a defined codec.
type accountReadBinding struct {
namespace, genericName string
codec types.RemoteCodec
key solana.PublicKey
isPda bool // flag to signify whether or not the account read is for a PDA
prefix []byte // only used for PDA public key calculation
namespace, genericName string
codec types.RemoteCodec
key solana.PublicKey
isPda bool // flag to signify whether or not the account read is for a PDA
prefix []byte // only used for PDA public key calculation
responseAddressHardCoder *commoncodec.HardCodeModifierConfig
readDefinition config.ReadDefinition
idl codec.IDL
inputIDLType interface{}
outputIDLTypeDef codec.IdlTypeDef
}

func newAccountReadBinding(namespace, genericName string, prefix []byte, isPda bool) *accountReadBinding {
return &accountReadBinding{
namespace: namespace,
genericName: genericName,
prefix: prefix,
isPda: isPda,
func newAccountReadBinding(namespace, genericName string, isPda bool, idl codec.IDL, inputIDLType interface{}, outputIDLTypeDef codec.IdlTypeDef, readDefinition config.ReadDefinition) *accountReadBinding {
rb := &accountReadBinding{
namespace: namespace,
genericName: genericName,
prefix: readDefinition.PDADefinition.Prefix,
isPda: isPda,
readDefinition: readDefinition,
idl: idl,
inputIDLType: inputIDLType,
outputIDLTypeDef: outputIDLTypeDef,
responseAddressHardCoder: nil,
}
}

var _ readBinding = &accountReadBinding{}
if readDefinition.ResponseAddressHardCoder != nil {
rb.responseAddressHardCoder = readDefinition.ResponseAddressHardCoder
}

func (b *accountReadBinding) SetCodec(codec types.RemoteCodec) {
b.codec = codec
return rb
}

func (b *accountReadBinding) SetModifier(commoncodec.Modifier) {}

func (b *accountReadBinding) SetAddress(key solana.PublicKey) {
b.key = key
}
var _ readBinding = &accountReadBinding{}

func (b *accountReadBinding) GetAddress(ctx context.Context, params any) (solana.PublicKey, error) {
// Return the bound key if normal account read
Expand All @@ -61,6 +69,32 @@ func (b *accountReadBinding) GetAddress(ctx context.Context, params any) (solana
return key, nil
}

func (b *accountReadBinding) GetGenericName() string {
return b.genericName
}

func (b *accountReadBinding) GetReadDefinition() config.ReadDefinition {
return b.readDefinition
}

func (b *accountReadBinding) GetIDLInfo() (idl codec.IDL, inputIDLTypeDef interface{}, outputIDLTypeDef codec.IdlTypeDef) {
return b.idl, b.inputIDLType, b.outputIDLTypeDef
}

func (b *accountReadBinding) GetAddressResponseHardCoder() *commoncodec.HardCodeModifierConfig {
return b.responseAddressHardCoder
}

func (b *accountReadBinding) SetAddress(key solana.PublicKey) {
b.key = key
}

func (b *accountReadBinding) SetCodec(codec types.RemoteCodec) {
b.codec = codec
}

func (b *accountReadBinding) SetModifier(commoncodec.Modifier) {}

func (b *accountReadBinding) CreateType(forEncoding bool) (any, error) {
return b.codec.CreateType(codec.WrapItemType(forEncoding, b.namespace, b.genericName), forEncoding)
}
Expand All @@ -69,6 +103,10 @@ func (b *accountReadBinding) Decode(ctx context.Context, bts []byte, outVal any)
return b.codec.Decode(ctx, bts, outVal, codec.WrapItemType(false, b.namespace, b.genericName))
}

func (b *accountReadBinding) QueryKey(_ context.Context, _ query.KeyFilter, _ query.LimitAndSort, _ any) ([]types.Sequence, error) {
return nil, errors.New("unimplemented")
}

// buildSeedsSlice encodes and builds the seedslist to calculate the PDA public key
func (b *accountReadBinding) buildSeedsSlice(ctx context.Context, params any) ([][]byte, error) {
flattenedSeeds := make([]byte, 0, solana.MaxSeeds*solana.MaxSeedLength)
Expand Down Expand Up @@ -104,7 +142,3 @@ func (b *accountReadBinding) buildSeedsSlice(ctx context.Context, params any) ([
}
return seedByteArray, nil
}

func (b *accountReadBinding) QueryKey(_ context.Context, _ query.KeyFilter, _ query.LimitAndSort, _ any) ([]types.Sequence, error) {
return nil, errors.New("unimplemented")
}
29 changes: 22 additions & 7 deletions pkg/solana/chainreader/bindings.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,18 @@ import (
commoncodec "github.com/smartcontractkit/chainlink-common/pkg/codec"
"github.com/smartcontractkit/chainlink-common/pkg/types"
"github.com/smartcontractkit/chainlink-common/pkg/types/query"

"github.com/smartcontractkit/chainlink-solana/pkg/solana/codec"
"github.com/smartcontractkit/chainlink-solana/pkg/solana/config"
)

type readBinding interface {
SetAddress(solana.PublicKey)
GetAddress(context.Context, any) (solana.PublicKey, error)
GetGenericName() string
GetReadDefinition() config.ReadDefinition
GetIDLInfo() (idl codec.IDL, inputIDLTypeDef interface{}, outputIDLTypeDef codec.IdlTypeDef)
GetAddressResponseHardCoder() *commoncodec.HardCodeModifierConfig
SetAddress(solana.PublicKey)
SetCodec(types.RemoteCodec)
SetModifier(commoncodec.Modifier)
CreateType(bool) (any, error)
Expand Down Expand Up @@ -47,9 +54,9 @@ func (b *bindingsRegistry) AddReadBinding(namespace, readName string, rBinding r
}

func (b *bindingsRegistry) GetReadBinding(namespace, readName string) (readBinding, error) {
rBindings, nameSpaceExists := b.namespaceBindings[namespace]
if !nameSpaceExists {
return nil, fmt.Errorf("%w: no read binding exists for namespace: %q", types.ErrInvalidConfig, namespace)
rBindings, err := b.GetReadBindings(namespace)
if err != nil {
return nil, err
}

rBinding, rBindingExists := rBindings[readName]
Expand All @@ -60,6 +67,14 @@ func (b *bindingsRegistry) GetReadBinding(namespace, readName string) (readBindi
return rBinding, nil
}

func (b *bindingsRegistry) GetReadBindings(namespace string) (readNameBindings, error) {
rBindings, nameSpaceExists := b.namespaceBindings[namespace]
if !nameSpaceExists {
return nil, fmt.Errorf("%w: no read binding exists for namespace: %q", types.ErrInvalidConfig, namespace)
}
return rBindings, nil
}

func (b *bindingsRegistry) CreateType(namespace, readName string, forEncoding bool) (any, error) {
rBinding, err := b.GetReadBinding(namespace, readName)
if err != nil {
Expand Down Expand Up @@ -112,7 +127,7 @@ func (b *bindingsRegistry) SetModifiers(modifier commoncodec.Modifier) {
}

func (b *bindingsRegistry) handleAddressSharing(boundContract *types.BoundContract) error {
shareGroup, isInAGroup := b.getShareGroup(*boundContract)
shareGroup, isInAGroup := b.getShareGroup(boundContract.Name)
if !isInAGroup {
return nil
}
Expand All @@ -135,8 +150,8 @@ func (b *bindingsRegistry) handleAddressSharing(boundContract *types.BoundContra
return nil
}

func (b *bindingsRegistry) getShareGroup(boundContract types.BoundContract) (*addressShareGroup, bool) {
shareGroup, sharesAddress := b.addressShareGroups[boundContract.Name]
func (b *bindingsRegistry) getShareGroup(nameSpace string) (*addressShareGroup, bool) {
shareGroup, sharesAddress := b.addressShareGroups[nameSpace]
if !sharesAddress {
return nil, false
}
Expand Down
27 changes: 23 additions & 4 deletions pkg/solana/chainreader/bindings_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ import (
commoncodec "github.com/smartcontractkit/chainlink-common/pkg/codec"
"github.com/smartcontractkit/chainlink-common/pkg/types"
"github.com/smartcontractkit/chainlink-common/pkg/types/query"

"github.com/smartcontractkit/chainlink-solana/pkg/solana/codec"
"github.com/smartcontractkit/chainlink-solana/pkg/solana/config"
)

func TestBindings_CreateType(t *testing.T) {
Expand Down Expand Up @@ -48,14 +51,30 @@ type mockBinding struct {
mock.Mock
}

func (_m *mockBinding) SetCodec(_ types.RemoteCodec) {}

func (_m *mockBinding) SetAddress(_ solana.PublicKey) {}

func (_m *mockBinding) GetAddress(_ context.Context, _ any) (solana.PublicKey, error) {
return solana.PublicKey{}, nil
}

func (_m *mockBinding) GetGenericName() string {
return ""
}

func (_m *mockBinding) GetReadDefinition() config.ReadDefinition {
return config.ReadDefinition{}
}

func (_m *mockBinding) GetIDLInfo() (idl codec.IDL, inputIDLTypeDef interface{}, outputIDLTypeDef codec.IdlTypeDef) {
return codec.IDL{}, codec.IdlTypeDef{}, codec.IdlTypeDef{}
}

func (_m *mockBinding) GetAddressResponseHardCoder() *commoncodec.HardCodeModifierConfig {
return &commoncodec.HardCodeModifierConfig{}
}

func (_m *mockBinding) SetAddress(_ solana.PublicKey) {}

func (_m *mockBinding) SetCodec(_ types.RemoteCodec) {}

func (_m *mockBinding) SetModifier(a commoncodec.Modifier) {
_m.Called(a)
}
Expand Down
Loading
Loading