Skip to content

Commit

Permalink
Moved lookups logic to separate file
Browse files Browse the repository at this point in the history
  • Loading branch information
silaslenihan committed Nov 20, 2024
1 parent 13cf316 commit a7b8b8b
Show file tree
Hide file tree
Showing 2 changed files with 296 additions and 289 deletions.
289 changes: 0 additions & 289 deletions pkg/solana/chainwriter/chain_writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"math/big"

"github.com/gagliardetto/solana-go"
"github.com/gagliardetto/solana-go/rpc"

commoncodec "github.com/smartcontractkit/chainlink-common/pkg/codec"
"github.com/smartcontractkit/chainlink-common/pkg/codec/encodings/binary"
Expand Down Expand Up @@ -47,60 +46,6 @@ type MethodConfig struct {
DebugIDLocation string
}

type Lookup interface {
Resolve(ctx context.Context, args any, derivedTableMap map[string]map[string][]*solana.AccountMeta, debugID string) ([]*solana.AccountMeta, error)
}

// AccountConstant represents a fixed address, provided in Base58 format, converted into a `solana.PublicKey`.
type AccountConstant struct {
Name string
Address string
IsSigner bool
IsWritable bool
}

// AccountLookup dynamically derives an account address from args using a specified location path.
type AccountLookup struct {
Name string
Location string
IsSigner bool
IsWritable bool
}

// PDALookups generates Program Derived Addresses (PDA) by combining a derived public key with one or more seeds.
type PDALookups struct {
Name string
// The public key of the PDA to be combined with seeds. If there are multiple PublicKeys
// there will be multiple PDAs generated by combining each PublicKey with the seeds.
PublicKey Lookup
// Seeds to be derived from an additional lookup
Seeds []Lookup
IsSigner bool
IsWritable bool
}

type ValueLookup struct {
Location string
}

// LookupTables represents a list of lookup tables that are used to derive addresses for a program.
type LookupTables struct {
DerivedLookupTables []DerivedLookupTable
StaticLookupTables []string
}

// DerivedLookupTable represents a lookup table that is used to derive addresses for a program.
type DerivedLookupTable struct {
Name string
Accounts Lookup
}

// AccountsFromLookupTable extracts accounts from a lookup table that was previously read and stored in memory.
type AccountsFromLookupTable struct {
LookupTablesName string
IncludeIndexes []int
}

func NewSolanaChainWriterService(reader client.Reader, txm txm.Txm, ge fees.Estimator, config ChainWriterConfig) (*SolanaChainWriterService, error) {
codecs, err := parseIDLCodecs(config)
if err != nil {
Expand Down Expand Up @@ -198,240 +143,6 @@ func GetAddresses(ctx context.Context, args any, accounts []Lookup, derivedTable
return addresses, nil
}

func (ac AccountConstant) Resolve(_ context.Context, _ any, _ map[string]map[string][]*solana.AccountMeta, debugID string) ([]*solana.AccountMeta, error) {
address, err := solana.PublicKeyFromBase58(ac.Address)
if err != nil {
return nil, errorWithDebugID(fmt.Errorf("error getting account from constant: %w", err), debugID)
}
return []*solana.AccountMeta{
{
PublicKey: address,
IsSigner: ac.IsSigner,
IsWritable: ac.IsWritable,
},
}, nil
}

func (al AccountLookup) Resolve(_ context.Context, args any, _ map[string]map[string][]*solana.AccountMeta, debugID string) ([]*solana.AccountMeta, error) {
derivedAddresses, err := GetAddressAtLocation(args, al.Location, debugID)
if err != nil {
return nil, errorWithDebugID(fmt.Errorf("error getting account from lookup: %w", err), debugID)
}

var metas []*solana.AccountMeta
for _, address := range derivedAddresses {
metas = append(metas, &solana.AccountMeta{
PublicKey: address,
IsSigner: al.IsSigner,
IsWritable: al.IsWritable,
})
}
return metas, nil
}

func (alt AccountsFromLookupTable) Resolve(_ context.Context, _ any, derivedTableMap map[string]map[string][]*solana.AccountMeta, debugID string) ([]*solana.AccountMeta, error) {
// Fetch the inner map for the specified lookup table name
innerMap, ok := derivedTableMap[alt.LookupTablesName]
if !ok {
return nil, errorWithDebugID(fmt.Errorf("lookup table not found: %s", alt.LookupTablesName), debugID)
}

var result []*solana.AccountMeta

// If no indices are specified, include all addresses
if len(alt.IncludeIndexes) == 0 {
for _, metas := range innerMap {
result = append(result, metas...)
}
return result, nil
}

// Otherwise, include only addresses at the specified indices
for publicKey, metas := range innerMap {
for _, index := range alt.IncludeIndexes {
if index < 0 || index >= len(metas) {
return nil, errorWithDebugID(fmt.Errorf("invalid index %d for account %s in lookup table %s", index, publicKey, alt.LookupTablesName), debugID)
}
result = append(result, metas[index])
}
}

return result, nil
}

func (pda PDALookups) Resolve(ctx context.Context, args any, derivedTableMap map[string]map[string][]*solana.AccountMeta, debugID string) ([]*solana.AccountMeta, error) {
publicKeys, err := GetAddresses(ctx, args, []Lookup{pda.PublicKey}, derivedTableMap, debugID)
if err != nil {
return nil, errorWithDebugID(fmt.Errorf("error getting public key for PDALookups: %w", err), debugID)
}

seeds, err := getSeedBytes(ctx, pda, args, derivedTableMap, debugID)
if err != nil {
return nil, errorWithDebugID(fmt.Errorf("error getting seeds for PDALookups: %w", err), debugID)
}

return generatePDAs(publicKeys, seeds, pda, debugID)
}

// getSeedBytes extracts the seeds for the PDALookups.
// 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, debugID string) ([][]byte, error) {
var seedBytes [][]byte

// Process AddressSeeds first (e.g., public keys)
for _, seed := range lookup.Seeds {
// Get the address(es) at the seed location
seedAddresses, err := GetAddresses(ctx, args, []Lookup{seed}, derivedTableMap, debugID)
if err != nil {
return nil, errorWithDebugID(fmt.Errorf("error getting address seed: %w", err), debugID)
}

// Add each address seed as bytes
for _, address := range seedAddresses {
seedBytes = append(seedBytes, address.PublicKey.Bytes())
}
}

return seedBytes, nil
}

// generatePDAs generates program-derived addresses (PDAs) from public keys and seeds.
func generatePDAs(publicKeys []*solana.AccountMeta, seeds [][]byte, lookup PDALookups, debugID string) ([]*solana.AccountMeta, error) {
if len(seeds) > 1 && len(publicKeys) > 1 {
return nil, errorWithDebugID(fmt.Errorf("multiple public keys and multiple seeds are not allowed"), debugID)
}

var addresses []*solana.AccountMeta
for _, publicKeyMeta := range publicKeys {
address, _, err := solana.FindProgramAddress(seeds, publicKeyMeta.PublicKey)
if err != nil {
return nil, errorWithDebugID(fmt.Errorf("error finding program address: %w", err), debugID)
}
addresses = append(addresses, &solana.AccountMeta{
PublicKey: address,
IsSigner: lookup.IsSigner,
IsWritable: lookup.IsWritable,
})
}
return addresses, nil
}

func (s *SolanaChainWriterService) getDerivedTableMap(ctx context.Context, lookupTables LookupTables, debugID string) (map[string]map[string][]*solana.AccountMeta, map[solana.PublicKey]solana.PublicKeySlice, error) {
derivedTableMap := make(map[string]map[string][]*solana.AccountMeta)
staticTableMap := make(map[solana.PublicKey]solana.PublicKeySlice)

// Read derived lookup tables
for _, derivedLookup := range lookupTables.DerivedLookupTables {
lookupTableMap, _, err := s.LoadTable(derivedLookup, ctx, s.reader, derivedTableMap, debugID)
if err != nil {
return nil, nil, errorWithDebugID(fmt.Errorf("error loading derived lookup table: %w", err), debugID)
}

// Merge the loaded table map into the result
for tableName, innerMap := range lookupTableMap {
if derivedTableMap[tableName] == nil {
derivedTableMap[tableName] = make(map[string][]*solana.AccountMeta)
}
for accountKey, metas := range innerMap {
derivedTableMap[tableName][accountKey] = metas
}
}
}

// Read static lookup tables
for _, staticTable := range lookupTables.StaticLookupTables {
// Parse the static table address
tableAddress, err := solana.PublicKeyFromBase58(staticTable)
if err != nil {
return nil, nil, errorWithDebugID(fmt.Errorf("invalid static lookup table address: %s, error: %w", staticTable, err), debugID)
}

// Fetch the account info for the static table
accountInfo, err := s.reader.GetAccountInfoWithOpts(ctx, tableAddress, &rpc.GetAccountInfoOpts{
Encoding: "base64",
Commitment: rpc.CommitmentConfirmed,
})
if err != nil || accountInfo == nil || accountInfo.Value == nil {
return nil, nil, errorWithDebugID(fmt.Errorf("error fetching account info for static table: %s, error: %w", staticTable, err), debugID)
}

// Decode the account data into an array of public keys
addresses, err := decodeLookupTable(accountInfo.Value.Data.GetBinary())
if err != nil {
return nil, nil, errorWithDebugID(fmt.Errorf("error decoding static lookup table data for %s: %w", staticTable, err), debugID)
}

// Add the static lookup table to the map
staticTableMap[tableAddress] = addresses
}

return derivedTableMap, staticTableMap, nil
}

func (s *SolanaChainWriterService) LoadTable(rlt DerivedLookupTable, ctx context.Context, reader client.Reader, derivedTableMap map[string]map[string][]*solana.AccountMeta, debugID string) (map[string]map[string][]*solana.AccountMeta, []*solana.AccountMeta, error) {
// Resolve all addresses specified by the identifier
lookupTableAddresses, err := GetAddresses(ctx, nil, []Lookup{rlt.Accounts}, nil, debugID)
if err != nil {
return nil, nil, errorWithDebugID(fmt.Errorf("error resolving addresses for lookup table: %w", err), debugID)
}

resultMap := make(map[string]map[string][]*solana.AccountMeta)
var lookupTableMetas []*solana.AccountMeta

// Iterate over each address of the lookup table
for _, addressMeta := range lookupTableAddresses {
// Fetch account info
accountInfo, err := reader.GetAccountInfoWithOpts(ctx, addressMeta.PublicKey, &rpc.GetAccountInfoOpts{
Encoding: "base64",
Commitment: rpc.CommitmentConfirmed,
})
if err != nil || accountInfo == nil || accountInfo.Value == nil {
return nil, nil, errorWithDebugID(fmt.Errorf("error fetching account info for address %s: %w", addressMeta.PublicKey.String(), err), debugID)
}

// Decode the account data into an array of public keys
addresses, err := decodeLookupTable(accountInfo.Value.Data.GetBinary())
if err != nil {
return nil, nil, errorWithDebugID(fmt.Errorf("error decoding lookup table data for address %s: %w", addressMeta.PublicKey.String(), err), debugID)
}

// Create the inner map for this lookup table
if resultMap[rlt.Name] == nil {
resultMap[rlt.Name] = make(map[string][]*solana.AccountMeta)
}

// Populate the inner map (keyed by the account public key)
for _, addr := range addresses {
resultMap[rlt.Name][addr.String()] = append(resultMap[rlt.Name][addr.String()], &solana.AccountMeta{
PublicKey: addr,
IsSigner: false,
IsWritable: false,
})
}

// Add the current lookup table address to the list of metas
lookupTableMetas = append(lookupTableMetas, addressMeta)
}

return resultMap, lookupTableMetas, nil
}

func decodeLookupTable(data []byte) (solana.PublicKeySlice, error) {
// Example logic to decode lookup table data; you may need to adjust based on the actual format of the data.
var addresses solana.PublicKeySlice

// Assuming the data is a list of 32-byte public keys in binary format:
for i := 0; i < len(data); i += solana.PublicKeyLength {
if i+solana.PublicKeyLength > len(data) {
return nil, fmt.Errorf("invalid lookup table data length")
}
address := solana.PublicKeyFromBytes(data[i : i+solana.PublicKeyLength])
addresses = append(addresses, address)
}

return addresses, nil
}

func (s *SolanaChainWriterService) FilterLookupTableAddresses(
accounts []*solana.AccountMeta,
derivedTableMap map[string]map[string][]*solana.AccountMeta,
Expand Down
Loading

0 comments on commit a7b8b8b

Please sign in to comment.