Explorar o código

[message-buffer 17/x] separate payer from admin (#819)

* feat(message-buffer): add payer to all contexts requiring lamports for rent

separate admin from payer of account creation/resize/deletion

* test(message-buffer): add test for same admin & payer

* feat(message-buffer/scripts): keep payer & admin the same in init-buffer script for simplicity
swimricky %!s(int64=2) %!d(string=hai) anos
pai
achega
c23fc8dbad

+ 5 - 3
pythnet/message_buffer/programs/message_buffer/src/instructions/create_buffer.rs

@@ -66,7 +66,7 @@ pub fn create_buffer<'info>(
         CreateBuffer::create_account(
             buffer_account,
             target_size as usize,
-            &ctx.accounts.admin,
+            &ctx.accounts.payer,
             &[signer_seeds.as_slice()],
             &ctx.accounts.system_program,
         )?;
@@ -95,10 +95,12 @@ pub struct CreateBuffer<'info> {
     )]
     pub whitelist: Account<'info, Whitelist>,
 
-    // Also pays for account creation
-    #[account(mut)]
     pub admin: Signer<'info>,
 
+    /// pays for account initialization
+    #[account(mut)]
+    pub payer: Signer<'info>,
+
     pub system_program: Program<'info, System>,
     // remaining_accounts:  - [AccumulatorInput PDA]
 }

+ 5 - 3
pythnet/message_buffer/programs/message_buffer/src/instructions/delete_buffer.rs

@@ -25,13 +25,15 @@ pub struct DeleteBuffer<'info> {
     )]
     pub whitelist: Account<'info, Whitelist>,
 
-    // Also the recipient of the lamports from closing the buffer account
-    #[account(mut)]
     pub admin: Signer<'info>,
 
+    /// Recipient of the lamports from closing the buffer account
+    #[account(mut)]
+    pub payer: Signer<'info>,
+
     #[account(
         mut,
-        close = admin,
+        close = payer,
         seeds = [allowed_program_auth.as_ref(), MESSAGE.as_bytes(), base_account_key.as_ref()],
         bump = message_buffer.load()?.bump,
     )]

+ 5 - 3
pythnet/message_buffer/programs/message_buffer/src/instructions/resize_buffer.rs

@@ -49,10 +49,12 @@ pub struct ResizeBuffer<'info> {
     )]
     pub whitelist: Account<'info, Whitelist>,
 
-    // Also pays for account creation
-    #[account(mut)]
     pub admin: Signer<'info>,
 
+    /// Pays for any additional rent needed to increase the buffer size
+    #[account(mut)]
+    pub payer: Signer<'info>,
+
     pub system_program: Program<'info, System>,
 
     /// If decreasing, Anchor will automatically check
@@ -64,7 +66,7 @@ pub struct ResizeBuffer<'info> {
         mut,
         realloc = target_size as usize,
         realloc::zero = false,
-        realloc::payer = admin,
+        realloc::payer = payer,
         seeds = [allowed_program_auth.as_ref(), MESSAGE.as_bytes(), base_account_key.as_ref()],
         bump = message_buffer.load()?.bump,
     )]

+ 4 - 2
pythnet/message_buffer/programs/message_buffer/src/lib.rs

@@ -145,11 +145,13 @@ pub mod message_buffer {
 #[derive(Accounts)]
 pub struct Initialize<'info> {
     /// Admin that can update the whitelist and create/resize/delete buffers
+    pub admin: Signer<'info>,
+
     #[account(mut)]
-    pub admin:          Signer<'info>,
+    pub payer:          Signer<'info>,
     #[account(
         init,
-        payer = admin,
+        payer = payer,
         seeds = [MESSAGE.as_bytes(), WHITELIST.as_bytes()],
         bump,
         space = 8 + Whitelist::INIT_SPACE,

+ 3 - 1
pythnet/message_buffer/programs/mock-cpi-caller/tests/cases/test_create_buffer.rs

@@ -50,6 +50,7 @@ async fn create_buffer_with_invalid_admin_should_fail() {
         space,
         context.whitelist(),
         invalid_admin.pubkey(),
+        context.payer.pubkey(),
         msg_buffer_pda,
     );
 
@@ -80,7 +81,7 @@ async fn create_buffer_with_invalid_size_should_fail() {
     );
     let cpi_caller_auth = MessageBufferTestContext::get_mock_cpi_auth();
     let _whitelist = context.whitelist();
-    let admin = context.default_admin();
+    let admin = context.admin();
 
     let (msg_buffer_pda, _) = find_msg_buffer_pda(cpi_caller_auth, pyth_price_acct);
     let invalid_create_buffer_ix = create_msg_buffer_ix(
@@ -89,6 +90,7 @@ async fn create_buffer_with_invalid_size_should_fail() {
         1,
         context.whitelist(),
         admin.pubkey(),
+        context.payer.pubkey(),
         msg_buffer_pda,
     );
 

+ 18 - 2
pythnet/message_buffer/programs/mock-cpi-caller/tests/cases/test_initialize.rs

@@ -3,14 +3,30 @@ use super::*;
 #[tokio::test]
 async fn test_initialize() {
     let context = &mut MessageBufferTestContext::initialize_context(false).await;
-    let admin = context.payer.insecure_clone();
+    let admin = Keypair::new();
+    let (_, whitelist_bump) = context.initialize(&admin).await.unwrap();
+
+    let (whitelist_acct_bump, admin_pubkey, allowed_programs_len, allowed_programs) =
+        context.fetch_whitelist().await.unwrap();
+
+    assert_eq!(whitelist_bump, whitelist_acct_bump);
+    assert_eq!(admin.pubkey(), admin_pubkey);
+
+    assert_eq!(0, allowed_programs_len);
+    assert_eq!(allowed_programs, vec![]);
+}
+
+#[tokio::test]
+async fn test_initialize_with_payer_as_admin() {
+    let context = &mut MessageBufferTestContext::initialize_context(false).await;
+    let admin = &context.payer.insecure_clone();
     let (_, whitelist_bump) = context.initialize(admin).await.unwrap();
 
     let (whitelist_acct_bump, admin_pubkey, allowed_programs_len, allowed_programs) =
         context.fetch_whitelist().await.unwrap();
 
     assert_eq!(whitelist_bump, whitelist_acct_bump);
-    assert_eq!(context.payer.pubkey(), admin_pubkey);
+    assert_eq!(admin.pubkey(), admin_pubkey);
 
     assert_eq!(0, allowed_programs_len);
     assert_eq!(allowed_programs, vec![]);

+ 9 - 3
pythnet/message_buffer/programs/mock-cpi-caller/tests/cases/test_resize_buffer.rs

@@ -100,7 +100,7 @@ async fn fail_resize_buffer_invalid_increase() {
     .unwrap();
 
     let whitelist = context.whitelist();
-    let admin = context.default_admin();
+    let admin = context.admin();
     let cpi_caller_auth = MessageBufferTestContext::get_mock_cpi_auth();
     let pyth_price_acct = MessageBufferTestContext::default_pyth_price_account();
     let (msg_buffer_pda, msg_buffer_bump) = MessageBufferTestContext::default_msg_buffer();
@@ -115,6 +115,7 @@ async fn fail_resize_buffer_invalid_increase() {
         target_size,
         whitelist,
         admin.pubkey(),
+        context.payer.pubkey(),
         msg_buffer_pda,
     );
 
@@ -154,6 +155,7 @@ async fn fail_resize_buffer_invalid_increase() {
         target_size,
         whitelist,
         admin.pubkey(),
+        context.payer.pubkey(),
         msg_buffer_pda,
     );
 
@@ -259,7 +261,7 @@ async fn fail_resize_initialized_buffer() {
     .unwrap();
 
     let payer = context.payer.pubkey();
-    let admin = context.default_admin();
+    let admin = context.admin();
     let pyth_price_acct = MessageBufferTestContext::default_pyth_price_account();
     let whitelist = context.whitelist();
     let cpi_caller_auth = MessageBufferTestContext::get_mock_cpi_auth();
@@ -295,6 +297,7 @@ async fn fail_resize_initialized_buffer() {
         target_size,
         whitelist,
         admin.pubkey(),
+        context.payer.pubkey(),
         msg_buffer_pda,
     );
 
@@ -317,6 +320,7 @@ async fn fail_resize_initialized_buffer() {
         target_size,
         whitelist,
         admin.pubkey(),
+        context.payer.pubkey(),
         msg_buffer_pda,
     );
 
@@ -341,7 +345,7 @@ async fn fail_resize_buffer_exceed_max_size() {
     .unwrap();
 
     let whitelist = context.whitelist();
-    let admin = context.default_admin();
+    let admin = context.admin();
     let cpi_caller_auth = MessageBufferTestContext::get_mock_cpi_auth();
     let pyth_price_acct = MessageBufferTestContext::default_pyth_price_account();
     let (msg_buffer_pda, _msg_buffer_bump) = MessageBufferTestContext::default_msg_buffer();
@@ -356,6 +360,7 @@ async fn fail_resize_buffer_exceed_max_size() {
             target_size,
             whitelist,
             admin.pubkey(),
+            context.payer.pubkey(),
             msg_buffer_pda,
         );
 
@@ -370,6 +375,7 @@ async fn fail_resize_buffer_exceed_max_size() {
         target_size,
         whitelist,
         admin.pubkey(),
+        context.payer.pubkey(),
         msg_buffer_pda,
     );
 

+ 2 - 2
pythnet/message_buffer/programs/mock-cpi-caller/tests/cases/test_set_allowed_programs.rs

@@ -3,8 +3,8 @@ use super::*;
 #[tokio::test]
 async fn test_set_allowed_programs() {
     let context = &mut MessageBufferTestContext::initialize_context(false).await;
-    let admin = context.default_admin();
-    context.initialize(admin).await.unwrap();
+    let admin = Keypair::new();
+    context.initialize(&admin).await.unwrap();
 
 
     let mock_cpi_caller_auth = MessageBufferTestContext::get_mock_cpi_auth();

+ 23 - 11
pythnet/message_buffer/programs/mock-cpi-caller/tests/program_test/mod.rs

@@ -98,7 +98,8 @@ impl MessageBufferTestContext {
         disable_loosen_cpi_limit: bool,
     ) -> Result<Self> {
         let mut context = Self::initialize_context(disable_loosen_cpi_limit).await;
-        context.initialize(context.default_admin()).await.unwrap();
+        let admin = Keypair::new();
+        context.initialize(&admin).await.unwrap();
         context
             .set_allowed_programs(&Self::default_allowed_programs())
             .await
@@ -127,8 +128,8 @@ impl MessageBufferTestContext {
         self.context.banks_client.get_balance(pubkey).await.unwrap()
     }
 
-    pub fn default_admin(&self) -> Keypair {
-        self.payer.insecure_clone()
+    pub fn admin(&self) -> Keypair {
+        self.admin.as_ref().unwrap().insecure_clone()
     }
 
     fn admin_pubkey(&self) -> Pubkey {
@@ -204,7 +205,7 @@ impl MessageBufferTestContext {
         }
     }
 
-    pub async fn initialize(&mut self, admin: Keypair) -> Result<(Pubkey, u8)> {
+    pub async fn initialize(&mut self, admin: &Keypair) -> Result<(Pubkey, u8)> {
         let (whitelist_pda, whitelist_bump) = Pubkey::find_program_address(
             &[MESSAGE.as_bytes(), WHITELIST.as_bytes()],
             &::message_buffer::id(),
@@ -213,7 +214,8 @@ impl MessageBufferTestContext {
         self.admin = Some(admin.insecure_clone());
         self.whitelist = Some(whitelist_pda);
 
-        let init_message_buffer_ix = initialize_ix(admin.pubkey(), whitelist_pda);
+        let init_message_buffer_ix =
+            initialize_ix(admin.pubkey(), self.payer.pubkey(), whitelist_pda);
 
         self.process_ixs(
             &[init_message_buffer_ix],
@@ -266,6 +268,7 @@ impl MessageBufferTestContext {
             target_size,
             self.whitelist(),
             admin.pubkey(),
+            self.payer.pubkey(),
             msg_buffer_pda,
         );
         self.process_ixs(&[create_msg_buffer_ix], vec![&admin])
@@ -291,13 +294,14 @@ impl MessageBufferTestContext {
 
         let (msg_buffer_pda, _) =
             find_msg_buffer_pda(Self::get_mock_cpi_auth(), pyth_price_account);
-        let admin = self.admin.as_ref().unwrap().insecure_clone();
+        let admin = self.admin();
 
         let delete_ix = delete_msg_buffer_ix(
             Self::get_mock_cpi_auth(),
             pyth_price_account,
             self.whitelist(),
             admin.pubkey(),
+            self.payer.pubkey(),
             msg_buffer_pda,
         );
 
@@ -326,6 +330,7 @@ impl MessageBufferTestContext {
                 target_size,
                 self.whitelist(),
                 admin.pubkey(),
+                self.payer.pubkey(),
                 msg_buffer_pda,
             );
             resize_ixs.push(resize_ix);
@@ -366,14 +371,15 @@ impl MessageBufferTestContext {
 
 pub type AddPriceParams = (u64, u64, u64, u64, u64);
 
-fn initialize_ix(admin: Pubkey, whitelist_pda: Pubkey) -> Instruction {
+fn initialize_ix(admin: Pubkey, payer: Pubkey, whitelist_pda: Pubkey) -> Instruction {
     let init_ix_discriminator = sighash("global", "initialize");
 
     Instruction::new_with_borsh(
         ::message_buffer::id(),
         &(init_ix_discriminator),
         vec![
-            AccountMeta::new(admin, true),
+            AccountMeta::new_readonly(admin, true),
+            AccountMeta::new(payer, true),
             AccountMeta::new(whitelist_pda, false),
             AccountMeta::new_readonly(System::id(), false),
         ],
@@ -403,6 +409,7 @@ pub fn create_msg_buffer_ix(
     target_size: u32,
     whitelist: Pubkey,
     admin: Pubkey,
+    payer: Pubkey,
     msg_buffer_pda: Pubkey,
 ) -> Instruction {
     let create_ix_discriminator = sighash("global", "create_buffer");
@@ -417,7 +424,8 @@ pub fn create_msg_buffer_ix(
         ),
         vec![
             AccountMeta::new_readonly(whitelist, false),
-            AccountMeta::new(admin, true),
+            AccountMeta::new_readonly(admin, true),
+            AccountMeta::new(payer, true),
             AccountMeta::new_readonly(System::id(), false),
             AccountMeta::new(msg_buffer_pda, false),
         ],
@@ -431,6 +439,7 @@ pub fn resize_msg_buffer_ix(
     target_size: u32,
     whitelist: Pubkey,
     admin: Pubkey,
+    payer: Pubkey,
     msg_buffer_pda: Pubkey,
 ) -> Instruction {
     let resize_ix_disc = sighash("global", "resize_buffer");
@@ -445,7 +454,8 @@ pub fn resize_msg_buffer_ix(
         ),
         vec![
             AccountMeta::new_readonly(whitelist, false),
-            AccountMeta::new(admin, true),
+            AccountMeta::new_readonly(admin, true),
+            AccountMeta::new(payer, true),
             AccountMeta::new_readonly(System::id(), false),
             AccountMeta::new(msg_buffer_pda, false),
         ],
@@ -458,6 +468,7 @@ pub fn delete_msg_buffer_ix(
     pyth_price_acct: Pubkey,
     whitelist: Pubkey,
     admin: Pubkey,
+    payer: Pubkey,
     msg_buffer_pda: Pubkey,
 ) -> Instruction {
     let delete_ix_disc = sighash("global", "delete_buffer");
@@ -467,7 +478,8 @@ pub fn delete_msg_buffer_ix(
         &(delete_ix_disc, cpi_caller_auth, pyth_price_acct),
         vec![
             AccountMeta::new_readonly(whitelist, false),
-            AccountMeta::new(admin, true),
+            AccountMeta::new_readonly(admin, true),
+            AccountMeta::new(payer, true),
             AccountMeta::new(msg_buffer_pda, false),
         ],
     )

+ 4 - 4
pythnet/message_buffer/scripts/setup_message_buffer.ts

@@ -86,10 +86,8 @@ async function main() {
     ...(await provider.connection.getLatestBlockhash()),
   });
 
-  const whitelistAdminBalance = await provider.connection.getBalance(
-    whitelistAdmin.publicKey
-  );
-  console.log(`whitelistAdminBalance: ${whitelistAdminBalance}`);
+  const payerBalance = await provider.connection.getBalance(payer.publicKey);
+  console.log(`payerBalance: ${payerBalance}`);
   console.log("Airdrop complete");
   console.groupEnd();
 
@@ -103,7 +101,9 @@ async function main() {
       .initialize()
       .accounts({
         admin: whitelistAdmin.publicKey,
+        payer: payer.publicKey,
       })
+      .signers([whitelistAdmin, payer])
       .rpc();
 
     console.log(`initializeTxnSig: ${initializeTxnSig}`);

+ 25 - 38
pythnet/message_buffer/tests/message_buffer.ts

@@ -11,6 +11,7 @@ import lumina from "@lumina-dev/test";
 import { assert } from "chai";
 import { AccountMeta, ComputeBudgetProgram } from "@solana/web3.js";
 import bs58 from "bs58";
+import NodeWallet from "@coral-xyz/anchor/dist/cjs/nodewallet";
 
 // Enables tool that runs in local browser for easier debugging of
 // transactions in this test -  https://lumina.fyi/debug
@@ -19,7 +20,6 @@ import bs58 from "bs58";
 const messageBufferProgram = anchor.workspace
   .MessageBuffer as Program<MessageBuffer>;
 const mockCpiProg = anchor.workspace.MockCpiCaller as Program<MockCpiCaller>;
-let whitelistAdmin = anchor.web3.Keypair.generate();
 
 const [mockCpiCallerAuth] = anchor.web3.PublicKey.findProgramAddressSync(
   [Buffer.from("upd_price_write"), messageBufferProgram.programId.toBuffer()],
@@ -71,46 +71,29 @@ const messageBufferPdaMeta2 = {
   isWritable: true,
 };
 
-console.log("3");
-
-let fundBalance = 100 * anchor.web3.LAMPORTS_PER_SOL;
-
 const discriminator = BorshAccountsCoder.accountDiscriminator("MessageBuffer");
 const messageBufferDiscriminator = bs58.encode(discriminator);
 
-describe("message_buffer", () => {
-  // Configure the client to use the local cluster.
-  let provider = anchor.AnchorProvider.env();
-  anchor.setProvider(provider);
+let provider = anchor.AnchorProvider.env();
+anchor.setProvider(provider);
 
-  const [whitelistPubkey, whitelistBump] =
-    anchor.web3.PublicKey.findProgramAddressSync(
-      [MESSAGE, Buffer.from("whitelist")],
-      messageBufferProgram.programId
-    );
-
-  before("transfer lamports to needed accounts", async () => {
-    const airdropTxnSig = await provider.connection.requestAirdrop(
-      whitelistAdmin.publicKey,
-      fundBalance
-    );
+const payer = provider.wallet as NodeWallet;
+let whitelistAdmin = anchor.web3.Keypair.generate();
 
-    await provider.connection.confirmTransaction({
-      signature: airdropTxnSig,
-      ...(await provider.connection.getLatestBlockhash()),
-    });
-    const whitelistAuthorityBalance = await provider.connection.getBalance(
-      whitelistAdmin.publicKey
-    );
-    assert.isTrue(whitelistAuthorityBalance === fundBalance);
-  });
+const [whitelistPubkey, whitelistBump] =
+  anchor.web3.PublicKey.findProgramAddressSync(
+    [MESSAGE, Buffer.from("whitelist")],
+    messageBufferProgram.programId
+  );
 
+describe("message_buffer", () => {
   it("Is initialized!", async () => {
     // Add your test here.
     const tx = await messageBufferProgram.methods
       .initialize()
       .accounts({
         admin: whitelistAdmin.publicKey,
+        payer: payer.publicKey,
       })
       .signers([whitelistAdmin])
       .rpc();
@@ -160,6 +143,7 @@ describe("message_buffer", () => {
       .accounts({
         whitelist: whitelistPubkey,
         admin: whitelistAdmin.publicKey,
+        payer: payer.publicKey,
         systemProgram: anchor.web3.SystemProgram.programId,
       })
       .signers([whitelistAdmin])
@@ -206,6 +190,7 @@ describe("message_buffer", () => {
       .accounts({
         whitelist: whitelistPubkey,
         admin: whitelistAdmin.publicKey,
+        payer: payer.publicKey,
         systemProgram: anchor.web3.SystemProgram.programId,
       })
       .signers([whitelistAdmin])
@@ -556,28 +541,26 @@ describe("message_buffer", () => {
       messageBufferAccountDataBefore
     );
 
-    const whitelistAuthorityBalanceBefore =
-      await provider.connection.getBalance(whitelistAdmin.publicKey);
-    console.log(
-      `whitelistAuthorityBalance: ${whitelistAuthorityBalanceBefore}`
+    const payerBalanceBefore = await provider.connection.getBalance(
+      payer.publicKey
     );
+    console.log(`payerBalanceBefore: ${payerBalanceBefore}`);
     const targetSize = 10 * 1024;
     await messageBufferProgram.methods
       .resizeBuffer(mockCpiCallerAuth, pythPriceAccountPk2, targetSize)
       .accounts({
         whitelist: whitelistPubkey,
         admin: whitelistAdmin.publicKey,
+        payer: payer.publicKey,
         systemProgram: anchor.web3.SystemProgram.programId,
       })
       .signers([whitelistAdmin])
       .rpc({ skipPreflight: true });
 
-    const whitelistAuthorityBalanceAfter = await provider.connection.getBalance(
-      whitelistAdmin.publicKey
-    );
-    assert.isTrue(
-      whitelistAuthorityBalanceAfter < whitelistAuthorityBalanceBefore
+    const payerBalanceAftger = await provider.connection.getBalance(
+      payer.publicKey
     );
+    assert.isTrue(payerBalanceAftger < payerBalanceBefore);
 
     const messageBufferAccountData = await getMessageBuffer(
       provider.connection,
@@ -607,6 +590,7 @@ describe("message_buffer", () => {
       .accounts({
         whitelist: whitelistPubkey,
         admin: whitelistAdmin.publicKey,
+        payer: payer.publicKey,
         systemProgram: anchor.web3.SystemProgram.programId,
         messageBuffer: messageBufferPda2,
       })
@@ -631,6 +615,7 @@ describe("message_buffer", () => {
           .accounts({
             whitelist: whitelistPubkey,
             admin: whitelistAdmin.publicKey,
+            payer: payer.publicKey,
             systemProgram: anchor.web3.SystemProgram.programId,
             messageBuffer: messageBufferPda2,
           })
@@ -649,6 +634,7 @@ describe("message_buffer", () => {
       .accounts({
         whitelist: whitelistPubkey,
         admin: whitelistAdmin.publicKey,
+        payer: payer.publicKey,
         messageBuffer: messageBufferPda2,
       })
       .signers([whitelistAdmin])
@@ -682,6 +668,7 @@ describe("message_buffer", () => {
       .accounts({
         whitelist: whitelistPubkey,
         admin: whitelistAdmin.publicKey,
+        payer: payer.publicKey,
         systemProgram: anchor.web3.SystemProgram.programId,
       })
       .signers([whitelistAdmin])