Skip to main content

TestContext

Struct TestContext 

Source
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: DirtyTracker

Tracks dirty accounts across all transactions in an iteration

§sigverify: bool

Whether 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

Source

pub fn create_mock_pyth_oracle(&mut self) -> MockPythOracleBuilder<'_>

Create a mock Pyth oracle builder

§Example
let oracle = ctx.create_mock_pyth_oracle()
    .price(100_00000000)  // $100
    .exponent(-8)
    .build()?;
Source

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 pubkey
  • price - New price value
  • exponent - Price exponent (typically -8)
Source

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

Source

pub fn new() -> Self

Source

pub fn with_invocation_callback<C: InvocationInspectCallback + 'static>( callback: C, ) -> Self

Source

pub fn with_compute_budget(self, compute_unit_limit: u64) -> Self

Source

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.

Source

pub fn add_program( &mut self, program_id: &Pubkey, program_path: &str, ) -> Result<()>

Source

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.

Source

pub fn from_svm(svm: LiteSVM) -> Self

Source

pub fn into_svm(self) -> LiteSVM

Source

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.

Source

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 clone
Source

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.

Source

pub fn tracked_accounts_count(&self) -> usize

Get count of tracked accounts (for debugging)

Source

pub fn programs_count(&self) -> usize

Get count of loaded programs (for debugging)

Source

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.

Source

pub fn account_exists(&self, pubkey: &Pubkey) -> bool

Check if a specific account exists in the SVM (for debugging)

Source

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.

Source

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.

Source

pub fn get_program_binary(&self, pubkey: &Pubkey) -> Option<&[u8]>

Get program binary by pubkey (for CFG analysis).

Source

pub fn take_snapshot(&mut self)

Take a snapshot of ALL accounts in the SVM. Called once after setup, before the fuzz loop begins.

Source

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.

Source

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.

Source

pub fn has_snapshot(&self) -> bool

Whether a snapshot has been taken.

Source

pub fn dirty_tracker(&self) -> &DirtyTracker

Get the dirty tracker for the current iteration.

Source

pub fn create_account(&mut self) -> GenericAccountBuilder<'_>

Account Creation Helpers

Source

pub fn create_mint(&mut self) -> MintAccountBuilder<'_>

Source

pub fn create_token_account(&mut self) -> TokenAccountBuilder<'_>

Source

pub fn transfer_tokens( &mut self, from: &Pubkey, to: &Pubkey, owner: &Keypair, amount: u64, ) -> Result<()>

Transfer tokens between accounts

Source

pub fn mint_to( &mut self, mint: &Pubkey, destination: &Pubkey, amount: u64, authority: &Rc<Keypair>, ) -> Result<()>

Source

pub fn warp_to_slot(&mut self, slot: u64)

Source

pub fn advance_slots(&mut self, slots: u64)

Source

pub fn set_sysvar<T>(&mut self, sysvar: &T)
where T: SysvarSerialize,

Source

pub fn slot(&self) -> u64

Getters

Source

pub fn next_slot(&self) -> u64

Returns the slot that the next transaction will likely see (current + 1)

Source

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

Source

pub fn get_account(&self, address: &Pubkey) -> Result<Account>

Source

pub fn read_account(&self, address: &Pubkey) -> Result<Account>

Source

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.

Source

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).

Source

pub fn token_balance(&self, token_account: &Pubkey) -> u64

Source

pub fn write_account( &mut self, address: &Pubkey, account: Account, ) -> Result<()>

Setters

Source

pub fn write_anchor_account<T: AnchorSerialize + Discriminator>( &mut self, address: &Pubkey, data: &T, ) -> Result<()>

Source

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);
Source

pub fn read_zero_copy_account_with_discriminator<T: Pod>( &self, address: &Pubkey, discriminator_len: usize, ) -> Result<T>

Read a zero-copy account with explicit discriminator length.

Use this for accounts with non-standard discriminator sizes.

§Example
// For accounts with 1-byte discriminator
let vault: StarVault = ctx.read_zero_copy_account_with_discriminator(&vault_addr, 1)?;
Source

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)?;
Source

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.

Source

pub fn update_account<F>(&mut self, pubkey: &Pubkey, f: F) -> Result<()>
where F: FnOnce(&mut Vec<u8>),

Update account data using a closure. Enables atomic read-modify-write pattern.

§Example
ctx.update_account(&reserve_pubkey, |data| {
    // Modify data in place (e.g., using bytemuck)
    let reserve: &mut Reserve = bytemuck::from_bytes_mut(&mut data[8..]);
    reserve.config.loan_to_value_pct = 80;
})?;
Source

pub fn raw_call(&mut self, instruction: Instruction) -> InstructionBuilder<'_>

Callers - each returns a builder

Source

pub fn program(&mut self, program_id: Pubkey) -> ProgramBuilder<'_>

Source

pub fn transaction(&mut self) -> TransactionBuilder<'_>

Source

pub fn send_batch(&mut self) -> Result<Option<TxOutcome>>

Trait Implementations§

Source§

impl Clone for TestContext

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V