Ошибка вызова Solana CPI. Ошибка: ошибка обработки инструкции 0: межпрограммный вызов с неавторизованной подписывающей стороной или учетной записью с возможностью записи

Я в основном преобразовал кампанию по краудфандингу, написанную на Native Rust (Solana CLI), в версию Anchor. Итак, мои тесты проходят нормально, за исключением теста пожертвования .

Пожалуйста, если какой-нибудь супертеневой разработчик Соланы поможет мне разобраться в этом, я буду благодарен. Я потратил почти неделю на отладку, но не смог понять. Очевидно, где-то моя логика или синтаксис неверны, но я не могу понять.

Честно говоря, я чувствую, что моя логика верна, потому что я много раз читал код. Но наверняка что-то не так......

lib.rs

      use anchor_lang::solana_program::system_program;
use anchor_lang::solana_program::program::{invoke /* , invoke_signed */};
use anchor_lang::solana_program::system_instruction;
use anchor_lang::solana_program::program_error::ProgramError;
declare_id!("HwHzMftiyTEYy6w8K6E3AipuF3vgswS1d7c6T6hUeRxf");


#[program]
pub mod solanacrowdfundingproject {
    use super::*;
    
    // `seeds` and `bump` tell us that our `writing_account` is a PDA that can be derived from their respective values
    pub fn initialize(ctx: Context<Initialize>, writing_account_bump: u8) -> ProgramResult {
        let writing_account = &mut ctx.accounts.writing_account;
        let authority = &mut ctx.accounts.authority;
        
        writing_account.bump = writing_account_bump;
        writing_account.count = 0;
        writing_account.authority = *authority.key; 
        writing_account.campaign_details = Vec::new();
        writing_account.withdraw_request = Vec::new();
        
        Ok(())
    }

    
    pub fn create_campaign
    (
        ctx : Context<CreateCampaign>, 
        name : String, 
        description : String, 
        image_link: String,      
    ) 
        -> ProgramResult {
               
        let writing_account = &mut ctx.accounts.writing_account;
        let authority = &mut ctx.accounts.authority;
        
        if name.len()  > 30 || description.len() > 50 {
            return Err(ErrorCode::NameOrDescriptionTooLong.into())
        }

        let campaign_data = CampaignDetails {
            admin : *authority.key,
            name : name.to_string(),
            description : description.to_string(),
            image_link : image_link.to_string(),
            amount_donated : 0,       
        };

        writing_account.count += 1;
        writing_account.campaign_details.push(campaign_data);
        
        Ok(())
    }
    
    
    pub fn withdraw(ctx : Context<Withdraw>, amount : u64) -> ProgramResult {
        let writing_account = &mut ctx.accounts.writing_account;
        let authority = &mut ctx.accounts.authority;
        
        
        let withdraw_data = WithdrawRequest {
            amount_withdrawn : amount,
            admin : *authority.to_account_info().key,
        };

        writing_account.withdraw_request.push(withdraw_data);
         
         **writing_account.to_account_info().try_borrow_mut_lamports()? -= amount;
         **authority.to_account_info().try_borrow_mut_lamports()? += amount;
        
        Ok(())

    }

    
    pub fn donate(ctx : Context<Donate>, amount : u64, donator_program_account_bump : u8) -> ProgramResult {
       
        let writing_account = &mut ctx.accounts.writing_account;
        let donator_program_account = &mut ctx.accounts.donator_program_account;
        let authority = &mut ctx.accounts.authority;

        donator_program_account.amount_donated = amount;
        donator_program_account.bump = donator_program_account_bump;
        

       let transfer_ix = system_instruction::transfer(
            &authority.to_account_info().key(),
            &donator_program_account.to_account_info().key(),
            amount,
       );

       #[warn(unused_must_use)]
       invoke(
           &transfer_ix, 
           &[
               authority.to_account_info(),
               donator_program_account.to_account_info(),
           ],
       )?;

        let mut campaign_data = CampaignDetails::try_from_slice(*writing_account.to_account_info().try_borrow_mut_data()?)
        .expect("Error deserializing data");
        campaign_data.amount_donated += **donator_program_account.to_account_info().lamports.borrow();

        **writing_account.to_account_info().try_borrow_mut_lamports()? += **donator_program_account.to_account_info().lamports.borrow();
        **donator_program_account.to_account_info().try_borrow_mut_lamports()? = 0;
        *donator_program_account.to_account_info().try_borrow_mut_data()? = &mut []; 
       
        Ok(())
    }


    #[derive(Accounts)]
    #[instruction(writing_account_bump : u8)]
    pub struct Initialize<'info> {
        #[account(init, 
            seeds = [b"please_____".as_ref(), authority.key().as_ref()], 
            bump = writing_account_bump, 
            payer = authority, 
            space = 9000)]
            pub writing_account : Account<'info, CampaignState>,
            #[account(mut)]
            pub authority : Signer<'info>,
            #[account(address = system_program::ID)]
            pub system_program : AccountInfo<'info>,
            
    }
    

    #[derive(Accounts)] 
    pub struct CreateCampaign<'info> {
       #[account(mut, has_one = authority)]
        pub writing_account  : Account<'info, CampaignState>,
        #[account(mut)]
        pub authority : Signer<'info>,         
    }


    #[derive(Accounts)]
    pub struct Withdraw<'info> {
        #[account(mut, has_one = authority)]
        pub writing_account : Account<'info, CampaignState>,
        #[account(mut)]
        pub authority : Signer<'info>,         
    }


  
    #[derive(Accounts)]
    #[instruction(donator_program_account_bump : u8)]
    pub struct Donate<'info> {
        #[account(mut)]
        pub writing_account : Account<'info, CampaignState>,
        #[account(mut)]
        pub authority : Signer<'info>,
        #[account(init,
            seeds = [b"donate____".as_ref(),
            authority.key().as_ref()], 
            bump = donator_program_account_bump, 
            payer = authority, 
            space = 100)]
        pub donator_program_account : Account<'info, DonatorProgramAccount>,
        #[account(address = system_program::ID)]
        pub system_program : AccountInfo<'info>,
    }

    

    #[derive(Debug, Clone, AnchorSerialize, AnchorDeserialize)]
    pub struct CampaignDetails  {
        pub admin: Pubkey,
        pub name: String,
        pub description: String,
        pub image_link: String,
        pub amount_donated: u64,          
    }



    #[derive(Debug, Clone, AnchorSerialize, AnchorDeserialize)]
    pub struct WithdrawRequest {
        pub amount_withdrawn : u64,
        pub admin : Pubkey,        
    }


    #[account]
    pub struct CampaignState {    
       pub campaign_details : Vec<CampaignDetails>,
       pub bump : u8,
       pub count : u8,
       pub authority: Pubkey,  
       pub withdraw_request : Vec<WithdrawRequest>,         
    }

    
    #[account]
    pub struct DonatorProgramAccount {
        pub amount_donated : u64,
        bump : u8,
    }
   
    
    #[error]
    pub enum ErrorCode {    
       
        #[msg("Name cannot be more than 30 characters and Description cannot be more than 50 characters")]
            NameOrDescriptionTooLong,
    }



} 

также мои тесты, как показано ниже

      const anchor = require('@project-serum/anchor');
const { PublicKey, Connection } = require("@solana/web3.js");
//const cluster = "http://localhost:8899";
const cluster = "https://api.devnet.solana.com";
const connection = new Connection(cluster, "confirmed");
const { SystemProgram, Keypair, /*SYSVAR_RENT_PUBKEY*/ } = anchor.web3; 
const { Buffer } = require('buffer');



// Specify provider environment. 
const provider = anchor.Provider.env();
//Set provider.
anchor.setProvider(provider);
//Specify the workspace 
const program = anchor.workspace.Solanacrowdfundingproject;
//const programID = await connection.programID(program);
const programID = new PublicKey("HwHzMftiyTEYy6w8K6E3AipuF3vgswS1d7c6T6hUeRxf");
// const otherUser (i.e Donator)
const donator = Keypair.generate();



describe('Solanacrowdfundingproject', () => {

  console.log(" Starting tests...");
  try {
    it('gets initialized', async () => {
      const { writingAccount, bump } = await getProgramDerivedCampaignWritingAccountAddress();
      let tx = await program.rpc.initialize(new anchor.BN(bump), {
        accounts: {
          writingAccount: writingAccount,
          authority: provider.wallet.publicKey,
          systemProgram: SystemProgram.programId,
          
        },

      });
      //Console.log the Transaction signature of the Initialization procedure. 
      console.log("Initialization transaction signature : ", tx);

      //Asserts and console.logs
      const account = await program.account.campaignState.fetch(writingAccount);
      console.log(" Created A Writing Account : ", account);
      assert.equal(account.authority.toBase58(), provider.wallet.publicKey.toBase58());
      console.log(" Account's count is :", account.count);
    });
  } catch (error) {
    console.log(error);
  }


  try {
    it('Creates a campaign', async () => {

      const { writingAccount } = await getProgramDerivedCampaignWritingAccountAddress();
      //Lets invocate the createCampaign function using provider.wallet.publicKey
      let tx = await program.rpc.createCampaign("Suveett", "Blockchain Speaker", "Enter a fancy giflink for Campaign",
        {
          accounts: {
            writingAccount: writingAccount,
            authority: provider.wallet.publicKey,
            

          },

        });
      //Asserts and Console Logs
      //Console.log the Transaction signature of the Initialization procedure. 
      console.log("Your CreateCampaign transaction signature", tx);
      let account = await program.account.campaignState.fetch(writingAccount);
      console.log("Writing Account after Campaign Creation :", account);
      console.log("This Writing account's address is : ", writingAccount.toBase58());
      console.log("This Writing account's admin is : ", account.campaignDetails[0].admin.toBase58());
      console.log("This Writing account's Campaign Details contains `name` :", account.campaignDetails[0].name);
      console.log("This Writing account's Campaign Details contains `description` :", account.campaignDetails[0].description);
      assert.equal(account.authority.toBase58(), provider.wallet.publicKey.toBase58());
      

    });

  } catch (error) {
    console.log(error);
  }



  try {
    it('Can Make a Donation', async () => {
      const signature = await connection.requestAirdrop(donator.publicKey, 1000000000);
      await connection.confirmTransaction(signature);
      console.log("Airdrop confirmed :", await connection.getBalance(donator.publicKey));
      const { writingAccount } = await getProgramDerivedCampaignWritingAccountAddress();
      const { donatorProgramAccount, bump } = await getProgramDerivedDonatorProgramAccountAddress();
      
      
      let balanceOfCampaignCreatorPreDonation = await connection.getBalance(writingAccount);
       console.log("Balance of Campaign before Donation : ", balanceOfCampaignCreatorPreDonation);
      
      let donateTx = await program.rpc.donate(new anchor.BN(1000000), new anchor.BN(bump),
        {
          accounts: {
            writingAccount: writingAccount,
            authority: donator.publicKey,
            donatorProgramAccount: donatorProgramAccount,
            systemProgram: SystemProgram.programId,
            
           
          },
           signers: [donator],
        });
      //Asserts and Console Logs
      //Console.log the Transaction signature of the Donation procedure. 
      let account = await program.account.donatorProgramAccount.fetch(donatorProgramAccount);
      console.log(" Created a New Donator Program Account : ", account);
      console.log(" Your Donation transaction signature is : ", donateTx);
     
      let balanceOfCampaignCreatorPostDonation = await connection.getBalance(writingAccount);
      console.log("Balance of Campaign after Donation : ", balanceOfCampaignCreatorPostDonation);

    });
  } catch (error) {
    console.log(error);
  }



  try {
    it('Can Make a Withdrawal', async () => {
      const { writingAccount } = await getProgramDerivedCampaignWritingAccountAddress();

      let withdrawTx = await program.rpc.withdraw(new anchor.BN(50000),
        {
          accounts: {
            writingAccount: writingAccount,
            authority: provider.wallet.publicKey,
            
          }

        });
      //Asserts and Console Logs
      //Console.log the Transaction signature of the Withdrawal procedure.
      console.log("Your Withdrawal transaction signature", withdrawTx);
      let balanceOfCampaignCreator = await connection.getBalance(writingAccount);
      console.log("Balance of Campaign after Withdrawal: ", balanceOfCampaignCreator);


    });
  } catch (error) {
    console.log(error);
  }


});



async function getProgramDerivedCampaignWritingAccountAddress() {
  const [writingAccount, bump] = await PublicKey.findProgramAddress(
    [Buffer.from('please_____'), provider.wallet.publicKey.toBuffer()],
    programID
  );

  console.log(`Got ProgramDerivedWritingAccountAddress: bump: ${bump}, pubkey: ${writingAccount.toBase58()}`);
  return { writingAccount, bump };

};


async function getProgramDerivedDonatorProgramAccountAddress() {
  const [donatorProgramAccount, bump] = await PublicKey.findProgramAddress(
    [Buffer.from('donate____'), donator.publicKey.toBuffer()],
    programID
  );

  console.log(`Got ProgramDerivedDonatorProgramAccountAddress: bump: ${bump}, pubkey: ${donatorProgramAccount.toBase58()}`);
  return { donatorProgramAccount, bump };

};

Может ли кто-нибудь просмотреть код и помочь мне? Кроме того, код строки ниже (где я получаю сообщение об ошибке):

https://github.com/Suveett/solana-crowd-funding-master-using-Anchor/blob/ad87fb2226c800fce1faabd355eb88b812441e0c/tests/solanacrowdfundingproject.js#L91

1 ответ

Попробуйте добавить mut в свою декларацию:

#[account(init, mut,seeds = [b"please_____".as_ref(), authority.key().as_ref()],bump = writing_account_bump, payer = authority,space = 9000)]

Другие вопросы по тегам