From 09cba079283c027b354c60779678cd4d85238bc5 Mon Sep 17 00:00:00 2001 From: swimricky Date: Tue, 18 Apr 2023 16:33:21 -0700 Subject: [PATCH 1/5] feat(accumulator-updater): add funding account for AccumulatorInput creation --- .../src/instructions/put_all.rs | 34 +++++++++++++++---- .../programs/accumulator_updater/src/lib.rs | 5 +-- .../src/state/accumulator_input.rs | 2 ++ .../src/instructions/add_price.rs | 4 ++- .../src/instructions/update_price.rs | 6 ++-- .../tests/accumulator_updater.ts | 14 ++++++++ 6 files changed, 53 insertions(+), 12 deletions(-) diff --git a/accumulator_updater/programs/accumulator_updater/src/instructions/put_all.rs b/accumulator_updater/programs/accumulator_updater/src/instructions/put_all.rs index 4adb7a49a7..d51d4fb17d 100644 --- a/accumulator_updater/programs/accumulator_updater/src/instructions/put_all.rs +++ b/accumulator_updater/programs/accumulator_updater/src/instructions/put_all.rs @@ -14,6 +14,7 @@ use { pub const ACCUMULATOR: &[u8; 11] = b"accumulator"; +pub const FUND: &[u8; 4] = b"fund"; pub fn put_all<'info>( ctx: Context<'_, '_, '_, 'info, PutAll<'info>>, @@ -39,17 +40,23 @@ pub fn put_all<'info>( &crate::ID, ); require_keys_eq!(accumulator_input_ai.key(), pda); - let signer_seeds = &[ + let signer_seeds = [ cpi_caller.as_ref(), ACCUMULATOR.as_ref(), base_account_key.as_ref(), &[bump], ]; + let fund_pda_bump = *ctx + .bumps + .get("fund") + .ok_or(AccumulatorUpdaterError::FundBumpNotFound)?; + let fund_signer_seeds = [ACCUMULATOR.as_ref(), FUND.as_ref(), &[fund_pda_bump]]; PutAll::create_account( accumulator_input_ai, 8 + AccumulatorInput::INIT_SPACE, - &ctx.accounts.payer, - signer_seeds, + &ctx.accounts.fund, + // seeds, + &[signer_seeds.as_slice(), fund_signer_seeds.as_slice()], &ctx.accounts.system_program, )?; loader = AccountLoader::::try_from_unchecked( @@ -86,22 +93,35 @@ pub fn is_uninitialized_account(ai: &AccountInfo) -> bool { ai.data_is_empty() && ai.owner == &system_program::ID } + #[derive(Accounts)] #[instruction( base_account_key: Pubkey)] pub struct PutAll<'info> { - #[account(mut)] - pub payer: Signer<'info>, + /// `Fund` is a system account that holds + /// the lamports that will be used to fund + /// `AccumulatorInput` account initialization + #[account( + mut, + seeds = [ + b"accumulator".as_ref(), + b"fund".as_ref(), + ], + owner = system_program::System::id(), + bump, + )] + pub fund: SystemAccount<'info>, pub whitelist_verifier: WhitelistVerifier<'info>, pub system_program: Program<'info, System>, // remaining_accounts: - [AccumulatorInput PDA] } + impl<'info> PutAll<'info> { fn create_account<'a>( account_info: &AccountInfo<'a>, space: usize, payer: &AccountInfo<'a>, - seeds: &[&[u8]], + seeds: &[&[&[u8]]], system_program: &AccountInfo<'a>, ) -> Result<()> { let lamports = Rent::get()?.minimum_balance(space); @@ -113,7 +133,7 @@ impl<'info> PutAll<'info> { from: payer.to_account_info(), to: account_info.to_account_info(), }, - &[seeds], + seeds, ), lamports, space.try_into().unwrap(), diff --git a/accumulator_updater/programs/accumulator_updater/src/lib.rs b/accumulator_updater/programs/accumulator_updater/src/lib.rs index 4fe3cd9e3a..88ad4e766b 100644 --- a/accumulator_updater/programs/accumulator_updater/src/lib.rs +++ b/accumulator_updater/programs/accumulator_updater/src/lib.rs @@ -88,8 +88,7 @@ pub mod accumulator_updater { #[derive(Accounts)] pub struct Initialize<'info> { #[account(mut)] - pub payer: Signer<'info>, - + pub payer: Signer<'info>, #[account( init, payer = payer, @@ -142,4 +141,6 @@ pub enum AccumulatorUpdaterError { AccumulatorInputNotWritable, #[msg("Accumulator Input not provided")] AccumulatorInputNotProvided, + #[msg("Fund Bump not found")] + FundBumpNotFound, } diff --git a/accumulator_updater/programs/accumulator_updater/src/state/accumulator_input.rs b/accumulator_updater/programs/accumulator_updater/src/state/accumulator_input.rs index dcaf395687..afd7079a7a 100644 --- a/accumulator_updater/programs/accumulator_updater/src/state/accumulator_input.rs +++ b/accumulator_updater/programs/accumulator_updater/src/state/accumulator_input.rs @@ -6,6 +6,7 @@ use { anchor_lang::prelude::*, }; + /// `AccumulatorInput` is an arbitrary set of bytes /// that will be included in the AccumulatorSysvar /// @@ -42,6 +43,7 @@ pub struct AccumulatorHeader { pub end_offsets: [u16; 255], // 510 } + impl AccumulatorHeader { // HEADER_LEN allows for append-only forward-compatibility for the header. // this is the number of bytes from the beginning of the account_info.data diff --git a/accumulator_updater/programs/mock-cpi-caller/src/instructions/add_price.rs b/accumulator_updater/programs/mock-cpi-caller/src/instructions/add_price.rs index d6713ee0e5..dd5b9e5bfe 100644 --- a/accumulator_updater/programs/mock-cpi-caller/src/instructions/add_price.rs +++ b/accumulator_updater/programs/mock-cpi-caller/src/instructions/add_price.rs @@ -61,7 +61,7 @@ impl<'info> AddPrice<'info> { inputs: Vec>, ) -> anchor_lang::Result<()> { let mut accounts = vec![ - AccountMeta::new(ctx.accounts.payer.key(), true), + AccountMeta::new(ctx.accounts.fund.key(), false), AccountMeta::new_readonly(ctx.accounts.accumulator_whitelist.key(), false), AccountMeta::new_readonly(ctx.accounts.ixs_sysvar.key(), false), AccountMeta::new_readonly(ctx.accounts.system_program.key(), false), @@ -113,6 +113,8 @@ pub struct AddPrice<'info> { pub pyth_price_account: AccountLoader<'info, PriceAccount>, #[account(mut)] pub payer: Signer<'info>, + #[account(mut)] + pub fund: SystemAccount<'info>, /// also needed for accumulator_updater pub system_program: Program<'info, System>, /// CHECK: whitelist diff --git a/accumulator_updater/programs/mock-cpi-caller/src/instructions/update_price.rs b/accumulator_updater/programs/mock-cpi-caller/src/instructions/update_price.rs index 608ca70789..efd6146d2a 100644 --- a/accumulator_updater/programs/mock-cpi-caller/src/instructions/update_price.rs +++ b/accumulator_updater/programs/mock-cpi-caller/src/instructions/update_price.rs @@ -45,8 +45,10 @@ pub struct UpdatePrice<'info> { bump, )] pub pyth_price_account: AccountLoader<'info, PriceAccount>, + // #[account(mut)] + // pub payer: Signer<'info>, #[account(mut)] - pub payer: Signer<'info>, + pub fund: SystemAccount<'info>, /// Needed for accumulator_updater pub system_program: Program<'info, System>, /// CHECK: whitelist @@ -91,7 +93,7 @@ impl<'info> UpdatePrice<'info> { values: Vec>, ) -> anchor_lang::Result<()> { let mut accounts = vec![ - AccountMeta::new(ctx.accounts.payer.key(), true), + AccountMeta::new(ctx.accounts.fund.key(), false), AccountMeta::new_readonly(ctx.accounts.accumulator_whitelist.key(), false), AccountMeta::new_readonly(ctx.accounts.ixs_sysvar.key(), false), AccountMeta::new_readonly(ctx.accounts.system_program.key(), false), diff --git a/accumulator_updater/tests/accumulator_updater.ts b/accumulator_updater/tests/accumulator_updater.ts index e096c1aff5..486b1e8391 100644 --- a/accumulator_updater/tests/accumulator_updater.ts +++ b/accumulator_updater/tests/accumulator_updater.ts @@ -15,6 +15,10 @@ const accumulatorUpdaterProgram = anchor.workspace .AccumulatorUpdater as Program; const mockCpiProg = anchor.workspace.MockCpiCaller as Program; let whitelistAuthority = anchor.web3.Keypair.generate(); +const [fundPda] = anchor.web3.PublicKey.findProgramAddressSync( + [Buffer.from("accumulator"), Buffer.from("fund")], + accumulatorUpdaterProgram.programId +); const pythPriceAccountId = new anchor.BN(1); const addPriceParams = { @@ -33,6 +37,7 @@ const [pythPriceAccountPk] = anchor.web3.PublicKey.findProgramAddressSync( mockCpiProg.programId ); +let fundBalance = 100 * anchor.web3.LAMPORTS_PER_SOL; describe("accumulator_updater", () => { // Configure the client to use the local cluster. let provider = anchor.AnchorProvider.env(); @@ -44,6 +49,10 @@ describe("accumulator_updater", () => { accumulatorUpdaterProgram.programId ); + before("transfer lamports to the fund", async () => { + await provider.connection.requestAirdrop(fundPda, fundBalance); + }); + it("Is initialized!", async () => { // Add your test here. const tx = await accumulatorUpdaterProgram.methods @@ -104,6 +113,7 @@ describe("accumulator_updater", () => { const mockCpiCallerAddPriceTxPubkeys = await mockCpiProg.methods .addPrice(addPriceParams) .accounts({ + fund: fundPda, systemProgram: anchor.web3.SystemProgram.programId, ixsSysvar: anchor.web3.SYSVAR_INSTRUCTIONS_PUBKEY, accumulatorWhitelist: whitelistPubkey, @@ -219,6 +229,9 @@ describe("accumulator_updater", () => { accumulatorAccounts .map((a) => a.toString()) .includes(accumulatorPdaKey.toString()); + + const fundBalanceAfter = await provider.connection.getBalance(fundPda); + assert.isTrue(fundBalance > fundBalanceAfter); }); it("Mock CPI Program - UpdatePrice", async () => { @@ -233,6 +246,7 @@ describe("accumulator_updater", () => { await mockCpiProg.methods .updatePrice(updatePriceParams) .accounts({ + fund: fundPda, pythPriceAccount: pythPriceAccountPk, ixsSysvar: anchor.web3.SYSVAR_INSTRUCTIONS_PUBKEY, accumulatorWhitelist: whitelistPubkey, From 9d833753b897b65f6462a7cba7be5034cba12711 Mon Sep 17 00:00:00 2001 From: swimricky Date: Wed, 19 Apr 2023 09:06:30 -0700 Subject: [PATCH 2/5] feat(accumulator-updater): add ix for testing cpi max size add steps to notes.md on how to test against solana-test-validator with features deactivated --- accumulator_updater/Anchor.toml | 2 + accumulator_updater/NOTES.md | 23 ++++++- .../programs/accumulator_updater/src/lib.rs | 1 + .../src/instructions/cpi_max_test.rs | 54 +++++++++++++++++ .../mock-cpi-caller/src/instructions/mod.rs | 3 + .../programs/mock-cpi-caller/src/lib.rs | 10 ++++ .../programs/mock-cpi-caller/src/message.rs | 1 + .../mock-cpi-caller/src/message/price.rs | 32 ++++++++++ .../tests/accumulator_updater.ts | 60 ++++++++++++++++++- 9 files changed, 182 insertions(+), 4 deletions(-) create mode 100644 accumulator_updater/programs/mock-cpi-caller/src/instructions/cpi_max_test.rs diff --git a/accumulator_updater/Anchor.toml b/accumulator_updater/Anchor.toml index c0e297964a..72f000ba4f 100644 --- a/accumulator_updater/Anchor.toml +++ b/accumulator_updater/Anchor.toml @@ -4,6 +4,8 @@ skip-lint = false [programs.localnet] accumulator_updater = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" mock_cpi_caller = "Dg5PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" +#accumulator_updater = "3xzsrbvWEM4SMM2W7wbVofpDi3zrdhb2Z2xLwMEGJQiZ" +#mock_cpi_caller = "EKwG38D6Sd67NqsAVc4R7Hqr6mZFPzFotzBchoQJRRDe" [registry] url = "https://api.apr.dev" diff --git a/accumulator_updater/NOTES.md b/accumulator_updater/NOTES.md index e0e7b7fe97..c5d4c587ba 100644 --- a/accumulator_updater/NOTES.md +++ b/accumulator_updater/NOTES.md @@ -1,12 +1,29 @@ +## Testing + +- run `anchor test` if no special customization for the test-validator is needed +- `anchor test` will run `solana-test-validator` will all features activated. + One of the features activated on the test-validator which is not currently activated on pythnet is + +``` +"GDH5TVdbTPUpRnXaRyQqiKUa7uZAbZ28Q2N9bhbKoMLm loosen cpi size restrictions #26641" +``` + +In order to run `solana-test-validator` with this feature deactivated, do the following: + +1. open a terminal and run `solana-test-validator --reset --deactivate-feature GDH5TVdbTPUpRnXaRyQqiKUa7uZAbZ28Q2N9bhbKoMLm` +2. open a separate terminal and run `anchor build` in the `accumulator_updater` dir +3. get the pubkeys of the program keypairs `solana address -k accumulator_updater/target/deploy/.json` +4. change the pubkeys in the `declare_id!` macro to these keypairs +5. update `Anchor.toml` `[programs.localnet]` programIds as well +6. run `anchor test --skip-local-validator` + ## Questions 1. Do we need to support multiple Whitelists? 2. Support multiple accumulators 1. should each accumulator store a different type of data? => implications for length of merkle proof - 2. -3. authority? -4. how to know what went into the `AccumulatorAccount` (for deserializing/proofs) +3. how to know what went into the `AccumulatorAccount` (for deserializing/proofs) 1. Header? ## To Do diff --git a/accumulator_updater/programs/accumulator_updater/src/lib.rs b/accumulator_updater/programs/accumulator_updater/src/lib.rs index 88ad4e766b..c8e6db313e 100644 --- a/accumulator_updater/programs/accumulator_updater/src/lib.rs +++ b/accumulator_updater/programs/accumulator_updater/src/lib.rs @@ -10,6 +10,7 @@ use { }; declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); +// declare_id!("3xzsrbvWEM4SMM2W7wbVofpDi3zrdhb2Z2xLwMEGJQiZ"); #[program] pub mod accumulator_updater { diff --git a/accumulator_updater/programs/mock-cpi-caller/src/instructions/cpi_max_test.rs b/accumulator_updater/programs/mock-cpi-caller/src/instructions/cpi_max_test.rs new file mode 100644 index 0000000000..de91bd5476 --- /dev/null +++ b/accumulator_updater/programs/mock-cpi-caller/src/instructions/cpi_max_test.rs @@ -0,0 +1,54 @@ +use { + crate::{ + instructions::{ + UpdatePrice, + UpdatePriceParams, + }, + message::{ + get_schemas, + price::{ + CompactPriceMessage, + DummyPriceMessage, + FullPriceMessage, + }, + AccumulatorSerializer, + }, + state::PythAccountType, + }, + anchor_lang::prelude::*, +}; + +pub fn cpi_max_test<'info>( + ctx: Context<'_, '_, '_, 'info, UpdatePrice<'info>>, + params: UpdatePriceParams, + num_messages: u8, +) -> Result<()> { + let mut inputs = vec![]; + let _schemas = get_schemas(PythAccountType::Price); + + { + let pyth_price_acct = &mut ctx.accounts.pyth_price_account.load_mut()?; + pyth_price_acct.update(params)?; + + let price_full_data = FullPriceMessage::from(&**pyth_price_acct).accumulator_serialize()?; + + inputs.push(price_full_data); + + + let price_compact_data = + CompactPriceMessage::from(&**pyth_price_acct).accumulator_serialize()?; + inputs.push(price_compact_data); + + for _ in 0..num_messages { + let price_dummy_data = + DummyPriceMessage::from(&**pyth_price_acct).accumulator_serialize()?; + inputs.push(price_dummy_data); + } + } + + let input_len = inputs.iter().map(|x| x.len()).sum::(); + msg!("input_len: {}", input_len); + + + UpdatePrice::emit_accumulator_inputs(ctx, inputs) +} diff --git a/accumulator_updater/programs/mock-cpi-caller/src/instructions/mod.rs b/accumulator_updater/programs/mock-cpi-caller/src/instructions/mod.rs index f495719e70..2c677a4f2b 100644 --- a/accumulator_updater/programs/mock-cpi-caller/src/instructions/mod.rs +++ b/accumulator_updater/programs/mock-cpi-caller/src/instructions/mod.rs @@ -1,9 +1,12 @@ use anchor_lang::solana_program::hash::hashv; pub use { add_price::*, + cpi_max_test::*, update_price::*, }; + mod add_price; +mod cpi_max_test; mod update_price; /// Generate discriminator to be able to call anchor program's ix diff --git a/accumulator_updater/programs/mock-cpi-caller/src/lib.rs b/accumulator_updater/programs/mock-cpi-caller/src/lib.rs index fae439cb79..09b6caba61 100644 --- a/accumulator_updater/programs/mock-cpi-caller/src/lib.rs +++ b/accumulator_updater/programs/mock-cpi-caller/src/lib.rs @@ -8,6 +8,7 @@ pub mod message; mod state; declare_id!("Dg5PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); +// declare_id!("EKwG38D6Sd67NqsAVc4R7Hqr6mZFPzFotzBchoQJRRDe"); #[program] pub mod mock_cpi_caller { @@ -28,6 +29,15 @@ pub mod mock_cpi_caller { ) -> Result<()> { instructions::update_price(ctx, params) } + + /// num_messages is the number of 1kb messages to send to the CPI + pub fn cpi_max_test<'info>( + ctx: Context<'_, '_, '_, 'info, UpdatePrice<'info>>, + params: UpdatePriceParams, + num_messages: u8, + ) -> Result<()> { + instructions::cpi_max_test(ctx, params, num_messages) + } } diff --git a/accumulator_updater/programs/mock-cpi-caller/src/message.rs b/accumulator_updater/programs/mock-cpi-caller/src/message.rs index bee298992f..ca70cc5732 100644 --- a/accumulator_updater/programs/mock-cpi-caller/src/message.rs +++ b/accumulator_updater/programs/mock-cpi-caller/src/message.rs @@ -8,6 +8,7 @@ pub enum MessageSchema { Full = 0, Compact = 1, Minimal = 2, + Dummy = 3, } impl MessageSchema { diff --git a/accumulator_updater/programs/mock-cpi-caller/src/message/price.rs b/accumulator_updater/programs/mock-cpi-caller/src/message/price.rs index ba1f50d05f..133aa55d6c 100644 --- a/accumulator_updater/programs/mock-cpi-caller/src/message/price.rs +++ b/accumulator_updater/programs/mock-cpi-caller/src/message/price.rs @@ -113,3 +113,35 @@ impl AccumulatorSerializer for FullPriceMessage { Ok(bytes) } } + + +#[repr(C)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct DummyPriceMessage { + pub header: MessageHeader, + pub data: [u8; 1017], +} + +impl DummyPriceMessage { + pub const SIZE: usize = 1017; +} + +impl From<&PriceAccount> for DummyPriceMessage { + fn from(_other: &PriceAccount) -> Self { + Self { + header: MessageHeader::new(MessageSchema::Dummy, Self::SIZE as u32), + data: [0u8; 1017], + } + } +} + +impl AccumulatorSerializer for DummyPriceMessage { + fn accumulator_serialize(&self) -> Result> { + let mut bytes = vec![]; + bytes.write_all(&self.header.schema.to_be_bytes())?; + bytes.write_all(&self.header.version.to_be_bytes())?; + bytes.write_all(&self.header.size.to_be_bytes())?; + bytes.extend_from_slice(&self.data); + Ok(bytes) + } +} diff --git a/accumulator_updater/tests/accumulator_updater.ts b/accumulator_updater/tests/accumulator_updater.ts index 486b1e8391..66e6eda995 100644 --- a/accumulator_updater/tests/accumulator_updater.ts +++ b/accumulator_updater/tests/accumulator_updater.ts @@ -287,6 +287,64 @@ describe("accumulator_updater", () => { assert.isTrue(pm.priceExpo.eq(updatePriceParams.priceExpo)); }); }); + + it("Mock CPI Program - CPI Max Test", async () => { + // with loosen CPI feature activated, max cpi instruction size len is 10KB + for (let num_msgs = 1; num_msgs < 9; num_msgs++) { + console.info(`testing num_msgs: ${num_msgs}`); + const updatePriceParams = { + price: new anchor.BN(10 * num_msgs + 5), + priceExpo: new anchor.BN(10 & (num_msgs + 6)), + ema: new anchor.BN(10 * num_msgs + 7), + emaExpo: new anchor.BN(10 * num_msgs + 8), + }; + + let accumulatorPdaMeta = getAccumulatorPdaMeta(pythPriceAccountPk); + await mockCpiProg.methods + .cpiMaxTest(updatePriceParams, num_msgs) + .accounts({ + fund: fundPda, + pythPriceAccount: pythPriceAccountPk, + ixsSysvar: anchor.web3.SYSVAR_INSTRUCTIONS_PUBKEY, + accumulatorWhitelist: whitelistPubkey, + accumulatorProgram: accumulatorUpdaterProgram.programId, + }) + .remainingAccounts([accumulatorPdaMeta]) + .preInstructions([ + ComputeBudgetProgram.setComputeUnitLimit({ units: 1_000_000 }), + ]) + .rpc({ + skipPreflight: true, + }); + + const pythPriceAccount = await mockCpiProg.account.priceAccount.fetch( + pythPriceAccountPk + ); + assert.isTrue(pythPriceAccount.price.eq(updatePriceParams.price)); + assert.isTrue(pythPriceAccount.priceExpo.eq(updatePriceParams.priceExpo)); + assert.isTrue(pythPriceAccount.ema.eq(updatePriceParams.ema)); + assert.isTrue(pythPriceAccount.emaExpo.eq(updatePriceParams.emaExpo)); + const accumulatorInput = + await accumulatorUpdaterProgram.account.accumulatorInput.fetch( + accumulatorPdaMeta.pubkey + ); + const updatedAccumulatorPriceMessages = + parseAccumulatorInput(accumulatorInput); + + console.log( + `updatedAccumulatorPriceMessages: ${JSON.stringify( + updatedAccumulatorPriceMessages, + null, + 2 + )}` + ); + updatedAccumulatorPriceMessages.forEach((pm) => { + assert.isTrue(pm.id.eq(addPriceParams.id)); + assert.isTrue(pm.price.eq(updatePriceParams.price)); + assert.isTrue(pm.priceExpo.eq(updatePriceParams.priceExpo)); + }); + } + }); }); export const getAccumulatorPdaMeta = ( @@ -339,7 +397,7 @@ function parseAccumulatorInput({ } else if (msgHeader.schema == 1) { accumulatorMessages.push(parseCompactPriceMessage(msgData)); } else { - console.warn("Unknown input index: " + i); + console.warn("unknown msgHeader.schema: " + i); continue; } start = endOffset; From 4d5c085188af2c8324845e9f7f6628e28186fc4a Mon Sep 17 00:00:00 2001 From: swimricky Date: Wed, 19 Apr 2023 09:13:54 -0700 Subject: [PATCH 3/5] test(accumulator-updater): fix max num of msgs to send --- .../programs/accumulator_updater/src/state/whitelist.rs | 2 +- accumulator_updater/tests/accumulator_updater.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/accumulator_updater/programs/accumulator_updater/src/state/whitelist.rs b/accumulator_updater/programs/accumulator_updater/src/state/whitelist.rs index 8986f4e0c2..ae5746f0c7 100644 --- a/accumulator_updater/programs/accumulator_updater/src/state/whitelist.rs +++ b/accumulator_updater/programs/accumulator_updater/src/state/whitelist.rs @@ -47,7 +47,7 @@ pub struct WhitelistVerifier<'info> { seeds = [b"accumulator".as_ref(), b"whitelist".as_ref()], bump = whitelist.bump, )] - pub whitelist: Account<'info, Whitelist>, + pub whitelist: Box>, /// CHECK: Instruction introspection sysvar #[account(address = sysvar::instructions::ID)] pub ixs_sysvar: UncheckedAccount<'info>, diff --git a/accumulator_updater/tests/accumulator_updater.ts b/accumulator_updater/tests/accumulator_updater.ts index 66e6eda995..9081d39a76 100644 --- a/accumulator_updater/tests/accumulator_updater.ts +++ b/accumulator_updater/tests/accumulator_updater.ts @@ -290,7 +290,7 @@ describe("accumulator_updater", () => { it("Mock CPI Program - CPI Max Test", async () => { // with loosen CPI feature activated, max cpi instruction size len is 10KB - for (let num_msgs = 1; num_msgs < 9; num_msgs++) { + for (let num_msgs = 1; num_msgs < 8; num_msgs++) { console.info(`testing num_msgs: ${num_msgs}`); const updatePriceParams = { price: new anchor.BN(10 * num_msgs + 5), From f367d42c1f1808a875f7e8b01f562dcbbf675c81 Mon Sep 17 00:00:00 2001 From: swimricky Date: Wed, 19 Apr 2023 10:27:17 -0700 Subject: [PATCH 4/5] refactor(accumulator-updater): address PR feedback from 771 update fund pda seeds, update consts, cleanup --- .../src/instructions/put_all.rs | 18 ++++++++---------- .../src/instructions/update_price.rs | 2 -- .../tests/accumulator_updater.ts | 2 +- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/accumulator_updater/programs/accumulator_updater/src/instructions/put_all.rs b/accumulator_updater/programs/accumulator_updater/src/instructions/put_all.rs index d51d4fb17d..3905833b8d 100644 --- a/accumulator_updater/programs/accumulator_updater/src/instructions/put_all.rs +++ b/accumulator_updater/programs/accumulator_updater/src/instructions/put_all.rs @@ -13,8 +13,9 @@ use { }; -pub const ACCUMULATOR: &[u8; 11] = b"accumulator"; -pub const FUND: &[u8; 4] = b"fund"; +pub const ACCUMULATOR: &str = "accumulator"; +pub const FUND: &str = "fund"; + pub fn put_all<'info>( ctx: Context<'_, '_, '_, 'info, PutAll<'info>>, @@ -34,7 +35,7 @@ pub fn put_all<'info>( let (pda, bump) = Pubkey::find_program_address( &[ cpi_caller.as_ref(), - ACCUMULATOR.as_ref(), + ACCUMULATOR.as_bytes(), base_account_key.as_ref(), ], &crate::ID, @@ -42,15 +43,15 @@ pub fn put_all<'info>( require_keys_eq!(accumulator_input_ai.key(), pda); let signer_seeds = [ cpi_caller.as_ref(), - ACCUMULATOR.as_ref(), + ACCUMULATOR.as_bytes(), base_account_key.as_ref(), &[bump], ]; let fund_pda_bump = *ctx .bumps - .get("fund") + .get(FUND) .ok_or(AccumulatorUpdaterError::FundBumpNotFound)?; - let fund_signer_seeds = [ACCUMULATOR.as_ref(), FUND.as_ref(), &[fund_pda_bump]]; + let fund_signer_seeds = [FUND.as_bytes(), &[fund_pda_bump]]; PutAll::create_account( accumulator_input_ai, 8 + AccumulatorInput::INIT_SPACE, @@ -102,10 +103,7 @@ pub struct PutAll<'info> { /// `AccumulatorInput` account initialization #[account( mut, - seeds = [ - b"accumulator".as_ref(), - b"fund".as_ref(), - ], + seeds = [b"fund".as_ref()], owner = system_program::System::id(), bump, )] diff --git a/accumulator_updater/programs/mock-cpi-caller/src/instructions/update_price.rs b/accumulator_updater/programs/mock-cpi-caller/src/instructions/update_price.rs index efd6146d2a..4825877a3b 100644 --- a/accumulator_updater/programs/mock-cpi-caller/src/instructions/update_price.rs +++ b/accumulator_updater/programs/mock-cpi-caller/src/instructions/update_price.rs @@ -45,8 +45,6 @@ pub struct UpdatePrice<'info> { bump, )] pub pyth_price_account: AccountLoader<'info, PriceAccount>, - // #[account(mut)] - // pub payer: Signer<'info>, #[account(mut)] pub fund: SystemAccount<'info>, /// Needed for accumulator_updater diff --git a/accumulator_updater/tests/accumulator_updater.ts b/accumulator_updater/tests/accumulator_updater.ts index 9081d39a76..b790cb1054 100644 --- a/accumulator_updater/tests/accumulator_updater.ts +++ b/accumulator_updater/tests/accumulator_updater.ts @@ -16,7 +16,7 @@ const accumulatorUpdaterProgram = anchor.workspace const mockCpiProg = anchor.workspace.MockCpiCaller as Program; let whitelistAuthority = anchor.web3.Keypair.generate(); const [fundPda] = anchor.web3.PublicKey.findProgramAddressSync( - [Buffer.from("accumulator"), Buffer.from("fund")], + [Buffer.from("fund")], accumulatorUpdaterProgram.programId ); From eb1b6d7a5b02ade751dcd3b50221deb593df8813 Mon Sep 17 00:00:00 2001 From: swimricky Date: Thu, 20 Apr 2023 13:33:22 -0700 Subject: [PATCH 5/5] feat(accumulator-updater): address PR comments Update cpi max test ix to take vec of msg sizes, clean up commented out code --- accumulator_updater/Anchor.toml | 2 - .../src/instructions/put_all.rs | 3 - .../programs/accumulator_updater/src/lib.rs | 1 - .../src/state/whitelist.rs | 3 +- .../src/instructions/cpi_max_test.rs | 25 ++------ .../src/instructions/update_price.rs | 9 +-- .../programs/mock-cpi-caller/src/lib.rs | 5 +- .../mock-cpi-caller/src/message/price.rs | 12 ++-- .../tests/accumulator_updater.ts | 57 ++++++++++++++++--- 9 files changed, 65 insertions(+), 52 deletions(-) diff --git a/accumulator_updater/Anchor.toml b/accumulator_updater/Anchor.toml index 72f000ba4f..c0e297964a 100644 --- a/accumulator_updater/Anchor.toml +++ b/accumulator_updater/Anchor.toml @@ -4,8 +4,6 @@ skip-lint = false [programs.localnet] accumulator_updater = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" mock_cpi_caller = "Dg5PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" -#accumulator_updater = "3xzsrbvWEM4SMM2W7wbVofpDi3zrdhb2Z2xLwMEGJQiZ" -#mock_cpi_caller = "EKwG38D6Sd67NqsAVc4R7Hqr6mZFPzFotzBchoQJRRDe" [registry] url = "https://api.apr.dev" diff --git a/accumulator_updater/programs/accumulator_updater/src/instructions/put_all.rs b/accumulator_updater/programs/accumulator_updater/src/instructions/put_all.rs index 9d0042f733..f3c5a0637c 100644 --- a/accumulator_updater/programs/accumulator_updater/src/instructions/put_all.rs +++ b/accumulator_updater/programs/accumulator_updater/src/instructions/put_all.rs @@ -13,12 +13,10 @@ use { }; - pub const ACCUMULATOR: &str = "accumulator"; pub const FUND: &str = "fund"; - pub fn put_all<'info>( ctx: Context<'_, '_, '_, 'info, PutAll<'info>>, base_account_key: Pubkey, @@ -125,7 +123,6 @@ impl<'info> PutAll<'info> { system_program: &AccountInfo<'a>, ) -> Result<()> { let lamports = Rent::get()?.minimum_balance(space); - system_program::create_account( CpiContext::new_with_signer( system_program.to_account_info(), diff --git a/accumulator_updater/programs/accumulator_updater/src/lib.rs b/accumulator_updater/programs/accumulator_updater/src/lib.rs index c8e6db313e..88ad4e766b 100644 --- a/accumulator_updater/programs/accumulator_updater/src/lib.rs +++ b/accumulator_updater/programs/accumulator_updater/src/lib.rs @@ -10,7 +10,6 @@ use { }; declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); -// declare_id!("3xzsrbvWEM4SMM2W7wbVofpDi3zrdhb2Z2xLwMEGJQiZ"); #[program] pub mod accumulator_updater { diff --git a/accumulator_updater/programs/accumulator_updater/src/state/whitelist.rs b/accumulator_updater/programs/accumulator_updater/src/state/whitelist.rs index ae5746f0c7..ae5a3ca160 100644 --- a/accumulator_updater/programs/accumulator_updater/src/state/whitelist.rs +++ b/accumulator_updater/programs/accumulator_updater/src/state/whitelist.rs @@ -47,7 +47,8 @@ pub struct WhitelistVerifier<'info> { seeds = [b"accumulator".as_ref(), b"whitelist".as_ref()], bump = whitelist.bump, )] - pub whitelist: Box>, + // Using a Box to move account from stack to heap + pub whitelist: Box>, /// CHECK: Instruction introspection sysvar #[account(address = sysvar::instructions::ID)] pub ixs_sysvar: UncheckedAccount<'info>, diff --git a/accumulator_updater/programs/mock-cpi-caller/src/instructions/cpi_max_test.rs b/accumulator_updater/programs/mock-cpi-caller/src/instructions/cpi_max_test.rs index de91bd5476..bf3d223d72 100644 --- a/accumulator_updater/programs/mock-cpi-caller/src/instructions/cpi_max_test.rs +++ b/accumulator_updater/programs/mock-cpi-caller/src/instructions/cpi_max_test.rs @@ -5,15 +5,9 @@ use { UpdatePriceParams, }, message::{ - get_schemas, - price::{ - CompactPriceMessage, - DummyPriceMessage, - FullPriceMessage, - }, + price::DummyPriceMessage, AccumulatorSerializer, }, - state::PythAccountType, }, anchor_lang::prelude::*, }; @@ -21,27 +15,16 @@ use { pub fn cpi_max_test<'info>( ctx: Context<'_, '_, '_, 'info, UpdatePrice<'info>>, params: UpdatePriceParams, - num_messages: u8, + msg_sizes: Vec, ) -> Result<()> { let mut inputs = vec![]; - let _schemas = get_schemas(PythAccountType::Price); { let pyth_price_acct = &mut ctx.accounts.pyth_price_account.load_mut()?; pyth_price_acct.update(params)?; - let price_full_data = FullPriceMessage::from(&**pyth_price_acct).accumulator_serialize()?; - - inputs.push(price_full_data); - - - let price_compact_data = - CompactPriceMessage::from(&**pyth_price_acct).accumulator_serialize()?; - inputs.push(price_compact_data); - - for _ in 0..num_messages { - let price_dummy_data = - DummyPriceMessage::from(&**pyth_price_acct).accumulator_serialize()?; + for msg_size in msg_sizes { + let price_dummy_data = DummyPriceMessage::new(msg_size).accumulator_serialize()?; inputs.push(price_dummy_data); } } diff --git a/accumulator_updater/programs/mock-cpi-caller/src/instructions/update_price.rs b/accumulator_updater/programs/mock-cpi-caller/src/instructions/update_price.rs index efd6146d2a..361f3f1d43 100644 --- a/accumulator_updater/programs/mock-cpi-caller/src/instructions/update_price.rs +++ b/accumulator_updater/programs/mock-cpi-caller/src/instructions/update_price.rs @@ -5,17 +5,13 @@ use { ACCUMULATOR_UPDATER_IX_NAME, }, message::{ - get_schemas, price::{ CompactPriceMessage, FullPriceMessage, }, AccumulatorSerializer, }, - state::{ - PriceAccount, - PythAccountType, - }, + state::PriceAccount, }, accumulator_updater::program::AccumulatorUpdater as AccumulatorUpdaterProgram, anchor_lang::{ @@ -45,8 +41,6 @@ pub struct UpdatePrice<'info> { bump, )] pub pyth_price_account: AccountLoader<'info, PriceAccount>, - // #[account(mut)] - // pub payer: Signer<'info>, #[account(mut)] pub fund: SystemAccount<'info>, /// Needed for accumulator_updater @@ -66,7 +60,6 @@ pub fn update_price<'info>( params: UpdatePriceParams, ) -> Result<()> { let mut inputs = vec![]; - let _schemas = get_schemas(PythAccountType::Price); { let pyth_price_acct = &mut ctx.accounts.pyth_price_account.load_mut()?; diff --git a/accumulator_updater/programs/mock-cpi-caller/src/lib.rs b/accumulator_updater/programs/mock-cpi-caller/src/lib.rs index 09b6caba61..674ae4ec6f 100644 --- a/accumulator_updater/programs/mock-cpi-caller/src/lib.rs +++ b/accumulator_updater/programs/mock-cpi-caller/src/lib.rs @@ -8,7 +8,6 @@ pub mod message; mod state; declare_id!("Dg5PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); -// declare_id!("EKwG38D6Sd67NqsAVc4R7Hqr6mZFPzFotzBchoQJRRDe"); #[program] pub mod mock_cpi_caller { @@ -34,9 +33,9 @@ pub mod mock_cpi_caller { pub fn cpi_max_test<'info>( ctx: Context<'_, '_, '_, 'info, UpdatePrice<'info>>, params: UpdatePriceParams, - num_messages: u8, + msg_sizes: Vec, ) -> Result<()> { - instructions::cpi_max_test(ctx, params, num_messages) + instructions::cpi_max_test(ctx, params, msg_sizes) } } diff --git a/accumulator_updater/programs/mock-cpi-caller/src/message/price.rs b/accumulator_updater/programs/mock-cpi-caller/src/message/price.rs index 133aa55d6c..2115c68cfd 100644 --- a/accumulator_updater/programs/mock-cpi-caller/src/message/price.rs +++ b/accumulator_updater/programs/mock-cpi-caller/src/message/price.rs @@ -119,22 +119,22 @@ impl AccumulatorSerializer for FullPriceMessage { #[derive(Clone, Debug, Eq, PartialEq)] pub struct DummyPriceMessage { pub header: MessageHeader, - pub data: [u8; 1017], + pub data: Vec, } + impl DummyPriceMessage { pub const SIZE: usize = 1017; -} -impl From<&PriceAccount> for DummyPriceMessage { - fn from(_other: &PriceAccount) -> Self { + pub fn new(msg_size: u16) -> Self { Self { - header: MessageHeader::new(MessageSchema::Dummy, Self::SIZE as u32), - data: [0u8; 1017], + header: MessageHeader::new(MessageSchema::Dummy, msg_size as u32), + data: vec![0u8; msg_size as usize], } } } + impl AccumulatorSerializer for DummyPriceMessage { fn accumulator_serialize(&self) -> Result> { let mut bytes = vec![]; diff --git a/accumulator_updater/tests/accumulator_updater.ts b/accumulator_updater/tests/accumulator_updater.ts index b790cb1054..434d2b7175 100644 --- a/accumulator_updater/tests/accumulator_updater.ts +++ b/accumulator_updater/tests/accumulator_updater.ts @@ -290,18 +290,21 @@ describe("accumulator_updater", () => { it("Mock CPI Program - CPI Max Test", async () => { // with loosen CPI feature activated, max cpi instruction size len is 10KB - for (let num_msgs = 1; num_msgs < 8; num_msgs++) { - console.info(`testing num_msgs: ${num_msgs}`); + let testCases = [[1024], [1024, 2048], [1024, 2048, 4096]]; + // for (let i = 1; i < 8; i++) { + for (let i = 0; i < testCases.length; i++) { + let testCase = testCases[i]; + console.info(`testCase: ${testCase}`); const updatePriceParams = { - price: new anchor.BN(10 * num_msgs + 5), - priceExpo: new anchor.BN(10 & (num_msgs + 6)), - ema: new anchor.BN(10 * num_msgs + 7), - emaExpo: new anchor.BN(10 * num_msgs + 8), + price: new anchor.BN(10 * i + 5), + priceExpo: new anchor.BN(10 & (i + 6)), + ema: new anchor.BN(10 * i + 7), + emaExpo: new anchor.BN(10 * i + 8), }; let accumulatorPdaMeta = getAccumulatorPdaMeta(pythPriceAccountPk); await mockCpiProg.methods - .cpiMaxTest(updatePriceParams, num_msgs) + .cpiMaxTest(updatePriceParams, testCase) .accounts({ fund: fundPda, pythPriceAccount: pythPriceAccountPk, @@ -345,6 +348,46 @@ describe("accumulator_updater", () => { }); } }); + + it("Mock CPI Program - CPI Max Test Fail", async () => { + // with loosen CPI feature activated, max cpi instruction size len is 10KB + let testCases = [[1024, 2048, 4096, 8192]]; + // for (let i = 1; i < 8; i++) { + for (let i = 0; i < testCases.length; i++) { + let testCase = testCases[i]; + console.info(`testCase: ${testCase}`); + const updatePriceParams = { + price: new anchor.BN(10 * i + 5), + priceExpo: new anchor.BN(10 & (i + 6)), + ema: new anchor.BN(10 * i + 7), + emaExpo: new anchor.BN(10 * i + 8), + }; + + let accumulatorPdaMeta = getAccumulatorPdaMeta(pythPriceAccountPk); + let errorThrown = false; + try { + await mockCpiProg.methods + .cpiMaxTest(updatePriceParams, testCase) + .accounts({ + fund: fundPda, + pythPriceAccount: pythPriceAccountPk, + ixsSysvar: anchor.web3.SYSVAR_INSTRUCTIONS_PUBKEY, + accumulatorWhitelist: whitelistPubkey, + accumulatorProgram: accumulatorUpdaterProgram.programId, + }) + .remainingAccounts([accumulatorPdaMeta]) + .preInstructions([ + ComputeBudgetProgram.setComputeUnitLimit({ units: 1_000_000 }), + ]) + .rpc({ + skipPreflight: true, + }); + } catch (_err) { + errorThrown = true; + } + assert.ok(errorThrown); + } + }); }); export const getAccumulatorPdaMeta = (