Instruction Introspection

Instruction Introspection ist eine leistungsstarke Funktion auf Solana, die es einem Programm ermöglicht, andere Anweisungen innerhalb derselben Transaktion zu analysieren, einschließlich solcher, die noch nicht ausgeführt wurden. Dies ermöglicht es, dynamisch auf diese Anweisungen zu reagieren oder sie zu erweitern – zum Beispiel durch das Einfügen von Schutzmaßnahmen, die Validierung von Verhalten oder die Integration von Anweisungen externer Programme in Ihre eigene Logik.
Dies wird durch ein spezielles Systemkonto namens Instructions sysvar ermöglicht. Sysvars sind schreibgeschützte Konten, die von der Solana-Laufzeit verwaltet werden und internen Status für Programme offenlegen (z.B. Uhr, Miete, Epochenplan usw.). Das Instructions-Sysvar stellt speziell die vollständige Liste der Anweisungen in der aktuellen Transaktion zusammen mit ihren Metadaten und serialisierten Daten bereit.
So serialisiert Solana diese Informationen zur Laufzeit:
// 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
}Das bedeutet, dass Sie durch das Lesen des Instructions sysvar-Kontos innerhalb Ihres Programms auf alle Anweisungen zugreifen können, die in der aktuellen Transaktion enthalten sind.
Sie müssen die Rohdaten nicht manuell parsen. Solana bietet Hilfsfunktionen:
load_current_index_checked: gibt den Index der aktuell ausgeführten Anweisung zurück.load_instruction_at_checked: ermöglicht das Laden einer bestimmten Anweisung anhand ihres Index in einem geparsten, deserialisierten Format.
Wie funktioniert es?
Jede Solana-Anweisung enthält:
die Programm-ID, auf die sie abzielt,
die Konten, mit denen sie interagiert (einschließlich Metadaten wie Unterzeichner/beschreibbar),
und die Datennutzlast (typischerweise ein Diskriminator + Argumente).
Und genau das liefert die Funktion load_instruction_at_checked vom Instructions-Sysvar:
program_id: das Programm, das diese Anweisung aufruft.
accounts: eine Liste der beteiligten Konten mit Metadaten.
data: die rohe Eingabenutzlast (wenn wir eine Anchor-Anweisung introspektieren, beginnt sie oft mit einem 8-Byte-Diskriminator gefolgt von Parametern).
Um andere Anweisungen sicher und effizient zu untersuchen, folge diesen Schritten:
Bestimme den aktuellen Anweisungsindex: Verwende
load_current_index_checked, um den Index der aktuell ausgeführten Anweisung zu finden.
Lade eine Zielanweisung zur Analyse: Mit dem Index kannst du
load_instruction_at_checked(index)verwenden, um eine andere Anweisung in derselben Transaktion zu laden – entweder die davor, danach oder an einer bestimmten Position.
Erstelle Einschränkungen, um bösartiges Verhalten zu verhindern: Die Anweisungsinspektion ist mächtig, eröffnet aber auch neue Angriffsflächen. Stelle sicher, dass du: validierst, dass die untersuchte Anweisung das erwartete Programm anspricht, bestätigst, dass Kontoadressen und Daten den erwarteten Mustern entsprechen, und keine Annahmen über die Reihenfolge der Anweisungen triffst, es sei denn, diese wird explizit erzwungen.
Durch diese Vorsichtsmaßnahmen kannst du die Anweisungsinspektion sicher nutzen, um leistungsstarke, komponierbare und sichere Solana-Programme zu erstellen.
Übliche Einschränkungen für die Inspektion
Bei der Verwendung der Anweisungsinspektion ist es wichtig, strenge Einschränkungen durchzusetzen, um bösartiges oder unerwartetes Verhalten zu verhindern. Die häufigsten Schutzmaßnahmen umfassen:
Anweisungsverifizierung: Validiere die Programm-ID und den Diskriminator der Anweisung, um sicherzustellen, dass es die richtige ist. Der Diskriminator (normalerweise die ersten 1, 4 oder 8 Bytes der Anweisungsdaten) identifiziert eindeutig, welche Funktion aufgerufen wird.
Variablenvalidierung: Nach dem Diskriminator solltest du kritische Variablen, die von der Anweisung verwendet werden, analysieren und überprüfen. Diese können Beträge, Richtungen (z.B. long/short) oder IDs umfassen, und du solltest immer bestätigen, dass diese Felder mit der erwarteten Logik deiner Integration oder Schutzmaßnahme übereinstimmen.
Kontoverifizierung: Validiere die Struktur und Identität der Konten der Anweisung. Überprüfe, dass erwartete Konten an bestimmten Positionen erscheinen (z.B. Unterzeichner, Tresor, Sicherheit) und stelle sicher, dass Rollen wie Unterzeichner/beschreibbarer Status deinen Annahmen entsprechen.
Durch die Anwendung dieser Einschränkungen stellen Sie sicher, dass Ihr Programm nur auf gültige und vertrauenswürdige Anweisungen reagiert, was Ihre Logik robuster, kombinierbarer und sicherer macht.
Zusammen mit der Atomarität von Transaktionen ermöglichen diese Prüfungen den Aufbau einer robusten, kombinierbaren Logik, die sicher mit anderen Programmen und Anweisungen innerhalb derselben Transaktion interagieren kann.