Skip to content

Commit

Permalink
🔥 Load genesis programs and accounts using Trident manifest
Browse files Browse the repository at this point in the history
  • Loading branch information
lukacan committed Oct 6, 2024
1 parent 69cf63b commit 695d736
Show file tree
Hide file tree
Showing 60 changed files with 3,332 additions and 3,015 deletions.
17 changes: 8 additions & 9 deletions .github/workflows/fuzz.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,8 @@ jobs:
working-directory: examples/fuzz-tests/arbitrary-limit-inputs-5 # Set the working directory for the fuzzing test
run: trident fuzz run-hfuzz fuzz_0 # Run the fuzz test with trident

unauthorized-access-2:
name: Fuzz Tests (unauthorized-access-2)
cpi-metaplex-7:
name: Fuzz Tests (cpi-metaplex-7)
needs: Fuzz-Tests
# Runs on an Ubuntu 20.04 runner for a different fuzz test
runs-on: ubuntu-20.04
Expand Down Expand Up @@ -143,18 +143,17 @@ jobs:
- name: Cache Target Folder
uses: actions/cache@v3
with:
path: examples/fuzz-tests/unauthorized-access-2/trident-tests/fuzz_tests/fuzzing/afl/afl_target # Cache the folder where build artifacts are stored
key: target-${{ runner.os }}-unauthorized-access-2 # Unique key for caching based on OS and test
path: examples/fuzz-tests/cpi-metaplex-7/trident-tests/fuzz_tests/fuzzing/honggfuzz/hfuzz_target # Cache the folder where build artifacts are stored
key: target-${{ runner.os }}-cpi-metaplex-7 # Unique key for caching based on OS and test

# Run the fuzzing test using Trident in the unauthorized-access-2 directory
# Run the fuzzing test using Trident in the cpi-metaplex-7 directory
- name: Test Fuzz
working-directory: examples/fuzz-tests/unauthorized-access-2 # Set the working directory for the fuzzing test
run: trident fuzz run-afl fuzz_0 # Run the fuzz test with trident

working-directory: examples/fuzz-tests/cpi-metaplex-7 # Set the working directory for the fuzzing test
run: trident fuzz run-hfuzz fuzz_0 # Run the fuzz test with trident

checks:
name: Fuzz Tests (Checks)
needs: [simple-cpi-6, arbitrary-limit-inputs-5,unauthorized-access-2]
needs: [simple-cpi-6, arbitrary-limit-inputs-5,cpi-metaplex-7]
runs-on: ubuntu-20.04
steps:
- run: echo "All fuzz tests completed successfully"
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ incremented upon a breaking change and the patch version will be incremented for

**Added**

- impr/ allow to specify programs and accounts in the Trident Manifest ([207](https://github.com/Ackee-Blockchain/trident/pull/207))
- impr/ added get_program_id function to the IxOps and FuzzTestExecutor ([199](https://github.com/Ackee-Blockchain/trident/pull/199))

**Changed**
Expand Down
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 0 additions & 6 deletions crates/client/src/commander/afl.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use crate::constants::*;
use fehler::{throw, throws};
use std::io::{Read, Write};
use std::path::PathBuf;
use std::process::Stdio;
use std::{fs::File, path::Path};
use tokio::{io::AsyncWriteExt, process::Command};
Expand All @@ -16,8 +15,6 @@ impl Commander {
pub async fn run_afl(&self, target: String) {
let config = Config::new();

let genesis_folder = PathBuf::from(self.root.to_string()).join("trident-genesis");

let build_args = config.get_afl_build_args();
let fuzz_args = config.get_afl_fuzz_args();

Expand Down Expand Up @@ -56,7 +53,6 @@ impl Commander {
Self::handle_child(&mut child).await?;

let mut child = Command::new("cargo")
.env("GENESIS_FOLDER", genesis_folder)
.arg("afl")
.arg("fuzz")
.args(fuzz_args)
Expand All @@ -74,7 +70,6 @@ impl Commander {
let crash_file = std::path::Path::new(&self.root as &str).join(crash_file_path);

let build_args = config.get_afl_build_args();
let genesis_folder = PathBuf::from(self.root.to_string()).join("trident-genesis");

if !crash_file.try_exists()? {
println!("{ERROR} The crash file [{:?}] not found", crash_file);
Expand All @@ -95,7 +90,6 @@ impl Commander {
// using exec rather than spawn and replacing current process to avoid unflushed terminal output after ctrl+c signal
let mut child = Command::new("cargo")
.env("RUSTFLAGS", rustflags)
.env("GENESIS_FOLDER", genesis_folder)
.arg("afl")
.arg("run")
.args(build_args)
Expand Down
12 changes: 0 additions & 12 deletions crates/client/src/commander/honggfuzz.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use fehler::{throw, throws};
use std::path::PathBuf;
use std::process;
use std::{os::unix::process::CommandExt, process::Stdio};
use tokio::process::Command;
Expand All @@ -20,8 +19,6 @@ impl Commander {
// arguments so we need to parse the variable content.
let hfuzz_run_args = std::env::var("HFUZZ_RUN_ARGS").unwrap_or_default();

let genesis_folder = PathBuf::from(self.root.to_string()).join("trident-genesis");

let mut rustflags = std::env::var("RUSTFLAGS").unwrap_or_default();

rustflags.push_str("--cfg honggfuzz");
Expand Down Expand Up @@ -50,7 +47,6 @@ impl Commander {
// enforce keep output to be true
fuzz_args.push_str("--keep_output");
let mut child = Command::new("cargo")
.env("GENESIS_FOLDER", genesis_folder)
.env("HFUZZ_RUN_ARGS", fuzz_args)
.env("CARGO_TARGET_DIR", cargo_target_dir)
.env("HFUZZ_WORKSPACE", hfuzz_workspace)
Expand All @@ -64,7 +60,6 @@ impl Commander {
}
false => {
let mut child = Command::new("cargo")
.env("GENESIS_FOLDER", genesis_folder)
.env("HFUZZ_RUN_ARGS", fuzz_args)
.env("CARGO_TARGET_DIR", cargo_target_dir)
.env("HFUZZ_WORKSPACE", hfuzz_workspace)
Expand Down Expand Up @@ -95,8 +90,6 @@ impl Commander {

let hfuzz_run_args = std::env::var("HFUZZ_RUN_ARGS").unwrap_or_default();

let genesis_folder = PathBuf::from(self.root.to_string()).join("trident-genesis");

let cargo_target_dir = std::env::var("CARGO_TARGET_DIR")
.unwrap_or_else(|_| config.get_env_arg(&EnvVariable::CargoTargetDir));
let hfuzz_workspace = std::env::var("HFUZZ_WORKSPACE")
Expand All @@ -113,7 +106,6 @@ impl Commander {
// enforce keep output to be true
fuzz_args.push_str("--keep_output");
let mut child = Command::new("cargo")
.env("GENESIS_FOLDER", genesis_folder)
.env("HFUZZ_RUN_ARGS", fuzz_args)
.env("CARGO_TARGET_DIR", cargo_target_dir)
.env("HFUZZ_WORKSPACE", hfuzz_workspace)
Expand All @@ -127,7 +119,6 @@ impl Commander {
}
false => {
let mut child = Command::new("cargo")
.env("GENESIS_FOLDER", genesis_folder)
.env("HFUZZ_RUN_ARGS", fuzz_args)
.env("CARGO_TARGET_DIR", cargo_target_dir)
.env("HFUZZ_WORKSPACE", hfuzz_workspace)
Expand All @@ -147,8 +138,6 @@ impl Commander {

let crash_file = std::path::Path::new(&self.root as &str).join(crash_file_path);

let genesis_folder = PathBuf::from(self.root.to_string()).join("trident-genesis");

if !crash_file.try_exists()? {
println!("{ERROR} The crash file [{:?}] not found", crash_file);
throw!(Error::CrashFileNotFound);
Expand All @@ -163,7 +152,6 @@ impl Commander {

// using exec rather than spawn and replacing current process to avoid unflushed terminal output after ctrl+c signal
std::process::Command::new("cargo")
.env("GENESIS_FOLDER", genesis_folder)
.env("CARGO_TARGET_DIR", cargo_target_dir)
.env("RUSTFLAGS", rustflags)
.arg("hfuzz")
Expand Down
1 change: 0 additions & 1 deletion crates/client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ pub mod fuzzing {
pub use trident_fuzz::*;

pub use solana_program_test::processor;
pub use trident_fuzz::program_test_client_blocking::FuzzingAccountBase64;
pub use trident_fuzz::program_test_client_blocking::FuzzingProgram;
pub use trident_fuzz::program_test_client_blocking::ProgramEntry;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ pub fn generate_source_code(idl_instructions: &[Idl]) -> String {

#(#fuzzing_programs)*

let mut client = ProgramTestClientBlocking::new(&#programs_array,&[]).unwrap();
let mut client = ProgramTestClientBlocking::new(&#programs_array,config).unwrap();

let _ = fuzz_data.run_with_runtime(&mut client,config);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ fn fuzz_iteration<T: FuzzTestExecutor<U> + std::fmt::Display, U>(

let mut client = ProgramTestClientBlocking::new(
&[fuzzing_program_dummy_2, fuzzing_program_dummy_example],
&[],
config,
)
.unwrap();

Expand Down
150 changes: 148 additions & 2 deletions crates/fuzz/src/config/fuzz.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
use serde::Deserialize;
use std::{
fs,
path::{Path, PathBuf},
str::FromStr,
};

use serde::{Deserialize, Serialize};
use solana_sdk::pubkey::Pubkey;

use super::discover_root;

#[derive(Debug, Deserialize, Clone, Default)]
pub struct Fuzz {
pub fuzzing_with_stats: bool,
pub allow_duplicate_txs: bool,
pub programs: Vec<FuzzProgram>,
pub accounts: Vec<FuzzAccount>,
}

#[derive(Default, Debug, Deserialize, Clone)]
Expand All @@ -12,13 +23,36 @@ pub struct _Fuzz {
pub fuzzing_with_stats: Option<bool>,
#[serde(default)]
pub allow_duplicate_txs: Option<bool>,
#[serde(default)]
pub programs: Option<Vec<_FuzzProgram>>,
#[serde(default)]
pub accounts: Option<Vec<_FuzzAccount>>,
}
impl From<_Fuzz> for Fuzz {
fn from(_f: _Fuzz) -> Self {
Self {
let mut _self = Self {
fuzzing_with_stats: _f.fuzzing_with_stats.unwrap_or_default(),
allow_duplicate_txs: _f.allow_duplicate_txs.unwrap_or_default(),
programs: vec![],
accounts: vec![],
};

if let Some(accounts) = _f.accounts {
for account in accounts {
_self
.accounts
.push(read_and_parse_account(&account.filename));
}
}
if let Some(programs) = _f.programs {
for account in programs {
_self
.programs
.push(read_and_parse_program(&account.program, &account.address));
}
}

_self
}
}

Expand All @@ -30,3 +64,115 @@ impl Fuzz {
self.allow_duplicate_txs
}
}

#[derive(Debug, Deserialize, Clone)]
pub struct _FuzzProgram {
pub address: String,
pub program: String,
}

#[derive(Debug, Deserialize, Clone)]
pub struct _FuzzAccount {
pub address: String,
pub filename: String,
}

#[derive(Debug, Deserialize, Clone)]
pub struct FuzzProgram {
pub address: Pubkey,
pub data: Vec<u8>,
}

#[derive(Debug, Deserialize, Clone)]
pub struct FuzzAccount {
pub pubkey: Pubkey,
pub account: Account,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Account {
pub lamports: u64,
pub data: String,
pub owner: Pubkey,
pub executable: bool,
pub rent_epoch: u64,
}

#[derive(Debug, Deserialize, Clone)]
pub struct FuzzAccountRaw {
pub pubkey: String,
pub account: AccountRaw,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct AccountRaw {
pub lamports: u64,
pub data: Vec<String>,
pub owner: String,
pub executable: bool,
#[serde(rename = "rentEpoch")]
pub rent_epoch: u64,
}

fn read_and_parse_program(filename: &str, program_address: &str) -> FuzzProgram {
let path = resolve_path(filename);

let program_data =
fs::read(path).unwrap_or_else(|_| panic!("Failed to read file: {}", filename));

let pubkey = Pubkey::from_str(program_address)
.unwrap_or_else(|_| panic!("Cannot parse the program address: {}", program_address));

FuzzProgram {
address: pubkey,
data: program_data,
}
}

fn read_and_parse_account(filename: &str) -> FuzzAccount {
let path = resolve_path(filename);

let file_content =
fs::read_to_string(path).unwrap_or_else(|_| panic!("Failed to read file: {}", filename));

let account_raw: FuzzAccountRaw = serde_json::from_str(&file_content)
.unwrap_or_else(|_| panic!("Failed to parse JSON from file: {}", filename));

let pubkey = Pubkey::from_str(&account_raw.pubkey)
.unwrap_or_else(|_| panic!("Cannot convert address for: {}", account_raw.pubkey));

let owner_address = Pubkey::from_str(&account_raw.account.owner).unwrap_or_else(|_| {
panic!(
"Cannot convert address for owner: {}",
account_raw.account.owner
)
});

let data_base_64 = account_raw.account.data.first().unwrap_or_else(|| {
panic!(
"Cannot read base64 data for account: {}",
account_raw.pubkey
)
});

let account = Account {
lamports: account_raw.account.lamports,
data: data_base_64.to_string(),
owner: owner_address,
executable: account_raw.account.executable,
rent_epoch: account_raw.account.rent_epoch,
};

FuzzAccount { pubkey, account }
}

fn resolve_path(filename: &str) -> PathBuf {
let path = Path::new(filename);
if path.is_absolute() {
path.to_path_buf()
} else {
discover_root()
.map(|cwd| cwd.join(path))
.unwrap_or_else(|_| panic!("Failed to resolve relative path: {}", path.display()))
}
}
Loading

0 comments on commit 695d736

Please sign in to comment.