From 176277562b124f9bdb70d7aed0e86594ba4d787b Mon Sep 17 00:00:00 2001 From: amit-momin Date: Thu, 20 Feb 2025 13:31:45 -0600 Subject: [PATCH] Added tests for CCIP types and commit method args transform --- .../relayinterface/lookups_test.go | 23 ++-- pkg/solana/chainwriter/chain_writer_test.go | 6 +- pkg/solana/chainwriter/transform_registry.go | 4 +- .../chainwriter/transform_registry_test.go | 104 +++++++++++++----- 4 files changed, 98 insertions(+), 39 deletions(-) diff --git a/integration-tests/relayinterface/lookups_test.go b/integration-tests/relayinterface/lookups_test.go index f46df6b9a..1055fe64e 100644 --- a/integration-tests/relayinterface/lookups_test.go +++ b/integration-tests/relayinterface/lookups_test.go @@ -318,8 +318,9 @@ func TestPDALookups(t *testing.T) { seed3 := ccipocr3.ChainSelector(4) bufSeed3 := make([]byte, 8) binary.LittleEndian.PutUint64(bufSeed3, uint64(seed3)) + seed4 := ccipocr3.Bytes32(chainwriter.GetRandomPubKey(t).Bytes()) - pda, _, err := solana.FindProgramAddress([][]byte{seed1, bufSeed2, bufSeed3}, programID) + pda, _, err := solana.FindProgramAddress([][]byte{seed1, bufSeed2, bufSeed3, seed4[:]}, programID) require.NoError(t, err) expectedMeta := []*solana.AccountMeta{ @@ -336,16 +337,18 @@ func TestPDALookups(t *testing.T) { Seeds: []chainwriter.Seed{ {Dynamic: chainwriter.Lookup{AccountLookup: &chainwriter.AccountLookup{Name: "seed1", Location: "test_seed"}}}, {Dynamic: chainwriter.Lookup{AccountLookup: &chainwriter.AccountLookup{Name: "seed2", Location: "another_seed"}}}, - {Dynamic: chainwriter.Lookup{AccountLookup: &chainwriter.AccountLookup{Name: "seed3", Location: "3rd_seed"}}}, + {Dynamic: chainwriter.Lookup{AccountLookup: &chainwriter.AccountLookup{Name: "seed3", Location: "ccip_chain_selector"}}}, + {Dynamic: chainwriter.Lookup{AccountLookup: &chainwriter.AccountLookup{Name: "seed4", Location: "ccip_bytes"}}}, }, IsSigner: false, IsWritable: true, }} args := map[string]interface{}{ - "test_seed": seed1, - "another_seed": seed2, - "3rd_seed": seed3, + "test_seed": seed1, + "another_seed": seed2, + "ccip_chain_selector": seed3, + "ccip_bytes": seed4, } result, err := pdaLookup.Resolve(ctx, args, nil, client.MultiClient{}) @@ -376,8 +379,10 @@ func TestPDALookups(t *testing.T) { t.Run("PDALookup resolves valid PDA with address lookup seeds", func(t *testing.T) { seed1 := chainwriter.GetRandomPubKey(t) seed2 := chainwriter.GetRandomPubKey(t) + addr3 := chainwriter.GetRandomPubKey(t) + seed3 := ccipocr3.UnknownEncodedAddress(addr3.String()) - pda, _, err := solana.FindProgramAddress([][]byte{seed1.Bytes(), seed2.Bytes()}, programID) + pda, _, err := solana.FindProgramAddress([][]byte{seed1.Bytes(), seed2.Bytes(), addr3.Bytes()}, programID) require.NoError(t, err) expectedMeta := []*solana.AccountMeta{ @@ -394,14 +399,16 @@ func TestPDALookups(t *testing.T) { Seeds: []chainwriter.Seed{ {Dynamic: chainwriter.Lookup{AccountLookup: &chainwriter.AccountLookup{Name: "seed1", Location: "test_seed"}}}, {Dynamic: chainwriter.Lookup{AccountLookup: &chainwriter.AccountLookup{Name: "seed2", Location: "another_seed"}}}, + {Dynamic: chainwriter.Lookup{AccountLookup: &chainwriter.AccountLookup{Name: "seed3", Location: "unknown_encoded_address"}}}, }, IsSigner: false, IsWritable: true, }} args := map[string]interface{}{ - "test_seed": seed1, - "another_seed": seed2, + "test_seed": seed1, + "another_seed": seed2, + "unknown_encoded_address": seed3, } result, err := pdaLookup.Resolve(ctx, args, nil, client.MultiClient{}) diff --git a/pkg/solana/chainwriter/chain_writer_test.go b/pkg/solana/chainwriter/chain_writer_test.go index c276ef637..d9eb97baa 100644 --- a/pkg/solana/chainwriter/chain_writer_test.go +++ b/pkg/solana/chainwriter/chain_writer_test.go @@ -822,7 +822,7 @@ func TestChainWriter_CCIPOfframp(t *testing.T) { }, }, ChainSpecificName: "commit", - ArgsTransform: "", + ArgsTransform: "CCIPCommit", LookupTables: chainwriter.LookupTables{}, Accounts: []chainwriter.Lookup{ {AccountConstant: &chainwriter.AccountConstant{ @@ -922,7 +922,7 @@ func TestChainWriter_CCIPOfframp(t *testing.T) { require.NoError(t, submitErr) }) - t.Run("CCIP commit is encoded successfully", func(t *testing.T) { + t.Run("CCIP commit is encoded successfully and ArgsTransform is applied correctly.", func(t *testing.T) { // mock txm txm := txmMocks.NewTxManager(t) // initialize chain writer @@ -963,6 +963,8 @@ func TestChainWriter_CCIPOfframp(t *testing.T) { dec := ag_binary.NewBorshDecoder(payload) err := dec.Decode(&decoded) require.NoError(t, err) + // The CCIPCommit ArgsTransform should remove the last account since no price updates were provided in the report + require.Len(t, tx.Message.Instructions[0].Accounts, 2) return true }), &txID, mock.Anything).Return(nil).Once() diff --git a/pkg/solana/chainwriter/transform_registry.go b/pkg/solana/chainwriter/transform_registry.go index fcfb3eea0..4bdb480a1 100644 --- a/pkg/solana/chainwriter/transform_registry.go +++ b/pkg/solana/chainwriter/transform_registry.go @@ -30,7 +30,7 @@ type ReportPostTransform struct { func FindTransform(id string) (func(context.Context, *SolanaChainWriterService, any, solana.AccountMetaSlice, string) (any, solana.AccountMetaSlice, error), error) { switch id { case "CCIPExecute": - return CCIPArgsTransform, nil + return CCIPExecuteArgsTransform, nil case "CCIPCommit": return CCIPCommitAccountTransform, nil default: @@ -40,7 +40,7 @@ func FindTransform(id string) (func(context.Context, *SolanaChainWriterService, // This Transform function looks up the token pool addresses in the accounts slice and augments the args // with the indexes of the token pool addresses in the accounts slice. -func CCIPArgsTransform(ctx context.Context, cw *SolanaChainWriterService, args any, accounts solana.AccountMetaSlice, toAddress string) (any, solana.AccountMetaSlice, error) { +func CCIPExecuteArgsTransform(ctx context.Context, cw *SolanaChainWriterService, args any, accounts solana.AccountMetaSlice, toAddress string) (any, solana.AccountMetaSlice, error) { // Fetch offramp config to use to fetch the router address offrampProgramConfig, ok := cw.config.Programs[ccipconsts.ContractNameOffRamp] if !ok { diff --git a/pkg/solana/chainwriter/transform_registry_test.go b/pkg/solana/chainwriter/transform_registry_test.go index f34da4f62..5142cd71a 100644 --- a/pkg/solana/chainwriter/transform_registry_test.go +++ b/pkg/solana/chainwriter/transform_registry_test.go @@ -1,59 +1,109 @@ package chainwriter_test import ( + "context" "testing" "github.com/gagliardetto/solana-go" + ccipconsts "github.com/smartcontractkit/chainlink-ccip/pkg/consts" "github.com/smartcontractkit/chainlink-ccip/pkg/types/ccipocr3" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-solana/pkg/monitoring/testutils" "github.com/smartcontractkit/chainlink-solana/pkg/solana/chainwriter" + "github.com/smartcontractkit/chainlink-solana/pkg/solana/client" + clientmocks "github.com/smartcontractkit/chainlink-solana/pkg/solana/client/mocks" ) -func Test_CCIPCommitTransform(t *testing.T) { +func Test_CCIPExecuteTransform(t *testing.T) { ctx := tests.Context(t) - offrampAddress, err := solana.NewRandomPrivateKey() - require.NoError(t, err) - key1, err := solana.NewRandomPrivateKey() - require.NoError(t, err) - key2, err := solana.NewRandomPrivateKey() + offrampAddress := chainwriter.GetRandomPubKey(t) + routerAddress := chainwriter.GetRandomPubKey(t) + + // simplified CCIP Config - only IDLs are required for CCIPExecute ArgsTransform + ccipCWConfig := chainwriter.ChainWriterConfig{ + Programs: map[string]chainwriter.ProgramConfig{ + ccipconsts.ContractNameOffRamp: { + IDL: ccipOfframpIDL, + }, + // Requires only the IDL for the CCIPArgsTransform to fetch the TokenAdminRegistry + ccipconsts.ContractNameRouter: { + IDL: ccipRouterIDL, + }, + }, + } + // mock client + rw := clientmocks.NewReaderWriter(t) + mc := *client.NewMultiClient(func(context.Context) (client.ReaderWriter, error) { + return rw, nil + }) + // initialize chain writer + cw, err := chainwriter.NewSolanaChainWriterService(testutils.NewNullLogger(), mc, nil, nil, ccipCWConfig) require.NoError(t, err) - t.Run("ArgsTransform does not affect accounts if token prices exist", func(t *testing.T) { + + destTokenAddr := chainwriter.GetRandomPubKey(t) + poolKeys := []solana.PublicKey{destTokenAddr} + poolKeys = append(poolKeys, chainwriter.CreateTestPubKeys(t, 1)...) + + args := chainwriter.ReportPreTransform{ + Info: ccipocr3.ExecuteReportInfo{ + AbstractReports: []ccipocr3.ExecutePluginReportSingleChain{{ + Messages: []ccipocr3.Message{{ + TokenAmounts: []ccipocr3.RampTokenAmount{{ + DestTokenAddress: ccipocr3.UnknownAddress(destTokenAddr.Bytes()), + }}, + }}, + }}, + }, + } + + accounts := []*solana.AccountMeta{{PublicKey: poolKeys[0]}, {PublicKey: poolKeys[1]}} + + t.Run("CCIPExecute ArgsTransform includes token indexes", func(t *testing.T) { + pda, _, err := solana.FindProgramAddress([][]byte{[]byte("token_admin_registry"), destTokenAddr.Bytes()}, routerAddress) + require.NoError(t, err) + + lookupTable := mockTokenAdminRegistryLookupTable(t, rw, pda) + mockFetchRouterAddress(t, rw, routerAddress, offrampAddress) + mockFetchLookupTableAddresses(t, rw, lookupTable, poolKeys) + transformedArgs, newAccounts, err := chainwriter.CCIPExecuteArgsTransform(ctx, cw, args, accounts, offrampAddress.String()) + require.NoError(t, err) + // Accounts should be unchanged + require.Len(t, newAccounts, 2) + typedArgs, ok := transformedArgs.(chainwriter.ReportPostTransform) + require.True(t, ok) + require.NotNil(t, typedArgs.TokenIndexes) + require.Len(t, typedArgs.TokenIndexes, 1) + }) +} + +func Test_CCIPCommitTransform(t *testing.T) { + ctx := tests.Context(t) + offrampAddress := chainwriter.GetRandomPubKey(t) + key1 := chainwriter.GetRandomPubKey(t) + key2 := chainwriter.GetRandomPubKey(t) + t.Run("CCIPCommit ArgsTransform does not affect accounts if token prices exist", func(t *testing.T) { args := struct { Info ccipocr3.CommitReportInfo }{ Info: ccipocr3.CommitReportInfo{ - TokenPrices: []ccipocr3.TokenPrice{{TokenID: ccipocr3.UnknownEncodedAddress(key1.PublicKey().String())}}, + TokenPrices: []ccipocr3.TokenPrice{{TokenID: ccipocr3.UnknownEncodedAddress(key1.String())}}, }, } - accounts := []*solana.AccountMeta{ - { - PublicKey: key1.PublicKey(), - }, - { - PublicKey: key2.PublicKey(), - }, - } - _, newAccounts, err := chainwriter.CCIPCommitAccountTransform(ctx, nil, args, accounts, offrampAddress.PublicKey().String()) + accounts := []*solana.AccountMeta{{PublicKey: key1},{PublicKey: key2}} + _, newAccounts, err := chainwriter.CCIPCommitAccountTransform(ctx, nil, args, accounts, offrampAddress.String()) require.NoError(t, err) require.Len(t, newAccounts, 2) }) - t.Run("ArgsTransform removes last account if token and gas prices do not exist", func(t *testing.T) { + t.Run("CCIPCommit ArgsTransform removes last account if token and gas prices do not exist", func(t *testing.T) { args := struct { Info ccipocr3.CommitReportInfo }{ Info: ccipocr3.CommitReportInfo{}, } - accounts := []*solana.AccountMeta{ - { - PublicKey: key1.PublicKey(), - }, - { - PublicKey: key2.PublicKey(), - }, - } - _, newAccounts, err := chainwriter.CCIPCommitAccountTransform(ctx, nil, args, accounts, offrampAddress.PublicKey().String()) + accounts := []*solana.AccountMeta{{PublicKey: key1},{PublicKey: key2}} + _, newAccounts, err := chainwriter.CCIPCommitAccountTransform(ctx, nil, args, accounts, offrampAddress.String()) require.NoError(t, err) require.Len(t, newAccounts, 1) })