Anchor程序的结构


了解 Anchor 程序的结构,包括关键宏及其在简化 Solana 程序开发中的作用。

Anchor 框架使用 Rust 宏 来减少样板代码,并简化编写 Solana 程序所需的常见安全检查的实现。

Anchor 程序中主要的宏包括:

  • declare_id:指定程序的链上地址。
  • #[program]:指定包含程序指令逻辑的模块。
  • #[derive(Accounts)]:用于结构体,表示指令所需的账户列表。
  • #[account]:用于结构体,为程序创建自定义账户类型。

示例程序

以下是一个简单程序,展示了上述宏的用法,以帮助理解 Anchor 程序的基本结构。

该程序包含一个名为 initialize 的指令,用于创建一个新账户(NewAccount),并使用 u64 值初始化。

// 引入 Anchor 框架的预置模块(包含常用宏、类型和功能)
use anchor_lang::prelude::*;

// 声明本程序的程序 ID(Program ID),部署到链上时需替换为真实值
declare_id!("11111111111111111111111111111111");

#[program] //Anchor 程序的主入口模块,定义一个叫 hello_anchor 的 Solana 程序(Program),里面会包含这个程序暴露给前端调用的所有处理逻辑
mod hello_anchor {
    use super::*;

    // 主入口函数之一:initialize
    // 接收一个 Context(上下文对象)和一个参数 data(u64 类型)
    // 创建并初始化一个 NewAccount 类型的账户,并设置其 data 字段
    pub fn initialize(ctx: Context<Initialize>, data: u64) -> Result<()> {
        // 设置新账户的 data 字段为传入值
        ctx.accounts.new_account.data = data;

        // 打印一条日志信息(在 Solana Explorer 中可见)
        msg!("Changed data to: {}!", data);

        Ok(())
    }
}

// 定义「指令所需要的账户」,用于上下文验证和账户检查
#[derive(Accounts)]
//'info 保证账户引用在程序执行期间是安全有效的
pub struct Initialize<'info> {
    // 创建一个新账户,类型为 NewAccount
    #[account(
        // - init:表示这是一个新账户,要初始化
        init, 
        // - payer = signer:签名者负责支付创建账户所需的租金
        payer = signer, 
        // - space = 8 + 8:账户所需的存储空间,8 是 Anchor 的账户标识符(discriminator),另 8 是 u64 的字节数
        space = 8 + 8
    )]
    pub new_account: Account<'info, NewAccount>, //Anchor 提供的账户封装,用于访问 Solana 上的数据账户并自动反序列化为 T 类型

    // 签名者账户(mutable,因为它需要支付租金)
    #[account(mut)]
    pub signer: Signer<'info>,

    // 引用系统程序(用于创建账户)
    pub system_program: Program<'info, System>,
}

// 定义「账户里的数据结构」,用于储存在区块链上的状态
// 默认使用 Anchor 的 #[account] 宏自动添加标识符等元数据
#[account]
pub struct NewAccount {
    // 存储一个 u64 类型的数据
    data: u64,
}

这个程序实现了一个非常典型的 Anchor 初始化账户逻辑。重点是:

  • #[account(init)] 的用法。

  • space = 8 + 8 中的 8 是 Anchor 自动为每个账户分配的前缀 discriminator。

  • Context<Initialize> 会自动帮你打包账户结构体。

  • Account 是一个泛型结构体,它包含:

    • 账户元数据(如账户地址、公钥、余额等)
    • 账户数据反序列化后得到的 Rust 结构体,这里就是 NewAccount

    📦 它的作用是:

    1. 自动帮你反序列化账户数据为 NewAccount
    2. 自动做一些安全检查(比如检查账户是否是预期类型)
    3. 允许你像操作普通 Rust 结构体那样操作 Solana 上的数据
  • 'info 生命周期告诉 Rust:这个账户在程序运行时有效

declare_id! 宏

declare_id! 宏用于指定程序的链上地址(程序 ID)。

use anchor_lang::prelude::*;

declare_id!("11111111111111111111111111111111");

默认情况下,程序 ID 是生成的密钥对的公钥,路径为 /target/deploy/your_program_name.json

使用以下命令可以将 declare_id 中的值与实际生成的密钥同步:

anchor keys sync

这在你克隆了一个仓库,但该仓库的 declare_id! 值与你本地生成的不一致时非常有用。

#[program] 属性

#[program] 属性用于标注包含所有指令处理函数的模块。模块内每个 pub fn 对应一个可被调用的指令。

#[program]
mod hello_anchor {
    use super::*;

    pub fn initialize(ctx: Context<Initialize>, data: u64) -> Result<()> {
        ctx.accounts.new_account.data = data;
        msg!("Changed data to: {}!", data);
        Ok(())
    }
}

指令上下文(Instruction Context)

每个指令函数的第一个参数是 Context<T>,其中 T 是实现了 Accounts trait 的结构体,指定该指令所需的账户。

pub fn initialize(ctx: Context<Initialize>, data: u64) -> Result<()> {
    ctx.accounts.new_account.data = data;
    msg!("Changed data to: {}!", data);
    Ok(())
}

Context 提供以下字段:

  • ctx.accounts: 指令所需的账户
  • ctx.program_id: 当前程序的 ID
  • ctx.remaining_accounts: 额外传入但未声明的账户
  • ctx.bumps: PDA 的 bump 种子

#[derive(Accounts)] 宏

该宏用于结构体,自动实现 Accounts trait,以声明指令所需的账户,并负责账户验证和序列化。

#[derive(Accounts)]
pub struct Initialize<'info> {
    #[account(init, payer = signer, space = 8 + 8)]
    pub new_account: Account<'info, NewAccount>,
    // 签名者账户,必须 mutable(因为它将支付 lamports,因此余额会发生变化)
    #[account(mut)]
    pub signer: Signer<'info>,
    pub system_program: Program<'info, System>,
}

账户验证

Anchor 通过两种方式验证账户的合法性:

  • 账户约束(Account Constraints):通过 #[account(...)] 属性添加约束,如 initmutseeds 等,用于确保账户满足特定条件。
  • 账户类型(Account Types):Anchor 提供内建账户类型(如 Account<T>Signer 等),用于类型检查和自动验证。

#[account] 属性

用于为程序创建自定义账户结构,宏将实现自动序列化、反序列化、添加 discriminator(用于标识账户类型)等功能。

#[account]
pub struct NewAccount {
    data: u64,
}

此宏还会在账户初始化时设置账户拥有者为当前程序 ID。


 上一篇
Solana 发行 Token Solana 发行 Token
Solana 发行 Token 全流程命令整理初始化目录mkdir new-token cd new-token 生成自定义前缀的钱包solana-keygen grind --starts-with noah:1 选中一个结果,例如:
2025-04-10
下一篇 
Solana中的长期经济体 Solana中的长期经济体
Solana 的加密经济系统旨在促进一个健康、可持续发展的长期经济体,其参与者激励机制与网络的安全性和去中心化相匹配。该经济体的主要参与者是验证者节点 (validator-client)。下面将讨论他们对网络的贡献、状态验证及其所需的激励
2025-03-09
  目录