Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ChainWriter: Specify Ignorable Errors for Optional Lookups #1091

Merged
merged 3 commits into from
Feb 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
11 changes: 9 additions & 2 deletions pkg/solana/chainwriter/chain_writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -167,6 +167,13 @@ 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 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
Expand Down Expand Up @@ -460,7 +467,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 {
Expand Down
12 changes: 9 additions & 3 deletions pkg/solana/chainwriter/lookups.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down
Loading