Trident 是一个基于 Rust 的框架,旨在帮助开发者模糊测试用 Anchor 编写的 Solana 程序。Trident 简化了测试过程,通过发现边界情况漏洞帮助开发者发布安全代码。它通过自动生成测试模板、自适应输入、引导式指令序列和不变量检查等功能提高了测试效率。
Trident 是一个基于 Rust 的框架,旨在帮助开发者进行模糊测试 Solana 程序,这些程序使用 Anchor 编写。Trident 由 Ackee Blockchain 开发并由 Solana 基金会支持,它简化了测试过程,并通过发现边缘案例漏洞帮助开发者交付安全的代码。
我们为 School of Solana 准备了一节额外的课程,供开发者学习如何使用 Trident 对 Solana 程序 进行模糊测试。
使用 Trident 进行 Solana 程序的模糊测试 - YouTube
Ackee Blockchain Security
3.06K 订阅者
Ackee Blockchain Security
搜索
稍后观看
分享
复制链接
信息
购物
点按以取消静音
如短时间内未开始播放,请尝试重新启动设备。
更多视频
你已退出
你观看的视频可能会被添加到电视的观看历史记录中,并影响电视推荐内容。要避免此情况,请在电脑上取消并登录 YouTube。
取消确认
分享
包含播放列表
检索分享信息时出错。 请稍后重试。
0:00
0:00 / 1:12:38 •直播的
•
Trident 的开发始于 2.5 年前的 2021 年,最初的名称是 Trdelník。Trident 在 2022 年的 Solana Riptide Hackathon 期间赢得了 Marinade Finance 社区奖,并在 2023 年获得了 Solana 基金会的开发资助。
特性
安装
Trident 通过 Rust 的 cargo 包管理器分发。要安装 Trident 及其依赖项,请按照以下步骤操作:
cargo install trident-cli
cargo install honggfuzz
trident init
此命令在你的项目中设置 Trident,生成必要的文件和配置。
模糊测试是一种自动化技术,它向你的程序提供生成的随机、无效或意外的输入数据。这有助于发现未知的错误和漏洞,从而可能防止零日漏洞利用。Trident 集成了由 Google 开发的著名模糊器 honggfuzz,以方便 Solana 程序 的模糊测试。
示例:设置一个新的 Anchor 项目
anchor init my-trident-fuzz-test
cd my-trident-fuzz-test
anchor build
创建一个名为 unchecked_arithmetic_0
的程序,其中包含用于测试的故意错误。
use anchor_lang::prelude::*;
const MAGIC_NUMBER: u8 = 254;
declare_id!("...."); // 在此处粘贴你的程序 ID
#[program]
pub mod unchecked_arithmetic_0 {
use super::*;
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
let counter = &mut ctx.accounts.counter;
counter.count = 0;
counter.authority = ctx.accounts.user.key();
Ok(())
}
pub fn update(ctx: Context<Update>, input1: u8, input2: u8) -> Result<()> {
let counter = &mut ctx.accounts.counter;
msg!("input1 = {}, input2 = {}", input1, input2);
counter.count = buggy_math_function(input1, input2).into();
Ok(())
}
}
pub fn buggy_math_function(input1: u8, input2: u8) -> u8 {
// INFO 取消注释 if 语句可以防止
// 除零和减法溢出 panic
// if input2 >= MAGIC_NUMBER {
// return 0;
// }
let divisor = MAGIC_NUMBER - input2;
input1 / divisor
}
#[derive(Accounts)]
pub struct Initialize<'info> {
#[account(init, payer = user, space = 8 + 40)]
pub counter: Account<'info, Counter>,
#[account(mut)]
pub user: Signer<'info>,
pub system_program: Program<'info, System>,
}
#[derive(Accounts)]
pub struct Update<'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,
}
trident init
修改位于 'trident-tests/fuzz_tests/fuzz_0/fuzz_instructions.rs' 的模糊测试模板,并完成 get_data
和 get_accounts
方法以及 FuzzAccounts
结构体的实现:
pub mod unchecked_arithmetic_0_fuzz_instructions {
use crate::accounts_snapshots::*;
use trident_client::{fuzzing::*, solana_sdk::native_token::LAMPORTS_PER_SOL};
#[derive(Arbitrary, DisplayIx, FuzzTestExecutor, FuzzDeserialize)]
pub enum FuzzInstruction {
Initialize(Initialize),
Update(Update),
}
#[derive(Arbitrary, Debug)]
pub struct Initialize {
pub accounts: InitializeAccounts,
pub data: InitializeData,
}
#[derive(Arbitrary, Debug)]
pub struct InitializeAccounts {
pub counter: AccountId,
pub user: AccountId,
pub system_program: AccountId,
}
#[derive(Arbitrary, Debug)]
pub struct InitializeData {}
#[derive(Arbitrary, Debug)]
pub struct Update {
pub accounts: UpdateAccounts,
pub data: UpdateData,
}
#[derive(Arbitrary, Debug)]
pub struct UpdateAccounts {
pub counter: AccountId,
pub authority: AccountId,
}
#[derive(Arbitrary, Debug)]
pub struct UpdateData {
pub input1: u8,
pub input2: u8,
}
impl<'info> IxOps<'info> for Initialize {
type IxData = unchecked_arithmetic_0::instruction::Initialize;
type IxAccounts = FuzzAccounts;
type IxSnapshot = InitializeSnapshot<'info>;
fn get_data(
&self,
_client: &mut impl FuzzClient,
_fuzz_accounts: &mut FuzzAccounts,
) -> Result<Self::IxData, FuzzingError> {
let data = unchecked_arithmetic_0::instruction::Initialize {};
Ok(data)
}
fn get_accounts(
&self,
client: &mut impl FuzzClient,
fuzz_accounts: &mut FuzzAccounts,
) -> Result<(Vec<Keypair>, Vec<AccountMeta>), FuzzingError> {
let user = fuzz_accounts.user.get_or_create_account(
self.accounts.user,
client,
5 * LAMPORTS_PER_SOL,
);
let counter = fuzz_accounts.counter.get_or_create_account(
self.accounts.counter,
client,
5 * LAMPORTS_PER_SOL,
);
let acc_meta = unchecked_arithmetic_0::accounts::Initialize {
counter: counter.pubkey(),
user: user.pubkey(),
system_program: SYSTEM_PROGRAM_ID,
}
.to_account_metas(None);
Ok((vec![user, counter], acc_meta))
}
}
impl<'info> IxOps<'info> for Update {
type IxData = unchecked_arithmetic_0::instruction::Update;
type IxAccounts = FuzzAccounts;
type IxSnapshot = UpdateSnapshot<'info>;
fn get_data(
&self,
_client: &mut impl FuzzClient,
_fuzz_accounts: &mut FuzzAccounts,
) -> Result<Self::IxData, FuzzingError> {
let data = unchecked_arithmetic_0::instruction::Update {
input1: self.data.input1,
input2: self.data.input2,
};
Ok(data)
}
fn get_accounts(
&self,
client: &mut impl FuzzClient,
fuzz_accounts: &mut FuzzAccounts,
) -> Result<(Vec<Keypair>, Vec<AccountMeta>), FuzzingError> {
let user = fuzz_accounts.user.get_or_create_account(
self.accounts.authority,
client,
15 * LAMPORTS_PER_SOL,
);
let counter = fuzz_accounts.counter.get_or_create_account(
self.accounts.counter,
client,
5 * LAMPORTS_PER_SOL,
);
let acc_meta = unchecked_arithmetic_0::accounts::Update {
counter: counter.pubkey(),
authority: user.pubkey(),
}
.to_account_metas(None);
Ok((vec![user], acc_meta))
}
}
#[doc = r" Use AccountsStorage<T> where T can be one of:"]
#[doc = r" Keypair, PdaStore, TokenStore, MintStore, ProgramStore"]
#[derive(Default)]
pub struct FuzzAccounts {
// The 'authority' and 'system_program' were automatically
// generated in the FuzzAccounts struct, as they are both
// used in the program. However, the 'authority' is in fact
// the user account, just named differently. Therefore, we will use only
// the generated user accounts for both 'user' and 'authority account' fields
// in this fuzz test. Additionally, there is no need to fuzz the 'system_program' account.
user: AccountsStorage<Keypair>,
counter: AccountsStorage<Keypair>,
}
}
修改位于 'trident-tests/fuzz_tests/fuzz_0/test_fuzz.rs' 的模糊测试模板:
use fuzz_instructions::unchecked_arithmetic_0_fuzz_instructions::FuzzInstruction;
use fuzz_instructions::unchecked_arithmetic_0_fuzz_instructions::Initialize;
use trident_client::{convert_entry, fuzz_trident, fuzzing::*};
use unchecked_arithmetic_0::entry;
use unchecked_arithmetic_0::ID as PROGRAM_ID;
mod accounts_snapshots;
mod fuzz_instructions;
const PROGRAM_NAME: &str = "unchecked_arithmetic_0";
struct MyFuzzData;
impl FuzzDataBuilder<FuzzInstruction> for MyFuzzData {
fn pre_ixs(u: &mut arbitrary::Unstructured) -> arbitrary::Result<Vec<FuzzInstruction>> {
let init = FuzzInstruction::Initialize(Initialize::arbitrary(u)?);
Ok(vec![init])
}
}
fn main() {
loop {
fuzz_trident!(fuzz_ix: FuzzInstruction, |fuzz_data: MyFuzzData| {
let mut client =
ProgramTestClientBlocking::new(PROGRAM_NAME, PROGRAM_ID, processor!(convert_entry!(entry)))
.unwrap();
let _ = fuzz_data.run_with_runtime(PROGRAM_ID, &mut client);
});
}
}
trident fuzz run fuzz_0
trident fuzz run-debug fuzz_0
trident-tests/fuzz_tests/fuzzing/hfuzz_workspace/fuzz_0/<CRASH_FILE>.fuzz
Trident 还支持集成测试,允许开发者在模拟环境中测试他们的 Solana 程序,该环境与实际的 Solana 区块链非常相似。这确保了程序与区块链和其他程序正确交互。
如需开发者支持,请在我们的 Discord #trident-chat 和 warpcast /trident 频道 中找到我们。请阅读文档,为我们的 GitHub 仓库 加星,并在 Twitter/X 上关注 Trident @TridentSolana 以获取更新。
- 原文链接: ackee.xyz/blog/introduci...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!