BCSkill (Block chain skill )
区块链中文技术社区

只讨论区块链底层技术
遵守一切相关法律政策!

私有链化部署Squads Protocol

前期调研

  • 目前v3已经停止升级,优先使用v4版本
  • Squads-Protocol 官方app.squads.so前端不开源,只开源了精简版本 squads-backup-kit
  • squads-backup-kit 前端不支持创建,只支持管理已有多签账户地址
  • 首次创建可以使用Squads CLI进行创建
  • v4程序地址:SQDS4ep65T869zMMBKyuUq6aD6EgTu8psMjkvj52pCf

squads-backup-kit

GitHub:https://github.com/Squads-Protocol/public-v4-client
在线地址:https://backup.app.squads.so/

Squads CLI 使用

https://docs.squads.so/main/development/cli/installation
https://docs.squads.so/main/development/cli/commands

创建keypair

mkdir squads-multisig
cd squads-multisig
solana-keygen new -o ./keypair-multisig.json
solana-keygen new -o ./keypair-1.json
solana-keygen new -o ./keypair-2.json
solana-keygen new -o ./keypair-3.json

生成测试账户地址

89CefQR7XpdSKsDAFMZVUu3ipqiW35BceuahoE8CiAxz
56uqkuNMGsozcYDuzKfkqUEPqDLQfXRncvoH73xHzHnt
G3YLqbreJj1spvBMajv8qgPXVzZ1WaN34LDn7ChFdAPY
Hj3KcgcuvcHRM9or6SbuLuTV8Xe5pDttKk3nQSXzP65d

分别给上面4个账户地址转1SOL,为后面演示 2/3多签测试做准备

创建多重签名

squads-multisig-cli multisig-create --rpc-url <RPC_URL> --program-id <PROGRAM_ID> --keypair <KEYPAIR_PATH> --config-authority <CONFIG_AUTHORITY> --members <MEMBER_1> <MEMBER_2> ... --threshold <THRESHOLD>

multisig-create参数

  • --rpc-url :(可选)Solana RPC 端点的 URL。如果未指定,则默认为主网。
  • --program-id :(可选)多重签名程序的 ID。如未指定,则默认为标准 ID。
  • --keypair :密钥对文件的路径。
  • --config-authority :(可选)程序配置机构的地址。
  • --members <MEMBER_...>:成员公钥列表,以空格分隔。
  • --threshold :执行多重签名交易所需的签名阈值数量。

测试创建

squads-multisig-cli multisig-create --rpc-url https://api.devnet.solana.com --program-id SQDS4ep65T869zMMBKyuUq6aD6EgTu8psMjkvj52pCf --keypair ./keypair-multisig.json --members 56uqkuNMGsozcYDuzKfkqUEPqDLQfXRncvoH73xHzHnt,7 --members G3YLqbreJj1spvBMajv8qgPXVzZ1WaN34LDn7ChFdAPY,7 --members Hj3KcgcuvcHRM9or6SbuLuTV8Xe5pDttKk3nQSXzP65d,7 --threshold 2

threshold为2,则代表 2/3通过

权限是数字,映射到以下内容:

  • 提议人 -1
  • 投票人 -2
  • 执行人 -4
  • 上述所有的 -7

或者任何权限组合(即提议者和投票者3)
例如 --members FcBpwMquaMURbYwpRFUrBrYgFwJzfWiBEGfHLbik1Wsm,7

执行命令后返回
https://solscan.io/tx/XRFQkd2HN4mFoQvKa2FUdKYr2WkETZmxAYbCkM87VjxFDBgUWCEQYEixPcodpQtu1kv8cVgu1MP5pQpcU995U9A?cluster=devnet

You're about to create a multisig, please review the details:

RPC Cluster URL:   https://api.devnet.solana.com
Program ID:        SQDS4ep65T869zMMBKyuUq6aD6EgTu8psMjkvj52pCf
Your Public Key:       89CefQR7XpdSKsDAFMZVUu3ipqiW35BceuahoE8CiAxz

Config Parameters

Config Authority:  None
Threshold:          2
Rent Collector:     None
Members amount:      3

Do you want to proceed? yes

⠒ Sending transaction...                                                                                                Transaction confirmed: XRFQkd2HN4mFoQvKa2FUdKYr2WkETZmxAYbCkM87VjxFDBgUWCEQYEixPcodpQtu1kv8cVgu1MP5pQpcU995U9A


Created Multisig: 9VrXzJ8zQ8PZQ1pBnzbfNVB8cJKneU6rdrjwUKypAybE. Signature: XRFQkd2HN4mFoQvKa2FUdKYr2WkETZmxAYbCkM87VjxFDBgUWCEQYEixPcodpQtu1kv8cVgu1MP5pQpcU995U9A

多签钱包地址为:9VrXzJ8zQ8PZQ1pBnzbfNVB8cJKneU6rdrjwUKypAybE

使用前端工具进行测试

https://backup.app.squads.so/

查看当前多签配置

https://backup.app.squads.so/#/config/

修改配置交易创建

squads-multisig-cli config-transaction-create --rpc-url <RPC_URL> --program-id <PROGRAM_ID> --keypair <KEYPAIR_PATH> --multisig-pubkey <MULTISIG_PUBLIC_KEY> --action <ACTION> [--memo <MEMO>]

添加新成员

squads-multisig-cli config-transaction-create --rpc-url https://api.devnet.solana.com --program-id SQDS4ep65T869zMMBKyuUq6aD6EgTu8psMjkvj52pCf --keypair ./keypair-multisig.json --multisig-pubkey 89CefQR7XpdSKsDAFMZVUu3ipqiW35BceuahoE8CiAxz --action "AddMember 56uqkuNMGsozcYDuzKfkqUEPqDLQfXRncvoH73xHzHnt 7" --memo "AddMember 56uqkuNMGsozcYDuzKfkqUEPqDLQfXRncvoH73xHzHnt 7"

其余修改操作参考 https://docs.squads.so/main/development/cli/commands

私有链部署

对于私有链部署,首先需要初始化程序so

solana program -u m dump SQDS4ep65T869zMMBKyuUq6aD6EgTu8psMjkvj52pCf ./roles/svm-node/files/spl/squads-protocol-v4.so
solana-genesis --upgradeable-program SQDS4ep65T869zMMBKyuUq6aD6EgTu8psMjkvj52pCf BPFLoaderUpgradeab1e11111111111111111111111 {{path}}/config/spl/squads-protocol-v4.so {{upgradeAccount}} 

链启动后,创建多签交易报错

⠂ Sending transaction...                                                                                                thread 'main' panicked at /root/.cargo/registry/src/index.crates.io-6f17d22bba15001f/squads-multisig-cli-0.1.3/src/command/multisig_create.rs:141:14:
Failed to fetch program config account: Error { request: None, kind: RpcError(ForUser("AccountNotFound: pubkey=BSTq9w3kZwNwpBXJEvTZz2G9ZTNyKBvoSeXMvwb4cNZr")) }

分析:缺少内部初始化账户

获取账户地址Data {注意需要base64格式}

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getAccountInfo",
    "params": [
      "BSTq9w3kZwNwpBXJEvTZz2G9ZTNyKBvoSeXMvwb4cNZr",
      {
        "encoding": "base64"
      }
    ]
  }
' | jq

返回

{
  "jsonrpc": "2.0",
  "result": {
    "context": {
      "apiVersion": "2.2.3",
      "slot": 373312028
    },
    "value": {
      "data": [
        "xNJa55CVjD/y4DkJcttfEtTBz1IxzaY5194JtJU/4aL4ooeXbGx+0AAAAAAAAAAA8uA5CXLbXxLUwc9SMc2mOdfeCbSVP+Gi+KKHl2xsftAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
        "base64"
      ],
      "executable": false,
      "lamports": 1893120,
      "owner": "SQDS4ep65T869zMMBKyuUq6aD6EgTu8psMjkvj52pCf",
      "rentEpoch": 18446744073709551615,
      "space": 144
    }
  },
  "id": 1
}

调整初始化参数,加载预设账户地址和data

solana-genesis --primordial-accounts-file accounts.json

accounts.json

{
  "BSTq9w3kZwNwpBXJEvTZz2G9ZTNyKBvoSeXMvwb4cNZr": {
    "balance": 100000000000,
    "owner": "SQDS4ep65T869zMMBKyuUq6aD6EgTu8psMjkvj52pCf",
    "executable": false,
    "data": "xNJa55CVjD/y4DkJcttfEtTBz1IxzaY5194JtJU/4aL4ooeXbGx+0AAAAAAAAAAA8uA5CXLbXxLUwc9SMc2mOdfeCbSVP+Gi+KKHl2xsftAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
  }
}

再次确认私链账户地址与solana devnet data数据一致

solana account BSTq9w3kZwNwpBXJEvTZz2G9ZTNyKBvoSeXMvwb4cNZr --url https://api.devnet.solana.com

参考

https://docs.eclipse.xyz/developers/developer-tooling/squads-multisig
https://docs.squads.so/main/additional-resources/what-if-the-squads-app-goes-down
https://github.com/assetCLI/assetCLI-init/blob/feb-25/local-dev.sh

Solana IDL 猜测器 - 从闭源 Solana 程序中恢复指令布局

Solana 生态系统充满活力,但许多程序尚未开源。Syndica在 2024 年 2 月编制的统计数据显示,按计算单元计算,Solana 排名前 100 的程序中,近 50% 发布了其接口定义语言 (IDL)。然而,对于排名前 1000 的程序,这一数字下降到只有 20%。此外,即使已发布的 IDL 也并不总是可靠的。发布的 IDL 过时且与部署的链上程序不匹配的情况并不少见。

作为审计人员和安全研究人员,当我们发现可能导致漏洞的有趣模式时,我们通常会在其他程序中寻找类似的弱点。然而,如果没有源代码或准确的 IDL,这个过程通常仅限于基本的 GitHub 搜索,经常会发现无人维护的项目。

大多数 Solana 程序都是用 Rust 编写的,并编译为 Solana 字节码格式 (sBPF),这是一种基于 eBPF 的格式。对编译后的 Rust 进行逆向工程具有挑战性,而与 sBPF 相关的逆向工程工具链仍在开发中。这种不透明性不仅阻碍了恶意行为者,而且还减缓了白帽黑客和安全研究人员识别和负责任地披露漏洞的工作。

要分析任何闭源 Solana 程序(无论是动态还是静态),基本前提是了解如何与其交互。这意味着了解它的指令、每条指令所需的账户以及这些账户的属性(如签名者或可写状态)。

为了应对这些挑战,我们的安全研究员齐秦领导了这项工作并开发了一个名为IDL Guesser的原型工具。该工具旨在直接从闭源 Solana 程序二进制文件中自动恢复指令定义、所需帐户(包括签名者/可写标志)和参数信息。

该博客概述了 IDL Guesser 背后的方法并讨论了未来改进的潜在领域。

利用锚点模式进行逆向工程

由于 Solana 开发的大部分内容都使用了 Anchor 框架(IDL 的概念也源于此),因此 IDL Guesser 目前专注于基于 Anchor 的程序。Anchor 通过为常见任务和检查提供宏和辅助函数,大大简化了开发。至关重要的是,这会导致编译输出中出现可预测的标准化代码结构,我们可以通过模式匹配将其用于分析。

为了进行调试,Anchor CLI 甚至提供了一个anchor expand\命令,该命令会显示由其宏生成的代码。检查此扩展代码可以深入了解 Anchor 所采用的模式,从而指导我们的逆向工程工作。

入口点和调度逻辑

我们来看看一个典型的 Anchor 程序经过宏扩展后的入口点结构:

#[no_mangle]
pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u64 {
    let (program_id, accounts, instruction_data) = unsafe {
        ::solana_program::entrypoint::deserialize(input)
    };
    match entry(&program_id, &accounts, &instruction_data) {
        Ok(()) => ::solana_program::entrypoint::SUCCESS,
        Err(error) => error.into(),
    }
}

pub fn entry<'info>(
    program_id: &Pubkey,
    accounts: &'info [AccountInfo<'info>],
    data: &[u8],
) -> anchor_lang::solana_program::entrypoint::ProgramResult {
    try_entry(program_id, accounts, data)
        .map_err(|e| {
            e.log();
            e.into()
        })
}

fn try_entry<'info>(
    program_id: &Pubkey,
    accounts: &'info [AccountInfo<'info>],
    data: &[u8],
) -> anchor_lang::Result<()> {
    if *program_id != ID {
        return Err(anchor_lang::error::ErrorCode::DeclaredProgramIdMismatch.into());
    }
    if data.len() < 8 {
        return Err(anchor_lang::error::ErrorCode::InstructionMissing.into());
    }
    dispatch(program_id, accounts, data)
}

fn dispatch<'info>(
    program_id: &Pubkey,
    accounts: &'info [AccountInfo<'info>],
    data: &[u8],
) -> anchor_lang::Result<()> {
    let mut ix_data: &[u8] = data;
    let sighash: [u8; 8] = {
        let mut sighash: [u8; 8] = [0; 8];
        sighash.copy_from_slice(&ix_data[..8]);
        ix_data = &ix_data[8..];
        sighash
    };
    use anchor_lang::Discriminator;
    match sighash {
        instruction::InitializeConfig::DISCRIMINATOR => {
            __private::__global::initialize_config(program_id, accounts, ix_data)
        }
        instruction::InitializePool::DISCRIMINATOR => {
            __private::__global::initialize_pool(program_id, accounts, ix_data)
        }
        // ... other instructions
    }
}

程序首先对原始输入进行反序列化。它在进入函数之前执行基本检查(例如验证program_id\并确保instruction_data\至少有 8 个字节长)dispatch\。在 中dispatch\, 的前 8 个字节instruction_data\被解释为指令鉴别符。该鉴别符确定应执行哪个特定的指令处理程序函数。

根据 Anchor 的文档,这个 8 字节鉴别器是从指令的命名空间和名称(例如global:initialize_config\)中派生出来的,方法是取其 SHA-256 哈希的前 8 个字节。IDL Guesser 不会尝试从编译后的代码中提取这些原始鉴别器字节(这可能很复杂),而是采用一种更简单的方法:它首先专注于提取指令名称,然后使用 Anchor 的标准哈希方法计算相应的鉴别器。

识别指令处理程序

我们如何找到指令名称及其对应的处理程序函数?Anchor 提供了另一种有用的模式。考虑一个典型指令处理程序的开头:

pub fn initialize_config<'info>( /* ... */ ) -> anchor_lang::Result<()> {
    // Log the instruction name - a key pattern for us!
    ::solana_program::log::sol_log("Instruction: InitializeConfig");

    // Deserialize instruction-specific parameters
    let ix = instruction::InitializeConfig::deserialize(&mut &__ix_data[..])
        .map_err(|_| /* ... */ )?;
    let instruction::InitializeConfig { /* ... parameters ... */ } = ix;

    // Process accounts via try_accounts
    let mut __bumps = /* ... */;
    let mut __reallocs = /* ... */;
    let mut __remaining_accounts: &[AccountInfo] = __accounts;
    let mut __accounts = InitializeConfig::try_accounts(
        __program_id,
        &mut __remaining_accounts,
        __ix_data,
        &mut __bumps,
        &mut __reallocs,
    )?;
    // ... 
}

Anchorsol_log\在每个处理程序的开头插入一个调用,记录指令的名称(例如“Instruction: InitializeConfig”\)以供日志解析。此日志记录提供了我们可以在编译后的二进制文件中搜索的独特签名。

img

用于记录指令名称*InitializeConfig 的*****sol_log\系统调用序列。

在汇编级别(如上所示),此日志调用通常会转换为特定的lddw\mov64\call\指令设置,sol_log\以使用指令名称字符串调用系统调用。

通过识别这些模式,IDL Guesser 可以可靠地定位指令处理程序的入口点并提取其名称。

提取账户信息

在初始日志记录和参数反序列化(通常由编译器内联)之后,处理程序通常会调用相应的try_accounts\函数。此函数负责解析和验证指令所需的帐户。

img

参数反序列化之后调用try_accounts\函数(此处为sub_662B0\)。

让我们检查一下Accounts\结构和生成的try_accounts\函数initialize_config\

// Account definition
#[derive(Accounts)]
pub struct InitializeConfig<'info> {
    #[account(init, payer = funder, space = WhirlpoolsConfig::LEN)]
    pub config: Account<'info, WhirlpoolsConfig>, // To be initialized
    #[account(mut)]
    pub funder: Signer<'info>,                   // Must be mutable and a signer
    pub system_program: Program<'info, System>,  // System program
}

// Generated try_accounts function (simplified)
fn try_accounts( /* ... */ ) -> anchor_lang::Result<Self> {
    // Check if enough accounts provided
    if __accounts.is_empty() {
        // Error 3005
        return Err(anchor_lang::error::ErrorCode::AccountNotEnoughKeys.into()); 
    }
    // Process 'config' account (index 0) - checks applied later
    let config = &__accounts[0];
    *__accounts = &__accounts[1..];

    // Process 'funder' account using Signer's try_accounts
    let funder: Signer = anchor_lang::Accounts::try_accounts(/* ... */)
        .map_err(|e| e.with_account_name("funder"))?; // Adds "funder" to error message

    // Process 'system_program' account
    let system_program: Program<System> = anchor_lang::Accounts::try_accounts(/* ... */)
        .map_err(|e| e.with_account_name("system_program"))?; // Adds "system_program"

    // Apply constraints checks
    if !config.is_writable { // Check defined indirectly via init
            // Error 2000
        return Err(anchor_lang::error::ErrorCode::ConstraintMut.into().with_account_name("config")); 
    }
    if !config.is_signer { // Check defined indirectly via init
       // Error 2002
       return Err(anchor_lang::error::ErrorCode::ConstraintSigner.into().with_account_name("config")); 
    }
    // ... other checks like rent exemption (Error 2005), owner, etc.

    if !funder.is_writable { // Check defined by #[account(mut)]
        // Error 2000
        return Err(anchor_lang::error::ErrorCode::ConstraintMut.into().with_account_name("funder")); 
    }
    // ... checks for funder.is_signer happen inside its own try_accounts

    Ok(Self { config, funder, system_program })
}

try_accounts\函数执行几个关键操作:

  1. 它会遍历预期的帐户,并尝试根据其类型(Account\Signer\、等)对其进行解析。如果在嵌套调用(如 for )Program\中解析失败,Anchor 会方便地将帐户名称(例如“funder”)附加到错误消息中。这使我们能够通过查找这些特定的错误处理模式来提取帐户名称。*try_accounts*****funder\
  2. 它根据结构中定义的属性应用约束检查(mut\signer\has_one\seeds\、 、租金豁免等) 。重要的是,每个约束违规通常映射到一个唯一的Anchor (例如,is 2000\、is 2002\)。*owner**Accounts**ErrorCode**ConstraintMut**ConstraintSigner*

img

检查需要初始化的帐户是否有足够的帐户密钥,如果失败则分支到错误3005\

img

约束检查条件跳转,导致特定的错误代码如2000\ConstraintMut\)或2002\ConstraintSigner\)。

通过分析函数的控制流图(CFG)try_accounts\,具体来说是遵循“快乐路径”(成功执行),IDL Guesser 可以拼凑出所需的账户:

  • 账户处理的顺序揭示了账户的预期顺序。
  • 与错误消息相关的字符串文字(例如“funder”)显示帐户名称。
  • 失败路径上出现的特定错误代码(如 2000、2002、2005)表示对每个帐户应用的约束(可变、签名者、可变等)。

提取参数

虽然提取指令名称和账户详细信息依赖于相对不同的模式(日志字符串、错误代码、特定函数调用),但恢复有关指令参数的信息更加困难。

Anchor 通常不会生成针对单个参数反序列化失败的详细错误消息。这意味着 Rust 源代码中定义的原始参数名称通常会在编译期间丢失。此外,负责从切片中顺序反序列化参数的代码ix_data\通常会由编译器优化和内联,这使得可靠的汇编级模式匹配非常困难。

ix_data\更有希望的未来方向可能涉及符号执行,通过分析如何使用来确定预期的字节长度以及可能确定每个参数的类型。

然而,在 IDL Guesser 原型中,采用了一种利用动态分析的替代且更简单的方法:由于交易大小限制,Solana 指令数据通常很短,因此我们可以迭代探测处理程序函数。通过稍微增加模拟输入数据的长度并观察执行跟踪中的变化(例如,通过之前失败的检查),我们可能会在新的反序列化步骤成功时推断出边界,并可能推断出参数的类型。

这种迭代反馈循环技术也用于重建账户的内部布局。具体细节这里就不展开了,感兴趣的读者可以去源代码中探索相关的实现。此外,它还可能用于验证或完善恢复的指令和账户信息。

实现细节

识别出这些模式后,实施过程涉及反汇编 sBPF 字节码并对指令序列和 CFG 执行模式匹配。我们以现有solana-sbpf\项目(参见static_analysis.rs)为基础,为 Solana 程序分析奠定了基础。

我们对基本静态分析实现进行了一些修改。原始版本倾向于在每次函数调用后过度拆分基本块。我们对此进行了调整,以创建更大、更易于管理的块。此外,我们还为系统调用(如abort\panic\)添加了特殊处理。这些更改使 CFG 更加精确,从而简化了模式匹配过程。

完整的实现是开源的,可以在IDLGuesser 存储库中获取。

当前代码处理许多常见场景,但也包括一些特殊情况的逻辑,例如UncheckedAccount\Sysvar\帐户。

这些逻辑try_accounts\通常由编译器内联,从而创建类似于init帐户的模式。然而,挑战仍然存在,特别是对于多个连续UncheckedAccount实例或更复杂的结构(如可选帐户和嵌套帐户上下文),此原型尚未完全处理。

结论

IDL Guesser 展示了一种可行的方法,利用框架的代码生成模式和简单的动态分析,从基于 Anchor 的闭源 Solana 程序中恢复基本结构信息(具有相应帐户和参数信息的指令)。虽然原型有局限性,在复杂情况下可能需要手动进行逆向工程并与链上数据进行交叉引用,但它成功地为大量程序恢复了类似 IDL 的信息。

我们发现此功能很有用,可以更广泛地分析交易数据,并有助于自动扫描与帐户限制相关的基本漏洞(例如缺少签名者检查)。通过揭示闭源程序的内部工作原理,我们希望 IDL Guesser 等工具能够为保护 Solana 生态系统做出贡献。

原文:https://www.sec3.dev/blog/idl-guesser-recovering-instruction-layouts-from-closed-source-solana-programs

Solana基础 - Solana RPC HTTP 方法

getAccountInfo

返回与提供的公钥的账户相关的所有信息,和以太坊 eth_getAccount类似

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getAccountInfo",
    "params": [
      "8TKG1ez28ZYzNgGTein6Pc97yvxWwnHecVL9ZhdZTxd5",
      {
        "encoding": "base58"
      }
    ]
  }
'

返回

{
  "jsonrpc": "2.0",
  "result": {
    "context": {
      "apiVersion": "2.2.3",
      "slot": 370321974
    },
    "value": {
      "data": [
        "",
        "base58"
      ],
      "executable": false,
      "lamports": 4994414840,
      "owner": "11111111111111111111111111111111",
      "rentEpoch": 18446744073709551615,
      "space": 0
    }
  },
  "id": 1
}

getBalance

返回所提供公钥的账户的 Lamport 余额, 和以太坊eth_getBalance类似

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getBalance",
    "params": [
      "8TKG1ez28ZYzNgGTein6Pc97yvxWwnHecVL9ZhdZTxd5"
    ]
  }
'

返回

{
  "jsonrpc": "2.0",
  "result": {
    "context": {
      "apiVersion": "2.2.3",
      "slot": 370322177
    },
    "value": 4994414840
  },
  "id": 1
}

getBlock

返回账本中已确认区块的身份和交易信息,和以太坊 eth_getBlockByNumber类似

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getBlock",
    "params": [
      358300848,
      {
        "encoding": "json",
        "maxSupportedTransactionVersion": 0,
        "transactionDetails": "full",
        "rewards": false
      }
    ]
  }
'

如果类似如下提示,这通常意味着你尝试访问的特定槽(Slot)存在问题,具体可能是该槽被跳过或者在长期存储中缺失

{
  "jsonrpc": "2.0",
  "error": {
    "code": -32009,
    "message": "Slot 100000430 was skipped, or missing in long-term storage"
  },
  "id": 1
}
{
  "jsonrpc": "2.0",
  "result": {
    "blockHeight": 346328196,
    "blockTime": 1738488135,
    "blockhash": "HGrdL4Z5vEvQVWchG2pnJEQ5dMxQhqiXncpL6GJvZimx",
    "parentSlot": 358300847,
    "previousBlockhash": "3QuyTMBRAK5PhLXehDCTMH4zgnfn7xgZnHG3fBnZFsiQ",
    "transactions": [
      ...
          {
            "meta": {
                "computeUnitsConsumed": 2100,
                "err": null,
                "fee": 5000,
                "innerInstructions": [],
                "loadedAddresses": {
                    "readonly": [],
                    "writable": []
                },
                "logMessages": [
                    "Program Vote111111111111111111111111111111111111111 invoke [1]",
                    "Program Vote111111111111111111111111111111111111111 success"
                ],
                "postBalances": [
                    912514572484583,
                    8402660357128602,
                    1
                ],
                "postTokenBalances": [],
                "preBalances": [
                    912514572489583,
                    8402660357128602,
                    1
                ],
                "preTokenBalances": [],
                "rewards": null,
                "status": {
                    "Ok": null
                }
            },
            "transaction": {
                "message": {
                    "accountKeys": [
                        "dv4ACNkpYPcE3aKmYDqZm9G5EB3J4MRoeE7WNDRBVJB",
                        "23AoPQc3EPkfLWb14cKiWNahh1H9rtb3UBk8gWseohjF",
                        "Vote111111111111111111111111111111111111111"
                    ],
                    "header": {
                        "numReadonlySignedAccounts": 0,
                        "numReadonlyUnsignedAccounts": 1,
                        "numRequiredSignatures": 1
                    },
                    "instructions": [
                        {
                            "accounts": [
                                1,
                                0
                            ],
                            "data": "67MGn8CZqesiDCbWWYBQre79LVuia8eBTRNppRhYeXWhLFMWQsHQrrLxehkcbeCZ9ncYWSaBVdzbL6iUumunuVXWuVgnYAZ6oLZVMsEUZohcJMkAa7sAhbmGAT4ZUjfcZd7LH99cRPUwUZu7HUaG5Q8Y7TM98PR6bJJoySwh8QpuxtWzBqpLAH4nv2cDY946EQPmXS5zvB",
                            "programIdIndex": 2,
                            "stackHeight": null
                        }
                    ],
                    "recentBlockhash": "3QuyTMBRAK5PhLXehDCTMH4zgnfn7xgZnHG3fBnZFsiQ"
                },
                "signatures": [
                    "56uXBR4rgLNbDCtCr8gyTktGYCBBsKdJ4mUEGS9rEkcS9tG9ZghksbZCUdjXdoLJqnScG2e4zQCxvUKE6ctwyjpw"
                ]
            },
            "version": "legacy"
        }
    ...
    ]
  },
  "id": 1
}

getBlockCommitment

返回特定区块的承诺

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getBlockCommitment",
    "params": [
      346328196
    ]
  }
'

返回

{
  "jsonrpc": "2.0",
  "result": {
    "commitment": null,
    "totalStake": 157541826450322300
  },
  "id": 1
}

getBlockHeight

返回节点当前的区块高度,和以太坊eth_blockNumber 类似

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getBlockHeight",
    "params": []
  }
'

返回

{
  "jsonrpc": "2.0",
  "result": 358302920,
  "id": 1
}

getBlockProduction

获取当前epoch期间内,所有产生区块的地址和相应的产块结果[应产出:实际产出]
range是当前周期的起始和当前slot区间

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getBlockProduction"
  }
'

注:对于产块结果,可以作为对应验证节点的稳定性判定

getBlocks

返回两个 slot 之间已确认的区块列表

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getBlocks",
    "params": [
      5, // 起始Slot
      10 // 结束Slot (间距<= 500000)
    ]
  }
'

getBlocksWithLimit

返回从给定槽开始的已确认区块列表
和getBlocks类似,区别就是由结束Slot改为了Limit

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getBlocksWithLimit",
    "params": [
      5, // 起始Slot
      3 // Limit (间距<= 500000)
    ]
  }
'

getBlockTime

返回某个区块的预计生产时间。
每个验证者都会定期向账本报告其 UTC 时间,方法是间歇性地为特定区块的投票添加时间戳。请求的区块时间是根据账本上记录的一组最近区块中投票时间戳的权益加权平均值计算得出的。

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getBlockTime",
    "params": [
      358319114
    ]
  }
'

getClusterNodes

返回有关参与集群的所有节点的信息

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getClusterNodes"
  }
'

getEpochInfo

返回有关当前纪元的信息

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getEpochInfo"
  }
'

返回

{
  "jsonrpc": "2.0",
  "result": {
    "absoluteSlot": 370353885, // 当前槽位
    "blockHeight": 358332171, // 当前区块高度
    "epoch": 857, // 当前epoch
    "slotIndex": 129885, // 当前 slot 相对于当前 epoch 的开始
    "slotsInEpoch": 432000, // 此纪元中的时隙数
    "transactionCount": 15406146576 // 自创世以来无错误处理的交易总数
  },
  "id": 1
}

getEpochSchedule

从此集群的创世配置中返回纪元调度信息

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getEpochSchedule"
  }
'

返回

{
  "jsonrpc": "2.0",
  "result": {
    "firstNormalEpoch": 0,
    "firstNormalSlot": 0,
    "leaderScheduleSlotOffset": 432000,
    "slotsPerEpoch": 432000,
    "warmup": false // 是否周期一开始很短,然后逐渐增长
  },
  "id": 1
}

getFeeForMessage

获取网络针对特定消息收取的费用,和以太坊eth_estimateGas类似

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getFeeForMessage",
    "params": [
      "AQABAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQAA",
      {
        "commitment": "processed"
      }
    ]
  }
'

返回

{
  "jsonrpc": "2.0",
  "result": {
    "context": { "slot": 5068 },
    "value": 5000
  },
  "id": 1
}

getFirstAvailableBlock

返回当前节点上可访问的最早的区块编号(block number)

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getFirstAvailableBlock"
  }
'

注:简单说就是查询当前RPC节点存储的历史区块最早的高度,由于存储成本问题,对于官方devnet testnet都只保留近期一部分数据,对于主网保留所有数据,对于历史数据保存需要使用bigtable外部存储,对于设置启动参数--limit-ledger-size,大概保存近期 600w左右区块数据

getGenesisHash

查询genesis hash

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getGenesisHash"
  }
'

返回

{
  "jsonrpc": "2.0",
  "result": "EtWTRABZaYq6iMfeYKouRu166VU2xqa1wcaWoxPkrZBG",
  "id": 1
}

getHealth

返回节点的当前健康状况。健康节点是指位于 HEALTH_CHECK_SLOT_DISTANCE最新集群确认槽内的节点。

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getHealth"
  }
'

注:如果节点健康将返回ok,可用于节点的监控指标

getHighestSnapshotSlot

返回节点具有快照的最高插槽信息。
这将找到最高的完整快照槽,以及基于完整快照槽的最高增量快照槽(如果有)

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getHighestSnapshotSlot"
  }
'

getIdentity

返回当前节点的身份公钥

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getIdentity"
  }
'

注:也就是返回对应RPC 配置的--identity的pubkey

getInflationGovernor

返回当前通货膨胀调控器

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getInflationGovernor"
  }
'

getInflationRate

返回当前时期的具体通胀值

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getInflationRate"
  }
'

getInflationReward

返回某个时期地址列表的通胀/权益奖励

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getInflationReward",
    "params": [
      [
        "6dmNQ5jwLeLk5REvio1JcMshcbvkYMwy26sJ8pbkvStu",
        "BGsqMegLpV6n6Ve146sSX2dTjUMj3M92HnU8BbNRMhF2"
      ],
      {
        "epoch": 2
      }
    ]
  }
'

getLargestAccounts

按 Lamport 余额返回前 20 个最大的账户(结果可能缓存长达两小时)

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getLargestAccounts",
    "params": []
  }
'

getLatestBlockhash

返回最新的区块哈希

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getLatestBlockhash",
    "params": [
      {
        "commitment": "processed"
      }
    ]
  }
'

注:当前值用于常规交易发送时的recentBlockhash

getLeaderSchedule

返回一个时期的领导者时间表

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getLeaderSchedule",
    "params": [
      null,
      {
        "identity": "4Qkev8aNZcqFNSRhQzwyLMFSsi94jHqE8WNVTJzTP99F"
      }
    ]
  }
'

getMaxRetransmitSlot

获取重传阶段看到的最大时隙。

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getMaxRetransmitSlot"
  }
'

getMaxShredInsertSlot

获取撕碎插入后看到的最大插槽。

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getMaxShredInsertSlot"
  }
'

getMinimumBalanceForRentExemption

返回免除账户租金所需的最低余额。

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getMinimumBalanceForRentExemption",
    "params": [
      50
    ]
  }
'

getMultipleAccounts

返回公钥列表的帐户信息。

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getMultipleAccounts",
    "params": [
      [
        "vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg",
        "4fYNw3dojWmQ4dXtSGE9epjRGy9pFSx62YypT7avPYvA"
      ],
      {
        "encoding": "base58"
      }
    ]
  }
'

getProgramAccounts

返回所提供程序 Pubkey 拥有的所有账户

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getProgramAccounts",
    "params": [
      "4Nd1mBQtrMJVYVfKf2PJy9NZUZdTAsp7D4xWLs4gDB4T",
      {
        "filters": [
          { "dataSize": 17 },
          {
            "memcmp": {
              "offset": 4,
              "bytes": "3Mc6vR"
            }
          }
        ]
      }
    ]
  }
'

getRecentPerformanceSamples

返回最近性能样本的列表(按槽位倒序排列)。性能样本每 60 秒采集一次,包括给定时间窗口内发生的事务和槽位数量。

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getRecentPerformanceSamples",
    "params": [
      2
    ]
  }
'

getRecentPrioritizationFees

返回最近区块的优先费用列表。

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getRecentPrioritizationFees",
    "params": [
      ["CxELquR1gPP8wHe33gZ4QxqGB3sZ9RSwsJ2KshVewkFY"]
    ]
  }
'

getSignaturesForAddress

返回已确认交易的签名,这些交易的列表中包含指定地址accountKeys。按时间倒序返回提供的签名或最近确认的区块的签名

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getSignaturesForAddress",
    "params": [
      "Vote111111111111111111111111111111111111111",
      {
        "limit": 1 // 1-1000, 默认1000
      }
    ]
  }
'

getSignatureStatuses

返回签名列表的状态。每个签名必须是 txid,即交易的第一个签名。
除非searchTransactionHistory包含配置参数,否则此方法仅搜索签名的近期状态缓存,其中保留所有活动插槽和MAX_RECENT_BLOCKHASHES根插槽的状态。

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getSignatureStatuses",
    "params": [
      [
        "5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW"
      ],
      {
        "searchTransactionHistory": true
      }
    ]
  }
'

返回

{
  "jsonrpc": "2.0",
  "result": {
    "context": {
      "apiVersion": "2.0.18",
      "slot": 24219064
    },
    "value": [
      {
        "confirmationStatus": "finalized", // processed->confirmed->finalized
        "confirmations": null,
        "err": null,
        "slot": 24218468,
        "status": {
          "Ok": null
        }
      }
    ]
  },
  "id": 1
}

getSlot

返回已达到给定或默认承诺级别的槽

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getSlot"
  }
'

注:默认是返回最近finalized的Slot

getSlotLeader

返回当前 slot 领导者

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getSlotLeader"
  }
'

注:如果定制链做Rollup,那返回的为sequencer的账户地址

getSlotLeaders

返回给定槽范围的槽领导者

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getSlotLeaders",
    "params": [
      100, // 起始Slot
      10 //Limit (1-5000)
    ]
  }
'

getStakeMinimumDelegation

以 lampors 为单位,返回股权最低授权。

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getStakeMinimumDelegation"
  }
'

注:如果限制外部节点的加入,可以修改链代码,将最小委托量修改为10000000000000000000

getSupply

返回有关当前供应的信息。

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getSupply"
  }
'

getTokenAccountBalance

返回 SPL 代币账户的代币余额

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getTokenAccountBalance",
    "params": [
      "3tykH6gHjjyqYHBigukDXZaj3K6XupjKxYLMA2Srfbc2" // 对应账户地址->ATA账户
    ]
  }
'

返回

{
  "jsonrpc": "2.0",
  "result": {
    "context": {
      "apiVersion": "2.1.15",
      "slot": 370370345
    },
    "value": {
      "amount": "100000000000",
      "decimals": 9,
      "uiAmount": 100.0, // amount /(10的decimals次方)
      "uiAmountString": "100" // uiAmount 转字符串
    }
  },
  "id": 1
}

注:查询地址为对应账户->对应Token(mint)->ATA账户地址

getTokenAccountsByDelegate

返回已获批准的代表的所有 SPL 代币账户

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getTokenAccountsByDelegate",
    "params": [
      "4Nd1mBQtrMJVYVfKf2PJy9NZUZdTAsp7D4xWLs4gDB4T",
      {
        "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
      },
      {
        "encoding": "jsonParsed"
      }
    ]
  }
'

getTokenAccountsByOwner

按代币所有者返回所有 SPL 代币账户

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getTokenAccountsByOwner",
    "params": [
      "8TKG1ez28ZYzNgGTein6Pc97yvxWwnHecVL9ZhdZTxd5", // 这里是Keypair原地址
      {
        "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
      },
      {
        "encoding": "jsonParsed"
      }
    ]
  }
'

返回

{
  "jsonrpc": "2.0",
  "result": {
    "context": {
      "apiVersion": "2.2.3",
      "slot": 370371798
    },
    "value": [
      {
        "account": {
          "data": {
            "parsed": {
              "info": {
                "isNative": false,
                "mint": "34GbKbRzLMfvpHEFywhe2KyheKMMxab8tQAFj5t2rBYT", // 代币标志位
                "owner": "8TKG1ez28ZYzNgGTein6Pc97yvxWwnHecVL9ZhdZTxd5",
                "state": "initialized",
                "tokenAmount": {
                  "amount": "100000000000",
                  "decimals": 9,
                  "uiAmount": 100.0,
                  "uiAmountString": "100"
                }
              },
              "type": "account"
            },
            "program": "spl-token",
            "space": 165
          },
          "executable": false,
          "lamports": 2039280,
          "owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
          "rentEpoch": 18446744073709551615,
          "space": 165
        },
        "pubkey": "3tykH6gHjjyqYHBigukDXZaj3K6XupjKxYLMA2Srfbc2" // ATA账户地址
      }
    ]
  },
  "id": 1
}

getTokenLargestAccounts

返回特定 SPL 代币类型的 20 个最大余额账户。

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getTokenLargestAccounts",
    "params": [
      "34GbKbRzLMfvpHEFywhe2KyheKMMxab8tQAFj5t2rBYT" // 代币mint地址
    ]
  }
'

返回

{
  "jsonrpc": "2.0",
  "result": {
    "context": {
      "apiVersion": "2.1.15",
      "slot": 370372669
    },
    "value": [
      {
        "address": "ChpLTT95FAZNVSeM79C7iun141Lp54dxjoURegsQAtZN",
        "amount": "100000000000",
        "decimals": 9,
        "uiAmount": 100.0,
        "uiAmountString": "100"
      },
      {
        "address": "3tykH6gHjjyqYHBigukDXZaj3K6XupjKxYLMA2Srfbc2",
        "amount": "100000000000",
        "decimals": 9,
        "uiAmount": 100.0,
        "uiAmountString": "100"
      }
    ]
  },
  "id": 1
}

getTokenSupply

返回 SPL 代币类型的总供应量

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getTokenSupply",
    "params": [
      "34GbKbRzLMfvpHEFywhe2KyheKMMxab8tQAFj5t2rBYT"
    ]
  }
'

返回

{
  "jsonrpc": "2.0",
  "result": {
    "context": {
      "apiVersion": "2.1.15",
      "slot": 370372872
    },
    "value": {
      "amount": "200000000000",
      "decimals": 9,
      "uiAmount": 200.0,
      "uiAmountString": "200"
    }
  },
  "id": 1
}

getTransaction

返回已确认交易的交易详情

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getTransaction",
    "params": [
      "5cvy15goDmKA81kaMKk8nuuWuSN3AgCssjTK4qTvm49FyeZPoHXp5DjatqvSoUTDYArjio5PHE2LCuzBss9P7BDK",
      "json"
    ]
  }
'

返回

{
  "jsonrpc": "2.0",
  "result": {
    "blockTime": 1743069997,
    "meta": {
      "computeUnitsConsumed": 4648,
      "err": null,
      "fee": 5000,
      "innerInstructions": [],
      "loadedAddresses": {
        "readonly": [],
        "writable": []
      },
      "logMessages": [
        "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [1]",
        "Program log: Instruction: MintToChecked",
        "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4498 of 4648 compute units",
        "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success",
        "Program ComputeBudget111111111111111111111111111111 invoke [1]",
        "Program ComputeBudget111111111111111111111111111111 success"
      ],
      "postBalances": [
        4994414840,
        1461600,
        2039280,
        1,
        934087680
      ],
      "postTokenBalances": [
        {
          "accountIndex": 2,
          "mint": "34GbKbRzLMfvpHEFywhe2KyheKMMxab8tQAFj5t2rBYT",
          "owner": "8TKG1ez28ZYzNgGTein6Pc97yvxWwnHecVL9ZhdZTxd5",
          "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
          "uiTokenAmount": {
            "amount": "100000000000",
            "decimals": 9,
            "uiAmount": 100.0,
            "uiAmountString": "100"
          }
        }
      ],
      "preBalances": [
        4994419840,
        1461600,
        2039280,
        1,
        934087680
      ],
      "preTokenBalances": [
        {
          "accountIndex": 2,
          "mint": "34GbKbRzLMfvpHEFywhe2KyheKMMxab8tQAFj5t2rBYT",
          "owner": "8TKG1ez28ZYzNgGTein6Pc97yvxWwnHecVL9ZhdZTxd5",
          "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
          "uiTokenAmount": {
            "amount": "0",
            "decimals": 9,
            "uiAmount": null,
            "uiAmountString": "0"
          }
        }
      ],
      "rewards": [],
      "status": {
        "Ok": null
      }
    },
    "slot": 370161711,
    "transaction": {
      "message": {
        "accountKeys": [
          "8TKG1ez28ZYzNgGTein6Pc97yvxWwnHecVL9ZhdZTxd5",
          "34GbKbRzLMfvpHEFywhe2KyheKMMxab8tQAFj5t2rBYT",
          "3tykH6gHjjyqYHBigukDXZaj3K6XupjKxYLMA2Srfbc2",
          "ComputeBudget111111111111111111111111111111",
          "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
        ],
        "header": {
          "numReadonlySignedAccounts": 0,
          "numReadonlyUnsignedAccounts": 2,
          "numRequiredSignatures": 1
        },
        "instructions": [
          {
            "accounts": [
              1,
              2,
              0
            ],
            "data": "ndcLHYJ5qd8F2",
            "programIdIndex": 4,
            "stackHeight": null
          },
          {
            "accounts": [],
            "data": "F7UDE7",
            "programIdIndex": 3,
            "stackHeight": null
          }
        ],
        "recentBlockhash": "BUuLwgC1zPLC1VcQ5xRrKSnDz2vheZaxW82GUJX59CLM"
      },
      "signatures": [
        "5cvy15goDmKA81kaMKk8nuuWuSN3AgCssjTK4qTvm49FyeZPoHXp5DjatqvSoUTDYArjio5PHE2LCuzBss9P7BDK"
      ]
    }
  },
  "id": 1
}

getTransactionCount

返回当前网络从创世以来所有的交易总数

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getTransactionCount"
  }
'

getVersion

返回当前查询RPC节点所运行的Solana 版本

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getVersion"
  }
'

getVoteAccounts

返回当前银行所有投票账户的账户信息和相关股份。

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getVoteAccounts",
    "params": [
      {
        "votePubkey": "3ZT31jkAGhUaw8jsy4bTknwBMP8i4Eueh52By4zXcsVw"
      }
    ]
  }
'

isBlockhashValid

查询指定块hash是否仍然有效

curl https://api.devnet.solana.com -s -X \
  POST -H "Content-Type: application/json" -d ' 
  {
    "jsonrpc": "2.0",
    "id": 45,
    "method": "isBlockhashValid",
    "params": [
      "J7rBdM6AecPDEZp8aPq5iPSNKVkU5Q76F3oAV4eW5wsW",
      {
        "commitment": "processed"
      }
    ]
  }
'

参考:https://solana.com/zh/docs/rpc/http

solana 交易 confirmationStatus 有几种状态

在 Solana 中,交易的 confirmationStatus 用于表示交易的确认状态,主要有以下几种状态:

1. finalized

  • 含义 :这是最高级别的确认状态。当交易达到 finalized 状态时,意味着该交易已经被集群中的大多数验证节点确认,并且不会被回滚。在区块链的共识机制下,这是一个非常安全的状态,表明交易已经被永久记录在区块链上。
  • 应用场景 :在需要确保交易不可逆转的场景中,如涉及资金转移、重要数据记录等,通常会等待交易达到 finalized 状态。

2. confirmed

  • 含义 :表示交易已经被集群中的一个验证节点确认。虽然交易处于 confirmed 状态,但仍然存在一定的风险,因为在某些情况下,该交易可能会被回滚。不过,这种情况相对较少发生。
  • 应用场景 :在一些对交易确认速度要求较高,而对交易最终确定性要求不是特别严格的场景中,可以使用 confirmed 状态。例如,在一些实时性要求较高的游戏或交互场景中,当用户完成操作后,可以快速告知用户交易已 confirmed ,让用户继续后续操作。

3. processed

  • 含义 :表示交易已经被集群接收并处理,但尚未被任何验证节点确认。这是交易确认过程中的初始状态,意味着交易已经进入了集群的处理流程,但还没有得到足够的确认。
  • 应用场景 :在开发过程中,当需要快速反馈交易是否已经被集群接收时,可以使用 processed 状态。例如,在用户提交交易后,立即告知用户交易已被接收并正在处理中。

代码示例

在使用 @solana/web3.js 进行交易时,可以指定 confirmationStatus 参数。以下是一个示例代码:

const { Connection, Keypair, LAMPORTS_PER_SOL, PublicKey, SystemProgram, Transaction, sendAndConfirmTransaction } = require('@solana/web3.js');

// 连接到 Solana 网络
const connection = new Connection('https://api-devnet.solana.com', 'confirmed');

// 生成发送方和接收方的密钥对
const fromKeypair = Keypair.generate();
const toPublicKey = new PublicKey('...'); // 替换为实际的接收方公钥

// 创建交易
const transaction = new Transaction().add(
    SystemProgram.transfer({
        fromPubkey: fromKeypair.publicKey,
        toPubkey: toPublicKey,
        lamports: 1 * LAMPORTS_PER_SOL,
    })
);

// 发送并确认交易,指定 confirmationStatus 为 'finalized'
sendAndConfirmTransaction(connection, transaction, [fromKeypair], {
    commitment: 'finalized'
})
.then((signature) => {
    console.log(`交易已成功发送,签名: ${signature}`);
})
.catch((error) => {
    console.error('转账过程中出现错误:', error);
});


在上述代码中, commitment 参数指定了交易的确认状态,这里设置为 finalized ,表示等待交易达到最终确认状态。你可以根据实际需求将其设置为 confirmed 或 processed 。
搜索