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

以太坊 Blob 交易的 RPC 实现:EIP-4844 Type-3 机制详解

一份实用的 RPC 层指南,涵盖 EIP-4844 type-3 blob 交易的发送、读取与对账,包括 blob gas 计费与 sidecar 可用性。

TL;DR

EIP-4844 blob 交易是一种 type-0x03 信封,可携带一个或多个 blob,但 blob 数据本身并不属于执行负载;交易通过版本化哈希(KZG 承诺)对其作出承诺,收据则记录 blobGasUsed 和 blobGasPrice。Blob 由独立的 blob 基础费用定价,该费用通过超额 blob gas 机制更新,因此执行 gas 和 blob gas 可以各自便宜或昂贵。通过 JSON-RPC 读取 blob 交易需要调用 eth_getTransactionByHash 获取 blobVersionedHashes,并通过收据获取 blobGasUsed 和 blobGasPrice;sidecar(blobs、commitments、proofs)随原始交易传输,但不存储在链上,并在保留窗口后被修剪。本文展示如何针对单一端点发送和验证 type-3 交易、显式计算总 blob 费用,并区分提供商特定的 sidecar 可用性与协议层保留。还涵盖 sidecar 被剥离、收据不完整、Dencun 之前的节点以及忽略 blob 维度的费用估算器等问题的排查。

Type-3 交易信封与 Blob 承诺机制

EIP-4844 blob 交易是一种带有新类型标识符 0x03 的普通交易信封,可携带一个或多个 blob。Blob 数据本身并不属于执行负载;相反,交易通过版本化哈希对 blob 作出承诺,该哈希是对 blob 多项式的 KZG 承诺。这意味着仅读取执行字段的 RPC 调用者无法判断是否发布了 blob 或花费了多少。权威规范是 EIP-4844: Shard Blob Transactions,JSON-RPC 方法语义定义在 以太坊 execution-apis 规范中。

读者必须识别的 type-3 字段包括 maxFeePerBlobGas、blobVersionedHashes 以及随原始交易传输但不存储在链上的 sidecar(blobs、commitments、proofs)。Sidecar 由共识层传播和保留,并在有界窗口后被修剪,而不是作为以太坊状态的一部分保留。因此,通过 JSON-RPC 读取 blob 内容是一种可用性查询,对于旧区块可能合法地失败,而区块本身完全可以查询。你必须区分“此端点不提供 blob sidecar”和“此 blob 已过期”。

由于 blob 数据不在执行负载中,区块承诺了它并不存储的数据。这就是为什么在区块中留存下来的是 blobVersionedHashes 而非 blob 字节。有关 OnFinality 如何暴露以太坊网络的更广泛视图,请参阅 /en/networks/eth

  • Type 0x03 信封:执行字段加上 blob 特定字段。
  • blobVersionedHashes:每个 blob 一个,由 KZG 承诺派生。
  • Sidecar:blobs、commitments、proofs——不存储在链上。
  • 收据:blobGasUsed 和 blobGasPrice 记录实际支付的 blob 费用。

二维费用市场:执行 Gas 与 Blob Gas

执行 gas 由 baseFee 加上优先费小费定价,而 blob 由独立的 blob 基础费用定价,该费用通过其自身的超额 blob gas 机制更新。这意味着交易可以同时在执行 gas 上便宜而在 blob gas 上昂贵。仅读取 eth_feeHistory 的 baseFeePerGas 的费用估算器会低估或完全忽略 blob 费用。有关执行费用估算,请参阅 以太坊费用历史与费用估算

Blob 费用计算是明确的:总 blob 费用等于 blobGasUsed 乘以 blobGasPrice,而 blobGasUsed 等于 blob 数量乘以协议常量 GAS_PER_BLOB(131072)。你应该对照自己的收据进行验证,而不是硬编码。并非所有实现都通过 eth_feeHistory 返回 blob 基础费用;一些提供商通过 eth_blobBaseFee 暴露它,或将其包含在区块头中作为 blobBaseFee。此行为有文档记录 / 因提供商而异。

发送 type-3 交易时,除了 maxFeePerGas 和 maxPriorityFeePerGas 之外,还必须设置 maxFeePerBlobGas。如果 maxFeePerBlobGas 太低,交易将被拒绝或卡住。Blob 费用市场是独立的,因此 blob 需求激增不一定影响执行 gas 价格。

  • 执行 gas:baseFee + 优先费小费。
  • Blob gas:来自超额 blob gas 的独立 blob 基础费用。
  • GAS_PER_BLOB = 131072(协议常量)。
  • type-3 交易必须设置 maxFeePerBlobGas。

通过 JSON-RPC 发送 Type-3 Blob 交易

要发送 blob 交易,你需要构造带有 blob sidecar 的 type-3 交易,并通过 eth_sendRawTransaction 提交。原始交易必须包含 blobs、commitments 和 proofs。节点将验证 KZG 承诺并将 sidecar 传播到共识层。execution-apis 规范定义 eth_sendRawTransaction 接受已签名的原始交易并返回交易哈希。有关生产端点,请参阅 /en/networks/eth

下面是一个可运行的 Node.js 脚本,使用 ethers.js 发送 type-3 交易。它假设你有一个提供商 URL 和一个有资金的签名者。它构造一个带有一个 blob 的 blob 交易,设置 maxFeePerBlobGas,并发送它。脚本打印交易哈希,然后轮询收据以显示 blobGasUsed 和 blobGasPrice。

注意 sidecar 不存储在链上;脚本仅发送它。发送后,你可以使用 eth_getTransactionByHash 和 eth_getTransactionReceipt 按哈希验证交易。收据将包含 blobGasUsed 和 blobGasPrice,你可以用它们计算总 blob 费用。

const { ethers } = require('ethers');

async function main() {
  const provider = new ethers.JsonRpcProvider(process.env.RPC_URL);
  const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider);

  // Example blob data (32 bytes)
  const blobData = '0x' + '00'.repeat(32);

  const tx = {
    to: wallet.address,
    value: 0,
    maxFeePerGas: ethers.parseUnits('10', 'gwei'),
    maxPriorityFeePerGas: ethers.parseUnits('1', 'gwei'),
    maxFeePerBlobGas: ethers.parseUnits('10', 'gwei'),
    blobs: [{ data: blobData }],
    kzg: undefined // ethers v6 handles KZG via provider
  };

  const sent = await wallet.sendTransaction(tx);
  console.log('Transaction hash:', sent.hash);

  const receipt = await sent.wait();
  console.log('blobGasUsed:', receipt.blobGasUsed.toString());
  console.log('blobGasPrice:', receipt.blobGasPrice.toString());
  const totalBlobFee = receipt.blobGasUsed * receipt.blobGasPrice;
  console.log('Total blob fee (wei):', totalBlobFee.toString());
}

main().catch(console.error);

按哈希读取与对账 Type-3 交易

实际有效的读取路径是:使用 eth_getTransactionByHash 按哈希检索交易以获取 blobVersionedHashes 和 blobGasUsed/blobGasPrice,然后检索收据并读取 blobGasUsed 和 blobGasPrice 以计算实际支付的 blob 费用。你还应检查端点是否暴露 blob-sidecar 方法;这有文档记录 / 因提供商而异。一些提供商提供 eth_getBlobSidecar 或 eth_getBlobs,但这些不属于核心 execution-apis 规范。

下面是一个可运行的 Node.js 脚本,它接受一个已知的 type-3 交易哈希,获取交易和收据,分类交易类型,提取版本化哈希,并打印一个表格,包含版本化哈希、blob 数量、blobGasUsed、blobGasPrice、推导出的总 blob 费用以及执行费用。它仅使用标准 JSON-RPC 方法。

该脚本使用 eth_getTransactionByHash 和 eth_getTransactionReceipt。它将执行费用计算为 gasUsed 乘以 effectiveGasPrice。它还检查 blobVersionedHashes 和 blobGasUsed 的存在以确认交易是 type-3。如果提供商剥离了 sidecar 字段,脚本仍将工作,因为它只读取链上字段。

const { ethers } = require('ethers');

async function inspectBlobTx(txHash) {
  const provider = new ethers.JsonRpcProvider(process.env.RPC_URL);

  const tx = await provider.getTransaction(txHash);
  if (!tx) throw new Error('Transaction not found');

  const receipt = await provider.getTransactionReceipt(txHash);
  if (!receipt) throw new Error('Receipt not found');

  const type = tx.type;
  console.log('Transaction type:', type);

  const versionedHashes = tx.blobVersionedHashes || [];
  const blobCount = versionedHashes.length;
  const blobGasUsed = receipt.blobGasUsed ? receipt.blobGasUsed.toString() : 'N/A';
  const blobGasPrice = receipt.blobGasPrice ? receipt.blobGasPrice.toString() : 'N/A';

  let totalBlobFee = 'N/A';
  if (receipt.blobGasUsed && receipt.blobGasPrice) {
    totalBlobFee = (receipt.blobGasUsed * receipt.blobGasPrice).toString();
  }

  const executionFee = (receipt.gasUsed * receipt.effectiveGasPrice).toString();

  console.log('\n--- Blob Transaction Report ---');
  console.log('Versioned Hashes:');
  versionedHashes.forEach((h, i) => console.log(`  ${i}: ${h}`));
  console.log('Blob Count:', blobCount);
  console.log('blobGasUsed:', blobGasUsed);
  console.log('blobGasPrice:', blobGasPrice);
  console.log('Total Blob Fee (wei):', totalBlobFee);
  console.log('Execution Fee (wei):', executionFee);
}

inspectBlobTx(process.argv[2]).catch(console.error);

Blob 费用计算与对照收据验证

总 blob 费用计算为 blobGasUsed 乘以 blobGasPrice。blobGasUsed 是 blob 数量乘以 GAS_PER_BLOB(131072)。你应该对照自己的收据进行验证,而不是硬编码。例如,如果你的收据显示 blobGasUsed = 131072 且 blobGasPrice = 1000000000 wei,则总 blob 费用为 131072 * 1000000000 = 131072000000000 wei。这与执行费用分开,执行费用是 gasUsed 乘以 effectiveGasPrice。

要验证,请获取收据并检查 blobGasUsed 是否等于版本化哈希数量乘以 131072。如果不是,提供商可能返回了不完整数据。还要检查 blobGasPrice 不为 null。一些提供商返回 blobGasUsed 但省略 blobGasPrice,这表明索引不完整。这是提供商特定的问题,而非协议问题。

Blob 基础费用根据超额 blob gas 更新,超额 blob gas 是前几个区块中使用的总 blob gas 相对于目标的函数。确切公式在 EIP-4844 中。如果提供商暴露了它,你可以从最新区块头读取当前 blob 基础费用,或者如果可用则使用 eth_blobBaseFee。同样,这因提供商而异。

  • 总 blob 费用 = blobGasUsed * blobGasPrice。
  • blobGasUsed = blobCount * 131072。
  • 对照版本化哈希数量验证 blobGasUsed。
  • 检查 blobGasPrice 不为 null;如果为 null,提供商索引不完整。

KZG 承诺与点评估验证

KZG 承诺是一种多项式承诺,允许验证者在不持有 blob 的情况下检查 blob 数据是否与一个小承诺匹配。这就是区块可以承诺它不存储的数据的原因。承诺是椭圆曲线上的一个点,版本化哈希由它派生。版本化哈希是出现在交易和区块中的内容。

点评估验证是读者对带外获得的数据执行的密码学操作,而不是 JSON-RPC 方法。要验证 blob,你需要 blob 数据、承诺和证明。如果可用,你可以从 sidecar 获取这些,或从共识层节点获取。验证本身使用 KZG 库,而非 JSON-RPC。execution-apis 规范没有定义点评估的方法。

如果你需要验证 blob 数据,你必须从保留它的来源获取 sidecar。Sidecar 在保留窗口后被修剪,因此对于旧 blob,你可能需要依赖归档共识层数据。这是保留窗口问题,而非永久保证。

  • KZG 承诺:对 blob 多项式的小承诺。
  • 版本化哈希:由承诺派生,存储在交易中。
  • 点评估:密码学验证,而非 JSON-RPC。
  • Sidecar 保留:有界窗口,非永久。

结果表:端点对 Type-3 字段和 Sidecar 方法的行为

使用下表记录你自己端点的行为。通过针对已知 type-3 交易哈希运行检查脚本来填写值。这将帮助你区分提供商特定的限制与协议层行为。

对于每一行,注意字段是存在、为 null 还是缺失。对于 sidecar 方法,检查端点是否支持 eth_getBlobSidecar、eth_getBlobs 或类似方法。为你的提供商记录结果。

  • 交易类型:0x03 存在?
  • blobVersionedHashes:存在?数量?
  • 收据中的 blobGasUsed:存在?值?
  • 收据中的 blobGasPrice:存在?值?
  • Sidecar 方法(eth_getBlobSidecar):支持?
  • Blob 基础费用方法(eth_blobBaseFee):支持?

排查常见的 Type-3 RPC 故障

提供商返回剥离了 sidecar 字段的 type-3 交易很常见。交易仍将具有 blobVersionedHashes,但 blobs、commitments 和 proofs 将缺失。这是预期的,因为 sidecar 不存储在链上。如果你需要 sidecar,你必须查询共识层节点或明确提供 blob sidecar 的提供商。这有文档记录 / 因提供商而异。

收据中 blobGasUsed 存在但 blobGasPrice 为 null 表明提供商的索引不完整。如果提供商未完全实现 EIP-4844 收据字段,就可能发生这种情况。在这种情况下,你无法仅从收据计算 blob 费用。你可能需要从区块头的 blobBaseFee 和交易的 maxFeePerBlobGas 推导,但实际的 blobGasPrice 由协议决定。

由于节点不是 Dencun 之后而拒绝 type-3 负载的 eth_sendRawTransaction 将返回类似“transaction type not supported”的错误。确保你的节点运行 Dencun 之后的客户端版本。同样,静默忽略 blob 维度的费用估算器会低估交易。始终显式设置 maxFeePerBlobGas。有关交易池行为的更多信息,请参阅 以太坊交易池与 txpool 命名空间

  • Sidecar 被剥离:预期;sidecar 不在链上。
  • blobGasPrice 为 null:提供商索引不完整。
  • Type-3 被拒绝:节点不是 Dencun 之后。
  • 费用估算器忽略 blob 费用:手动设置 maxFeePerBlobGas。

限制、权衡与提供商差异

有文档记录的协议行为:type-3 交易、blob gas、GAS_PER_BLOB、版本化哈希以及收据字段 blobGasUsed 和 blobGasPrice 在 EIP-4844 和 execution-apis 规范中定义。因提供商而异:对 blob sidecar 方法的支持、eth_feeHistory 中 blob 基础费用的可用性以及收据索引的完整性。你必须对照自己的端点进行验证。

Blob 可用性是保留窗口问题,而非永久保证。Sidecar 在有界窗口后被修剪,因此通过 JSON-RPC 读取 blob 内容对于旧区块可能失败,而区块本身完全可以查询。EIP-7594(PeerDAS)改变了 sidecar 的传播方式,因此你必须在网络升级后重新验证。始终检查最新规范和你的提供商文档。

有关批量收据检索,请参阅 eth_getBlockReceipts:一次调用获取批量收据。有关追踪,请参阅 以太坊交易追踪:trace 与 debug。有关端点选择,请参阅 RPC 端点指南(RPC Assistant)

  • 协议定义:type-3 字段、blob gas、收据字段。
  • 提供商特定:sidecar 方法、blob 基础费用可用性。
  • 保留窗口:sidecar 被修剪,非永久。
  • EIP-7594 改变传播;升级后重新验证。

后续步骤:将 Blob 交易集成到你的工作流

要将 blob 交易集成到你的工作流,首先确保你的节点或提供商是 Dencun 之后并支持 type-3 交易。使用检查脚本验证你的端点返回 blobVersionedHashes 和收据 blob 字段。如果你需要发送 blob,请使用像 ethers.js 这样处理 KZG 承诺和 sidecar 构造的库。

对于生产环境,如果你需要读取 blob 数据,考虑使用提供可靠 blob sidecar 可用性的提供商。OnFinality 提供以太坊 RPC 端点;详情请参阅 /en/networks/ethRPC 定价。你还可以探索 OnFinality Learn 中心 获取更多指南,以及 API 服务 获取托管访问。

最后,始终从收据计算总 blob 费用并与你的预期进行比较。监控 blob 基础费用趋势以安排你的 blob 发布时机。记住 blob gas 和执行 gas 是独立的,因此分别优化它们。

  • 验证端点支持 type-3 和收据 blob 字段。
  • 使用 ethers.js 或类似库发送 blob。
  • 如果需要,选择具有 sidecar 可用性的提供商。
  • 监控 blob 基础费用以优化成本。

永远不用担心基础设施

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

开始