diff --git a/contracts/Anchor.toml b/contracts/Anchor.toml index 7d18084b0..30b788caa 100644 --- a/contracts/Anchor.toml +++ b/contracts/Anchor.toml @@ -30,5 +30,4 @@ access_controller = "9xi644bRR8birboDGdTiwBq3C7VEeR7VuamRYYXCubUW" contract-reader-interface = "6AfuXF6HapDUhQfE4nQG9C1SGtA1YjP3icaJyRfU4RyE" log-read-test = "J1zQwrBNBngz26jRPNWsUSZMHJwBwpkoDitXRV95LdK4" ocr_2 = "cjg3oHmg9uuPsP8D6g29NWvhySJkdYdAo9D25PRbKXJ" # need to rename the idl to satisfy anchor.js... -store = "HEvSKofvBgfaexv23kMabbYqxasxU3mQ4ibBMEmJWHny" -write_test = "39vbQVpEMtZtg3e6ZSE7nBSzmNZptmW45WnLkbqEe4TU" \ No newline at end of file +store = "HEvSKofvBgfaexv23kMabbYqxasxU3mQ4ibBMEmJWHny" \ No newline at end of file diff --git a/contracts/Cargo.lock b/contracts/Cargo.lock index 8e45fdbf4..25e8b1f5c 100644 --- a/contracts/Cargo.lock +++ b/contracts/Cargo.lock @@ -2671,13 +2671,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "write-test" -version = "0.1.0" -dependencies = [ - "anchor-lang", -] - [[package]] name = "zerocopy" version = "0.7.32" diff --git a/contracts/programs/contract-reader-interface/src/lib.rs b/contracts/programs/contract-reader-interface/src/lib.rs index 838190fe9..b02b68888 100644 --- a/contracts/programs/contract-reader-interface/src/lib.rs +++ b/contracts/programs/contract-reader-interface/src/lib.rs @@ -17,6 +17,19 @@ pub mod contract_reader_interface { Ok(()) } + + pub fn initialize_lookup_table( + ctx: Context, + lookup_table: Pubkey, + ) -> Result<()> { + let account = &mut ctx.accounts.write_data_account; + account.version = 1; + account.administrator = ctx.accounts.admin.key(); + account.pending_administrator = Pubkey::default(); + account.lookup_table = lookup_table; + + Ok(()) + } } #[derive(Accounts)] @@ -37,6 +50,34 @@ pub struct Initialize<'info> { pub system_program: Program<'info, System>, } +#[derive(Accounts)] +pub struct InitializeLookupTableData<'info> { + /// PDA for LookupTableDataAccount, derived from seeds and created by the System Program + #[account( + init, + payer = admin, + space = size_of::() + 8, + seeds = [b"data"], + bump + )] + pub write_data_account: Account<'info, LookupTableDataAccount>, + + /// Admin account that pays for PDA creation and signs the transaction + #[account(mut)] + pub admin: Signer<'info>, + + /// System Program required for PDA creation + pub system_program: Program<'info, System>, +} + +#[account] +pub struct LookupTableDataAccount { + pub version: u8, // Version of the data account + pub administrator: Pubkey, // Administrator public key + pub pending_administrator: Pubkey, // Pending administrator public key + pub lookup_table: Pubkey, // Address of the lookup table +} + #[account] pub struct DataAccount { pub idx: u64, diff --git a/contracts/programs/write_test/Cargo.toml b/contracts/programs/write_test/Cargo.toml deleted file mode 100644 index ee46888c6..000000000 --- a/contracts/programs/write_test/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "write-test" -version = "0.1.0" -description = "Created with Anchor" -edition = "2021" - -[lib] -crate-type = ["cdylib", "lib"] -name = "write_test" - -[features] -no-entrypoint = [] -no-idl = [] -no-log-ix-name = [] -cpi = ["no-entrypoint"] -default = [] - -[dependencies] -anchor-lang = "0.29.0" diff --git a/contracts/programs/write_test/Xargo.toml b/contracts/programs/write_test/Xargo.toml deleted file mode 100644 index 475fb71ed..000000000 --- a/contracts/programs/write_test/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] diff --git a/contracts/programs/write_test/src/lib.rs b/contracts/programs/write_test/src/lib.rs deleted file mode 100644 index 8d8fa3cac..000000000 --- a/contracts/programs/write_test/src/lib.rs +++ /dev/null @@ -1,51 +0,0 @@ -use anchor_lang::prelude::*; - -declare_id!("39vbQVpEMtZtg3e6ZSE7nBSzmNZptmW45WnLkbqEe4TU"); - -#[program] -pub mod write_test { - use super::*; - - pub fn initialize(ctx: Context, lookup_table: Pubkey) -> Result<()> { - let data = &mut ctx.accounts.data_account; - data.version = 1; - data.administrator = ctx.accounts.admin.key(); - data.pending_administrator = Pubkey::default(); - data.lookup_table = lookup_table; - - Ok(()) - } -} - -#[derive(Accounts)] -pub struct Initialize<'info> { - /// PDA account, derived from seeds and created by the System Program in this instruction - #[account( - init, // Initialize the account - payer = admin, // Specify the payer - space = DataAccount::SIZE, // Specify the account size - seeds = [b"data"], // Define the PDA seeds - bump // Use the bump seed - )] - pub data_account: Account<'info, DataAccount>, - - /// Admin account that pays for PDA creation and signs the transaction - #[account(mut)] - pub admin: Signer<'info>, - - /// System Program is required for PDA creation - pub system_program: Program<'info, System>, -} - -#[account] -pub struct DataAccount { - pub version: u8, - pub administrator: Pubkey, - pub pending_administrator: Pubkey, - pub lookup_table: Pubkey, -} - -impl DataAccount { - /// The total size of the `DataAccount` struct, including the discriminator - pub const SIZE: usize = 8 + 1 + 32 * 3; // 8 bytes for discriminator + 1 byte for version + 32 bytes * 3 pubkeys -} diff --git a/integration-tests/relayinterface/chain_components_test.go b/integration-tests/relayinterface/chain_components_test.go index d3ccf13a5..0d440efcb 100644 --- a/integration-tests/relayinterface/chain_components_test.go +++ b/integration-tests/relayinterface/chain_components_test.go @@ -30,8 +30,8 @@ import ( "github.com/smartcontractkit/chainlink-solana/integration-tests/solclient" "github.com/smartcontractkit/chainlink-solana/integration-tests/utils" "github.com/smartcontractkit/chainlink-solana/pkg/solana/chainreader" - "github.com/smartcontractkit/chainlink-solana/pkg/solana/client" "github.com/smartcontractkit/chainlink-solana/pkg/solana/config" + solanautils "github.com/smartcontractkit/chainlink-solana/pkg/solana/utils" ) func TestChainComponents(t *testing.T) { @@ -233,13 +233,13 @@ func (h *helper) Init(t *testing.T) { privateKey, err := solana.PrivateKeyFromBase58(solclient.DefaultPrivateKeysSolValidator[1]) require.NoError(t, err) - h.rpcURL, h.wsURL = setupTestValidator(t, privateKey.PublicKey().String()) + h.rpcURL, h.wsURL = solanautils.SetupTestValidatorWithAnchorPrograms(t, privateKey.PublicKey().String(), []string{"contract-reader-interface"}) h.wsClient, err = ws.Connect(tests.Context(t), h.wsURL) h.rpcClient = rpc.New(h.rpcURL) require.NoError(t, err) - client.FundTestAccounts(t, []solana.PublicKey{privateKey.PublicKey()}, h.rpcURL) + solanautils.FundAccounts(t, tests.Context(t), []solana.PrivateKey{privateKey}, h.rpcClient) pubkey, err := solana.PublicKeyFromBase58(programPubKey) require.NoError(t, err) @@ -380,26 +380,3 @@ func (h *helper) waitForTX(t *testing.T, sig solana.Signature, commitment rpc.Co } const programPubKey = "6AfuXF6HapDUhQfE4nQG9C1SGtA1YjP3icaJyRfU4RyE" - -// upgradeAuthority is admin solana.PrivateKey as string -func setupTestValidator(t *testing.T, upgradeAuthority string) (string, string) { - t.Helper() - - soPath := filepath.Join(utils.ContractsDir, "contract_reader_interface.so") - - _, err := os.Stat(soPath) - if err != nil { - t.Log(err.Error()) - t.FailNow() - } - - flags := []string{ - "--warp-slot", "42", - "--upgradeable-program", - programPubKey, - soPath, - upgradeAuthority, - } - - return client.SetupLocalSolNodeWithFlags(t, flags...) -} diff --git a/pkg/solana/chainwriter/chain_writer_test.go b/pkg/solana/chainwriter/chain_writer_test.go index d931fb6d8..43fb2d800 100644 --- a/pkg/solana/chainwriter/chain_writer_test.go +++ b/pkg/solana/chainwriter/chain_writer_test.go @@ -24,7 +24,7 @@ import ( txmMocks "github.com/smartcontractkit/chainlink-solana/pkg/solana/txm/mocks" ) -var writeTestIdlJSON = `{"version": "0.1.0","name": "write_test","instructions": [{"name": "initialize","accounts": [{"name": "dataAccount","isMut": true,"isSigner": false,"docs": ["PDA account, derived from seeds and created by the System Program in this instruction"]},{"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 is required for PDA creation"]}],"args": [{"name": "lookupTable","type": "publicKey"}]}],"accounts": [{"name": "DataAccount","type": {"kind": "struct","fields": [{"name": "version","type": "u8"},{"name": "administrator","type": "publicKey"},{"name": "pendingAdministrator","type": "publicKey"},{"name": "lookupTable","type": "publicKey"}]}}]}` +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"}}]}}]}` func TestChainWriter_GetAddresses(t *testing.T) { ctx := tests.Context(t) @@ -415,11 +415,11 @@ func TestChainWriter_SubmitTransaction(t *testing.T) { cwConfig := chainwriter.ChainWriterConfig{ Programs: map[string]chainwriter.ProgramConfig{ - "39vbQVpEMtZtg3e6ZSE7nBSzmNZptmW45WnLkbqEe4TU": { + "6AfuXF6HapDUhQfE4nQG9C1SGtA1YjP3icaJyRfU4RyE": { Methods: map[string]chainwriter.MethodConfig{ - "initialize": { + "initializeLookupTable": { FromAddress: admin.String(), - ChainSpecificName: "initialize", + ChainSpecificName: "initializeLookupTable", LookupTables: chainwriter.LookupTables{ DerivedLookupTables: []chainwriter.DerivedLookupTable{ { @@ -474,7 +474,7 @@ func TestChainWriter_SubmitTransaction(t *testing.T) { }, }, }, - IDL: writeTestIdlJSON, + IDL: testContractIDLJson, }, }, } @@ -486,7 +486,7 @@ func TestChainWriter_SubmitTransaction(t *testing.T) { t.Run("fails with invalid ABI", func(t *testing.T) { invalidCWConfig := chainwriter.ChainWriterConfig{ Programs: map[string]chainwriter.ProgramConfig{ - "write_test": { + "invalid_program": { Methods: map[string]chainwriter.MethodConfig{ "invalid": { ChainSpecificName: "invalid", @@ -504,21 +504,21 @@ 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, "39vbQVpEMtZtg3e6ZSE7nBSzmNZptmW45WnLkbqEe4TU", "initialize", args, txID, programID.String(), nil, nil) + submitErr := cw.SubmitTransaction(ctx, "6AfuXF6HapDUhQfE4nQG9C1SGtA1YjP3icaJyRfU4RyE", "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, "write_test", "initialize", args, txID, programID.String(), nil, nil) + submitErr := cw.SubmitTransaction(ctx, "contract_reader_interface", "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, "39vbQVpEMtZtg3e6ZSE7nBSzmNZptmW45WnLkbqEe4TU", "badMethod", args, txID, programID.String(), nil, nil) + submitErr := cw.SubmitTransaction(ctx, "6AfuXF6HapDUhQfE4nQG9C1SGtA1YjP3icaJyRfU4RyE", "badMethod", args, txID, programID.String(), nil, nil) require.Error(t, submitErr) }) @@ -526,7 +526,7 @@ func TestChainWriter_SubmitTransaction(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("39vbQVpEMtZtg3e6ZSE7nBSzmNZptmW45WnLkbqEe4TU") + 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 @@ -549,7 +549,7 @@ func TestChainWriter_SubmitTransaction(t *testing.T) { "seed1": seed1, "seed2": seed2, } - submitErr := cw.SubmitTransaction(ctx, "39vbQVpEMtZtg3e6ZSE7nBSzmNZptmW45WnLkbqEe4TU", "initialize", args, txID, programID.String(), nil, nil) + submitErr := cw.SubmitTransaction(ctx, "6AfuXF6HapDUhQfE4nQG9C1SGtA1YjP3icaJyRfU4RyE", "initializeLookupTable", args, txID, programID.String(), nil, nil) require.NoError(t, submitErr) }) } diff --git a/pkg/solana/chainwriter/helpers.go b/pkg/solana/chainwriter/helpers.go index bc256c60a..4fdd4f419 100644 --- a/pkg/solana/chainwriter/helpers.go +++ b/pkg/solana/chainwriter/helpers.go @@ -139,7 +139,7 @@ func InitializeDataAccount( pda, _, err := solana.FindProgramAddress([][]byte{[]byte("data")}, programID) require.NoError(t, err) - discriminator := GetDiscriminator("initialize") + discriminator := GetDiscriminator("initialize_lookup_table") instructionData := append(discriminator[:], lookupTable.Bytes()...) diff --git a/pkg/solana/chainwriter/lookups_test.go b/pkg/solana/chainwriter/lookups_test.go index 53972feac..630c47b9a 100644 --- a/pkg/solana/chainwriter/lookups_test.go +++ b/pkg/solana/chainwriter/lookups_test.go @@ -276,10 +276,10 @@ func TestLookupTables(t *testing.T) { sender, err := solana.NewRandomPrivateKey() require.NoError(t, err) - url := utils.SetupTestValidatorWithAnchorPrograms(t, utils.PathToAnchorConfig, sender.PublicKey().String()) + url, _ := utils.SetupTestValidatorWithAnchorPrograms(t, sender.PublicKey().String(), []string{"contract-reader-interface"}) rpcClient := rpc.New(url) - utils.FundAccounts(ctx, []solana.PrivateKey{sender}, rpcClient, t) + utils.FundAccounts(t, ctx, []solana.PrivateKey{sender}, rpcClient) cfg := config.NewDefault() solanaClient, err := client.NewClient(url, cfg, 5*time.Second, nil) @@ -401,8 +401,8 @@ func TestLookupTables(t *testing.T) { }) t.Run("Derived lookup table resolves properly with PDALookup address", func(t *testing.T) { - // Deployed write_test contract - programID := solana.MustPublicKeyFromBase58("39vbQVpEMtZtg3e6ZSE7nBSzmNZptmW45WnLkbqEe4TU") + // Deployed contract_reader_interface contract + programID := solana.MustPublicKeyFromBase58("6AfuXF6HapDUhQfE4nQG9C1SGtA1YjP3icaJyRfU4RyE") lookupKeys := chainwriter.CreateTestPubKeys(t, 5) lookupTable := chainwriter.CreateTestLookupTable(ctx, t, rpcClient, sender, lookupKeys) diff --git a/pkg/solana/utils/utils.go b/pkg/solana/utils/utils.go index 974ca6813..36870de66 100644 --- a/pkg/solana/utils/utils.go +++ b/pkg/solana/utils/utils.go @@ -159,7 +159,7 @@ func NewExtendLookupTableInstruction( ) } -func FundAccounts(ctx context.Context, accounts []solana.PrivateKey, solanaGoClient *rpc.Client, t *testing.T) { +func FundAccounts(t *testing.T, ctx context.Context, accounts []solana.PrivateKey, solanaGoClient *rpc.Client) { sigs := []solana.Signature{} for _, v := range accounts { sig, err := solanaGoClient.RequestAirdrop(ctx, v.PublicKey(), 1000*solana.LAMPORTS_PER_SOL, rpc.CommitmentFinalized) @@ -193,11 +193,7 @@ func FundAccounts(ctx context.Context, accounts []solana.PrivateKey, solanaGoCli } } -func DeployAllPrograms(t *testing.T, pathToAnchorConfig string, admin solana.PrivateKey) *rpc.Client { - return rpc.New(SetupTestValidatorWithAnchorPrograms(t, pathToAnchorConfig, admin.PublicKey().String())) -} - -func SetupTestValidatorWithAnchorPrograms(t *testing.T, pathToAnchorConfig string, upgradeAuthority string) string { +func SetupTestValidatorWithAnchorPrograms(t *testing.T, upgradeAuthority string, programs []string) (string, string) { anchorData := struct { Programs struct { Localnet map[string]string @@ -205,15 +201,18 @@ func SetupTestValidatorWithAnchorPrograms(t *testing.T, pathToAnchorConfig strin }{} // upload programs to validator - anchorBytes, err := os.ReadFile(pathToAnchorConfig) + anchorBytes, err := os.ReadFile(PathToAnchorConfig) require.NoError(t, err) require.NoError(t, toml.Unmarshal(anchorBytes, &anchorData)) - flags := []string{} - for k, v := range anchorData.Programs.Localnet { + flags := []string{"--warp-slot", "42"} + for i := range programs { + k := programs[i] + v := anchorData.Programs.Localnet[k] + fmt.Println("Program: ", k, v) k = strings.Replace(k, "-", "_", -1) flags = append(flags, "--upgradeable-program", v, filepath.Join(ContractsDir, k+".so"), upgradeAuthority) } - url, _ := client.SetupLocalSolNodeWithFlags(t, flags...) - return url + rpcURL, wsURL := client.SetupLocalSolNodeWithFlags(t, flags...) + return rpcURL, wsURL }