From 8fe11c1c71908e8ad935588bdc89ffb4b60b7440 Mon Sep 17 00:00:00 2001 From: Silas Lenihan Date: Fri, 13 Dec 2024 14:59:09 -0500 Subject: [PATCH] addressed feedback comments --- .../localnet/write_test-keypair.json | 6 ----- integration-tests/go.sum | 2 ++ .../relayinterface/lookups_test.go | 9 ++++---- pkg/solana/chainwriter/ccip_example_config.go | 13 +++-------- pkg/solana/chainwriter/chain_writer.go | 7 +----- pkg/solana/chainwriter/chain_writer_test.go | 17 +++++++------- pkg/solana/chainwriter/helpers.go | 22 +------------------ pkg/solana/chainwriter/lookups.go | 14 ++++++++---- 8 files changed, 29 insertions(+), 61 deletions(-) delete mode 100644 contracts/artifacts/localnet/write_test-keypair.json diff --git a/contracts/artifacts/localnet/write_test-keypair.json b/contracts/artifacts/localnet/write_test-keypair.json deleted file mode 100644 index dfb18e9c4..000000000 --- a/contracts/artifacts/localnet/write_test-keypair.json +++ /dev/null @@ -1,6 +0,0 @@ -[ - 26, 39, 164, 161, 246, 97, 149, 0, 58, 187, 146, 162, 53, 35, 107, 2, 117, - 242, 83, 171, 48, 7, 63, 240, 69, 221, 239, 45, 97, 55, 112, 106, 192, 228, - 214, 205, 123, 71, 58, 23, 62, 229, 166, 213, 149, 122, 96, 145, 35, 150, 16, - 156, 247, 199, 242, 108, 173, 80, 62, 231, 39, 196, 27, 192 -] diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 3dd59b75e..7f20fb5c2 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -955,6 +955,8 @@ github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6 github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= +github.com/linkedin/goavro/v2 v2.12.0 h1:rIQQSj8jdAUlKQh6DttK8wCRv4t4QO09g1C4aBWXslg= +github.com/linkedin/goavro/v2 v2.12.0/go.mod h1:KXx+erlq+RPlGSPmLF7xGo6SAbh8sCQ53x064+ioxhk= github.com/linxGnu/grocksdb v1.7.16 h1:Q2co1xrpdkr5Hx3Fp+f+f7fRGhQFQhvi/+226dtLmA8= github.com/linxGnu/grocksdb v1.7.16/go.mod h1:JkS7pl5qWpGpuVb3bPqTz8nC12X3YtPZT+Xq7+QfQo4= github.com/lithammer/dedent v1.1.0 h1:VNzHMVCBNG1j0fh3OrsFRkVUwStdDArbgBWoPAffktY= diff --git a/integration-tests/relayinterface/lookups_test.go b/integration-tests/relayinterface/lookups_test.go index 1d7e9f799..1b91dc8df 100644 --- a/integration-tests/relayinterface/lookups_test.go +++ b/integration-tests/relayinterface/lookups_test.go @@ -1,7 +1,6 @@ package relayinterface import ( - "context" "reflect" "testing" "time" @@ -153,7 +152,7 @@ func TestPDALookups(t *testing.T) { IsWritable: true, } - ctx := context.Background() + ctx := tests.Context(t) result, err := pdaLookup.Resolve(ctx, nil, nil, nil) require.NoError(t, err) require.Equal(t, expectedMeta, result) @@ -184,7 +183,7 @@ func TestPDALookups(t *testing.T) { IsWritable: true, } - ctx := context.Background() + ctx := tests.Context(t) args := map[string]interface{}{ "test_seed": seed1, "another_seed": seed2, @@ -206,7 +205,7 @@ func TestPDALookups(t *testing.T) { IsWritable: true, } - ctx := context.Background() + ctx := tests.Context(t) args := map[string]interface{}{ "test_seed": []byte("data"), } @@ -242,7 +241,7 @@ func TestPDALookups(t *testing.T) { IsWritable: true, } - ctx := context.Background() + ctx := tests.Context(t) args := map[string]interface{}{ "test_seed": seed1, "another_seed": seed2, diff --git a/pkg/solana/chainwriter/ccip_example_config.go b/pkg/solana/chainwriter/ccip_example_config.go index 89038fd6a..acdaf3d35 100644 --- a/pkg/solana/chainwriter/ccip_example_config.go +++ b/pkg/solana/chainwriter/ccip_example_config.go @@ -2,8 +2,6 @@ package chainwriter import ( "fmt" - - commoncodec "github.com/smartcontractkit/chainlink-common/pkg/codec" ) func TestConfig() { @@ -22,14 +20,9 @@ func TestConfig() { executionReportSingleChainIDL := `{"name":"ExecutionReportSingleChain","type":{"kind":"struct","fields":[{"name":"source_chain_selector","type":"u64"},{"name":"message","type":{"defined":"Any2SolanaRampMessage"}},{"name":"root","type":{"array":["u8",32]}},{"name":"proofs","type":{"vec":{"array":["u8",32]}}}]}},{"name":"Any2SolanaRampMessage","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":"RampMessageHeader"}},{"name":"sender","type":{"vec":"u8"}},{"name":"data","type":{"vec":"u8"}},{"name":"receiver","type":{"array":["u8",32]}},{"name":"extra_args","type":{"defined":"SolanaExtraArgs"}}]}},{"name":"RampMessageHeader","type":{"kind":"struct","fields":[{"name":"message_id","type":{"array":["u8",32]}},{"name":"source_chain_selector","type":"u64"},{"name":"dest_chain_selector","type":"u64"},{"name":"sequence_number","type":"u64"},{"name":"nonce","type":"u64"}]}},{"name":"SolanaExtraArgs","type":{"kind":"struct","fields":[{"name":"compute_units","type":"u32"},{"name":"allow_out_of_order_execution","type":"bool"}]}}` executeConfig := MethodConfig{ - FromAddress: userAddress, - InputModifications: commoncodec.ModifiersConfig{ - // remove merkle root since it isn't a part of the on-chain type - &commoncodec.DropModifierConfig{ - Fields: []string{"Message.ExtraArgs.MerkleRoot"}, - }, - }, - ChainSpecificName: "execute", + FromAddress: userAddress, + InputModifications: nil, + ChainSpecificName: "execute", // LookupTables are on-chain stores of accounts. They can be used in two ways: // 1. As a way to store a list of accounts that are all associated together (i.e. Token State registry) // 2. To compress the transactions in a TX and reduce the size of the TX. (The traditional way) diff --git a/pkg/solana/chainwriter/chain_writer.go b/pkg/solana/chainwriter/chain_writer.go index 8c82038f1..e07842201 100644 --- a/pkg/solana/chainwriter/chain_writer.go +++ b/pkg/solana/chainwriter/chain_writer.go @@ -251,7 +251,7 @@ func (s *SolanaChainWriterService) SubmitTransaction(ctx context.Context, contra } // Prepare transaction - programID, err := solana.PublicKeyFromBase58(contractName) + programID, err := solana.PublicKeyFromBase58(toAddress) if err != nil { return errorWithDebugID(fmt.Errorf("error parsing program ID: %w", err), debugID) } @@ -281,11 +281,6 @@ func (s *SolanaChainWriterService) SubmitTransaction(ctx context.Context, contra return nil } -var ( - _ services.Service = &SolanaChainWriterService{} - _ types.ContractWriter = &SolanaChainWriterService{} -) - // GetTransactionStatus returns the current status of a transaction in the underlying chain's TXM. func (s *SolanaChainWriterService) GetTransactionStatus(ctx context.Context, transactionID string) (types.TransactionStatus, error) { return s.txm.GetTransactionStatus(ctx, transactionID) diff --git a/pkg/solana/chainwriter/chain_writer_test.go b/pkg/solana/chainwriter/chain_writer_test.go index 1490b519e..c7fab54e0 100644 --- a/pkg/solana/chainwriter/chain_writer_test.go +++ b/pkg/solana/chainwriter/chain_writer_test.go @@ -25,7 +25,7 @@ import ( txmMocks "github.com/smartcontractkit/chainlink-solana/pkg/solana/txm/mocks" ) -var testContractIDLJson = `{"version":"0.1.0","name":"contract_reader_interface","instructions":[{"name":"initialize","accounts":[{"name":"data","isMut":true,"isSigner":false},{"name":"signer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"testIdx","type":"u64"},{"name":"value","type":"u64"}]},{"name":"initializeLookupTable","accounts":[{"name":"writeDataAccount","isMut":true,"isSigner":false,"docs":["PDA for LookupTableDataAccount, derived from seeds and created by the System Program"]},{"name":"admin","isMut":true,"isSigner":true,"docs":["Admin account that pays for PDA creation and signs the transaction"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System Program required for PDA creation"]}],"args":[{"name":"lookupTable","type":"publicKey"}]}],"accounts":[{"name":"LookupTableDataAccount","type":{"kind":"struct","fields":[{"name":"version","type":"u8"},{"name":"administrator","type":"publicKey"},{"name":"pendingAdministrator","type":"publicKey"},{"name":"lookupTable","type":"publicKey"}]}},{"name":"DataAccount","type":{"kind":"struct","fields":[{"name":"idx","type":"u64"},{"name":"bump","type":"u8"},{"name":"u64Value","type":"u64"},{"name":"u64Slice","type":{"vec":"u64"}}]}}]}` +var testContractIDLJson = `{"version":"0.1.0","name":"contractReaderInterface","instructions":[{"name":"initialize","accounts":[{"name":"data","isMut":true,"isSigner":false},{"name":"signer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"testIdx","type":"u64"},{"name":"value","type":"u64"}]},{"name":"initializeLookupTable","accounts":[{"name":"writeDataAccount","isMut":true,"isSigner":false,"docs":["PDA for LookupTableDataAccount, derived from seeds and created by the System Program"]},{"name":"admin","isMut":true,"isSigner":true,"docs":["Admin account that pays for PDA creation and signs the transaction"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System Program required for PDA creation"]}],"args":[{"name":"lookupTable","type":"publicKey"}]}],"accounts":[{"name":"LookupTableDataAccount","type":{"kind":"struct","fields":[{"name":"version","type":"u8"},{"name":"administrator","type":"publicKey"},{"name":"pendingAdministrator","type":"publicKey"},{"name":"lookupTable","type":"publicKey"}]}},{"name":"DataAccount","type":{"kind":"struct","fields":[{"name":"idx","type":"u64"},{"name":"bump","type":"u8"},{"name":"u64Value","type":"u64"},{"name":"u64Slice","type":{"vec":"u64"}}]}}]}` func TestChainWriter_GetAddresses(t *testing.T) { ctx := tests.Context(t) @@ -401,7 +401,7 @@ func TestChainWriter_SubmitTransaction(t *testing.T) { // create lookup table addresses seed2 := []byte("seed2") - programID := chainwriter.GetRandomPubKey(t) + programID := solana.MustPublicKeyFromBase58("6AfuXF6HapDUhQfE4nQG9C1SGtA1YjP3icaJyRfU4RyE") derivedTablePda := mustFindPdaProgramAddress(t, [][]byte{seed2}, programID) // mock data account response from program derivedLookupTablePubkey := mockDataAccountLookupTable(t, rw, derivedTablePda) @@ -416,7 +416,7 @@ func TestChainWriter_SubmitTransaction(t *testing.T) { cwConfig := chainwriter.ChainWriterConfig{ Programs: map[string]chainwriter.ProgramConfig{ - "6AfuXF6HapDUhQfE4nQG9C1SGtA1YjP3icaJyRfU4RyE": { + "contractReaderInterface": { Methods: map[string]chainwriter.MethodConfig{ "initializeLookupTable": { FromAddress: admin.String(), @@ -505,21 +505,21 @@ func TestChainWriter_SubmitTransaction(t *testing.T) { t.Run("fails to encode payload if args with missing values provided", func(t *testing.T) { txID := uuid.NewString() args := map[string]interface{}{} - submitErr := cw.SubmitTransaction(ctx, "6AfuXF6HapDUhQfE4nQG9C1SGtA1YjP3icaJyRfU4RyE", "initializeLookupTable", args, txID, programID.String(), nil, nil) + submitErr := cw.SubmitTransaction(ctx, "contractReaderInterface", "initializeLookupTable", args, txID, programID.String(), nil, nil) require.Error(t, submitErr) }) t.Run("fails if invalid contract name provided", func(t *testing.T) { txID := uuid.NewString() args := map[string]interface{}{} - submitErr := cw.SubmitTransaction(ctx, "contract_reader_interface", "initializeLookupTable", args, txID, programID.String(), nil, nil) + submitErr := cw.SubmitTransaction(ctx, "badContract", "initializeLookupTable", args, txID, programID.String(), nil, nil) require.Error(t, submitErr) }) t.Run("fails if invalid method provided", func(t *testing.T) { txID := uuid.NewString() args := map[string]interface{}{} - submitErr := cw.SubmitTransaction(ctx, "6AfuXF6HapDUhQfE4nQG9C1SGtA1YjP3icaJyRfU4RyE", "badMethod", args, txID, programID.String(), nil, nil) + submitErr := cw.SubmitTransaction(ctx, "contractReaderInterface", "badMethod", args, txID, programID.String(), nil, nil) require.Error(t, submitErr) }) @@ -527,7 +527,6 @@ func TestChainWriter_SubmitTransaction(t *testing.T) { recentBlockHash := solana.Hash{} rw.On("LatestBlockhash", mock.Anything).Return(&rpc.GetLatestBlockhashResult{Value: &rpc.LatestBlockhashResult{Blockhash: recentBlockHash, LastValidBlockHeight: uint64(100)}}, nil).Once() txID := uuid.NewString() - configProgramID := solana.MustPublicKeyFromBase58("6AfuXF6HapDUhQfE4nQG9C1SGtA1YjP3icaJyRfU4RyE") txm.On("Enqueue", mock.Anything, account1.String(), mock.MatchedBy(func(tx *solana.Transaction) bool { // match transaction fields to ensure it was built as expected @@ -538,7 +537,7 @@ func TestChainWriter_SubmitTransaction(t *testing.T) { require.Equal(t, account1, tx.Message.AccountKeys[1]) // account constant require.Equal(t, account2, tx.Message.AccountKeys[2]) // account lookup require.Equal(t, account3, tx.Message.AccountKeys[3]) // pda lookup - require.Equal(t, configProgramID, tx.Message.AccountKeys[4]) // instruction program ID + require.Equal(t, programID, tx.Message.AccountKeys[4]) // instruction program ID require.Len(t, tx.Message.AddressTableLookups, 1) // address table look contains entry require.Equal(t, derivedLookupTablePubkey, tx.Message.AddressTableLookups[0].AccountKey) // address table return true @@ -550,7 +549,7 @@ func TestChainWriter_SubmitTransaction(t *testing.T) { "seed1": seed1, "seed2": seed2, } - submitErr := cw.SubmitTransaction(ctx, "6AfuXF6HapDUhQfE4nQG9C1SGtA1YjP3icaJyRfU4RyE", "initializeLookupTable", args, txID, programID.String(), nil, nil) + submitErr := cw.SubmitTransaction(ctx, "contractReaderInterface", "initializeLookupTable", args, txID, programID.String(), nil, nil) require.NoError(t, submitErr) }) } diff --git a/pkg/solana/chainwriter/helpers.go b/pkg/solana/chainwriter/helpers.go index 22a2d1c67..8b7276276 100644 --- a/pkg/solana/chainwriter/helpers.go +++ b/pkg/solana/chainwriter/helpers.go @@ -56,7 +56,7 @@ func GetValuesAtLocation(args any, location string) ([][]byte, error) { } func GetDebugIDAtLocation(args any, location string) (string, error) { - debugIDList, err := GetValueAtLocation(args, location) + debugIDList, err := GetValuesAtLocation(args, location) if err != nil { return "", err } @@ -67,26 +67,6 @@ func GetDebugIDAtLocation(args any, location string) (string, error) { return debugID, nil } -func GetValueAtLocation(args any, location string) ([][]byte, error) { - path := strings.Split(location, ".") - - valueList, err := traversePath(args, path) - if err != nil { - return nil, err - } - - var values [][]byte - for _, value := range valueList { - byteArray, ok := value.([]byte) - if !ok { - return nil, fmt.Errorf("invalid value format at path: %s", location) - } - values = append(values, byteArray) - } - - return values, nil -} - func errorWithDebugID(err error, debugID string) error { if debugID == "" { return err diff --git a/pkg/solana/chainwriter/lookups.go b/pkg/solana/chainwriter/lookups.go index 37de7a567..7f2b45afe 100644 --- a/pkg/solana/chainwriter/lookups.go +++ b/pkg/solana/chainwriter/lookups.go @@ -212,6 +212,7 @@ func decodeBorshIntoType(data []byte, typ reflect.Type) (interface{}, error) { // It handles both AddressSeeds (which are public keys) and ValueSeeds (which are byte arrays from input args). func getSeedBytes(ctx context.Context, lookup PDALookups, args any, derivedTableMap map[string]map[string][]*solana.AccountMeta, reader client.Reader) ([][]byte, error) { var seedBytes [][]byte + maxSeedLength := 32 for _, seed := range lookup.Seeds { if lookupSeed, ok := seed.(AccountLookup); ok { @@ -220,7 +221,13 @@ func getSeedBytes(ctx context.Context, lookup PDALookups, args any, derivedTable if err != nil { return nil, fmt.Errorf("error getting address seed: %w", err) } - seedBytes = append(seedBytes, bytes...) + // validate seed length + for _, b := range bytes { + if len(b) > maxSeedLength { + return nil, fmt.Errorf("seed byte array exceeds maximum length of %d: got %d bytes", maxSeedLength, len(b)) + } + seedBytes = append(seedBytes, b) + } } else { // Get address seeds from the lookup seedAddresses, err := GetAddresses(ctx, args, []Lookup{seed}, derivedTableMap, reader) @@ -240,10 +247,9 @@ func getSeedBytes(ctx context.Context, lookup PDALookups, args any, derivedTable // generatePDAs generates program-derived addresses (PDAs) from public keys and seeds. func generatePDAs(publicKeys []*solana.AccountMeta, seeds [][]byte, lookup PDALookups) ([]*solana.AccountMeta, error) { - if len(seeds) > 1 && len(publicKeys) > 1 { - return nil, fmt.Errorf("multiple public keys and multiple seeds are not allowed") + if len(seeds) > 16 { + return nil, fmt.Errorf("seed maximum exceeded: %d", len(seeds)) } - var addresses []*solana.AccountMeta for _, publicKeyMeta := range publicKeys { address, _, err := solana.FindProgramAddress(seeds, publicKeyMeta.PublicKey)