Solana 60 天课程

2025年02月27日更新 89 人订阅
原价: ¥ 66 限时优惠
专栏简介 开始 Solana - 安装与故障排除 Solana 和 Rust 中的算术与基本类型 Solana Anchor 程序 IDL Solana中的Require、Revert和自定义错误 Solana程序是可升级的,并且没有构造函数 Solidity开发者的Rust基础 Rust不寻常的语法 Rust 函数式过程宏 Rust 结构体与属性式和自定义派生宏 Rust 和 Solana 中的可见性与“继承” Solana时钟及其他“区块”变量 Solana 系统变量详解 Solana 日志、“事件”与交易历史 Tx.origin、msg.sender 和 onlyOwner 在 Solana 中:识别调用者 Solana 计算单元与交易费用介绍 在 Solana 和 Anchor 中初始化账户 Solana 计数器教程:在账户中读写数据 使用 Solana web3 js 和 Anchor 读取账户数据 在Solana中创建“映射”和“嵌套映射” Solana中的存储成本、最大存储容量和账户调整 在 Solana 中读取账户余额的 Anchor 方法:address(account).balance 功能修饰符(view、pure、payable)和回退函数在 Solana 中不存在的原因 在 Solana 上实现 SOL 转账及构建支付分配器 使用不同签名者修改账户 PDA(程序派生地址)与 Solana 中的密钥对账户 理解 Solana 中的账户所有权:从PDA中转移SOL Anchor 中的 Init if needed 与重初始化攻击 Solana 中的多重调用:批量交易与交易大小限制 Solana 中的所有者与权限 在Solana中删除和关闭账户与程序 在 Anchor 中:不同类型的账户 在链上读取另一个锚点程序账户数据 在 Anchor 中的跨程序调用(CPI) SPL Token 的运作方式 使用 Anchor 和 Web3.js 转移 SPL Token Solana 教程 - 如何实现 Token 出售 基础银行教程 Metaplex Token 元数据工作原理 使用Metaplex实施代币元数据 使用 LiteSVM 进行时间旅行测试 Solana Token-2022 标准规范 生息代币第一部分 计息代币第二部分 Solana 指令自省 Solana 中的 Ed25519 签名验证 Solana - Switchboard 预言机使用 原生Solana:程序入口与执行 原生 Solana :读取账户数据 原生 Solana :Borsh 序列化 原生 Solana:使用 invoke 和 invoke signed 进行跨程序调用 原生 Solana :创建存储账户 (一) 原生 Solana:创建存储账户 二 原生 Solana: 函数分发 原生 Solana:关键安全检查 Rust 程序到 SBF 编译 sBPF 虚拟机和指令集介绍 跟踪 sBPF 指令执行和计算成本 Solana 程序执行与输入序列化 指令处理器和运行时设置 sBPF 内存布局和寄存器约定 使用 sBPF 汇编读取 Solana 指令输入 Solana 系统调用:sBPF 汇编中的日志记录

Solana 指令自省

  • RareSkills
  • 发布于 2025-10-23 08:51
  • 阅读 1580

本文介绍了Solana程序如何通过指令自省(instruction introspection)读取同一交易中其他指令的内容。

指令内省(Instruction introspection)使 Solana 程序能够在同一笔交易中读取除自身以外的指令。

通常,一个程序只能读取以自身为目标的指令。Solana 运行时将每条指令路由到指令中指定的程序。

一个 Solana 交易可以包含多条指令,每条指令都以不同的程序为目标。例如,程序 A 可能收到指令 Ax,而程序 B 在同一交易中收到指令 Bx。通过内省,程序 B 可以读取指令 AxBx 的内容。

例如,假设你希望确保与你的 DeFi 程序的任何交互都必须首先在同一交易中向你的 treasury 转账 0.5 SOL。你可以通过内省指令来执行此规则,如果所需的 0.5 SOL 转账指令未包含在与你的程序交互的指令之前,则拒绝整个交易。

在本文中,我们将学习内省的工作原理以及如何在你的 Solana 程序中实现它。

交易和指令

在我们了解指令内省之前,让我们详细回顾一下交易和指令。

Solana 交易是一个具有两个字段的结构体:一个消息(message)和签署它的签名(signatures)。该消息包含一个按顺序执行的指令(instructions)数组。

一个高级图表,说明 Solana 交易的组成部分。它显示了一个交易由一个“消息(Message)”组成,其中包含指令,以及一个签署该消息的“签名数组(Array of signatures)”。

下面的代码(直接来自 Solana SDK)显示了一个交易的结构体表示:

pub struct Transaction {
    pub signatures: Vec<Signature>,
    pub message: Message,
}

交易消息

交易消息(transaction message)包含指令列表,以及指令将共同访问的所有帐户密钥的并集。它还包含运行时需要的一些附加数据,例如最近的区块哈希(recent block hash)和消息头(message header)。

pub struct Message {
    pub instructions: Vec<Instruction>,
    pub account_keys: Vec<Address>,
    pub recent_blockhash: Hash,
    pub header: MessageHeader,
}

以下是每个组件的详细分解:

  • 指令(Instructions):每个指令是对链上程序的一次调用。一个指令包含三个组件:

    • 程序 ID(Program ID):具有被调用指令的业务逻辑的程序的地址。
    • 帐户(Accounts):进入交易 帐户密钥(account keys) 的索引。这些索引将指令映射到它需要读取或写入的特定帐户。
    • 指令数据(Instruction data):一个字节数组,用于指定要在程序上调用的函数以及指令所需的任何参数。
  • 帐户密钥(Account keys):这是每个指令中列出的所有帐户的并集。
  • 最近的区块哈希(Recent blockhash):一个最近的区块哈希,将交易与一个短时间窗口的插槽(slots)绑定并防止重放。
  • 消息头(Message header):它指定有多少帐户签署了交易,以及哪些帐户是只读的,哪些是可写的。

指令结构体(Instruction struct)

以下是 Instruction 结构体定义,来自 GitHub 上的 Solana 源代码

pub struct Instruction {
    /// Pubkey of the program that executes this instruction.
    pub program_id: Pubkey,
    /// Metadata describing accounts that should be passed to the program.
    pub accounts: Vec<AccountMeta>,
    /// Opaque data passed to the program for its own interpretation.
    pub data: Vec<u8>,
}

pub struct AccountMeta {
    /// An account's public key.
    pub pubkey: Pubkey,
    // True if the instruction requires a signature for this pubkey
    /// in the transaction's signatures list.
    pub is_signer: bool,
    /// True if the account data or metadata may be mutated during program execution.
    pub is_writable: bool,
}

指令使用的每个帐户都由 AccountMeta 类型表示,它存储帐户的公钥以及签名者和可写标志。

交易和指令之间关系的总结

为了将所有内容放在一起,下图显示了一个交易、一个消息和指令之间的关系。

一个 交易(Transaction) 包含一个签名列表和一个消息。一个 消息(Message) 包含一个头部、帐户密钥列表、一个最近的区块哈希和一个指令列表。一个 指令(Instruction) 包含一个程序 ID,它使用的帐户(这索引到消息结构体中的 帐户密钥(account keys) 列表)和指令数据。

一个代码片段,显示 Solana 交易的嵌套 Rust 数据结构。它展示了顶层 Transaction 结构体由签名和一个消息组成。Message 结构体包含一个头部、帐户密钥、最近的 block_hash 和一个指令向量。它还显示了 Instruction 结构体持有 program_id、帐户和数据。

使用指令 Sysvar 进行指令内省

让我们首先检查 Solana Sysvar 帐户,来讨论内省是如何工作的。

一个 sysvar 是一个特殊的只读帐户,它包含由 Solana 运行时维护的动态更新的数据,并将内部网络状态暴露给程序。我们实际上是从这个帐户读取数据——我们没有对一个程序进行 CPI 调用。

我们在本系列的前一篇文章中讨论了不同类型的 Sysvar。要了解更多关于它们的信息,请阅读文章“Solana Sysvars Explained

指令内省使用指令 Sysvar 帐户来访问当前交易的序列化指令向量(program_id、帐户和数据)。例如,在一个包含多个指令的交易中,一个程序可以读取和分析任何指令,而不仅仅是当前指令。

这个动画展示了一个指令内省场景,其中,当指令 1 正在执行时,程序可以读取指令 2 和指令 3 的内容。

与 Solana 中的常规帐户不同,指令 Sysvar 帐户不存储数据;它仅在交易的生命周期内填充,并在执行完成后清除。

指令 Sysvar 帐户地址是 Sysvar1nstructions1111111111111111111111111。它包含当前交易中所有指令的序列化列表。每个条目都包含程序 ID、帐户和指令数据,就像我们之前看到的那样。以下是每个反序列化指令的 Rust 结构体,与之前重现的相同:

pub struct Instruction {
    /// Pubkey of the program that executes this instruction
    pub program_id: Pubkey,

    /// Metadata describing accounts that should be passed to the program
    pub accounts: Vec<AccountMeta>,

    /// Opaque data passed to the program for its own interpretation
    pub data: Vec<u8>,
}

Solana Rust SDK 提供了几个辅助函数来访问指令 sysvar 帐户中的序列化指令。但是,SDK 没有提供返回所有指令的单个函数;相反,它只提供反序列化特定索引处的单个指令的函数。

你仍然可以手动读取和反序列化 sysvar 帐户中的指令列表,但是这样做容易出错,因此,应该使用 SDK 反序列化指令。

以下是 Solana Rust SDK 为内省提供的两个关键辅助函数:

  1. load_current_index_checked – 程序可以使用此辅助函数来了解它们在交易列表中的索引,然后通过它们的相对位置查找另一个指令。
  2. load_instruction_at_checked – 加载特定索引处的指令,并将其反序列化为一个 Instruction 结构体。一旦你使用 load_current_index_checked 函数获得了当前索引,你就可以使用此函数来内省较早或较晚的指令。我们将在本文后面的章节中看到如何做到这一点。

首先,要理解这些辅助函数是如何工作的,让我们看看指令 sysvar 帐户的布局。它被组织成三个区域:

  1. 头部(header)
  2. 指令(instructions)
  3. 当前正在执行的指令的索引(index)

1. 头部区域

头部指定了交易中的指令数量和指令偏移量(指向指令开始的位置)。下图显示了一个具有 2 个指令的头部,因此有两个偏移量:一个从内存位置 6 开始,另一个从内存位置 20 开始。

一个 sysvar 头部内存布局的图表。前 2 个字节将 num_instructions 表示为 u16。接下来是一个 u16 值的数组,表示每个指令起点的字节偏移量,在本例中为 6 和 20。

2. 指令区域

指令区域从偏移量指示的字节位置开始(下图中的红色框只是偏移量的视觉标记,而不是实际的内存位置)。从该位置开始,它包含帐户元数据、程序 ID、指令数据的长度,最后是指令数据本身。如果我们有多个指令,则每个指令都会重复此结构。

![一个图表,显示了来自 Solana 指令 Sysvar 帐户的指令区域的示例布局。它使用一个红色框来视觉上指示偏移量指向的位置,标记指令的开始。然后布局显示了帐户信息字段(包含 num_accounts、account.meta 和 account.pubkey),然后是 program_id、data_len 和数据本身...

剩余50%的内容订阅专栏后可查看

点赞 1
收藏 1
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论