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

eth_getRawTransactionByHash:获取原始交易字节

了解 eth_getRawTransactionByHash 如何返回原始 RLP 或类型化信封字节,如何根据交易哈希验证这些字节,以及如何安全地重新广播。

TL;DR

eth_getRawTransactionByHash 以十六进制编码的原始字节形式返回交易:对于传统交易是裸 RLP,对于 EIP-2718 交易是类型化信封。eth_getTransactionByHash 返回的解码对象是节点对这些相同字节的解释,因此两者必须一致。你可以通过对原始字节计算 keccak256 并将结果与请求的交易哈希及节点的 transactionHash 字段进行比较来验证原始字节。通过 eth_sendRawTransaction 重新广播相同的原始字节会保留 nonce 和签名,这在交易从内存池中被丢弃时非常有用。本指南涵盖该方法、信封布局、可运行的 Node.js 验证,以及因客户端和提供商而异的限制。

原始交易字节与解码后的交易对象

以太坊 JSON-RPC 规范将 eth_getRawTransactionByHash 定义为一种方法,给定交易哈希后返回该交易的原始字节。结果是以 0x 开头的十六进制字符串,而不是带有命名字段的 JSON 对象。对于传统交易,这些字节是已签名交易的 RLP 编码;对于 EIP-2718 交易,它们是一个类型字节后跟不透明负载。权威参考是 以太坊 JSON-RPC eth_getRawTransactionByHash 文档。

相比之下,eth_getTransactionByHash 返回一个解码对象,包含 nonce、gasPrice 或 maxFeePerGas、input、v、r 和 s 等字段。该对象是节点对原始字节的解释。原始字节才是规范产物:它们是已签名的内容、在对等网络中传播的内容,以及被包含在区块中的内容。当你需要重新广播、审计或独立验证交易时,仅靠解码对象是不够的,因为它可能省略或规范化细节。

两种表示必须一致。如果你用 keccak256 对原始字节进行哈希,应该得到你请求的交易哈希。如果你解码原始字节,应该恢复出与 eth_getTransactionByHash 报告的相同 nonce、类型和签名字段。不匹配表明端点正在代理、缓存或返回来自不同链或状态的数据。

  • eth_getRawTransactionByHash 返回十六进制编码的字节,而不是 JSON 对象。
  • eth_getTransactionByHash 返回这些字节的解码解释。
  • 原始字节是已签名的产物;解码对象是一种视图。
  • 两者之间的哈希一致性是主要的校验方式。

EIP-2718 类型化信封布局与解析器陷阱

EIP-2718 引入了类型化交易信封:一个类型字节后跟不透明负载。EIP-2718 规范将其定义为 TransactionType || TransactionPayload。传统交易是特殊情况,整个字节串是裸 RLP,没有前导类型字节。这意味着假设每个原始交易都是 RLP 的解析器会误读类型化交易,因为第一个字节是类型标识符,而不是 RLP 列表前缀。

常见的类型字节包括 0x01 表示访问列表交易(EIP-2930),0x02 表示动态费用交易(EIP-1559),0x03 表示 blob 交易(EIP-4844)。对于这些类型,类型字节之后的负载本身是 RLP 编码的,但外层框架不是单个 RLP 列表。要深入了解类型 3 信封和 blob 特定字段,请参阅以太坊 blob 交易与 EIP-4844。

健壮的解码器应检查第一个字节。如果是 0x01、0x02 或 0x03,则将剩余部分视为类型化负载并相应解码。如果是 RLP 列表前缀(如 0xf8 或 0xf9),则将整个字节串视为传统 RLP。这种分支是正确解码器与静默产生垃圾字段的解码器之间的区别。

  • 传统:裸 RLP,无类型字节。
  • 类型 0x01:访问列表(EIP-2930)。
  • 类型 0x02:动态费用(EIP-1559)。
  • 类型 0x03:blob(EIP-4844)。
  • 解码前始终根据前导字节进行分支。

根据交易哈希验证原始字节

交易哈希定义为原始交易字节的 keccak256。这对传统交易和类型化交易都成立:类型字节包含在哈希原像中。因此,最直接的验证是对 eth_getRawTransactionByHash 返回的字节计算 keccak256,并将结果与你请求的哈希进行比较。如果不同,则端点返回的是不同交易或不同链的字节。

第二个检查是与 eth_getTransactionByHash 进行比较。解码对象应报告相同的 transactionHash、相同的类型和相同的 nonce。如果原始字节解码为类型 0x02 交易,但解码对象显示类型 0x00,则存在不一致。这可能发生在配置错误的代理或提供陈旧数据的端点上。

要更广泛地了解交易数据如何在区块和收据中组织,请参阅eth_getBlockReceipts 批量收据。收据确认包含,而原始字节确认确切的已签名负载。

  • keccak256(rawBytes) 必须等于请求的交易哈希。
  • 解码对象的 transactionHash 必须匹配相同的值。
  • 解码对象的类型和 nonce 应与原始字节匹配。
  • 不匹配表明端点被代理、数据陈旧或链错误。

可运行的 Node.js 示例:获取、检测类型并验证哈希

以下 Node.js 脚本使用内置的 fetch API 和 ethers 库进行 keccak256 和 RLP 解码。它获取原始字节,从前导字节检测交易类型,计算 keccak256 哈希,并将其与请求的哈希进行比较。它还获取解码对象以进行第二来源检查。

将 RPC_URL 替换为你的提供商端点。OnFinality 的以太坊网络页面列出了支持的端点,以太坊 RPC 节点指南涵盖了连接基础知识。该脚本刻意保持最小化,以便你可以将其适配到自己的验证工作流中。

const { keccak256 } = require('ethers/lib/utils');
const RPC_URL = process.env.RPC_URL || 'https://api.onfinality.io/public/eth';
const TX_HASH = process.env.TX_HASH;

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

function detectType(rawHex) {
  const first = parseInt(rawHex.slice(2, 4), 16);
  if (first === 0x01) return '0x01 access list (EIP-2930)';
  if (first === 0x02) return '0x02 dynamic fee (EIP-1559)';
  if (first === 0x03) return '0x03 blob (EIP-4844)';
  return 'legacy RLP';
}

(async () => {
  const raw = await rpc('eth_getRawTransactionByHash', [TX_HASH]);
  if (!raw) throw new Error('raw transaction not found');
  const type = detectType(raw);
  const computed = keccak256(raw);
  const decoded = await rpc('eth_getTransactionByHash', [TX_HASH]);
  console.log('requested hash:', TX_HASH);
  console.log('computed hash :', computed);
  console.log('hash match    :', computed.toLowerCase() === TX_HASH.toLowerCase());
  console.log('detected type :', type);
  console.log('decoded type  :', decoded ? decoded.type : 'n/a');
  console.log('decoded nonce :', decoded ? decoded.nonce : 'n/a');
  console.log('decoded hash  :', decoded ? decoded.hash : 'n/a');
})();

无需重新签名即可重新广播原始字节

当交易从内存池中被丢弃时,已签名的原始字节在 nonce 被消耗之前仍然有效。你可以使用 eth_sendRawTransaction 重新广播完全相同的字节。由于字节已包含签名、nonce 和 gas 参数,重新广播会保留所有这些内容。这比重新签名更安全,因为重新签名可能产生不同的哈希并创建竞争交易。

以太坊交易池与内存池 txpool 命名空间解释了如何跟踪待处理交易。待处理的原始交易可能在收据存在之前通过 eth_getRawTransactionByHash 获得,因为它位于池中而非区块中。一旦被挖出,相同的原始字节成为区块的一部分,收据变得可用。

对于 MEV 相关工作流,原始字节也是捆绑提交的输入。请参阅以太坊 MEV 捆绑与 eth_sendBundle了解捆绑如何携带已签名交易。同样的原则适用:原始字节是传播的单位。

  • 重新广播相同的原始字节以保留 nonce 和签名。
  • 除非打算替换交易,否则不要重新签名。
  • 待处理的原始字节可能在收据之前可用。
  • 已挖出的原始字节是区块的一部分;收据确认包含。

可运行的 curl 示例:获取原始字节并比较端点

快速 curl 检查有助于确认端点是否支持 eth_getRawTransactionByHash 以及两个端点是否一致。以下示例向两个 RPC URL 发送相同的请求并打印原始字节。如果一个端点返回错误或不同的字节串,则存在提供商或配置问题。

这也是测试提供商是否将方法限制在某个命名空间或标志之后的有用方法。某些客户端默认禁用原始交易检索。API 服务页面描述了 OnFinality 如何公开 JSON-RPC 端点,RPC 定价涵盖了计划级别的访问。

TX_HASH=0xYOUR_TRANSACTION_HASH

for URL in https://api.onfinality.io/public/eth https://your-other-rpc.example/eth; do
  echo "== $URL =="
  curl -s -X POST "$URL" \
    -H 'Content-Type: application/json' \
    -d '{"jsonrpc":"2.0","id":1,"method":"eth_getRawTransactionByHash","params":["'"$TX_HASH"'"]}' \
    | head -c 400
  echo
done

结果表:在你的端点上测量原始交易检索

由于提供商行为和客户端支持各不相同,最可靠的方法是在你自己的端点上进行测量。使用下表记录你观察到的结果。不要依赖其他环境发布的数字;你的结果取决于你的客户端、提供商和网络条件。

针对你的端点运行 Node.js 或 curl 示例,并填写每一行。目标是记录该方法是否可用、哈希是否验证通过,以及解码对象是否一致。这将模糊的“它能工作”转变为可复现的检查。

  • 端点 URL:你测试的确切 RPC URL。
  • 方法可用:是/否,或如果被限制则记录错误代码。
  • 返回的原始字节:前 20 个十六进制字符以供参考。
  • 检测到的类型:传统、0x01、0x02 或 0x03。
  • keccak256 匹配:与请求哈希比较为 true/false。
  • 解码对象匹配:类型、nonce 和哈希一致为 true/false。
  • 备注:任何提供商特定的标志或命名空间要求。

故障排除:不匹配、缺失字节和被拒绝的重新广播

如果 eth_getRawTransactionByHash 返回 null,该节点可能不知道这笔交易。这可能发生在节点未完全同步、交易在不同链上或哈希不正确的情况下。首先检查哈希长度和前缀,然后确认节点的链 ID 和同步状态。

如果该方法返回诸如“method not found”或“method not enabled”之类的错误,客户端可能将原始交易检索限制在某个标志或命名空间之后。这是因客户端而异的已记录行为。某些客户端仅在交易位于池中或仅对最近区块公开它。请查阅你的客户端文档和提供商支持的方法。

如果原始字节的 keccak256 与请求的哈希不匹配,端点可能正在代理到不同的链或返回缓存数据。与第二个端点进行比较。如果 eth_sendRawTransaction 以 nonce 错误拒绝重新广播,那是正确的拒绝:nonce 已被消耗,意味着原始交易已被挖出或替换。不要将其视为错误。

  • null 结果:检查哈希、链 ID 和同步状态。
  • 方法未找到:客户端可能将方法限制在标志之后。
  • 哈希不匹配:怀疑代理、缓存或链错误。
  • 重新广播时 nonce 拒绝:nonce 已被消耗。
  • 原始字节包含签名;它们不仅仅是消息体。

原始交易检索的限制与权衡

并非每个客户端都默认启用 eth_getRawTransactionByHash。有些需要标志,有些仅在特定命名空间下公开它,有些提供商根本不路由它。这是因客户端和提供商而异的已记录行为,因此在构建依赖它的工作流之前,应验证支持情况。

原始字节是完整的已签名交易,包括签名。它们不是未签名的消息体。如果你需要验证签名,必须解码字节并提取 v、r 和 s,然后恢复签名者。没有这一步,仅凭原始字节无法告诉你谁签了名。

重新广播并不保证包含。如果 nonce 已被消耗,网络将拒绝该交易。如果 gas 价格已变动,交易可能保持待处理。原始字节保留原始参数,这既是它们的优势也是限制:不重新签名就无法调整费用。

  • 客户端支持各异;有些将方法限制在标志之后。
  • 原始字节包含签名,而不仅仅是消息体。
  • 重新广播保留 nonce 和签名,但不保证包含。
  • 费用变更需要重新签名并产生新的哈希。

下一步:将原始字节集成到你的工作流中

首先在交易监控中添加验证步骤。每当你获取交易时,也获取其原始字节并确认 keccak256 哈希。这能及早发现端点不一致。OnFinality Learn 中心有关于交易池、收据和 blob 交易的相关指南。

对于生产系统,考虑将原始字节与解码对象一起缓存。这让你无需再次 RPC 调用即可重新广播,并提供确切已签名负载的审计跟踪。如果你使用多个提供商,请对每个提供商运行结果表以记录支持和行为。

最后,查看你的提供商的方法支持和计划限制。以太坊网络页面和 RPC 定价页面描述了可用的端点和访问层级。将原始交易检索与收据检查和内存池监控相结合,以获得交易生命周期的完整视图。

  • 将 keccak256 验证添加到交易监控中。
  • 缓存原始字节以用于重新广播和审计。
  • 使用结果表测试每个提供商。
  • 与收据和内存池监控相结合。

永远不用担心基础设施

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

开始