From c09fef644e869188d86e0cbfd5b9a54e37d761f7 Mon Sep 17 00:00:00 2001 From: Silas Lenihan Date: Wed, 19 Feb 2025 15:01:47 -0500 Subject: [PATCH] Specify Ignorable Errors for Optional Lookups --- .../relayinterface/lookups_test.go | 39 ++++++++++++++++--- pkg/solana/chainwriter/chain_writer.go | 12 +++++- pkg/solana/chainwriter/chain_writer_test.go | 2 + pkg/solana/chainwriter/lookups.go | 12 ++++-- 4 files changed, 55 insertions(+), 10 deletions(-) diff --git a/integration-tests/relayinterface/lookups_test.go b/integration-tests/relayinterface/lookups_test.go index 910b2cf69..b8401710b 100644 --- a/integration-tests/relayinterface/lookups_test.go +++ b/integration-tests/relayinterface/lookups_test.go @@ -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) { @@ -569,6 +569,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) @@ -608,8 +623,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) } @@ -654,8 +676,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) } diff --git a/pkg/solana/chainwriter/chain_writer.go b/pkg/solana/chainwriter/chain_writer.go index 169824cf0..74c22804a 100644 --- a/pkg/solana/chainwriter/chain_writer.go +++ b/pkg/solana/chainwriter/chain_writer.go @@ -155,7 +155,7 @@ func GetAddresses(ctx context.Context, args any, accounts []Lookup, derivedTable var addresses []*solana.AccountMeta for _, accountConfig := range accounts { meta, err := accountConfig.Resolve(ctx, args, derivedTableMap, client) - if accountConfig.Optional && err != nil { + if accountConfig.Optional && err != nil && isIgnorableError(err) { // skip optional accounts if they are not found continue } @@ -167,6 +167,14 @@ func GetAddresses(ctx context.Context, args any, accounts []Lookup, derivedTable return addresses, nil } +// These errors are ignorable if the lookup is optional. +func isIgnorableError(err error) bool { + return err == nil || + errors.Is(err, ErrLookupNotFoundAtLocation) || + errors.Is(err, ErrLookupTableNotFound) || + errors.Is(err, ErrGettingSeedAtLocation) +} + // FilterLookupTableAddresses takes a list of accounts and two lookup table maps // (one for derived tables, one for static tables) and filters out any addresses that are // not used by the accounts. It returns a map of only those lookup table @@ -460,7 +468,7 @@ func (s *SolanaChainWriterService) ResolveLookupTables(ctx context.Context, args // Load the lookup table - note: This could be multiple tables if the lookup is a PDALookups that resolves to more // than one address lookupTableMap, err := s.loadTable(ctx, args, derivedLookup) - if derivedLookup.Optional && err != nil { + if derivedLookup.Optional && err != nil && isIgnorableError(err) { continue } if err != nil { diff --git a/pkg/solana/chainwriter/chain_writer_test.go b/pkg/solana/chainwriter/chain_writer_test.go index 17732416e..e6d9b1912 100644 --- a/pkg/solana/chainwriter/chain_writer_test.go +++ b/pkg/solana/chainwriter/chain_writer_test.go @@ -6,6 +6,7 @@ import ( _ "embed" "encoding/json" "errors" + "fmt" "math/big" "testing" @@ -303,6 +304,7 @@ func TestChainWriter_GetAddresses(t *testing.T) { args := Arguments{} accounts, err := chainwriter.GetAddresses(ctx, args, accountLookupConfig, nil, mc) + fmt.Println(err) require.NoError(t, err) require.Empty(t, accounts) }) diff --git a/pkg/solana/chainwriter/lookups.go b/pkg/solana/chainwriter/lookups.go index 2ae81b16f..e3782b56d 100644 --- a/pkg/solana/chainwriter/lookups.go +++ b/pkg/solana/chainwriter/lookups.go @@ -13,6 +13,12 @@ import ( "github.com/smartcontractkit/chainlink-solana/pkg/solana/codec" ) +var ( + ErrLookupNotFoundAtLocation = fmt.Errorf("error getting account from lookup") + ErrLookupTableNotFound = fmt.Errorf("lookup table not found") + ErrGettingSeedAtLocation = fmt.Errorf("error getting address seed for location") +) + type Lookup struct { Optional bool AccountConstant *AccountConstant `json:"accountConstant,omitempty"` @@ -146,7 +152,7 @@ func (ac AccountConstant) Resolve() ([]*solana.AccountMeta, error) { func (al AccountLookup) Resolve(args any) ([]*solana.AccountMeta, error) { derivedValues, err := GetValuesAtLocation(args, al.Location) if err != nil { - return nil, fmt.Errorf("error getting account from lookup: %w", err) + return nil, fmt.Errorf("%w: %v", ErrLookupNotFoundAtLocation, err) } var metas []*solana.AccountMeta @@ -206,7 +212,7 @@ func (alt AccountsFromLookupTable) Resolve(derivedTableMap map[string]map[string // Fetch the inner map for the specified lookup table name innerMap, ok := derivedTableMap[alt.LookupTableName] if !ok { - return nil, fmt.Errorf("lookup table not found: %s", alt.LookupTableName) + return nil, fmt.Errorf("%w: %s", ErrLookupTableNotFound, alt.LookupTableName) } var result []*solana.AccountMeta @@ -332,7 +338,7 @@ func getSeedBytesCombinations( // Get value from a location (This doesn't have to be an address, it can be any value) bytes, err := GetValuesAtLocation(args, lookupSeed.Location) if err != nil { - return nil, fmt.Errorf("error getting address seed for location %q: %w", lookupSeed.Location, err) + return nil, fmt.Errorf("%w %q: %w", ErrGettingSeedAtLocation, lookupSeed.Location, err) } // append each byte array to the expansions for _, b := range bytes {