01 / Accounts
状态放在账户,程序负责执行
在 Solana 程序中,状态通常存储在独立账户里;交易指令会声明程序需要读取或写入的账户。开发者必须清楚每个账户的所有者、可写权限和生命周期。
程序账户保存可执行代码或程序相关信息,由运行时管理。
数据账户保存应用状态,需明确由哪个程序拥有和修改。
指令账户调用时传入的账户集合,权限由约束和运行时共同决定。
02 / Instruction
把账户约束写在接口上
Anchor 的账户约束可以帮助程序表达“这个账户必须由谁拥有”“这次调用是否允许写入”“种子和派生地址是否匹配”。约束减少了隐含假设,但仍需测试权限和边界。
#[derive(Accounts)]
pub struct Update<'info> {
#[account(mut, has_one = authority)]
pub state: Account<'info, State>,
pub authority: Signer<'info>,
}
#[account]
pub struct State {
pub authority: Pubkey,
pub value: u64,
}读接口时先问:哪些账户可写?谁必须签名?账户所有者是否正确?约束失败时状态是否完全回滚?
03 / PDA
PDA 是可验证的地址关系
程序派生地址通常由种子和程序 ID 决定,并由程序代表其执行逻辑。种子设计、bump 保存方式、初始化权限和账户关闭路径都应纳入测试。
| 检查点 | 要确认的问题 |
|---|---|
| 种子 | 种子是否包含正确的用户、市场或资源标识?是否可能碰撞或被替换? |
| 所有者 | 数据账户是否由预期程序拥有,而不是只检查地址格式? |
| 初始化 | 谁能创建、覆盖或关闭账户?初始化是否只能发生一次? |
| 代币权限 | 铸造、转移和委托权限是否明确,是否存在意外的升级或冻结路径? |
04 / Test
测试账户组合,不只测试函数返回值
Solana 程序的错误经常来自账户组合和权限,而不是单个参数。测试应覆盖错误的 owner、缺失签名、只读账户、错误 PDA、重复初始化和不同用户之间的隔离。
01准备账户创建状态、用户和代币账户。
02调用指令传入正确与错误账户组合。
03检查状态验证数据、余额和事件。
04检查拒绝确认权限错误会回滚。
05 / Boundary
不要把账户约束当成完整审计
框架可以减少样板代码,但不会自动证明经济模型、跨程序调用、代币权限和升级策略安全。部署前固定依赖,测试真实账户组合,检查升级权限和紧急处理流程。
不要做:把主网密钥写入脚本;默认所有传入账户可信;只测试成功路径;把程序地址或框架名称当成安全保证。