General
指令內省

指令內省

指令內省

指令內省

指令內省是 Solana 上一項強大的功能,允許程式分析同一交易中的其他指令,包括尚未執行的指令。這使您能夠動態地回應或增強這些指令,例如注入安全措施、驗證行為,或將外部程式的指令整合到您自己的邏輯中。

這是通過一個名為 Instructions 的特殊系統帳戶(sysvar)實現的。Sysvar 是由 Solana 運行時維護的只讀帳戶,向程式公開內部狀態(例如時鐘、租金、時代計劃等)。Instructions sysvar 專門公開當前交易中的完整指令列表,以及它們的元數據和序列化數據。

以下是 Solana 在運行時如何序列化這些資訊:

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
}

這意味著通過在您的程式中讀取 Instructions sysvar 帳戶,您可以訪問當前交易中包含的所有指令。

您不需要手動解析原始位元組。Solana 提供了輔助函數:

  • load_current_index_checked:返回當前執行指令的索引。

  • load_instruction_at_checked:允許您按索引以解析、反序列化的格式載入特定指令。

如何運作?

每個 Solana 指令包含:

  • 它目標的程式 ID,

  • 它互動的帳戶(包括簽名者/可寫等元數據),

  • 以及數據負載(通常是識別碼 + 參數)。

而這正是 Instructions sysvar 中的 load_instruction_at_checked 函數提供的內容:

  • program_id:此指令調用的程式。

  • accounts:包含元數據的相關帳戶列表。

  • data:原始輸入負載(如果我們正在內省一個 Anchor 指令,它通常以 8 字節的識別碼開頭,後跟參數)。

為了安全且高效地檢查其他指令,請按照以下步驟進行:

  • 確定當前指令的索引:使用load_current_index_checked來找到當前執行指令的索引。

請注意:您的指令可能不是交易中的第一個(索引為0)。

  • 加載目標指令進行分析:獲得索引後,使用load_instruction_at_checked(index)加載同一交易中的另一個指令——可以是前一個、後一個或特定位置的指令。

這對於驗證行為、確保預期輸入或在程序間安全地組合非常有用。

  • 建立約束以防止惡意行為:指令檢查功能非常強大,但也引入了新的攻擊面。請確保:驗證被檢查的指令是否針對預期的程序,確認帳戶地址和數據是否符合預期模式,並避免對指令順序做出假設,除非有明確的強制規定。

通過採取這些預防措施,您可以安全地利用指令檢查功能來構建強大、可組合且安全的Solana程序。

Common Constraints for Introspection

在使用指令檢查時,必須強制執行嚴格的約束以防止惡意或意外行為。最常見的安全措施包括:

  • 指令驗證:驗證指令的程序ID和識別碼,以確保其正確。識別碼(通常是指令數據的前1、4或8個字節)唯一標識正在調用的功能。

此步驟確保您正在檢查預期的指令,而不是被偽造或格式錯誤的指令。

  • 變量驗證:在識別碼之後,解析並檢查指令使用的關鍵變量。這些可能包括金額、方向(例如多/空)或ID,您應始終確認這些字段是否符合您的集成或安全措施的預期邏輯。

  • 帳戶驗證:驗證指令帳戶的結構和身份。檢查預期帳戶是否出現在特定位置(例如簽署者、保管庫、抵押品),並確保角色如簽署者/可寫狀態符合您的假設。

通過應用這些限制,您可以確保您的程式僅回應有效且受信任的指令,從而使您的邏輯更加穩健、可組合和安全。

結合交易的原子性,這些檢查允許您構建穩健、可組合的邏輯,能夠在同一筆交易中安全地與其他程式和指令互動。

請記住,Solana 的交易是原子的。如果任何指令失敗,整個交易將被回滾。

Blueshift © 2025Commit: e573eab