Logo
新用户订阅 RPC,首月享 6.5 折优惠查看优惠
OnFinality Learn
网络与协议指南阅读约 12 分钟

通过 RPC 查询 Solana 历史数据:方法、限制与生产模式

了解如何通过 JSON-RPC 查询 Solana 历史数据:getSignaturesForAddress、getTransaction、getBlock 及分页模式,并附有实际限制和生产注意事项。

TL;DR

本文介绍如何通过 JSON-RPC 查询 Solana 历史数据,涵盖全节点与归档节点的区别、getSignaturesForAddress 和 getTransaction 等关键方法、可运行的分页模式、实际限制以及生产注意事项。

直接回答:如何查询 Solana 历史数据

要通过 RPC 查询 Solana 历史数据,您需要使用一组 JSON-RPC 方法,这些方法返回交易签名、交易详情和区块数据。主要方法是 getSignaturesForAddress,它返回给定地址的交易签名列表,并使用 beforelimit 参数进行分页。然后,您可以使用 getTransaction 获取每笔交易的完整详情。对于区块级数据,请使用 getBlockgetBlocks。但是,历史数据的可用性取决于节点类型:标准全节点仅保留最近时隙的有限窗口(通常为几天),而归档节点则存储从创世区块开始的整个账本,仅受磁盘空间限制。公共 RPC 端点通常运行全节点,因此对于较旧的时隙可能会返回错误。为了持续的生产访问,您需要专用的归档端点或索引器。

本指南将介绍具体机制,提供一个可运行的 Node.js 脚本,并讨论实际限制和生产模式。如需快速参考,请参阅 Solana API 指南Solana 网络页面

Solana 上“历史”的含义:全节点与归档节点

在 Solana 上,“历史”数据不是按时间定义的,而是按时隙号定义的。全节点(也称为验证器或 RPC 节点)保留最近时隙的滚动窗口(通常为最近几天),以提供实时查询。此窗口可通过 --limit-ledger-size 参数进行配置,但默认情况下,节点会修剪较旧的时隙以节省磁盘空间。当您查询已修剪的时隙时,节点会返回类似 "Slot X was skipped, or missing due to ledger jump to recent snapshot" 的错误。

另一方面,归档节点存储从创世区块开始的整个账本,仅受 RocksDB 磁盘容量限制。归档节点对于查询早于全节点窗口的数据至关重要。Solana 文档指出,归档节点是“存储从创世区块开始的所有区块的节点”,用于历史查询。返回历史数据的 JSON-RPC 方法包括:getSignaturesForAddressgetTransactiongetBlockgetBlockTimegetBlocksgetTransactionCountgetHighestSnapshotSlot。这些方法在全节点和归档节点上均可用,但它们可以访问的历史深度取决于节点的保留策略。

要深入了解 Solana 节点如何管理账本存储,请参阅 Solana 验证器文档

  • 全节点:保留最近时隙的有限窗口(例如,最近 2 天)。
  • 归档节点:存储从创世区块开始的整个账本,受磁盘限制。
  • 已修剪的时隙返回错误,如“Slot was skipped, or missing due to ledger jump to recent snapshot”。

用于历史数据的关键 RPC 方法

以下 JSON-RPC 方法是您在 Solana 上查询历史数据的主要工具:

getSignaturesForAddress 返回给定地址的交易签名列表,按从新到旧排序。它接受 before(起始签名,不包含)和 limit(最大 1000)参数进行分页。每个项目包括 signaturesloterrmemoblockTimeconfirmationStatus

getTransaction 通过签名获取单笔交易。它接受 encoding 参数(例如 jsonParsed)和 commitment(例如 confirmed)。响应包括 slotblockTimemeta(包含 errfeepreBalancespostBalancesinnerInstructionslogMessages)和 transaction(包含 messagesignatures)。

getBlock 按区块号返回区块,包括交易和奖励。getBlocks 返回指定范围内的区块号列表。getBlockTime 返回给定区块号的时间戳。getTransactionCount 返回节点处理的总交易数(本身不是历史数据,但有助于了解上下文)。getHighestSnapshotSlot 返回可用快照的最高区块号,这可以指示全节点在没有归档数据的情况下可以服务的最早点。

有关完整参考,请参阅 Solana RPC API 文档

可运行示例:分页浏览地址的历史记录

下面是一个独立的 Node.js 脚本,它向后分页浏览地址的交易历史并获取解析后的交易详情。它使用 fetch API(Node 18+)和公共 RPC 端点(请替换为您自己的端点)。该脚本打印每笔交易的签名、区块号、区块时间和任何错误。

要运行它,请将其保存为 solana-history.js 并使用 node solana-history.js 执行。它将每页获取最多 1000 个签名,然后获取每笔交易,并带有小延迟以避免速率限制。当返回的签名少于限制或发生错误时,脚本停止。

const endpoint = 'https://api.mainnet-beta.solana.com'; // 替换为您的 RPC 端点
const address = 'YourBase58AddressHere'; // 替换为要查询的地址

async function rpcCall(method, params) {
  const response = await fetch(endpoint, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ jsonrpc: '2.0', id: 1, method, params })
  });
  const json = await response.json();
  if (json.error) throw new Error(json.error.message);
  return json.result;
}

async function getHistory() {
  let before = undefined;
  let total = 0;
  while (true) {
    const params = [address, { limit: 1000, before: before }];
    const signatures = await rpcCall('getSignaturesForAddress', params);
    if (signatures.length === 0) break;
    for (const sigInfo of signatures) {
      const tx = await rpcCall('getTransaction', [sigInfo.signature, { encoding: 'jsonParsed' }]);
      console.log(`Signature: ${sigInfo.signature}`);
      console.log(`Slot: ${sigInfo.slot}, BlockTime: ${sigInfo.blockTime}`);
      if (tx && tx.meta) {
        console.log(`Error: ${tx.meta.err || 'None'}`);
        console.log(`Fee: ${tx.meta.fee}`);
      }
      total++;
      await new Promise(resolve => setTimeout(resolve, 200)); // 速率限制礼貌
    }
    before = signatures[signatures.length - 1].signature;
    if (signatures.length < 1000) break;
  }
  console.log(`Total transactions fetched: ${total}`);
}

getHistory().catch(err => console.error(err));

预期的 JSON 结构和字段含义

当您调用 getSignaturesForAddress 时,结果数组中的每个项目如下所示:

signature 是 base58 编码的交易签名。slot 是交易被包含的区块号。err 在交易成功时为 null,否则为描述错误的对象。memo 是可选的备忘录字符串。blockTime 是区块的 Unix 时间戳。confirmationStatus 表示确认级别(例如 'finalized')。

当您调用 getTransaction 时,响应包括 slotblockTimemeta 对象。meta 包含 err(成功时为 null)、fee(以 lamports 为单位)、preBalancespostBalances(交易前后的余额数组)、innerInstructionslogMessagestransaction 对象包含 message(包含 accountKeysinstructions 等)和 signatures 数组。

要验证数据,您可以使用 getBlockTimeblockTime 与区块号进行交叉检查,或比较 preBalancespostBalancesfee 以确保一致性。

  • getSignaturesForAddress 结果项:{ signature, slot, err, memo, blockTime, confirmationStatus }
  • getTransaction 结果:{ slot, blockTime, meta: { err, fee, preBalances, postBalances, innerInstructions, logMessages }, transaction: { message, signatures } }

常见故障及解决方法

查询历史数据时,您可能会遇到几个常见错误:

  1. Slot was skipped, or missing due to ledger jump to recent snapshot:这表示节点已修剪该区块号。解决方法:使用归档节点或专用的历史数据提供商。公共 RPC 通常历史有限。

  1. 速率限制:公共端点可能返回 HTTP 429 或 JSON-RPC 错误,如 "Too many requests"。解决方法:实现指数退避,降低请求频率,或使用具有更高限制的专用端点。OnFinality 提供 专用 API 服务,具有可扩展的速率限制。

  1. 响应大小:对于复杂交易,使用 jsonParsedgetTransaction 可能返回较大的响应。解决方法:如果不需要解析后的指令,请使用 json 编码,或通过使用 getSignaturesForAddress 然后有选择地使用 getTransaction 仅获取特定字段。

  1. 区块号间隙:如果区块被跳过,getBlocks 可能返回间隙。解决方法:通过检查 null 响应来优雅地处理缺失的区块号。

  1. 非归档端点:如果您需要早于几天的数据,请确保您的端点是归档节点。使用 getHighestSnapshotSlot 检查可用的最早区块号。

权衡与限制

通过 RPC 查询历史数据具有固有的权衡。首先,全节点的保留窗口不是一个基准;它取决于节点配置、磁盘空间和修剪设置。例如,OnFinality 的端点可能具有不同的保留策略,因此请始终验证您的用例的实际可用性。

其次,分页浏览地址的完整历史可能很慢且资源密集,尤其是对于高活动地址。每页 1000 个签名需要 1000 次单独的 getTransaction 调用,这可能会触发速率限制并花费大量时间。对于生产环境,请考虑使用索引器或提供批量访问的数据服务。

第三,公共 RPC 端点并非为大量历史查询而设计。它们是共享的且有限速。对于持续的生产历史数据,您应该使用专用或归档端点,或使用像 OnFinality 的通用历史区块链数据访问 这样的索引器。

最后,JSON-RPC 方法返回原始数据;您必须自行处理解析和存储。对于大规模分析,数据仓库或索引器更高效。

  • 全节点保留是可配置的,不是固定基准。
  • 对于大型历史记录,分页速度较慢;请考虑使用索引器。
  • 公共 RPC 有限速;生产环境请使用专用端点。
  • 原始 RPC 数据需要额外处理才能进行分析。

生产模式与后续步骤

对于需要可靠历史数据的生产应用程序,请考虑以下模式:

  1. 使用归档端点:如果您需要完整历史记录,请订阅归档 RPC 服务。OnFinality 提供 Solana RPC 端点,具有可配置的保留策略和高可用性。

  1. 缓存和索引:不要重复查询 RPC,而是使用 getSignaturesForAddressgetTransaction 构建本地交易索引,并将其存储在数据库中。这减少了 RPC 负载并加快了查询速度。

  1. 使用 Webhooks 或流式传输:对于实时数据,使用 Solana 的 WebSocket 订阅来捕获交易,并存储以供后续分析。

  1. 考虑第三方索引器:像 Helius 或 QuickNode 这样的服务提供历史数据 API,抽象了复杂性。但是,它们可能有自己的限制和成本。

  1. 监控速率限制:实现具有指数退避的健壮重试逻辑,并遵守 Retry-After 标头(如果存在)。

如需更多指导,请探索 Solana API 指南定价页面 以选择适合您需求的计划。另请阅读 访问历史区块链数据 以获得更广泛的视角。

永远不用担心基础设施

OnFinality 消除了 DevOps 的繁重工作,让您能够更聪明、更快地构建。

开始