General
Instruction Introspection

Instruction Introspection

Instruction Introspection with Pinocchio

Pinocchio uses different types than the solana_program crate. This means we had to create a new SDK optimized for Pinocchio.

With the help of Orion, we created a set of helpers that respected the "original" implementation but made the Instruction sysvar — one of the most expensive sysvars to use — one of the most efficient.

For this reason, we can use Instruction Introspection in Pinocchio without installing any external crates and can access all the information we need by importing these functions:

use pinocchio::sysvars::instructions::{
    load_current_index_checked, 
    load_instruction_at_checked
}; 

How to use Introspection

As mentioned previously, Instruction Introspection simply deserializes the data from the Instruction sysvar account to provide all the data we need about our instructions.

We start by checking the current index. We can do this by using the load_current_index_checked function, like so:

let index = load_current_index_checked(&self.accounts.sysvar_instructions)?;

After this, we can check an instruction at a relative index using load_instruction_at_checked. This time, we'll check the instruction immediately following the one performing the introspection, like so:

// We load the next instruction, which is the one we want to check for the correct input.
let instruction = load_instruction_at_checked(index as usize + 1, &self.accounts.sysvar_instructions)?;

Before going to the next step, we should ask ourselves what information is essential to prevent a malicious attack.

We usually start by checking if the program being used is the one we expect. In this example, we're introspecting another instruction from the system program, so we can do it like so:

if instruction.get_program_id() != &pinocchio_system::ID {
    return Err(ProgramError::InvalidInstructionData);
}

Next, we check if the instruction is the one we expect. To do so, we compare the discriminator and the instruction data with the expected values. We start by creating an instruction_data variable and checking against it, like so:

let mut instruction_data = [0u8; 12];
instruction_data[0..4].copy_from_slice(&2u32.to_le_bytes());
instruction_data[4..12].copy_from_slice(&100_000_000u64.to_le_bytes());
 
if instruction.get_instruction_data() != instruction_data {
    return Err(ProgramError::InvalidInstructionData);
}

We can then perform more program-specific checks based on the logic of the instruction we are checking.

We can also check the accounts present in the introspected instruction. This step requires us to know the exact structure of the account struct, since we'll be requesting the data or pubkey of an account at a specific index, like this:

if instruction.get_account_meta_at(0)?.key() != self.accounts.from.key() {
    return Err(ProgramError::InvalidAccountData);
}
Contents
View Source
Blueshift © 2025Commit: e508535