Ошибка вызова 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 };
};
Может ли кто-нибудь просмотреть код и помочь мне? Кроме того, код строки ниже (где я получаю сообщение об ошибке):
1 ответ
Попробуйте добавить mut в свою декларацию:
#[account(init, mut,seeds = [b"please_____".as_ref(), authority.key().as_ref()],bump = writing_account_bump, payer = authority,space = 9000)]