面向开发者 / JSON-RPC
历史与保留
节点是一个 RPC 索引,不是归档。它大致保留两分钟的区块,超出这个范围的查询回答 null 而不是报错,这正是本页出现在你写索引器之前而不是之后的原因。
节点保留什么
| 数据 | 保留 |
|---|---|
| EVM block history | ~2 minuteskeep_evm_blocks() = 120,000 / EVM_BLOCK_MS - 480 blocks at the default. Older blocks return null and their logs become unqueryable |
| Mini-block history | 20,000KEEP_MINI_BLOCKS, roughly 200 seconds at the 10 ms scheduling target |
| Receipts and bodies | 200,000MAX_RECEIPTS, evicted first-in-first-out |
| Full history | not on the nodeit lives in Postgres behind the explorer, fed by the sequencer's spool. This RPC is an index, not an archive |
区块窗口是推导出来的,而不是固定的:keep_evm_blocks() = 120_000 / EVM_BLOCK_MS, 在默认节律下就是 480 个区块。日志随它所在的区块一起被逐出。回执和交易体有自己的先进先出上限, 可能在它们所在的区块消失之前就先消失。
读取由一份已发布的写时复制快照提供服务,从不拿执行锁,这正是一次缓慢的 eth_call 无法拖住区块生产、而一个卡住的区块也无法拖住读取的原因。这就是让这个窗口可以接受的设计:节点 为快速地服务当下而优化,并把历史交给一个为此而建的东西。
这会怎样失败
窗口之外你拿到的是 null,不是错误
对任何已被逐出的东西,eth_getTransactionReceipt、eth_getTransactionByHash 以及每一种区块查找都回答 null。没有 任何东西能区分「从未存在」和「存在过、已被丢弃」。
三种看起来正确、其实不正确的模式:
- 用很长的退避去轮询一份回执。两次尝试之间等得够久,回执就可能在两次轮询之 间被逐出。它在迷你区块约 10 毫秒的调度目标之内就已存在:要尽早、频繁地轮询,而不是耐心地 等待。
- 从区块 0 开始回填。
earliest是最早的仍被保留的区块,而且它会随着你的工作向前移动。一个 追着它跑的回填循环永远到不了一个固定的起点。 - 每晚对一次账本。等到一个每晚运行的作业跑起来时,它想要的每一个区块都已经 没了。
该怎么做
完整的历史是存在的:它从排序器流进浏览器背后的 Postgres,而那个东西就是为当归档而建的。所以:
- 对当下,也就是最近几秒,用节点。订阅和在一个窄范围上的
eth_getLogs正是它快的地方。 - 对过去,读浏览器。它还有调用轨迹,而节点根本不暴露这个,因为没有
debug命名空间。 - 要保留你自己的历史,就订阅并在事件到达时立刻持久化,而不是事后再去查询 它们。迷你区块流是保真度最高的来源;一个慢的处理器会被从流里踢掉,所以把数据推进一个队列, 到别处去处理。
// Right: capture as it happens, persist immediately.
socket.send(JSON.stringify({
jsonrpc: "2.0", id: 1, method: "eth_subscribe",
params: ["logs", { address: MY_CONTRACT }],
}));
// Wrong on this chain: a range that has already scrolled out of the window
// returns an empty array rather than an error, so this looks like "no events".
await client.getLogs({ address: MY_CONTRACT, fromBlock: 0n, toBlock: "latest" });还有,如果你把一个事件库按 (blockNumber, logIndex) 做键,请把交易哈希也加进去: 这里的 logIndex 是按交易逐一编号的,所以那个二元组并不唯一。
跨越一次重启的回执
状态能挺过一次重启;回执索引不能
回执和日志的索引保存在内存里。曾经观察到一笔已被确认的转账,其回执在排序器重启后消失,而 余额变化正确地留了下来:所以一份回执并不是任何东西的持久记录。把浏览器当作发生了什么的 记录,把节点的回执当作一份快速而易腐的收讫回复。