pub struct TestContext {
pub svm: LiteSVM,
pub pending_instructions: Vec<Instruction>,
pub snapshot: Option<SvmSnapshot>,
pub dirty_tracker: DirtyTracker,
pub sigverify: bool,
/* private fields */
}Fields§
§svm: LiteSVM§pending_instructions: Vec<Instruction>§snapshot: Option<SvmSnapshot>Snapshot of initial state for fast restore (always-on in fuzz mode)
dirty_tracker: DirtyTrackerTracks dirty accounts across all transactions in an iteration
sigverify: boolWhether signature verification is enabled on the SVM. When false (default for fuzzing), transactions use dummy signatures to skip ed25519 computation (~77µs per tx).
Implementations§
Source§impl TestContext
impl TestContext
Sourcepub fn create_mock_pyth_oracle(&mut self) -> MockPythOracleBuilder<'_>
pub fn create_mock_pyth_oracle(&mut self) -> MockPythOracleBuilder<'_>
Sourcepub fn update_pyth_price(
&mut self,
oracle: &Pubkey,
price: i64,
exponent: i32,
) -> Result<()>
pub fn update_pyth_price( &mut self, oracle: &Pubkey, price: i64, exponent: i32, ) -> Result<()>
Update the price on an existing Pyth oracle
§Arguments
oracle- The oracle account pubkeyprice- New price valueexponent- Price exponent (typically -8)
Sourcepub fn refresh_pyth_oracle(&mut self, oracle: &Pubkey) -> Result<()>
pub fn refresh_pyth_oracle(&mut self, oracle: &Pubkey) -> Result<()>
Refresh a Pyth oracle’s timestamp and slot to make it “fresh” Use this before operations that check oracle staleness
Source§impl TestContext
impl TestContext
pub fn new() -> Self
pub fn with_invocation_callback<C: InvocationInspectCallback + 'static>( callback: C, ) -> Self
pub fn with_compute_budget(self, compute_unit_limit: u64) -> Self
Sourcepub fn analyze_program_coverage(program_data: &[u8]) -> Option<(usize, usize)>
pub fn analyze_program_coverage(program_data: &[u8]) -> Option<(usize, usize)>
Analyze a program binary and return (total_edges, total_instructions). Only counts reachable code from the program entrypoint via BFS. Only counts edges from conditional jump instructions (matching runtime tracking). Used for coverage percentage calculation.
pub fn add_program( &mut self, program_id: &Pubkey, program_path: &str, ) -> Result<()>
Sourcepub fn add_program_from_bytes(
&mut self,
program_id: &Pubkey,
program_data: &[u8],
) -> Result<()>
pub fn add_program_from_bytes( &mut self, program_id: &Pubkey, program_data: &[u8], ) -> Result<()>
Load a program from raw ELF bytes into the SVM.
Same as add_program() but takes &[u8] instead of a file path.
Runs coverage analysis and stores program data for debuggable SVM reloading.
pub fn from_svm(svm: LiteSVM) -> Self
pub fn into_svm(self) -> LiteSVM
Sourcepub fn clone_with_invocation_callback<C: InvocationInspectCallback + 'static>(
&self,
callback: C,
) -> Self
pub fn clone_with_invocation_callback<C: InvocationInspectCallback + 'static>( &self, callback: C, ) -> Self
Clone this context and set an invocation callback for coverage tracking. The source SVM must have been created with debuggable mode (via CRUCIBLE_FUZZ_DEBUGGABLE env var) for register tracing to work. Cloning preserves the debuggable state and loaded programs.
NOTE: For better performance in fuzzing loops, prefer using set_invocation_callback after
cloning the fixture instead of this method. This method performs an additional SVM clone
beyond the fixture clone, which can be expensive.
Sourcepub fn set_invocation_callback<C: InvocationInspectCallback + 'static>(
&mut self,
callback: C,
)
pub fn set_invocation_callback<C: InvocationInspectCallback + 'static>( &mut self, callback: C, )
Set an invocation callback for coverage tracking on this context.
Unlike clone_with_invocation_callback, this modifies the context in place
without performing an additional SVM clone.
The SVM must have been created with debuggable mode (via CRUCIBLE_FUZZ_DEBUGGABLE env var) for register tracing to work.
Usage pattern for fuzzing loops:
let mut fixture = template_fixture.clone(); // Single clone
fixture.ctx.set_invocation_callback(callback); // No additional cloneSourcepub fn track_account(&mut self, pubkey: Pubkey)
pub fn track_account(&mut self, pubkey: Pubkey)
Track an account pubkey so it gets copied when cloning with invocation callback. Called internally by account builders.
Sourcepub fn tracked_accounts_count(&self) -> usize
pub fn tracked_accounts_count(&self) -> usize
Get count of tracked accounts (for debugging)
Sourcepub fn programs_count(&self) -> usize
pub fn programs_count(&self) -> usize
Get count of loaded programs (for debugging)
Sourcepub fn svm_account_count(&self) -> usize
pub fn svm_account_count(&self) -> usize
Get total number of accounts in the SVM’s internal HashMap. Used to detect unbounded account growth across fuzzing iterations.
Sourcepub fn account_exists(&self, pubkey: &Pubkey) -> bool
pub fn account_exists(&self, pubkey: &Pubkey) -> bool
Check if a specific account exists in the SVM (for debugging)
Sourcepub fn get_program_coverage_totals(&self) -> &HashMap<Pubkey, (usize, usize)>
pub fn get_program_coverage_totals(&self) -> &HashMap<Pubkey, (usize, usize)>
Get the total CFG edge and instruction counts for all loaded programs. Returns HashMap<Pubkey, (total_edges, total_instructions)> Used by the fuzzer to calculate coverage percentages.
Sourcepub fn get_program_binaries(&self) -> HashMap<Pubkey, Vec<u8>>
pub fn get_program_binaries(&self) -> HashMap<Pubkey, Vec<u8>>
Get program binaries for CFG analysis. Returns a map from program pubkey to binary data. Used by the fuzzer for per-instruction CFG divergence analysis.
Sourcepub fn get_program_binary(&self, pubkey: &Pubkey) -> Option<&[u8]>
pub fn get_program_binary(&self, pubkey: &Pubkey) -> Option<&[u8]>
Get program binary by pubkey (for CFG analysis).
Sourcepub fn take_snapshot(&mut self)
pub fn take_snapshot(&mut self)
Take a snapshot of ALL accounts in the SVM. Called once after setup, before the fuzz loop begins.
Sourcepub fn begin_iteration(&mut self)
pub fn begin_iteration(&mut self)
Prepare for a new iteration: clear dirty tracker and pending state. Called at the start of each fuzzing iteration.
Sourcepub fn restore_snapshot(&mut self) -> usize
pub fn restore_snapshot(&mut self) -> usize
Restore only dirty accounts from snapshot. Returns count restored. Much faster than full SVM clone when only ~5-20 accounts were modified.
Sourcepub fn has_snapshot(&self) -> bool
pub fn has_snapshot(&self) -> bool
Whether a snapshot has been taken.
Sourcepub fn dirty_tracker(&self) -> &DirtyTracker
pub fn dirty_tracker(&self) -> &DirtyTracker
Get the dirty tracker for the current iteration.
Sourcepub fn create_account(&mut self) -> GenericAccountBuilder<'_>
pub fn create_account(&mut self) -> GenericAccountBuilder<'_>
Account Creation Helpers
pub fn create_mint(&mut self) -> MintAccountBuilder<'_>
pub fn create_token_account(&mut self) -> TokenAccountBuilder<'_>
Sourcepub fn transfer_tokens(
&mut self,
from: &Pubkey,
to: &Pubkey,
owner: &Keypair,
amount: u64,
) -> Result<()>
pub fn transfer_tokens( &mut self, from: &Pubkey, to: &Pubkey, owner: &Keypair, amount: u64, ) -> Result<()>
Transfer tokens between accounts
pub fn mint_to( &mut self, mint: &Pubkey, destination: &Pubkey, amount: u64, authority: &Rc<Keypair>, ) -> Result<()>
pub fn warp_to_slot(&mut self, slot: u64)
pub fn advance_slots(&mut self, slots: u64)
pub fn set_sysvar<T>(&mut self, sysvar: &T)where
T: SysvarSerialize,
Sourcepub fn next_slot(&self) -> u64
pub fn next_slot(&self) -> u64
Returns the slot that the next transaction will likely see (current + 1)
Sourcepub fn account_has_data(&self, pubkey: &Pubkey, min_size: usize) -> bool
pub fn account_has_data(&self, pubkey: &Pubkey, min_size: usize) -> bool
Check if account exists AND has at least min_size bytes of data
pub fn get_account(&self, address: &Pubkey) -> Result<Account>
pub fn read_account(&self, address: &Pubkey) -> Result<Account>
Sourcepub fn read_anchor_account<T: AnchorDeserialize + Discriminator>(
&self,
address: &Pubkey,
) -> Result<T>
pub fn read_anchor_account<T: AnchorDeserialize + Discriminator>( &self, address: &Pubkey, ) -> Result<T>
Read anchor account at address and deserialize the data. Uses the type’s DISCRIMINATOR to determine how many bytes to skip.
Sourcepub fn read_account_with_discriminator<T: AnchorDeserialize>(
&self,
address: &Pubkey,
discriminator_len: usize,
) -> Result<T>
pub fn read_account_with_discriminator<T: AnchorDeserialize>( &self, address: &Pubkey, discriminator_len: usize, ) -> Result<T>
Read account with explicit discriminator length (for non-standard accounts).
pub fn token_balance(&self, token_account: &Pubkey) -> u64
pub fn write_anchor_account<T: AnchorSerialize + Discriminator>( &mut self, address: &Pubkey, data: &T, ) -> Result<()>
Sourcepub fn read_zero_copy_account<T: Pod>(&self, address: &Pubkey) -> Result<T>
pub fn read_zero_copy_account<T: Pod>(&self, address: &Pubkey) -> Result<T>
Read a zero-copy account (skips 8-byte discriminator).
Read a zero-copy account with standard 8-byte discriminator.
Use this for accounts with #[account(zero_copy)] attribute which use
bytemuck for serialization instead of Borsh.
§Example
let reserve: Reserve = ctx.read_zero_copy_account(&reserve_addr)?;
println!("Reserve slot: {}", reserve.last_update.slot);Sourcepub fn read_zero_copy_account_with_discriminator<T: Pod>(
&self,
address: &Pubkey,
discriminator_len: usize,
) -> Result<T>
pub fn read_zero_copy_account_with_discriminator<T: Pod>( &self, address: &Pubkey, discriminator_len: usize, ) -> Result<T>
Sourcepub fn write_zero_copy_account<T: Pod>(
&mut self,
address: &Pubkey,
data: &T,
) -> Result<()>
pub fn write_zero_copy_account<T: Pod>( &mut self, address: &Pubkey, data: &T, ) -> Result<()>
Write a zero-copy account (preserves 8-byte discriminator).
Use this for accounts with #[account(zero_copy)] attribute which use
bytemuck for serialization instead of Borsh.
§Example
let mut reserve: Reserve = ctx.read_zero_copy_account(&reserve_addr)?;
reserve.last_update.mark_fresh(current_slot);
ctx.write_zero_copy_account(&reserve_addr, &reserve)?;Sourcepub fn write_zero_copy_account_with_discriminator<T: Pod>(
&mut self,
address: &Pubkey,
data: &T,
discriminator_len: usize,
) -> Result<()>
pub fn write_zero_copy_account_with_discriminator<T: Pod>( &mut self, address: &Pubkey, data: &T, discriminator_len: usize, ) -> Result<()>
Write a zero-copy account with explicit discriminator length.
Sourcepub fn update_account<F>(&mut self, pubkey: &Pubkey, f: F) -> Result<()>
pub fn update_account<F>(&mut self, pubkey: &Pubkey, f: F) -> Result<()>
Sourcepub fn raw_call(&mut self, instruction: Instruction) -> InstructionBuilder<'_>
pub fn raw_call(&mut self, instruction: Instruction) -> InstructionBuilder<'_>
Callers - each returns a builder
pub fn program(&mut self, program_id: Pubkey) -> ProgramBuilder<'_>
pub fn transaction(&mut self) -> TransactionBuilder<'_>
pub fn send_batch(&mut self) -> Result<Option<TxOutcome>>
Trait Implementations§
Auto Trait Implementations§
impl Freeze for TestContext
impl !RefUnwindSafe for TestContext
impl Send for TestContext
impl Sync for TestContext
impl Unpin for TestContext
impl UnsafeUnpin for TestContext
impl !UnwindSafe for TestContext
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more