本文详细讲解了 Solana Anchor 框架中 [derive(Accounts)] 宏的作用及其四种常见账户类型(Account、UncheckedAccount、Signer、Program),通过代码示例阐明其功能与应用场景,并分析了 Solana 交易中账户预指定的必要性。
在 Solana 的 Anchor 框架中,#[derive(Accounts)] 是一个宏,用于定义结构体,该结构体包含程序执行期间访问的所有账户引用。本文将详细讲解其作用,分析 Solana 交易中账户指定的必要性,并介绍四种常见账户类型:Account、UncheckedAccount、Signer 和 Program,通过代码示例阐明其功能与应用场景。
Solana 的高性能源于交易的并行处理能力。例如,Alice 和 Bob 的交易若不冲突,可同时执行。但若两者试图修改同一账户,则并行化失败。为避免冲突,Solana 要求交易事先声明所有访问的账户。运行时根据这些声明判断是否存在竞争,确保交易顺序或失败处理(如优先级费用决定)。因此,每个 Anchor 函数需配备独立的 #[derive(Accounts)] 结构体,列出程序可能访问的账户。这种机制与以太坊的 EIP-2930 访问列表交易有相似之处。
常用的账户类型包括:
以下初始化存储的代码展示了三种类型:
#[derive(Accounts)]
pub struct Initialize<'info> {
#[account(init,
payer = signer,
space = size_of::<MyStorage>() + 8,
seeds = [],
bump)]
pub my_storage: Account<'info, MyStorage>,
#[account(mut)]
pub signer: Signer<'info>,
pub system_program: Program<'info, System>,
}
读取余额时则引入第四种类型:
#[derive(Accounts)]
pub struct ReadBalance<'info> {
/// CHECK: although we read this account's balance, we don't do anything with the information
#[account(mut)]
pub acct: UncheckedAccount<'info>,
}
这些类型通过 use anchor_lang::prelude::*; 导入,各自承担验证和交互功能。
Account 类型确保加载的账户由程序拥有,若所有者不匹配,则加载失败。这是防止误读非程序创建数据的重要安全措施。
以下示例尝试将非程序拥有的账户传入,结果失败:
use anchor_lang::prelude::*;
declare_id!("7wCSwt6CxCCLnmcXhL3v96512N82EUZ9z7ayC1LtHPNj");
#[program]
pub mod account_types {
use super::*;
pub fn foo(ctx: Context<Foo>) -> Result<()> {
Ok(())
}
}
#[derive(Accounts)]
pub struct Foo<'info> {
some_account: Account<'info, SomeAccount>,
}
#[account]
pub struct SomeAccount {}
import * as anchor from "@coral-xyz/anchor";
import { Program } from "@coral-xyz/anchor";
import { AccountTypes } from "../target/types/account_types";
describe("account_types", () => {
async function airdropSol(publicKey, amount) {
let airdropTx = await anchor.getProvider().connection.requestAirdrop(
publicKey,
amount * anchor.web3.LAMPORTS_PER_SOL
);
await confirmTransaction(airdropTx);
}
async function confirmTransaction(tx) {
const latestBlockHash = await anchor.getProvider().connection.getLatestBlockhash();
await anchor.getProvider().connection.confirmTransaction({
blockhash: latestBlockHash.blockhash,
lastValidBlockHeight: latestBlockHash.lastValidBlockHeight,
signature: tx,
});
}
anchor.setProvider(anchor.AnchorProvider.env());
const program = anchor.workspace.AccountTypes as Program<AccountTypes>;
it("Wrong owner with Account", async () => {
const newKeypair = anchor.web3.Keypair.generate();
await airdropSol(newKeypair.publicKey, 10);
await program.methods.foo().accounts({ someAccount: newKeypair.publicKey }).rpc();
});
});
1) account_types
Wrong owner with Account:
Error: AnchorError caused by account: some_account. Error Code: AccountOwnedByWrongProgram. Error Number: 3007. Error Message: The given account is owned by a different program than expected.
Program log: Left:
Program log: 11111111111111111111111111111111
Program log: Right:
Program log: 7wCSwt6CxCCLnmcXhL3v96512N82EUZ9z7ayC1LtHPNj
若添加 init 宏,账户所有权会转移给程序,但此例未使用。更多细节见 [文档](https:...
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!