General
Introspection des Instructions

Introspection des Instructions

Introspection des Instructions

L'Introspection des Instructions est une fonctionnalité puissante de Solana qui permet à un programme d'analyser d'autres instructions au sein d'une même transaction, y compris celles qui n'ont pas encore été exécutées. Cela vous permet de répondre de manière dynamique à ces instructions ou de les compléter, par exemple en ajoutant des mesures de sécurité, en validant leur comportement ou en intégrant dans votre propre logique des instructions provenant de programmes externes.

Cela est rendu possible grâce à un compte de système spécial appelé sysvar Instructions. Les Sysvars sont des comptes en lecture seule gérés par le runtime de Solana qui exposent l'état interne aux programmes (par exemple l'horloge, la rente, le calendrier des époques, etc.). La sysvar Instructions expose précisément la liste complète des instructions de la transaction en cours, ainsi que leurs métadonnées et leurs données sérialisées.

Voici comment Solana sérialise ces informations lors de l'exécution :

rust
// First encode the number of instructions:
//  0..2 - num_instructions
//
// Then a table of offsets of where to find them in the data
//  3..2 * num_instructions table of instruction offsets
//
// Each instruction is then encoded as:
//   0..2 - num_accounts
//   2 - meta_byte -> (bit 0 signer, bit 1 is_writable)
//   3..35 - pubkey - 32 bytes
//   35..67 - program_id
//   67..69 - data len - u16
//   69..data_len - data
#[cfg(not(target_os = "solana"))]
fn serialize_instructions(instructions: &[BorrowedInstruction]) -> Vec<u8> {
    // 64 bytes is a reasonable guess, calculating exactly is slower in benchmarks
    let mut data = Vec::with_capacity(instructions.len() * (32 * 2));
    append_u16(&mut data, instructions.len() as u16);
    for _ in 0..instructions.len() {
        append_u16(&mut data, 0);
    }
 
    for (i, instruction) in instructions.iter().enumerate() {
        let start_instruction_offset = data.len() as u16;
        let start = 2 + (2 * i);
        data[start..start + 2].copy_from_slice(&start_instruction_offset.to_le_bytes());
        append_u16(&mut data, instruction.accounts.len() as u16);
        for account_meta in &instruction.accounts {
            let mut account_meta_flags = InstructionsSysvarAccountMeta::empty();
            if account_meta.is_signer {
                account_meta_flags |= InstructionsSysvarAccountMeta::IS_SIGNER;
            }
            if account_meta.is_writable {
                account_meta_flags |= InstructionsSysvarAccountMeta::IS_WRITABLE;
            }
            append_u8(&mut data, account_meta_flags.bits());
            append_slice(&mut data, account_meta.pubkey.as_ref());
        }
 
        append_slice(&mut data, instruction.program_id.as_ref());
        append_u16(&mut data, instruction.data.len() as u16);
        append_slice(&mut data, instruction.data);
    }
    data
}

Cela signifie qu'en lisant le compte sysvar Instructions dans votre programme, vous pouvez accéder à toutes les instructions incluses dans la transaction en cours.

Vous n'avez pas besoin de parser manuellement les octets bruts. En effet, Solana fournit des fonctions d'aide :

  • load_current_index_checked: renvoie l'index de l'instruction en cours d'exécution.
  • load_instruction_at_checked: vous permet de charger une instruction précise à partir de son index dans un format parsé et désérialisé.

Comment ça marche ?

Chaque instruction de Solana contient :

  • l'ID du programme qu'elle cible,
  • les comptes avec lesquels elle interagit (y compris les métadonnées telles que signataire/modifiable en écriture (writable)),
  • et la charge utile des données (généralement un discriminateur + des arguments).

Et c'est exactement ce que vous offre la fonction load_instruction_at_checked de la sysvar Instructions :

  • program_id: le programme appelé par cette instruction.
  • accounts: une liste des comptes impliqués avec leurs métadonnées.
  • data: la charge utile brute (si nous introspectons une instruction anchor, elle commence souvent par un discriminateur de 8 octets suivi de paramètres).

Pour introspecter d'autres instructions de manière sûre et efficace, procédez comme suit :

  • Déterminer l'Index de l'Instruction Actuelle : Utilisez load_current_index_checked pour trouver l'index de l'instruction en cours d'exécution.

Gardez à l'esprit que votre instruction n'est peut-être pas la première (index 0) dans la transaction

  • Charger une Instruction Cible à Analyser : Une fois l'index en votre possession, utilisez load_instruction_at_checked(index) pour charger une autre instruction de la même transaction, soit celle qui précède, celle qui suit ou celle qui se trouve à une position donnée.

Cela est utile pour valider un comportement, garantir les entrées attendues ou composer en toute sécurité entre les programmes

  • Définir des Contraintes pour Empêcher les Comportements Malveillants : L'introspection des instructions est puissante mais elle introduit de nouvelles zones d'attaque. Assurez-vous de : vérifier que l'instruction introspectée cible bien le programme attendu, confirmer que les adresses et les données du compte correspondent aux modèles attendus et éviter toute hypothèse concernant l'ordre des instructions, sauf si celui-ci est explicitement imposé.

En prenant ces précautions, vous pouvez exploiter en toute sécurité l'introspection des instructions pour créer des programmes Solana puissants, composables et sécurisés.

Contraintes Courantes pour l'Introspection

Lorsque vous utilisez l'introspection d'instructions, il est essentiel d'appliquer des contraintes strictes afin d'éviter tout comportement malveillant ou inattendu. Les mesures de protection les plus courantes comprennent :

  • la Vérification des Instructions : Vérifier l'ID du programme et le discriminateur de l'instruction pour vous assurer qu'ils sont corrects. Le discriminateur (généralement les 1, 4 ou 8 premiers octets des données d'instruction) identifie de manière unique la fonction appelée.

Cette étape vous permet de vous assurer que vous inspectez bien l'instruction souhaitée et non une instruction falsifiée ou malformée.

  • la Validation des Variables : Après le discriminateur, analysez et vérifiez les variables critiques utilisées par l'instruction. Il peut s'agir de montants, de directives (par exemple, long/short) ou d'identifiants. Vous devez toujours vérifier que ces champs correspondent à la logique attendue de votre intégration ou de vos mesures de sécurité.
  • la Vérification des Comptes : Valider la structure et l'identité des comptes de l'instruction. Vérifiez que les comptes attendus apparaissent aux emplacements prévus (par exemple, signataire, vault, collatéral) et assurez-vous que les rôles tels que signataire/modifiable en écriture correspondent à vos hypothèses.

En appliquant ces contraintes, vous vous assurez que votre programme ne répond qu'à des instructions valides et fiables, ce qui rend votre logique plus robuste, modulable et sécurisée.

Associées à l'atomicité des transactions, ces vérifications vous permettent de créer une logique robuste et modulable capable d'interagir en toute sécurité avec d'autres programmes et instructions au sein d'une même transaction.

N'oubliez pas que les transactions sur Solana sont atomiques. Si une instruction échoue, l'ensemble de la transaction est annulé

Blueshift © 2025Commit: 6d01265