Skip to content

fix(node): warm KZG trusted setup at startup - #24775

Merged
PhilWindle merged 2 commits into
merge-train/spartan-v5from
spl/a-1425-gossip-validation-taking-too-long
Jul 27, 2026
Merged

fix(node): warm KZG trusted setup at startup#24775
PhilWindle merged 2 commits into
merge-train/spartan-v5from
spl/a-1425-gossip-validation-taking-too-long

Conversation

@spalladino

Copy link
Copy Markdown
Contributor

Context

An RPC node logged Gossip validation for checkpoint_attestation took 15533ms, approaching mcache eviction window of 8400ms shortly after startup. Investigation (see A-1425) showed the validations were not slow — the Node.js event loop was fully blocked for 12–15s, and the attestations were queued behind it. The blocking call is getKzg(), which lazily builds the @crate-crypto/node-eth-kzg precomputation tables synchronously (~2s locally, 12–15s under the pod's CPU limits) on first use.

On a plain RPC node that first use is the archiver reconstructing blobs during sync, or the proposal handler uploading blobs right after a checkpoint arrives — both immediately adjacent to gossip traffic, so the stalled loop overruns the gossipsub mcache window (8.4s) and attestation forwarding is skipped. Sequencers already avoid this via Sequencer.init(), so only non-sequencer nodes were affected.

Approach

Warm the KZG singleton in createAztecNodeService, right after the existing bb.js WASM singleton init and before any subsystem runs. The cost is unchanged (every full node reconstructs blobs and pays it eventually) — it just moves off the gossip path to a point where blocking the loop is harmless. getKzg() is an idempotent per-process singleton, so this is a no-op for sequencers and for tests that pre-warm via warmBlobKzg (all e2e setups do), and adds no work for pure unit tests, which never construct a full node.

Fixes A-1425

The lazy KZG singleton (getKzg) builds its precomputation tables
synchronously on first use, blocking the event loop for ~2s locally and
12-15s under production CPU limits. On an RPC node the first use is the
archiver reconstructing blobs or the proposal handler uploading them,
right after a checkpoint arrives, so the stalled loop overruns the
gossipsub mcache window and attestation forwarding is skipped.

Warm it in the node factory alongside the bb.js singleton, before any
subsystem runs, keeping the cost off the gossip path.
Move the trusted-setup load timing and logging out of the node factory
and into getKzg itself, gated on an optional logger argument, using
elapsedSync for the measurement. Keeps the factory call site to a plain
getKzg(log) and makes the timing available to any warm-up caller.
@PhilWindle PhilWindle added the port-to-next Forward-port this merged PR into next label Jul 27, 2026
@PhilWindle
PhilWindle enabled auto-merge July 27, 2026 10:32
@PhilWindle
PhilWindle merged commit df5279a into merge-train/spartan-v5 Jul 27, 2026
25 checks passed
@PhilWindle
PhilWindle deleted the spl/a-1425-gossip-validation-taking-too-long branch July 27, 2026 10:32
AztecBot pushed a commit that referenced this pull request Jul 27, 2026
## Context

An RPC node logged `Gossip validation for checkpoint_attestation took
15533ms, approaching mcache eviction window of 8400ms` shortly after
startup. Investigation (see A-1425) showed the validations were not slow
— the Node.js event loop was fully blocked for 12–15s, and the
attestations were queued behind it. The blocking call is `getKzg()`,
which lazily builds the `@crate-crypto/node-eth-kzg` precomputation
tables synchronously (~2s locally, 12–15s under the pod's CPU limits) on
first use.

On a plain RPC node that first use is the archiver reconstructing blobs
during sync, or the proposal handler uploading blobs right after a
checkpoint arrives — both immediately adjacent to gossip traffic, so the
stalled loop overruns the gossipsub mcache window (8.4s) and attestation
forwarding is skipped. Sequencers already avoid this via
`Sequencer.init()`, so only non-sequencer nodes were affected.

## Approach

Warm the KZG singleton in `createAztecNodeService`, right after the
existing bb.js WASM singleton init and before any subsystem runs. The
cost is unchanged (every full node reconstructs blobs and pays it
eventually) — it just moves off the gossip path to a point where
blocking the loop is harmless. `getKzg()` is an idempotent per-process
singleton, so this is a no-op for sequencers and for tests that pre-warm
via `warmBlobKzg` (all e2e setups do), and adds no work for pure unit
tests, which never construct a full node.

Fixes A-1425
@AztecBot

Copy link
Copy Markdown
Collaborator

✅ Successfully ported to port-to-next-staging #24964.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

port-to-next Forward-port this merged PR into next

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants