10分钟Solana-性能web3-1.4 Solana 合约基础

1 阅读4分钟

欢迎订阅专栏10分钟Solana-性能web3

Solana 上的智能合约被称为程序(Program)。与以太坊合约不同,Solana 的程序是无状态的——程序的代码与它处理的数据(账户)是分离的。所有可变的状态都存储在独立的数据账户(Data Account) 中,由程序负责读写。

🧬 开发语言与工具链

Solana 程序主要用 Rust 编写,通过 LLVM 编译器框架编译为 eBPF 字节码,在 Solana 虚拟机 (SVM) 上执行。

核心工具链包括:

  • Rust & Cargo:主要的编程语言和构建系统。
  • Solana CLI:用于与网络交互,如创建钱包、部署程序等。
  • solana-program SDK:提供编写链上程序所需的基础类型和宏。

🏗️ Anchor 框架:Solana 开发的“标准答案”

在实际开发中,Anchor 是事实上的标准框架。它通过 Rust 宏极大地简化了开发流程。

  • 主要功能
    • 项目脚手架anchor init 命令一键生成完整的项目结构,包含程序、测试、部署配置和 TypeScript 类型。
    • 账户验证与序列化:通过 #[derive(Accounts)] 宏声明式地定义和验证交易账户。
    • 程序逻辑:通过 #[program] 宏定义指令处理函数。
    • 状态定义:通过 #[account] 宏定义链上账户的数据结构。

💻 一个标准的 Solana 程序

这是一个由 Anchor 框架生成的极简计数器程序,它定义了一个初始化指令,用于创建一个新账户并存储一个数字。

// 引入 Anchor 框架的预导入模块
use anchor_lang::prelude::*;

// 声明程序的链上地址(部署时会自动更新)
declare_id!("11111111111111111111111111111111");

// #[program] 宏定义了程序的指令入口
#[program]
mod hello_anchor {
    use super::*;

    // 这是一个名为 initialize 的指令
    // 它接收一个 Context<Initialize> 和一个 u64 类型的 data
    pub fn initialize(ctx: Context<Initialize>, data: u64) -> Result<()> {
        // 将传入的 data 存储到新账户的 data 字段中
        ctx.accounts.new_account.data = data;
        // 打印日志
        msg!("Changed data to: {}!", data);
        Ok(())
    }
}

// #[derive(Accounts)] 宏用于定义指令所需的账户列表及约束
#[derive(Accounts)]
pub struct Initialize<'info> {
    // 定义了一个新账户,它将被初始化 (init)
    // 由 signer 支付创建账户的费用 (payer)
    // 并分配 8(Anchor 账户鉴别器)+ 8(u64 数据)字节的空间 (space)
    #[account(init, payer = signer, space = 8 + 8)]
    pub new_account: Account<'info, NewAccount>,
    // 交易签名者,被标记为可变 (mut) 以支付 lamports
    #[account(mut)]
    pub signer: Signer<'info>,
    // 系统程序,用于创建新账户
    pub system_program: Program<'info, System>,
}

// #[account] 宏定义了链上账户的数据结构
#[account]
pub struct NewAccount {
    data: u64, // 存储一个 64 位无符号整数
}

简单来说,这个合约的 initialize 指令会创建一个由 NewAccount 结构定义的新账户,并将传入的数字保存其中。

🚀 部署与调用

部署和调用 Solana 程序,本质上是链上账户的创建与使用:

  1. 编译:将 Rust 代码编译为 eBPF 字节码(.so 文件)。
  2. 部署:使用 solana program deploy 命令将编译好的字节码部署到链上,这会创建一个程序账户(Program Account)
  3. 调用:通过构造交易(Transaction) 来调用程序。交易中必须明确列出所有被读取或修改的账户,包括程序本身、指令涉及的账户和签名者。
  4. 客户端交互:Anchor 会自动生成 IDL (接口描述语言) 文件,前端可以使用它配合 TypeScript 客户端类型安全地调用合约。

🛡️ 安全最佳实践

  • 账户验证:始终验证传入的账户是否符合预期(如所有者、是否可写、是否是 PDA),推荐使用 Anchor 的账户约束自动完成。
  • 所有权与权限:严格遵循账户模型,确保只有账户的 owner 程序才能修改其数据。
  • PDA 签名:使用 PDA 时,必须验证 seedsbump,确保程序有权限代表 PDA 进行操作。
  • 重入防护:虽然 Solana 的并行模型降低了风险,但在跨程序调用 (CPI) 时仍需注意重入攻击。
  • 关闭账户:提供关闭账户的指令,并将剩余的 lamports 退还给用户,避免存储浪费。
  • 代码审计:在主网部署前,务必进行专业的安全审计。

🧪 开发环境准备

  1. 安装 Rust
    curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
    
  2. 安装 Solana CLI
    sh -c "$(curl -sSfL https://release.solana.com/stable/install)"
    
  3. 安装 Anchor CLI
    cargo install avm --git https://github.com/solana-foundation/anchor --locked && avm install 1.0.0 && avm use 1.0.0
    
  4. 验证安装
    anchor --version
    solana --version
    rustc --version
    

💎 总结

  • 无状态程序:Solana 程序的代码与状态(数据账户)是分离的。
  • 核心语言:主要使用 Rust 开发,编译为 eBPF 字节码。
  • 官方推荐框架:使用 Anchor 可以大幅简化开发、测试和部署流程。
  • 开发流程:通过 Solana CLIAnchor CLI 实现从编译、部署到测试的全流程管理。
  • 前端交互:Anchor 生成的 IDL 可实现类型安全的前端调用。

如果想深入了解某个部分,比如 Anchor 的 PDA 账户管理或如何进行跨程序调用(CPI),可以随时再问我。