For the complete documentation index, see llms.txt. This page is also available as Markdown.

Reading via the RPC Node

The RPC Node is how a chain or application exposes Espresso finality over a standard EVM JSON-RPC interface. It is a lightweight service (the Espresso Rollup Node Proxy) that sits in front of a chain's existing full node and enforces Espresso finality on the traffic that passes through it.

The proxy is additive: it does not replace or modify the full node. You keep running your full node exactly as before and place the proxy in front of it. Clients that want Espresso finality point their RPC URL at the proxy; everything else continues to work unchanged.

For why this matters and how applications read settled state, see Reading from Espresso. This page covers how the proxy works.

RPC Node architecture

What it does

The proxy intercepts every JSON-RPC request (including batch requests) before forwarding it to the full node, and introduces an Espresso-specific block tag (default: "espresso"):

  • When a request references the Espresso tag, the proxy replaces it with the current L2 block number that has been confirmed by Espresso's HotShot consensus, then forwards the request transparently to the full node.

  • It can also be configured to intercept a standard tag instead — such as "finalized", "safe" or in fact any tag — and back it with Espresso finality, so existing clients that already use that tag get faster finality with no code changes at all.

In either case the proxy resolves the tag to max(espresso finalized, eth finalized), so clients always get Espresso's faster finality when it is ahead while safely falling back to Ethereum finality otherwise.

How finality stays safe

A background verifier loop continuously compares the data finalized by Espresso against the corresponding blocks on the full node. The Espresso-finalized block number only advances when the full node's state matches what Espresso has finalized. Until the verifier confirms its first batch, the Espresso-finalized position has not advanced, so the max(espresso finalized, eth finalized) resolution returns the chain's Ethereum-finalized block; once batches start being confirmed, the tag tracks Espresso's faster finality whenever it is ahead.

The resulting "espresso" block number is monotonically increasing — it never moves backwards, even across restarts, L1 reorgs, or sequencer reorgs. The position is persisted to disk, so on restart the proxy resumes where it left off. This gives clients a stable, safe cursor into the chain that only ever moves forward.

Edge cases and failure behavior

The proxy is designed to degrade safely: under every fault below it keeps forwarding ordinary JSON-RPC traffic (latest, finalized, specific block numbers, eth_call, eth_sendRawTransaction, etc.) to the full node unchanged. Only the resolution of the Espresso tag is affected, and it is built so the tag never moves backwards and never returns unverified data. The underlying guarantee is the tag's resolution to max(espresso finalized, eth finalized): a background verifier advances the Espresso-finalized position only when the full node's block is byte-for-byte identical to what Espresso finalized, and independently fast-forwards the stored position to the Ethereum-finalized block whenever Ethereum is ahead.

Espresso is down or unreachable

If the query service is unreachable or HotShot stops producing blocks, the verifier simply cannot advance the Espresso-finalized position — it logs the error and retries on the next interval. The proxy keeps serving every request. Meanwhile the finality poller keeps reading the full node's Ethereum-finalized block and the verifier fast-forwards the stored position to it, so the "espresso" tag continues to move forward at Ethereum's finality cadence rather than stalling or erroring. In effect the tag transparently degrades to Ethereum finality for the duration of the outage. When Espresso recovers, the verifier resumes from the persisted position and the tag goes back to tracking Espresso's faster finality whenever it is ahead.

Espresso data disagrees with the full node (verification mismatch)

This is the core safety case. When the block Espresso finalized does not match the corresponding block on the full node (block hash or encoded contents differ), the verifier refuses to advance: it logs the mismatch and does not update the stored position, then retries on the next interval. The "espresso" tag holds at the last verified block (still tracking Ethereum finality via the fast-forward above). Clients never see Espresso-finalized data that the full node has not reproduced exactly.

Force inclusion (transactions included via Ethereum)

If a transaction isn't included by the sequencer, anyone can force it through via Ethereum — but only after the chain's force-inclusion window has elapsed. A user submits the transaction to the inbox contract on Ethereum, and the sequencer is given that window to include it normally first; only once the window passes without inclusion can the transaction be forced in directly. Because a force-included transaction bypasses normal sequencer ordering, the block the full node derives may diverge from what Espresso confirmed, which breaks Espresso confirmations at that point: the verifier sees a mismatch and stops advancing the Espresso-finalized position.

This resolves on its own through the Ethereum-finality fallback. Once Ethereum finality advances past the block where the force inclusion happened, the verifier fast-forwards the stored position to the Ethereum-finalized block and the "espresso" tag resumes moving forward. Throughout, the tag never moves backwards and never returns unverified data — it simply tracks Ethereum finality across the affected range instead of Espresso's faster finality.

Chain reorgs

The stored Espresso-finalized block number is monotonically increasing — it only ever moves forward, never backwards, across reorgs and restarts (the position is persisted to disk). Because Espresso finalizes the chain's ordering first, a later L2 reorg can't rewrite it: the reorged blocks won't match what Espresso already finalized, which the verifier treats as a mismatch (see above) and refuses to advance on. The position served to clients therefore never decreases. And the mismatch is only transient — the batch poster ultimately settles the first valid block that was sent to Espresso, so the L2's canonical chain re-converges with Espresso's finalized ordering and the verifier resumes advancing.

Full node behind or not yet synced

If the full node does not yet have the block Espresso has finalized, the verifier logs that the block isn't available yet and retries on the next interval — it does not advance until the full node catches up. The proxy keeps serving from the last verified position. The same applies on first startup before the verifier has confirmed its first batch: the "espresso" tag returns the Ethereum-finalized block until verification catches up.

For how the proxy persists this state and resumes after a restart or crash, see Restart and crash recovery in the operator guide.

Supported stacks

The RPC Node supports both OP Stack (--mode op) and Arbitrum Nitro / Orbit (--mode nitro) rollups.

To run and configure your own, see Run an RPC Node.

Last updated