Skip to content

Commit

Permalink
Updated PDALookups Seeds field and fixed default accounts
Browse files Browse the repository at this point in the history
  • Loading branch information
silaslenihan committed Dec 27, 2024
1 parent f6302ac commit 54e3ae3
Show file tree
Hide file tree
Showing 6 changed files with 188 additions and 168 deletions.
24 changes: 12 additions & 12 deletions integration-tests/relayinterface/lookups_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,8 @@ func TestPDALookups(t *testing.T) {
pdaLookup := chainwriter.PDALookups{
Name: "TestPDA",
PublicKey: chainwriter.AccountConstant{Name: "ProgramID", Address: programID.String()},
Seeds: []chainwriter.Lookup{
chainwriter.AccountConstant{Name: "seed", Address: seed.String()},
Seeds: []chainwriter.Seed{
{Dynamic: chainwriter.AccountConstant{Name: "seed", Address: seed.String()}},
},
IsSigner: false,
IsWritable: true,
Expand Down Expand Up @@ -175,9 +175,9 @@ func TestPDALookups(t *testing.T) {
pdaLookup := chainwriter.PDALookups{
Name: "TestPDA",
PublicKey: chainwriter.AccountConstant{Name: "ProgramID", Address: programID.String()},
Seeds: []chainwriter.Lookup{
chainwriter.AccountLookup{Name: "seed1", Location: "test_seed"},
chainwriter.AccountLookup{Name: "seed2", Location: "another_seed"},
Seeds: []chainwriter.Seed{
{Dynamic: chainwriter.AccountLookup{Name: "seed1", Location: "test_seed"}},
{Dynamic: chainwriter.AccountLookup{Name: "seed2", Location: "another_seed"}},
},
IsSigner: false,
IsWritable: true,
Expand All @@ -198,8 +198,8 @@ func TestPDALookups(t *testing.T) {
pdaLookup := chainwriter.PDALookups{
Name: "TestPDA",
PublicKey: chainwriter.AccountConstant{Name: "ProgramID", Address: programID.String()},
Seeds: []chainwriter.Lookup{
chainwriter.AccountLookup{Name: "seed1", Location: "MissingSeed"},
Seeds: []chainwriter.Seed{
{Dynamic: chainwriter.AccountLookup{Name: "seed1", Location: "MissingSeed"}},
},
IsSigner: false,
IsWritable: true,
Expand Down Expand Up @@ -233,9 +233,9 @@ func TestPDALookups(t *testing.T) {
pdaLookup := chainwriter.PDALookups{
Name: "TestPDA",
PublicKey: chainwriter.AccountConstant{Name: "ProgramID", Address: programID.String()},
Seeds: []chainwriter.Lookup{
chainwriter.AccountLookup{Name: "seed1", Location: "test_seed"},
chainwriter.AccountLookup{Name: "seed2", Location: "another_seed"},
Seeds: []chainwriter.Seed{
{Dynamic: chainwriter.AccountLookup{Name: "seed1", Location: "test_seed"}},
{Dynamic: chainwriter.AccountLookup{Name: "seed2", Location: "another_seed"}},
},
IsSigner: false,
IsWritable: true,
Expand Down Expand Up @@ -403,8 +403,8 @@ func TestLookupTables(t *testing.T) {
Accounts: chainwriter.PDALookups{
Name: "DataAccountPDA",
PublicKey: chainwriter.AccountConstant{Name: "WriteTest", Address: programID.String()},
Seeds: []chainwriter.Lookup{
chainwriter.AccountLookup{Name: "seed1", Location: "seed1"},
Seeds: []chainwriter.Seed{
{Dynamic: chainwriter.AccountLookup{Name: "seed1", Location: "seed1"}},
},
IsSigner: false,
IsWritable: false,
Expand Down
44 changes: 22 additions & 22 deletions pkg/solana/chainwriter/ccip_example_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ func TestConfig() {
IsWritable: false,
},
// Seeds would be used if the user needed to look up addresses to use as seeds, which isn't the case here.
Seeds: []Lookup{
AccountLookup{Location: "Message.TokenAmounts.DestTokenAddress"},
Seeds: []Seed{
{Dynamic: AccountLookup{Location: "Message.TokenAmounts.DestTokenAddress"}},
},
IsSigner: false,
IsWritable: false,
Expand Down Expand Up @@ -86,9 +86,9 @@ func TestConfig() {
IsWritable: false,
},
// Similar to the RegistryTokenState above, the user is looking up PDA accounts based on the dest tokens.
Seeds: []Lookup{
AccountLookup{Location: "Message.TokenAmounts.DestTokenAddress"},
AccountLookup{Location: "Message.Header.DestChainSelector"},
Seeds: []Seed{
{Dynamic: AccountLookup{Location: "Message.TokenAmounts.DestTokenAddress"}},
{Dynamic: AccountLookup{Location: "Message.Header.DestChainSelector"}},
},
IsSigner: false,
IsWritable: false,
Expand Down Expand Up @@ -120,13 +120,13 @@ func TestConfig() {
IsWritable: false,
},
// The seed is the receiver address.
Seeds: []Lookup{
AccountLookup{
Seeds: []Seed{
{Dynamic: AccountLookup{
Name: "Receiver",
Location: "Message.Receiver",
IsSigner: false,
IsWritable: false,
},
}},
},
},
// Account constant
Expand All @@ -146,8 +146,8 @@ func TestConfig() {
IsWritable: false,
},
// The seed, once again, is the destination token address.
Seeds: []Lookup{
AccountLookup{Location: "Message.TokenAmounts.DestTokenAddress"},
Seeds: []Seed{
{Dynamic: AccountLookup{Location: "Message.TokenAmounts.DestTokenAddress"}},
},
IsSigner: false,
IsWritable: false,
Expand Down Expand Up @@ -175,9 +175,9 @@ func TestConfig() {
IsSigner: false,
IsWritable: false,
},
Seeds: []Lookup{
AccountLookup{Location: "Message.Header.DestChainSelector"},
AccountLookup{Location: "Message.Header.SourceChainSelector"},
Seeds: []Seed{
{Dynamic: AccountLookup{Location: "Message.Header.DestChainSelector"}},
{Dynamic: AccountLookup{Location: "Message.Header.SourceChainSelector"}},
},
IsSigner: false,
IsWritable: false,
Expand All @@ -191,11 +191,11 @@ func TestConfig() {
IsSigner: false,
IsWritable: false,
},
Seeds: []Lookup{
AccountLookup{
Seeds: []Seed{
{Dynamic: AccountLookup{
// The seed is the merkle root of the report, as passed into the input params.
Location: "args.MerkleRoot",
},
}},
},
IsSigner: false,
IsWritable: false,
Expand All @@ -211,9 +211,9 @@ func TestConfig() {
},
// In this case, the user configured multiple seeds. These will be used in conjunction
// with the public key to generate one or multiple PDA accounts.
Seeds: []Lookup{
AccountLookup{Location: "Message.Receiver"},
AccountLookup{Location: "Message.Header.DestChainSelector"},
Seeds: []Seed{
{Dynamic: AccountLookup{Location: "Message.Receiver"}},
{Dynamic: AccountLookup{Location: "Message.Header.DestChainSelector"}},
},
},
// Account constant
Expand Down Expand Up @@ -284,11 +284,11 @@ func TestConfig() {
IsSigner: false,
IsWritable: false,
},
Seeds: []Lookup{
AccountLookup{
Seeds: []Seed{
{Dynamic: AccountLookup{
// The seed is the merkle root of the report, as passed into the input params.
Location: "args.MerkleRoots",
},
}},
},
IsSigner: false,
IsWritable: false,
Expand Down
109 changes: 104 additions & 5 deletions pkg/solana/chainwriter/chain_writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"math/big"

"github.com/gagliardetto/solana-go"
addresslookuptable "github.com/gagliardetto/solana-go/programs/address-lookup-table"
"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 @@ -80,22 +82,22 @@ func parseIDLCodecs(config ChainWriterConfig) (map[string]types.Codec, error) {
for program, programConfig := range config.Programs {
var idl codec.IDL
if err := json.Unmarshal([]byte(programConfig.IDL), &idl); err != nil {
return nil, fmt.Errorf("failed to unmarshal IDL: %w", err)
return nil, fmt.Errorf("failed to unmarshal IDL for program: %s, error: %w", program, err)
}
idlCodec, err := codec.NewIDLInstructionsCodec(idl, binary.LittleEndian())
if err != nil {
return nil, fmt.Errorf("failed to create codec from IDL: %w", err)
return nil, fmt.Errorf("failed to create codec from IDL for program: %s, error: %w", program, err)
}
for method, methodConfig := range programConfig.Methods {
if methodConfig.InputModifications != nil {
modConfig, err := methodConfig.InputModifications.ToModifier(codec.DecoderHooks...)
if err != nil {
return nil, fmt.Errorf("failed to create input modifications: %w", err)
return nil, fmt.Errorf("failed to create input modifications for method %s.%s, error: %w", program, method, err)
}
// add mods to codec
idlCodec, err = codec.NewNamedModifierCodec(idlCodec, method, modConfig)
if err != nil {
return nil, fmt.Errorf("failed to create named codec: %w", err)
return nil, fmt.Errorf("failed to create named codec for method %s.%s, error: %w", program, method, err)
}
}
}
Expand Down Expand Up @@ -250,6 +252,10 @@ func (s *SolanaChainWriterService) SubmitTransaction(ctx context.Context, contra

codec := s.codecs[contractName]
encodedPayload, err := codec.Encode(ctx, args, method)

discriminator := GetDiscriminator(methodConfig.ChainSpecificName)
encodedPayload = append(discriminator[:], encodedPayload...)

if err != nil {
return errorWithDebugID(fmt.Errorf("error encoding transaction payload: %w", err), debugID)
}
Expand Down Expand Up @@ -302,7 +308,7 @@ func (s *SolanaChainWriterService) SubmitTransaction(ctx context.Context, contra
}

// Enqueue transaction
if err = s.txm.Enqueue(ctx, accounts[0].PublicKey.String(), tx, &transactionID, blockhash.Value.LastValidBlockHeight); err != nil {
if err = s.txm.Enqueue(ctx, methodConfig.FromAddress, tx, &transactionID, blockhash.Value.LastValidBlockHeight); err != nil {
return errorWithDebugID(fmt.Errorf("error enqueuing transaction: %w", err), debugID)
}

Expand All @@ -327,6 +333,99 @@ func (s *SolanaChainWriterService) GetFeeComponents(ctx context.Context) (*types
}, nil
}

func (s *SolanaChainWriterService) ResolveLookupTables(ctx context.Context, args any, lookupTables LookupTables) (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 {
// 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, s.reader, derivedTableMap)
if err != nil {
return nil, nil, fmt.Errorf("error loading derived lookup table: %w", err)
}

// 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 {
addressses, err := getLookupTableAddresses(ctx, s.reader, staticTable)
if err != nil {
return nil, nil, fmt.Errorf("error fetching static lookup table address: %w", err)
}
staticTableMap[staticTable] = addressses
}

return derivedTableMap, staticTableMap, nil
}

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

// Nested map in case the lookup table resolves to multiple addresses
resultMap := make(map[string]map[string][]*solana.AccountMeta)
var lookupTableMetas []*solana.AccountMeta

// Iterate over each address of the lookup table
for _, addressMeta := range lookupTableAddresses {
// Read the full list of addresses from the lookup table
addresses, err := getLookupTableAddresses(ctx, reader, addressMeta.PublicKey)
if err != nil {
return nil, nil, fmt.Errorf("error fetching lookup table address: %s, error: %w", addressMeta.PublicKey, err)
}

// 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][addressMeta.PublicKey.String()] = append(resultMap[rlt.Name][addressMeta.PublicKey.String()], &solana.AccountMeta{
PublicKey: addr,
IsSigner: addressMeta.IsSigner,
IsWritable: addressMeta.IsWritable,
})
}

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

return resultMap, lookupTableMetas, nil
}

func getLookupTableAddresses(ctx context.Context, reader client.Reader, tableAddress solana.PublicKey) (solana.PublicKeySlice, error) {
// Fetch the account info for the static table
accountInfo, err := reader.GetAccountInfoWithOpts(ctx, tableAddress, &rpc.GetAccountInfoOpts{
Encoding: "base64",
Commitment: rpc.CommitmentFinalized,
})

if err != nil || accountInfo == nil || accountInfo.Value == nil {
return nil, fmt.Errorf("error fetching account info for table: %s, error: %w", tableAddress.String(), err)
}
alt, err := addresslookuptable.DecodeAddressLookupTableState(accountInfo.GetBinary())
if err != nil {
return nil, fmt.Errorf("error decoding address lookup table state: %w", err)
}
return alt.Addresses, nil
}

func (s *SolanaChainWriterService) Start(_ context.Context) error {
return s.StartOnce(ServiceName, func() error {
return nil
Expand Down
24 changes: 12 additions & 12 deletions pkg/solana/chainwriter/chain_writer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,9 @@ func TestChainWriter_GetAddresses(t *testing.T) {
Accounts: chainwriter.PDALookups{
Name: "DataAccountPDA",
PublicKey: chainwriter.AccountConstant{Name: "WriteTest", Address: programID.String()},
Seeds: []chainwriter.Lookup{
Seeds: []chainwriter.Seed{
// extract seed2 for PDA lookup
chainwriter.AccountLookup{Name: "seed2", Location: "seed2"},
{Dynamic: chainwriter.AccountLookup{Name: "seed2", Location: "seed2"}},
},
IsSigner: derivedTablePdaLookupMeta.IsSigner,
IsWritable: derivedTablePdaLookupMeta.IsWritable,
Expand Down Expand Up @@ -129,9 +129,9 @@ func TestChainWriter_GetAddresses(t *testing.T) {
chainwriter.PDALookups{
Name: "DataAccountPDA",
PublicKey: chainwriter.AccountConstant{Name: "WriteTest", Address: solana.SystemProgramID.String()},
Seeds: []chainwriter.Lookup{
Seeds: []chainwriter.Seed{
// extract seed1 for PDA lookup
chainwriter.AccountLookup{Name: "seed1", Location: "seed1"},
{Dynamic: chainwriter.AccountLookup{Name: "seed1", Location: "seed1"}},
},
IsSigner: pdaLookupMeta.IsSigner,
IsWritable: pdaLookupMeta.IsWritable,
Expand Down Expand Up @@ -272,9 +272,9 @@ func TestChainWriter_FilterLookupTableAddresses(t *testing.T) {
Accounts: chainwriter.PDALookups{
Name: "DataAccountPDA",
PublicKey: chainwriter.AccountConstant{Name: "WriteTest", Address: programID.String()},
Seeds: []chainwriter.Lookup{
Seeds: []chainwriter.Seed{
// extract seed1 for PDA lookup
chainwriter.AccountLookup{Name: "seed1", Location: "seed1"},
{Dynamic: chainwriter.AccountLookup{Name: "seed1", Location: "seed1"}},
},
IsSigner: true,
IsWritable: true,
Expand All @@ -289,9 +289,9 @@ func TestChainWriter_FilterLookupTableAddresses(t *testing.T) {
Accounts: chainwriter.PDALookups{
Name: "MiscPDA",
PublicKey: chainwriter.AccountConstant{Name: "UnusedAccount", Address: unusedProgramID.String()},
Seeds: []chainwriter.Lookup{
Seeds: []chainwriter.Seed{
// extract seed2 for PDA lookup
chainwriter.AccountLookup{Name: "seed2", Location: "seed2"},
{Dynamic: chainwriter.AccountLookup{Name: "seed2", Location: "seed2"}},
},
IsSigner: true,
IsWritable: true,
Expand Down Expand Up @@ -428,9 +428,9 @@ func TestChainWriter_SubmitTransaction(t *testing.T) {
Accounts: chainwriter.PDALookups{
Name: "DataAccountPDA",
PublicKey: chainwriter.AccountConstant{Name: "WriteTest", Address: programID.String()},
Seeds: []chainwriter.Lookup{
Seeds: []chainwriter.Seed{
// extract seed2 for PDA lookup
chainwriter.AccountLookup{Name: "seed2", Location: "seed2"},
{Dynamic: chainwriter.AccountLookup{Name: "seed2", Location: "seed2"}},
},
IsSigner: false,
IsWritable: false,
Expand Down Expand Up @@ -459,9 +459,9 @@ func TestChainWriter_SubmitTransaction(t *testing.T) {
chainwriter.PDALookups{
Name: "DataAccountPDA",
PublicKey: chainwriter.AccountConstant{Name: "WriteTest", Address: solana.SystemProgramID.String()},
Seeds: []chainwriter.Lookup{
Seeds: []chainwriter.Seed{
// extract seed1 for PDA lookup
chainwriter.AccountLookup{Name: "seed1", Location: "seed1"},
{Dynamic: chainwriter.AccountLookup{Name: "seed1", Location: "seed1"}},
},
IsSigner: false,
IsWritable: false,
Expand Down
Loading

0 comments on commit 54e3ae3

Please sign in to comment.