trident_fuzz/
program_test_client_blocking.rsuse solana_program_runtime::invoke_context::BuiltinFunctionWithContext;
use solana_program_test::ProgramTest;
use solana_program_test::ProgramTestContext;
use solana_sdk::account::Account;
use solana_sdk::account_info::AccountInfo;
use solana_sdk::clock::Clock;
use solana_sdk::entrypoint::ProgramResult;
use solana_sdk::sysvar::Sysvar;
use solana_sdk::{
account::AccountSharedData, hash::Hash, pubkey::Pubkey, rent::Rent, signature::Keypair,
transaction::VersionedTransaction,
};
use tokio::runtime::Builder;
use crate::config::Config;
use crate::error::*;
use crate::fuzz_client::FuzzClient;
pub type ProgramEntry = for<'info> fn(
program_id: &Pubkey,
accounts: &'info [AccountInfo<'info>],
instruction_data: &[u8],
) -> ProgramResult;
pub struct ProgramTestClientBlocking {
ctx: ProgramTestContext,
rt: tokio::runtime::Runtime,
}
pub struct FuzzingProgram {
pub program_name: String,
pub program_id: Pubkey,
pub entry: Option<BuiltinFunctionWithContext>,
}
impl FuzzingProgram {
pub fn new(
program_name: &str,
program_id: &Pubkey,
entry_fn: Option<BuiltinFunctionWithContext>,
) -> FuzzingProgram {
Self {
program_name: program_name.to_string(),
program_id: *program_id,
entry: entry_fn,
}
}
}
impl ProgramTestClientBlocking {
pub fn new(program_: &[FuzzingProgram], config: &Config) -> Result<Self, FuzzClientError> {
let mut program_test = ProgramTest::default();
for x in program_ {
if let Some(entry) = x.entry {
program_test.add_builtin_program(&x.program_name, x.program_id, entry);
}
}
for account in config.fuzz.accounts.iter() {
program_test.add_account_with_base64_data(
account.pubkey,
account.account.lamports,
account.account.owner,
&account.account.data,
)
}
for program in config.fuzz.programs.iter() {
program_test.add_account(
program.address,
Account {
lamports: Rent::default().minimum_balance(program.data.len()).max(1),
data: program.data.clone(),
owner: solana_sdk::bpf_loader::id(),
executable: true,
rent_epoch: 0,
},
);
}
let rt: tokio::runtime::Runtime = Builder::new_current_thread().enable_all().build()?;
let ctx = rt.block_on(program_test.start_with_context());
Ok(Self { ctx, rt })
}
}
#[macro_export]
macro_rules! convert_entry {
($entry:expr) => {
unsafe { core::mem::transmute::<ProgramEntry, ProcessInstruction>($entry) }
};
}
impl FuzzClient for ProgramTestClientBlocking {
fn payer(&self) -> Keypair {
self.ctx.payer.insecure_clone()
}
fn get_account(&mut self, key: &Pubkey) -> AccountSharedData {
let account = self
.rt
.block_on(self.ctx.banks_client.get_account_with_commitment(
*key,
solana_sdk::commitment_config::CommitmentLevel::Confirmed,
))
.unwrap_or_default();
match account {
Some(account) => account.into(),
None => {
let account = AccountSharedData::new(0, 0, &solana_sdk::system_program::ID);
self.ctx.set_account(key, &account);
account
}
}
}
fn get_last_blockhash(&self) -> Hash {
self.ctx.last_blockhash
}
fn process_transaction(
&mut self,
transaction: impl Into<VersionedTransaction>,
) -> Result<(), FuzzClientError> {
Ok(self
.rt
.block_on(self.ctx.banks_client.process_transaction(transaction))?)
}
fn set_account_custom(&mut self, address: &Pubkey, account: &AccountSharedData) {
self.ctx.set_account(address, account);
}
fn forward_in_time(&mut self, seconds: i64) -> Result<(), FuzzClientError> {
let mut clock = self
.rt
.block_on(self.ctx.banks_client.get_sysvar::<Clock>())?;
let new_timestamp = clock.unix_timestamp.saturating_add(seconds);
clock.unix_timestamp = new_timestamp;
self.ctx.set_sysvar(&clock);
Ok(())
}
fn warp_to_slot(&mut self, warp_slot: u64) {
let _ = self.ctx.warp_to_slot(warp_slot);
}
fn warp_to_epoch(&mut self, warp_epoch: u64) {
let _ = self.ctx.warp_to_epoch(warp_epoch);
}
fn get_sysvar<T: Sysvar>(&mut self) -> T {
self.rt
.block_on(self.ctx.banks_client.get_sysvar::<T>())
.unwrap_or_default()
}
}