Для разработчиков / 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 history20,000KEEP_MINI_BLOCKS, roughly 200 seconds at the 10 ms scheduling target
Receipts and bodies200,000MAX_RECEIPTS, evicted first-in-first-out
Full historynot 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 нет.
  • Чтобы держать собственную историю, подписывайтесь и сохраняйте по мере прихода событий, а не запрашивайте их потом. Поток мини-блоков - источник наивысшей точности; медленный обработчик из него выбрасывают, так что кладите в очередь и обрабатывайте в другом месте.
javascript
// 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 здесь нумеруется в пределах транзакции, так что эта пара не уникальна.

Квитанции через перезапуск

Состояние переживает перезапуск; индекс квитанций - нет

Индекс квитанций и логов держится в памяти. Наблюдался случай, когда квитанция подтверждённого перевода исчезла после перезапуска секвенсера, а изменение баланса сохранилось корректно, - так что квитанция не является надёжной записью о чём бы то ни было. Считайте записью о случившемся обозреватель, а квитанцию узла - быстрым и скоропортящимся подтверждением приёма.