本文详细介绍了 Solana Anchor 框架中的 [derive(Accounts)]
宏,解释了 Solana 并行交易处理机制及其账户访问控制的重要性,并深入探讨了 Account
、UncheckedAccount
、Signer
和 Program
四种账户类型的使用场景和实现细节。
#[derive(Accounts)]
:不同类型的账户#[derive(Accounts)]
在Solana Anchor中是一个类似属性的宏,用于包含所有在执行期间将被函数访问的账户的引用。
Solana之所以如此快速的一个原因是它以并行的方式执行交易。也就是说,如果Alice和Bob都想进行一笔交易,Solana将尝试同时处理他们的交易。然而,如果他们的交易因访问同一存储而发生冲突,就会出现问题。例如,假设Alice和Bob都在尝试写入同一账户。显然,他们的交易不能并行执行。
为了让Solana知道Alice和Bob的交易不能并行处理,Alice和Bob都必须提前指定所有他们的交易将更新的账户。
由于Alice和Bob都指定了一个(存储)账户,Solana运行时可以推断出两个交易之间存在冲突。必须选择一个(推测是支付了更高优先级费用的那个),另一个最终将失败。
这就是为什么每个函数都有自己单独的#[derive(Accounts)]
结构体。结构体中的每个字段都是程序在执行期间打算(但并不要求)访问的账户。
一些以太坊开发者可能会注意到这一要求与EIP 2930访问列表交易的相似性。
账户的类型向Anchor信号你打算如何与该账户交互。
在我们初始化存储的代码中,我们看到了三种不同的“类型”账户:
Account
Signer
Program
这里是代码片段:
当我们读取账户余额时,我们看到了第四种类型:
UncheckedAccount
这里是我们使用的代码:
我们用绿色框标出的每个项目都是通过文件顶部的anchor_lang::prelude::*;
引入的。
Account
、UncheckedAccount
、Signer
和Program
的目的是在继续之前对传入的账户进行某种检查,并且还提供与这些账户交互的函数。
我们将在接下来的部分进一步解释这四种类型。
Account
类型会检查被加载的账户的所有者是否确实被程序拥有。如果所有者不匹配,则不会加载。这作为一种重要的安全措施,以防止意外读取程序未创建的数据。
在以下示例中,我们创建了一个密钥对账户,并尝试将其传递给foo
。因为该账户不属于程序,所以交易失败。
Rust:
use anchor_lang::prelude::*;
declare_id!("ETnqC8mvPRyUVXyXoph22EQ1GS5sTs1zndkn5eGMYWfs");
##[program]
pub mod account_types {
use super::*;
pub fn foo(ctx: Context<Foo>) -> Result<()> {
// 我们不对账户SomeAccount做任何操作
Ok(())
}
}
##[derive(Accounts)]
pub struct Foo<'info> {
some_account: Account<'info, SomeAccount>,
}
##[account]
pub struct SomeAccount {}
Typescript:
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("账户拥有者错误", async () => {
const newKeypair = anchor.web3.Keypair.generate();
await airdropSol(newKeypair.publicKey, 10);
await program.methods
.foo()
.accounts({someAccount: newKeypair
.publicKey}).rpc();
});
});
这是执行测试后的输出:
如果我们向Account
添加一个init
宏,那么它将尝试将所有权从系统程序转...
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!