以太坊 JSON-RPC 提供两个主要的区块获取方法——eth_getBlockByNumber 和 eth_getBlockByHash——每个方法都接受一个布尔型 fullTx 参数,用于在完整交易对象数组和 32 字节交易哈希数组之间进行选择。紧凑的哈希形式体积小得多,对于按哈希单独获取交易的索引器来说是正确的默认选择,而完整形式则适用于一次性区块处理。交易数量方法(eth_getBlockTransactionCountByNumber 和 eth_getBlockTransactionCountByHash)仅返回数量,从而无需传输区块主体即可进行低成本覆盖检查。旧版叔块/ommer 方法(eth_getUncleByBlockNumberAndIndex、eth_getUncleCountByBlockNumber、eth_getUncleByBlockHashAndIndex、eth_getUncleCountByBlockHash)对于合并后的区块返回 null 或 0x0,因为以太坊区块不再包含叔块,但解析器仍必须处理这些字段,以兼容合并前的历史区块和一些非以太坊 EVM 链。本文提供可运行的 Node.js 示例、可复现的测量方法以及通过 RPC 进行区块获取的故障排除手册。
区块获取方法与 fullTx 布尔值
以太坊 JSON-RPC 规范定义了两种主要的区块获取方法:eth_getBlockByNumber(blockParameter, fullTx) 和 eth_getBlockByHash(blockHash, fullTx)。两者都接受一个布尔型 fullTx 参数,用于控制区块交易列表的表示形式。当 fullTx 为 true 时,响应包含完整交易对象数组;当为 false 时,则包含 32 字节交易哈希数组。这个布尔值是控制响应大小和下游处理逻辑的最重要参数。
哈希形式体积小得多,因为每个交易哈希恰好是 32 字节,而完整交易对象包含 from、to、value、gas、gasPrice、input、v、r、s 等字段,还可能包含 accessList 或 maxFeePerGas。对于包含数百笔交易的区块,有效载荷大小的差异可能达到几个数量级。将按哈希单独获取交易的索引器应默认使用 fullTx=false,以避免重复传输相同数据。
两种形式是一致的:紧凑形式中的每个哈希都对应一个完整对象,eth_getTransactionByHash 返回的字段与之相同。然而,在重组期间,由给定哈希标识的区块可能被替换,交易列表也可能发生变化。固定数字区块号可使读取可复现,因为区块号是稳定的坐标,而 'latest' 则是移动的目标。
- eth_getBlockByNumber(blockParameter, fullTx) — blockParameter 可以是十六进制区块号,也可以是命名标签:'latest'、'pending'、'safe'、'finalized'。
- eth_getBlockByHash(blockHash, fullTx) — blockHash 是 32 字节哈希;fullTx 控制交易表示形式。
- fullTx=true 返回完整交易对象数组;fullTx=false 返回 32 字节交易哈希数组。
- 对于按哈希单独获取交易的索引器,哈希形式是正确的默认选择。
命名区块标签:latest、pending、safe 和 finalized
blockParameter 参数除了接受数字区块号外,还接受命名标签。'latest' 指节点已知的最新区块,'pending' 指尚未最终确定的区块(可能不作为规范区块存在),'safe' 指在共识规则下已合理的区块,'finalized' 指在共识规则下不可逆的区块。在权益证明下,'safe' 和 'finalized' 具有共识层定义的特定含义:'safe' 是最新的合理检查点,而 'finalized' 是最新的最终确定检查点。
'safe' 与 'finalized' 之间的差异对于需要不同保证级别的应用很重要。'safe' 区块极不可能发生重组,但不保证不可逆;'finalized' 区块在协议的最终性机制下被视为不可逆。对于可复现的读取,固定数字区块号更可取,因为它消除了节点在请求时认为哪个区块是 'latest' 的歧义。
并非每个客户端都支持 'safe' 和 'finalized' 标签。此行为因客户端而异。如果客户端不支持这些标签,它可能会返回错误或回退到 'latest'。在生产环境中依赖这些标签之前,请务必查阅客户端文档并在你的端点上进行测试。
- 'latest' — 节点已知的最新区块;可能在请求之间发生变化。
- 'pending' — 尚未最终确定的区块;可能不作为规范区块存在。
- 'safe' — PoS 下最新的合理检查点;极不可能重组但并非不可逆。
- 'finalized' — PoS 下最新的最终确定检查点;被视为不可逆。
- 固定数字区块号可使读取可复现并消除歧义。
用于低成本覆盖检查的交易数量方法
eth_getBlockTransactionCountByNumber 和 eth_getBlockTransactionCountByHash 方法仅返回区块中的交易数量,而不传输区块主体。这对于低成本覆盖检查非常有用:索引器可以验证已处理的区块交易数量是否符合预期,而无需下载完整交易列表。计数以十六进制字符串返回,与其他 JSON-RPC 数值一致。
当与区块获取的哈希形式结合使用时,这些方法尤其有价值。流水线可以使用 fullTx=false 获取区块以获取交易哈希,然后使用 eth_getBlockTransactionCountByNumber 确认计数与哈希数组的长度匹配。如果计数不一致,则区块可能已发生重组,或者提供者可能截断了响应。
对于批量收据获取,eth_getBlockReceipts 在单次调用中返回区块的所有收据,这比逐个获取收据更高效。eth_getBlockReceipts 批量收据 一文详细介绍了该方法。
- eth_getBlockTransactionCountByNumber(blockParameter) — 返回由编号或标签标识的区块的交易数量。
- eth_getBlockTransactionCountByHash(blockHash) — 返回由哈希标识的区块的交易数量。
- 使用这些方法验证交易哈希数组的长度是否与预期计数匹配。
- 结合 eth_getBlockReceipts 实现高效的批量收据获取。
旧版叔块方法与合并后的行为
旧版叔块/ommer 方法包括 eth_getUncleByBlockNumberAndIndex、eth_getUncleCountByBlockNumber、eth_getUncleByBlockHashAndIndex 和 eth_getUncleCountByBlockHash。这些方法是为工作量证明以太坊设计的,当时区块可以引用叔块(也称为 ommer),这些区块有效但不属于规范链。自 2022 年 9 月合并以来,以太坊区块不再包含叔块,因此这些方法对于合并后的区块返回 null 或 0x0。
然而,解析器仍必须处理这些字段,因为合并前的历史区块和一些非以太坊 EVM 链确实存在叔块。Block 对象包含 'uncles' 数组和 'sha3Uncles' 字段。'uncles' 数组包含叔块的哈希,'sha3Uncles' 是叔块列表的 Keccak-256 哈希。空的 'uncles' 数组本身并不能证明区块是合并后的;要确定这一点,请固定到合并区块号以上的区块。以太坊 execution-apis 模式记录了合并后叔块的移除。
叔块方法已弃用,应视为只读的旧版接口,绝不能作为新共识数据的来源。对于合并前的区块,它们对于历史分析仍然有用。对于合并后的区块,它们实际上是无操作。以太坊 JSON-RPC 规范 记录了这些方法及其参数。
- eth_getUncleByBlockNumberAndIndex(blockParameter, index) — 返回给定索引处的叔块,如果没有则返回 null。
- eth_getUncleCountByBlockNumber(blockParameter) — 返回区块中的叔块数量,如果没有则返回 0x0。
- eth_getUncleByBlockHashAndIndex(blockHash, index) — 返回由哈希标识的区块在给定索引处的叔块。
- eth_getUncleCountByBlockHash(blockHash) — 返回由哈希标识的区块的叔块数量。
- 合并后的区块返回 null 或 0x0;合并前的区块和一些非以太坊 EVM 链可能存在叔块。
可运行的 Node.js 示例:比较完整表示与哈希表示
以下 Node.js 示例以两种表示形式获取同一区块,断言哈希数组长度等于完整数组长度,并验证每个哈希与对应完整交易的哈希匹配。它使用 Node.js 18 及更高版本中内置的 fetch API。将 RPC_URL 替换为你的端点,例如 OnFinality 的 以太坊 RPC 节点。
此示例演示了两种形式之间的一致性,并为构建使用紧凑形式进行存储、使用完整形式进行处理的索引器提供了基础。断言逻辑可以扩展以检查其他字段,例如区块号和父哈希,以检测重组。
const RPC_URL = 'https://your-ethereum-rpc-endpoint';
async function rpcCall(method, params) {
const response = await fetch(RPC_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ jsonrpc: '2.0', id: 1, method, params })
});
const data = await response.json();
if (data.error) throw new Error(data.error.message);
return data.result;
}
async function compareBlockRepresentations(blockNumberHex) {
const [blockWithHashes, blockWithFullTx] = await Promise.all([
rpcCall('eth_getBlockByNumber', [blockNumberHex, false]),
rpcCall('eth_getBlockByNumber', [blockNumberHex, true])
]);
const hashes = blockWithHashes.transactions;
const fullTxs = blockWithFullTx.transactions;
console.log('Block number:', blockWithHashes.number);
console.log('Hash array length:', hashes.length);
console.log('Full tx array length:', fullTxs.length);
if (hashes.length !== fullTxs.length) {
throw new Error('Length mismatch: ' + hashes.length + ' vs ' + fullTxs.length);
}
for (let i = 0; i < hashes.length; i++) {
if (hashes[i] !== fullTxs[i].hash) {
throw new Error('Hash mismatch at index ' + i + ': ' + hashes[i] + ' vs ' + fullTxs[i].hash);
}
}
console.log('All hashes match corresponding full transaction hashes.');
return { hashes, fullTxs };
}
// Example: fetch a specific block by number
compareBlockRepresentations('0x112A880').catch(console.error);可复现方法:比较不同表示形式的区块大小
要测量完整表示与哈希表示之间的大小差异,你可以使用 fullTx=true 和 fullTx=false 获取同一区块,将每个响应序列化为 JSON,并比较字节长度。此方法在不同端点和区块号之间是可复现的。结果会因区块而异,因此对多个区块进行采样并将结果记录在表格中很有用。
以下 Node.js 代码片段以两种表示形式获取区块,测量 JSON 字符串长度,并打印比率。你可以针对自己的端点运行此代码,并填写下面的结果表。对于给定的区块和端点,测量是确定性的,但绝对大小取决于交易数量以及每笔交易输入数据的大小。
使用数字区块号而不是 'latest' 以确保可复现性。如果你要在不同提供者之间进行比较,请使用相同的区块号和相同的 fullTx 设置。请注意,某些提供者可能会限制或截断极大的区块,这可能会影响测量。
async function measureBlockSize(blockNumberHex) {
const [blockWithHashes, blockWithFullTx] = await Promise.all([
rpcCall('eth_getBlockByNumber', [blockNumberHex, false]),
rpcCall('eth_getBlockByNumber', [blockNumberHex, true])
]);
const hashesJson = JSON.stringify(blockWithHashes);
const fullTxJson = JSON.stringify(blockWithFullTx);
const hashesBytes = Buffer.byteLength(hashesJson, 'utf8');
const fullTxBytes = Buffer.byteLength(fullTxJson, 'utf8');
console.log('Block:', blockNumberHex);
console.log('Hashes form bytes:', hashesBytes);
console.log('Full tx form bytes:', fullTxBytes);
console.log('Ratio (full/hashes):', (fullTxBytes / hashesBytes).toFixed(2));
return { blockNumberHex, hashesBytes, fullTxBytes, ratio: fullTxBytes / hashesBytes };
}
// Example: measure a block
measureBlockSize('0x112A880').catch(console.error);供读者测量的结果表
使用下表记录你自己的测量结果。针对你的端点对多个区块号运行测量代码片段,包括低活动和高活动区块的混合。表格列记录区块号、交易数量、哈希形式的字节大小、完整交易形式的字节大小以及比率。这将帮助你了解特定用例的存储和带宽权衡。
由于结果取决于区块和提供者,因此没有单一的正确值。目标是为你自己的基础设施建立基线。如果你使用 OnFinality 的 API 服务,你可以针对你的专用端点运行这些测量。有关定价考虑,请参阅 RPC 定价。
- 区块号 | 交易数量 | 哈希形式字节 | 完整交易形式字节 | 比率(完整/哈希)
- 0x112A880 | (填写) | (填写) | (填写) | (填写)
- 0x112A881 | (填写) | (填写) | (填写) | (填写)
- 0x112A882 | (填写) | (填写) | (填写) | (填写)
- 根据需要为其他区块添加行。
通过 RPC 进行区块获取的故障排除
当区块获取失败或返回意外结果时,原因通常是几个常见问题之一。首先,检查区块参数格式是否正确:数字区块号必须是十六进制编码字符串(例如 '0x112A880'),命名标签必须小写。其次,验证 fullTx 布尔值是布尔值,而不是字符串。第三,确认区块存在于你的端点所服务的链上;高于链尖的区块号将返回 null。
如果你对应该存在的区块收到 null 结果,节点可能落后于链尖。检测 RPC 节点落后于链尖 一文介绍了诊断滞后的方法。如果你使用 'safe' 或 'finalized' 标签并收到错误,客户端可能不支持这些标签;请回退到 'latest' 或数字区块号。如果 eth_getBlockTransactionCountByNumber 返回的交易数量与交易数组的长度不匹配,则区块可能已发生重组,或者提供者可能截断了响应。
对于重组相关问题,以太坊区块重组检测与 RPC 深度 一文提供了更深入的处理。有关索引器对账,请参阅 逐块 EVM 索引器对账。
- 确保区块参数是十六进制编码字符串或有效的命名标签。
- 验证 fullTx 是布尔值,而不是字符串。
- 检查区块是否存在于你的端点所服务的链上。
- 如果 'safe' 或 'finalized' 失败,客户端可能不支持这些标签。
- 交易数量不匹配可能表明发生了重组或提供者截断。
限制与权衡
通过 JSON-RPC 进行区块获取存在若干限制。某些提供者会限制或截断极大的区块,这可能导致交易列表不完整。并非每个客户端都支持 'safe' 和 'finalized' 标签;此行为因客户端而异。叔块方法已弃用,应视为只读的旧版接口,绝不能作为新共识数据的来源。
完整交易表示形式很方便,但在带宽和存储方面可能代价高昂。哈希表示形式紧凑,但需要额外调用来获取交易详情。选择取决于你的用例:如果你需要立即处理区块中的每笔交易,完整形式可能更简单;如果你正在构建单独存储交易的索引器,哈希形式更高效。
有关以太坊 RPC 节点设置和使用的更广泛概述,请参阅 以太坊 RPC 节点指南(RPC Assistant)。有关网络和协议主题的更多文章,请访问 OnFinality Learn 中心。
- 提供者的限制或截断可能影响极大的区块。
- 'safe' 和 'finalized' 标签并非普遍支持。
- 叔块方法已弃用,合并后返回 null 或 0x0。
- 完整交易形式会增加带宽和存储成本。
- 哈希形式需要额外调用来获取交易详情。
构建稳健区块获取流水线的后续步骤
要构建稳健的区块获取流水线,首先固定数字区块号以实现可复现的读取。将哈希形式作为索引器的默认选择,仅在需要时获取完整交易。通过比较 eth_getBlockTransactionCountByNumber 返回的交易数量与交易数组的长度来实现一致性检查。通过监控父哈希并使用适合你应用的确认深度来处理重组。
对于高吞吐量索引,考虑使用 eth_getBlockReceipts 批量获取收据,并将其与哈希形式结合以最小化有效载荷大小。如果你运行自己的节点,请通过监控区块号确保它不落后于链尖。对于托管基础设施,OnFinality 的 以太坊 RPC 节点 和 API 服务 提供支持这些方法的端点。
最后,在解析器中保留已弃用的叔块方法以实现历史兼容性,但不要依赖它们获取新的共识数据。以太坊 execution-apis 仓库记录了模式以及合并后叔块的移除。有关定价和计划详情,请参阅 RPC 定价。
- 固定数字区块号以实现可复现的读取。
- 索引器默认使用 fullTx=false;仅在需要时获取完整交易。
- 使用 eth_getBlockTransactionCountByNumber 进行低成本覆盖检查。
- 监控父哈希和确认深度以处理重组。
- 保留叔块方法以实现历史兼容性,但不要依赖它们获取新数据。