Solana笔记 09.详解 IDL

跟我一起从0开始学习Solana合约开发,一起实操,一起做项目。这是一个系列文章,系统地记录了我的学习笔记。

IDL(Interface Definition Language)文件是一个标准化的 JSON 文件,用于描述程序的指令(可以理解为函数)和账户。它类似于以太坊的 ABI 文件,帮助我们与智能合约交互。本文将通过一个简单示例,带你了解 IDL 的结构、生成过程以及它在程序开发中的实际作用。

在使用 Anchor 框架开发 Solana 程序时,运行 anchor build 命令会自动生成 IDL 文件,生成路径为 /target/idl/<program-name>.json

指令部分:从代码到 JSON 的映射

在 IDL 文件里有一个 instructions 数组对象,里面的每一个元素都对应主程序的一个函数。观看下面的主程序代码:

#[program]
mod hello_anchor {
    use super::*;
    pub fn initialize(ctx: Context<Initialize>, data: u64) -> Result<()> {
        ctx.accounts.new_account.data = data;
        msg!("Changed data to: {}!", data);
        Ok(())
    }
}

#[derive(Accounts)]
pub struct Initialize<'info> {
    #[account(init, payer = signer, space = 8 + 8)]
    pub new_account: Account<'info, NewAccount>,
    #[account(mut)]
    pub signer: Signer<'info>,
    pub system_program: Program<'info, System>,
}

这段代码包含:

  1. 一个 initialize 函数,接收一个 Initialize 账户上下文和一个 data 字段作为参数。

  2. Initialize 账户上下文包含了 3 个字段:new_account, signer, 和 system_program

下面我们来看它的 IDL 文件内容:

"instructions": [
    {
      "name": "initialize",
      "discriminator": [175, 175, 109, 31, 13, 152, 155, 237],
      "accounts": [
        {
          "name": "new_account",
          "writable": true,
          "signer": true
        },
        {
          "name": "signer",
          "writable": true,
          "signer": true
        },
        {
          "name": "system_program",
          "address": "11111111111111111111111111111111"
        }
      ],
      "args": [
        {
          "name": "data",
          "type": "u64"
        }
      ]
    }
],

通过对比,可以看到 instructions 数组的第一个元素对应主程序的 initialize 函数及其所需的账户集合和参数。

主程序代码 IDL 对应部分(JSON)
pub fn initialize(...) "name": "initialize"
ctx.accounts.new_account "accounts": [{ "name": "new_account", "writable": true }]
data: u64 "args": [{ "name": "data", "type": "u64" }]

自定义账户的结构和 IDL 表示

在上面的代码里,还有一个 NewAccount 自定义账户,它的定义如下:

#[account]
pub struct NewAccount {
    data: u64,
}

这个账户信息也会包含在 IDL 文件里,在与 instructions 同一层级的字段里,有下面的内容:

"instructions": [...],
"accounts": [
    {
      "name": "NewAccount",
      "discriminator": [176, 95, 4, 118, 91, 177, 125, 232]
    }
],
"types": [
    {
      "name": "NewAccount",
      "type": {
        "kind": "struct",
        "fields": [
          {
            "name": "data",
            "type": "u64"
          }
        ]
      }
    }
]

通过这段代码,可以看到 accounts 数组的第一个元素对应自定义账户结构体 NewAccount。而这个结构体的类型和字段信息在 types 数组对象里。

主程序代码 IDL 对应部分(JSON)
#[account] pub struct NewAccount {...} "name": "NewAccount"
Rust 结构体 data: u64 "fields": [{ "name": "data", "type": "u64" }]

Discriminator:区分指令和账户的关键

无论是 instruction 还是 account 都有一个 discriminator 字段,这个是什么意思呢?

Anchor 为程序中的每个指令和账户类型分配了一个唯一的 8 字节标识符(discriminator)。用于确保程序的安全性。其生成方式是:

  1. 对“前缀 + 名称”组合进行 Sha256 哈希。

  2. 提取哈希值的前 8 个字节作为结果。

示例

  • 对于指令 initialize,其输入为 global:initialize

  • 对于账户 NewAccount,其输入为 account:NewAccount

总之,Discriminator 是通过对前缀与指令或账户名称组合后的字符串进行 Sha256 哈希计算,并取其前 8 个字节生成的。从 Anchor v0.30 开始,这些 discriminators 会包含在 IDL 文件中。

需要注意的是,在使用 Anchor 开发时,通常无需直接与这些 discriminators 交互。这里主要是为了说明 discriminator 的生成方式及其用途。

IDL 文件的自动化支持

Anchor 框架还会为这个 IDL 文件自动生成 typescript 类型文件,生成路径为 /target/types/<program-name>.ts。我们在编写测试代码时,实际上就是引用了这个 .ts 文件。

需要提醒的是,在 rust 代码里的 蛇形命名(下划线连接) 字段生成 .ts 文件时,会自动改为 驼峰命名 以遵循 typescript 开发的命名习惯

结尾

本文通过示例代码、IDL 文件结构以及相关概念的剖析,帮助你掌握了 IDL 的作用及其在 Solana Anchor 开发中的重要性。在下一篇文章中,我们将深入探讨 SPL 代币标准的具体实现。

如果你对 Web3 前沿探索Go 后端技术,以及产品与哲学的深度思考感兴趣,可以关注我的公众号:认知那些事。每一篇文章,都是我精心打磨的干货。扫码关注,一起探索技术与思想的广阔世界。

认知那些事.png

  • 原创
  • 学分: 12
  • 分类: Solana
  • 标签:
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
认知那些事
认知那些事
0x2b62...95a0
人立于天地之间,必然有我们的出路。