>_ DevTrendszh

语言

首页

语言

板块

前端 后端 移动端 DevOps AI / ML 游戏开发 区块链 嵌入式 安全
Rust

如何编写 Solana 智能合约而不会被样板代码逼疯

如果你曾经尝试过用纯 Rust 为 Solana 编写程序,你可能记得那种感觉:五行业务逻辑需要写五十行账户验证检查、通过 Borsh 手动反序列化,以及签名验证。交易中密钥顺序搞错了——你就会得到一个运行时错误,有时需要数小时才能调试出来。

在以太坊世界中,开发者早已习惯了 Solidity 和 Hardhat 或 Foundry 等现成的封装库。在 Solana 中,类似的标杆变成了 Anchor——一个处理数据解析、访问控制检查和客户端代码生成等日常工作的框架。

Anchor 为你处理的工作

本质上,Anchor 是构建在 Rust 之上的 DSL(领域特定语言)。它不会改变 Solana 内部的 BPF 执行模型,而是将底层调用打包成易于理解的声明式宏。

当你使用 Anchor 编写程序时,框架解决四个主要任务:

  1. 账户数据的序列化和反序列化,无需手动调用解包方法。
  2. 通过属性直接在结构体签名中进行账户约束验证(所有者检查、签名验证、内存初始化)。
  3. IDL(接口描述语言)规范组装——以太坊 ABI 的等价物。
  4. 创建可直接用于前端或测试的现成 TypeScript 和 Rust 客户端,用于与合约交互。

实际代码长什么样

让我们看一个经典的计数器示例。在纯 Solana SDK 中,你需要手动解析字节数组 InstructionData,提取账户切片,检查调用者地址的 is_signer,并验证系统程序地址。

以下是同一合约在 Anchor 中的样子:

use anchor_lang::prelude::*;

declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");

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

    pub fn initialize(ctx: Context<Initialize>, start: u64) -> Result<()> {
        let counter = &mut ctx.accounts.counter;
        counter.authority = *ctx.accounts.authority.key;
        counter.count = start;
        Ok(())
    }

    pub fn increment(ctx: Context<Increment>) -> Result<()> {
        let counter = &mut ctx.accounts.counter;
        counter.count += 1;
        Ok(())
    }
}

#[derive(Accounts)]
pub struct Initialize<'info> {
    #[account(init, payer = authority, space = 48)]
    pub counter: Account<'info, Counter>,
    pub authority: Signer<'info>,
    pub system_program: Program<'info, System>,
}

#[derive(Accounts)]
pub struct Increment<'info> {
    #[account(mut, has_one = authority)]
    pub counter: Account<'info, Counter>,
    pub authority: Signer<'info>,
}

#[account]
pub struct Counter {
    pub authority: Pubkey,
    pub count: u64,
}

差异立竿见影。所有验证逻辑都移到了 InitializeIncrement 结构体中。

#[account(init, payer = authority, space = 48)] 属性告诉运行时:创建一个新账户,为其分配 48 字节空间,并向 authority 钱包收取租金。

#[account(mut, has_one = authority)] 结构会自动检查 Counter 结构内的 authority 字段是否与传入的账户 authority 匹配,以及该账户是否实际签署了交易。如果没有签名或传入了不同的钱包,执行将在进入函数体之前中止 increment

IDL 和前端,不再头疼

Anchor 捆绑包中最方便的是 JSON 格式的 IDL 文件。编译器在构建项目时会自动组装它。

IDL 描述了程序的所有指令、数据结构和可能的自定义错误类型。基于这个文件,@anchor-lang/core 库为 JavaScript 或 TypeScript 生成类型化接口。

在客户端组装交易时,你不再需要记住字节偏移量。从前端调用方法变成了一次常规的函数调用:

await program.methods
  .increment()
  .accounts({
    counter: counterPubkey,
    authority: wallet.publicKey,
  })
  .rpc();

如果你忘记传递必需的账户或指定了错误的参数类型,TypeScript 会高亮显示错误。

内置模糊测试,查找漏洞

在智能合约中,犯错的代价太高,这就是测试扮演特殊角色的原因。CLI 包含与 Crucible 工具的集成,用于覆盖率引导的模糊测试。

anchor fuzz init 命令生成测试工具,anchor fuzz run 用随机输入数据运行它,尝试找出导致 panic 或无效账户状态的边界情况。这有助于在部署到测试网之前捕获不明显的溢出或遗漏的检查。

# Инициализация фазз-тестов
anchor fuzz init program_name

# Запуск тестов в release-сборке
anchor fuzz run program_name test_name --release

安装和入门

为了管理工具链版本,开发者创建了一个名为 AVM(Anchor 版本管理器)的实用工具。这可以避免 Solana 编译器与框架本身之间的版本冲突,当不同项目需要不同的子版本时。

该工具只需一行命令即可安装:

curl -sSfL https://raw.githubusercontent.com/otter-sec/anchor/master/avm/install | sh

安装后,你可以切换到夜间构建版本或为工作区固定特定版本:

avm nightly
avm nightly --disable

谁将从 Anchor 中受益

如果你刚开始学习 Solana 开发,不使用 Anchor 几乎毫无意义。你会花数周时间重新发明账户解析和鉴别符验证的轮子。

该框架涵盖了主要需求:

  • 为 Rust 后端开发者提供严格类型检查和针对常见验证漏洞模式的保护。
  • 为前端开发者提供现成的 TypeScript 类型和发送交易的便捷方法。

唯一可能觉得 Anchor 过于冗余的情况是编写需要极致二进制大小优化(计算预算限制)的微型程序,在这种情况下,每个字节的指令都至关重要。在所有其他场景中,它都是事实上的标准,节省了数百小时的工作时间。

相关项目