Skip to content

Commit

Permalink
addressed feedback comments
Browse files Browse the repository at this point in the history
  • Loading branch information
silaslenihan committed Dec 20, 2024
1 parent a283b55 commit e9bf92b
Show file tree
Hide file tree
Showing 8 changed files with 29 additions and 61 deletions.
6 changes: 0 additions & 6 deletions contracts/artifacts/localnet/write_test-keypair.json

This file was deleted.

2 changes: 2 additions & 0 deletions integration-tests/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -951,6 +951,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=
Expand Down
9 changes: 4 additions & 5 deletions integration-tests/relayinterface/lookups_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package relayinterface

import (
"context"
"reflect"
"testing"
"time"
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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,
Expand All @@ -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"),
}
Expand Down Expand Up @@ -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,
Expand Down
13 changes: 3 additions & 10 deletions pkg/solana/chainwriter/ccip_example_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ package chainwriter

import (
"fmt"

commoncodec "github.com/smartcontractkit/chainlink-common/pkg/codec"
)

func TestConfig() {
Expand All @@ -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)
Expand Down
7 changes: 1 addition & 6 deletions pkg/solana/chainwriter/chain_writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down Expand Up @@ -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)
Expand Down
17 changes: 8 additions & 9 deletions pkg/solana/chainwriter/chain_writer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand All @@ -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(),
Expand Down Expand Up @@ -505,29 +505,28 @@ 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)
})

t.Run("submits transaction successfully", func(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
Expand All @@ -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
Expand All @@ -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)
})
}
Expand Down
22 changes: 1 addition & 21 deletions pkg/solana/chainwriter/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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
Expand Down
14 changes: 10 additions & 4 deletions pkg/solana/chainwriter/lookups.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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)
Expand All @@ -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)
Expand Down

0 comments on commit e9bf92b

Please sign in to comment.