Skip to content

perf(node): stop rebuilding settlement attestations on every retry - #1392

Closed
mattsse wants to merge 1 commit into
mainfrom
mattsse/perf-settlement-retry-rebuild
Closed

perf(node): stop rebuilding settlement attestations on every retry#1392
mattsse wants to merge 1 commit into
mainfrom
mattsse/perf-settlement-retry-rebuild

Conversation

@mattsse

@mattsse mattsse commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

While a batch boundary waits for L1 confirmation the leader re-proposed it every 500 ms, rebuilding the attestation each time: previous_batch walks a whole batch of blocks reading receipts, followed by a portal multicall and three more L1 reads. Every re-broadcast made each follower repeat that work inline on its block-import loop, and each returned signature made the leader repeat it again to verify a statement it had signed itself.

The leader now verifies a follower signature against its own stored proposal at (height, digest); the digest commits to the whole statement, so this is equivalent, and collect_follower_settlement_signatures no longer needs the chain provider. The previous_batch walk is memoized per boundary height on AttestationContext, which is sound given instant finality. A follower remembers the last statement it signed and answers re-broadcasts from memory. The retry backs off from 500 ms to at most 5 s while the same boundary stays pending, resets when a new boundary appears, no longer fires immediately on entering the loop, and is skipped once every quorum member has signed.

Measured with the mock-based unit tests added here at the default 120-block interval: a cold build reads 121 receipt sets, 2 headers and makes 5 L1 requests; a retry at the same boundary reads 1 receipt set and 1 header; a re-broadcast or a follower-signature verification reads nothing and makes no L1 request. Moving proposal validation off the follower's import loop is a follow-up.

While a batch boundary waits for its L1 confirmation the leader re-proposed
it every 500 ms, and each proposal rebuilt the attestation from scratch:
`previous_batch` walks back through a whole batch of blocks reading receipts
and decoding logs, followed by a portal multicall and three more L1 reads.
Every re-broadcast then made each follower repeat that same work inline on
its block-import loop, and each returned signature made the leader repeat it
once more to verify a statement it had signed itself.

Four changes remove that work:

- The leader verifies a follower signature against its own stored proposal at
  (height, digest) instead of rebuilding it. The precheck already establishes
  that the leader signed at that digest, and the digest commits to the whole
  statement, so the comparison is equivalent. `collect_follower_settlement_signatures`
  no longer needs a chain provider at all.
- `previous_batch` is memoized on `AttestationContext` for the boundary height
  it was computed at. The zone has instant finality, so on the canonical chain
  the walk is a pure function of that height.
- A follower remembers the last (height, digest, signature) it returned and
  answers a re-broadcast of the same statement from memory.
- The retry backs off from 500 ms to at most 5 s while the same boundary stays
  pending, restarting at the base interval when a new boundary becomes pending,
  and skips re-proposing once every quorum member has signed.

Measured with the mock-based unit tests in replication.rs: a cold build reads
121 blocks of receipts and 2 sealed headers plus 5 L1 requests at the default
120-block batch interval; a retry at the same boundary now reads 1 receipt set
and 1 header, and a re-broadcast or a follower-signature verification reads
nothing at all.
@mattsse

mattsse commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

this is too complex

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant