Skip to content

Commit

Permalink
Merge branch 'develop' into NONEVM-1064/querykey-integration-tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ilija42 authored Feb 20, 2025
2 parents 77708c9 + 3333854 commit 382b20d
Show file tree
Hide file tree
Showing 12 changed files with 336 additions and 155 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ require (
github.com/hashicorp/go-plugin v1.6.2
github.com/jackc/pgx/v4 v4.18.3
github.com/lib/pq v1.10.9
github.com/mitchellh/mapstructure v1.5.0
github.com/pelletier/go-toml/v2 v2.2.3
github.com/prometheus/client_golang v1.20.5
github.com/smartcontractkit/chainlink-ccip v0.0.0-20250203132120-f0d42463e405
Expand Down Expand Up @@ -99,7 +100,6 @@ require (
github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect
github.com/miekg/dns v1.1.61 // indirect
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/moby/sys/sequential v0.6.0 // indirect
github.com/moby/sys/user v0.3.0 // indirect
github.com/moby/sys/userns v0.1.0 // indirect
Expand Down
217 changes: 148 additions & 69 deletions integration-tests/relayinterface/chain_components_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,79 +113,113 @@ func DisableTests(it *SolanaChainComponentsInterfaceTester[*testing.T]) {
}

func RunChainComponentsSolanaTests[T WrappedTestingT[T]](t T, it *SolanaChainComponentsInterfaceTester[T]) {
testCases := Testcase[T]{
Name: "Test address groups where first namespace shares address with second namespace",
Test: func(t T) {
ctx := tests.Context(t)
cfg := it.buildContractReaderConfig(t)
cfg.AddressShareGroups = [][]string{{AnyContractNameWithSharedAddress1, AnyContractNameWithSharedAddress2, AnyContractNameWithSharedAddress3}}
cr := it.GetContractReaderWithCustomCfg(t, cfg)

t.Run("Namespace is part of an address share group that doesn't have a registered address and provides no address during Bind", func(t T) {
bound1 := []types.BoundContract{{
Name: AnyContractNameWithSharedAddress1,
}}
require.Error(t, cr.Bind(ctx, bound1))
})

addressToBeShared := it.Helper.CreateAccount(t, *it, AnyContractName, AnyValueToReadWithoutAnArgument, CreateTestStruct(0, it)).String()
t.Run("Namespace is part of an address share group that doesn't have a registered address and provides an address during Bind", func(t T) {
bound1 := []types.BoundContract{{Name: AnyContractNameWithSharedAddress1, Address: addressToBeShared}}

require.NoError(t, cr.Bind(ctx, bound1))
testCases := []Testcase[T]{
{
Name: "Test address groups where first namespace shares address with second namespace",
Test: func(t T) {
ctx := tests.Context(t)
cfg := it.buildContractReaderConfig(t)
cfg.AddressShareGroups = [][]string{{AnyContractNameWithSharedAddress1, AnyContractNameWithSharedAddress2, AnyContractNameWithSharedAddress3}}
cr := it.GetContractReaderWithCustomCfg(t, cfg)

t.Run("Namespace is part of an address share group that doesn't have a registered address and provides no address during Bind", func(t T) {
bound1 := []types.BoundContract{{
Name: AnyContractNameWithSharedAddress1,
}}
require.Error(t, cr.Bind(ctx, bound1))
})

addressToBeShared := it.Helper.CreateAccount(t, *it, AnyContractName, AnyValueToReadWithoutAnArgument, CreateTestStruct(0, it)).String()
t.Run("Namespace is part of an address share group that doesn't have a registered address and provides an address during Bind", func(t T) {
bound1 := []types.BoundContract{{Name: AnyContractNameWithSharedAddress1, Address: addressToBeShared}}

require.NoError(t, cr.Bind(ctx, bound1))

var prim uint64
require.NoError(t, cr.GetLatestValue(ctx, bound1[0].ReadIdentifier(MethodReturningUint64), primitives.Unconfirmed, nil, &prim))
assert.Equal(t, AnyValueToReadWithoutAnArgument, prim)
})

t.Run("Namespace is part of an address share group that has a registered address and provides that same address during Bind", func(t T) {
bound2 := []types.BoundContract{{
Name: AnyContractNameWithSharedAddress2,
Address: addressToBeShared}}
require.NoError(t, cr.Bind(ctx, bound2))

var prim uint64
require.NoError(t, cr.GetLatestValue(ctx, bound2[0].ReadIdentifier(MethodReturningUint64), primitives.Unconfirmed, nil, &prim))
assert.Equal(t, AnyValueToReadWithoutAnArgument, prim)
assert.Equal(t, addressToBeShared, bound2[0].Address)
})

t.Run("Namespace is part of an address share group that has a registered address and provides a wrong address during Bind", func(t T) {
key, err := solana.NewRandomPrivateKey()
require.NoError(t, err)

bound2 := []types.BoundContract{{
Name: AnyContractNameWithSharedAddress2,
Address: key.PublicKey().String()}}
require.Error(t, cr.Bind(ctx, bound2))
})

t.Run("Namespace is part of an address share group that has a registered address and provides no address during Bind", func(t T) {
bound3 := []types.BoundContract{{Name: AnyContractNameWithSharedAddress3}}
require.NoError(t, cr.Bind(ctx, bound3))

var prim uint64
require.NoError(t, cr.GetLatestValue(ctx, bound3[0].ReadIdentifier(MethodReturningUint64), primitives.Unconfirmed, nil, &prim))
assert.Equal(t, AnyValueToReadWithoutAnArgument, prim)
assert.Equal(t, addressToBeShared, bound3[0].Address)

// when run in a loop Bind address won't be set, so check if CR Method works without set address.
prim = 0
require.NoError(t, cr.GetLatestValue(ctx, types.BoundContract{
Address: "",
Name: AnyContractNameWithSharedAddress3,
}.ReadIdentifier(MethodReturningUint64), primitives.Unconfirmed, nil, &prim))
assert.Equal(t, AnyValueToReadWithoutAnArgument, prim)
})

t.Run("Namespace is not part of an address share group that has a registered address and provides no address during Bind", func(t T) {
require.Error(t, cr.Bind(ctx, []types.BoundContract{{Name: AnyContractName}}))
})
},
},

var prim uint64
require.NoError(t, cr.GetLatestValue(ctx, bound1[0].ReadIdentifier(MethodReturningUint64), primitives.Unconfirmed, nil, &prim))
assert.Equal(t, AnyValueToReadWithoutAnArgument, prim)
})
{Name: ContractReaderGetLatestValueGetTokenPrices,
Test: func(t T) {
cr := it.GetContractReader(t)
bindings := it.GetBindings(t)
ctx := tests.Context(t)

t.Run("Namespace is part of an address share group that has a registered address and provides that same address during Bind", func(t T) {
bound2 := []types.BoundContract{{
Name: AnyContractNameWithSharedAddress2,
Address: addressToBeShared}}
require.NoError(t, cr.Bind(ctx, bound2))
bound := BindingsByName(bindings, AnyContractName)[0]

var prim uint64
require.NoError(t, cr.GetLatestValue(ctx, bound2[0].ReadIdentifier(MethodReturningUint64), primitives.Unconfirmed, nil, &prim))
assert.Equal(t, AnyValueToReadWithoutAnArgument, prim)
assert.Equal(t, addressToBeShared, bound2[0].Address)
})
require.NoError(t, cr.Bind(ctx, bindings))

t.Run("Namespace is part of an address share group that has a registered address and provides a wrong address during Bind", func(t T) {
key, err := solana.NewRandomPrivateKey()
require.NoError(t, err)
type TimestampedUnixBig struct {
Value *big.Int `json:"value"`
Timestamp uint32 `json:"timestamp"`
}

bound2 := []types.BoundContract{{
Name: AnyContractNameWithSharedAddress2,
Address: key.PublicKey().String()}}
require.Error(t, cr.Bind(ctx, bound2))
})
res := make([]TimestampedUnixBig, 2)

t.Run("Namespace is part of an address share group that has a registered address and provides no address during Bind", func(t T) {
bound3 := []types.BoundContract{{Name: AnyContractNameWithSharedAddress3}}
require.NoError(t, cr.Bind(ctx, bound3))
byteTokens := make([][]byte, 0, 2)
pubKey1, err := solana.PublicKeyFromBase58(GetTokenPricesPubKey1)
require.NoError(t, err)
pubKey2, err := solana.PublicKeyFromBase58(GetTokenPricesPubKey2)
require.NoError(t, err)

var prim uint64
require.NoError(t, cr.GetLatestValue(ctx, bound3[0].ReadIdentifier(MethodReturningUint64), primitives.Unconfirmed, nil, &prim))
assert.Equal(t, AnyValueToReadWithoutAnArgument, prim)
assert.Equal(t, addressToBeShared, bound3[0].Address)

// when run in a loop Bind address won't be set, so check if CR Method works without set address.
prim = 0
require.NoError(t, cr.GetLatestValue(ctx, types.BoundContract{
Address: "",
Name: AnyContractNameWithSharedAddress3,
}.ReadIdentifier(MethodReturningUint64), primitives.Unconfirmed, nil, &prim))
assert.Equal(t, AnyValueToReadWithoutAnArgument, prim)
})

t.Run("Namespace is not part of an address share group that has a registered address and provides no address during Bind", func(t T) {
require.Error(t, cr.Bind(ctx, []types.BoundContract{{Name: AnyContractName}}))
})
},
byteTokens = append(byteTokens, pubKey1.Bytes())
byteTokens = append(byteTokens, pubKey2.Bytes())
require.NoError(t, cr.GetLatestValue(ctx, bound.ReadIdentifier(GetTokenPrices), primitives.Unconfirmed, map[string]any{"tokens": byteTokens}, &res))
require.Equal(t, "7048352069843304521481572571769838000081483315549204879493368331", res[0].Value.String())
require.Equal(t, uint32(1700000001), res[0].Timestamp)
require.Equal(t, "17980346130170174053328187512531209543631592085982266692926093439168", res[1].Value.String())
require.Equal(t, uint32(1800000002), res[1].Timestamp)
}},
}

RunTests(t, it, []Testcase[T]{testCases})
RunTests(t, it, testCases)
RunContractReaderTests(t, it)
RunChainWriterTests(t, it)
}
Expand Down Expand Up @@ -261,6 +295,7 @@ func RunChainWriterTests[T WrappedTestingT[T]](t T, it *SolanaChainComponentsInt

// GetLatestValue method
const (
ContractReaderNotFoundReadsReturnZeroedResponses = "Get latest value not found reads return zeroed responses"
ContractReaderGetLatestValueUsingMultiReader = "Get latest value using multi reader"
ContractReaderBatchGetLatestValueUsingMultiReader = "Batch Get latest value using multi reader"
ContractReaderGetLatestValueWithAddressHardcodedIntoResponse = "Get latest value with AddressHardcoded into response"
Expand All @@ -269,14 +304,50 @@ const (
ChainWriterLookupTableTest = "Set contract value using a lookup table for addresses"
)

type TimestampedUnixBig struct {
Value *big.Int `json:"value"`
Timestamp uint32 `json:"timestamp"`
}

func RunContractReaderInLoopTests[T WrappedTestingT[T]](t T, it ChainComponentsInterfaceTester[T]) {
//RunContractReaderInterfaceTests(t, it, false, true)
testCases := []Testcase[T]{
{
Name: ContractReaderNotFoundReadsReturnZeroedResponses,
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))

dAccRes := contractprimary.DataAccount{}
require.NoError(t, cr.GetLatestValue(ctx, bound.ReadIdentifier(ReadUninitializedPDA), primitives.Unconfirmed, nil, &dAccRes))
require.Equal(t, contractprimary.DataAccount{}, dAccRes)

mR3Res := contractprimary.MultiRead3{}
batchGetLatestValueRequest := make(types.BatchGetLatestValuesRequest)
batchGetLatestValueRequest[bound] = []types.BatchRead{
{
ReadName: ReadUninitializedPDA,
Params: nil,
ReturnVal: &dAccRes,
},
{
ReadName: MultiReadWithParamsReuse,
Params: map[string]any{"ID": 999},
ReturnVal: &mR3Res,
},
}

batchResult, err := cr.BatchGetLatestValues(ctx, batchGetLatestValueRequest)
require.NoError(t, err)

result, err := batchResult[bound][0].GetResult()
require.NoError(t, err)
require.Equal(t, &contractprimary.DataAccount{}, result)

result, err = batchResult[bound][1].GetResult()
require.NoError(t, err)
require.Equal(t, &contractprimary.MultiRead3{}, result)
},
},
{
Name: ContractReaderGetLatestValueWithAddressHardcodedIntoResponse,
Test: func(t T) {
Expand Down Expand Up @@ -791,6 +862,7 @@ func (h *helper) runInitialize(
}

const (
ReadUninitializedPDA = "ReadUninitializedPDA"
MultiRead = "MultiRead"
ReadWithAddressHardCodedIntoResponse = "ReadWithAddressHardCodedIntoResponse"
MultiReadWithParamsReuse = "MultiReadWithParamsReuse"
Expand Down Expand Up @@ -871,6 +943,13 @@ func (it *SolanaChainComponentsInterfaceTester[T]) buildContractReaderConfig(t T
AnyContractName: {
IDL: idl,
Reads: map[string]config.ReadDefinition{
ReadUninitializedPDA: {
ChainSpecificName: "DataAccount",
ReadType: config.Account,
PDADefinition: codec.PDATypeDef{
Prefix: []byte("AAAAAAAAAA"),
},
},
ReadWithAddressHardCodedIntoResponse: readWithAddressHardCodedIntoResponseDef,
GetTokenPrices: {
ChainSpecificName: "USDPerToken",
Expand Down
42 changes: 35 additions & 7 deletions integration-tests/relayinterface/lookups_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ func TestAccountLookups(t *testing.T) {
IsWritable: chainwriter.MetaBool{Value: true},
}}
_, err := lookupConfig.AccountLookup.Resolve(testArgs)
require.Error(t, err)
require.ErrorIs(t, err, chainwriter.ErrLookupNotFoundAtLocation)
})

t.Run("AccountLookup works with MetaBool bitmap lookups", func(t *testing.T) {
Expand Down Expand Up @@ -360,8 +360,7 @@ func TestPDALookups(t *testing.T) {
}

_, err := pdaLookup.Resolve(ctx, args, nil, client.MultiClient{})
require.Error(t, err)
require.Contains(t, err.Error(), "key not found")
require.ErrorIs(t, err, chainwriter.ErrGettingSeedAtLocation)
})

t.Run("PDALookup resolves valid PDA with address lookup seeds", func(t *testing.T) {
Expand Down Expand Up @@ -569,6 +568,21 @@ func TestLookupTables(t *testing.T) {
require.Contains(t, err.Error(), "error fetching account info for table") // Example error message
})

t.Run("Derived lookup table fails with invalid table name", func(t *testing.T) {
derivedTableMap := map[string]map[string][]*solana.AccountMeta{
"DerivedTable": {},
}
accountsFromLookupTable := chainwriter.Lookup{
AccountsFromLookupTable: &chainwriter.AccountsFromLookupTable{
LookupTableName: "InvalidTable",
IncludeIndexes: []int{},
},
}

_, err = accountsFromLookupTable.Resolve(ctx, nil, derivedTableMap, multiClient)
require.ErrorIs(t, err, chainwriter.ErrLookupTableNotFound)
})

t.Run("Static lookup table fails with invalid address", func(t *testing.T) {
invalidTable := chainwriter.GetRandomPubKey(t)

Expand Down Expand Up @@ -608,8 +622,15 @@ func TestLookupTables(t *testing.T) {
derivedTableMap, _, err := cw.ResolveLookupTables(ctx, testArgs, lookupConfig)
require.NoError(t, err)

addresses, ok := derivedTableMap["DerivedTable"][table.String()]
require.True(t, ok)
accountsFromLookupTable := chainwriter.Lookup{
AccountsFromLookupTable: &chainwriter.AccountsFromLookupTable{
LookupTableName: "DerivedTable",
IncludeIndexes: []int{},
},
}

addresses, err := accountsFromLookupTable.Resolve(ctx, nil, derivedTableMap, multiClient)
require.NoError(t, err)
for i, address := range addresses {
require.Equal(t, pubKeys[i], address.PublicKey)
}
Expand Down Expand Up @@ -654,8 +675,15 @@ func TestLookupTables(t *testing.T) {
derivedTableMap, _, err := cw.ResolveLookupTables(ctx, args, lookupConfig)
require.NoError(t, err)

addresses, ok := derivedTableMap["DerivedTable"][lookupTable.String()]
require.True(t, ok)
accountsFromLookupTable := chainwriter.Lookup{
AccountsFromLookupTable: &chainwriter.AccountsFromLookupTable{
LookupTableName: "DerivedTable",
IncludeIndexes: []int{},
},
}

addresses, err := accountsFromLookupTable.Resolve(ctx, args, derivedTableMap, multiClient)
require.NoError(t, err)
for i, address := range addresses {
require.Equal(t, lookupKeys[i], address.PublicKey)
}
Expand Down
Loading

0 comments on commit 382b20d

Please sign in to comment.