Solana CPI 调用错误。错误:处理指令 0 时出错:未经授权的签名者或可写帐户进行跨程序调用
我基本上已将用 Native Rust (Solana CLI) 编写的众筹活动转换为锚定版本。 所以除了捐赠测试之外,我的测试运行良好。
如果一些 Solana 超级神秘的开发人员可以帮助我解决这个问题,我将不胜感激。我花了将近一周的时间调试,但无法弄清楚。显然我的逻辑或语法在某些地方是错误的,但我无法理解。
老实说,我觉得我的逻辑是对的,因为我已经把代码读了很多遍了。但肯定有问题......
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("
I have basically converted a Crowd Funding Campaign written in Native Rust (Solana CLI) into an Anchor Version.
So my tests are running fine except the donation test.
Please if some Solana super shadowy dev can help me figure this out, I shall be grateful. I have spent almost a week debugging, but unable to figure out. Obviously somewhere my logic or syntax is wrong, but I am unable to understand.
Honestly, I feel my logic is right, because I have read the code so many times. But surely, something is wrong......
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,
}
}
also my tests as below
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 };
};
Can someone go through the code and help me?
Also, the line Code below (where I am getting the error):
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
尝试在声明中添加 mut:
#[account(init, mut,seeds = [b"please_____".as_ref(), Authority.key().as_ref()],bump =writing_account_bump, payer = Authority,空间 = 9000)]
Try to add mut in your declaration:
#[account(init, mut,seeds = [b"please_____".as_ref(), authority.key().as_ref()],bump = writing_account_bump, payer = authority,space = 9000)]