Skip to content

feat(p2p): retain finalized txs a configurable margin behind finality#24329

Merged
PhilWindle merged 8 commits into
merge-train/spartan-v5from
phil/a-1274-tx-pool-finality-retention-margin
Jul 7, 2026
Merged

feat(p2p): retain finalized txs a configurable margin behind finality#24329
PhilWindle merged 8 commits into
merge-train/spartan-v5from
phil/a-1274-tx-pool-finality-retention-margin

Conversation

@PhilWindle

@PhilWindle PhilWindle commented Jun 26, 2026

Copy link
Copy Markdown
Collaborator

What

Adds keepFinalizedTxsForSlots to the v2 tx pool. Instead of deleting a finalized tx's data at the finalized tip, the pool keeps it for a configurable number of slots behind finality. Default is 0 (current behaviour). Prover nodes raise it automatically.

Why

A prover node fetches a checkpoint's txs from its tx pool (TxProvider → pool first, reqresp fallback) and re-reads them for failure upload.

How

  • New keepFinalizedTxsForSlots config (P2PConfig + env P2P_KEEP_FINALIZED_TXS_FOR_SLOTS, default 0).
  • handleFinalizedBlock resolves the slot margin to a block cutoff: target slot = finalizedSlot − margin, find the checkpoint at or before it, and use that checkpoint's last block as the deletion cutoff. Applied to both the active-pool deletion and the DeletedPool finalize path. Rounds to a checkpoint boundary, so it can retain slightly more than the configured margin but never less, and never deletes past the finalized block. A margin of 0 short-circuits to the previous behaviour.
  • The "checkpoint at or before the target slot" lookup is a single reverse range query, not a slot-by-slot scan: a new { fromSlot, limit, reverse } variant on CheckpointsQuery (backed by BlockStore.getCheckpointsBySlot, a one-pass walk of the existing slot index) returns the nearest checkpoint at or before the slot with limit: 1, reverse: true. All CheckpointsQuery resolution now lives in getCheckpointsData.
  • Prover nodes: createAztecNodeService floors keepFinalizedTxsForSlots at (proofSubmissionEpochs + 1) × epochDuration (read from the rollup), taking the max with any operator-configured value and warning when it raises it. This matches the prover-node catch-up window.

Testing

  • New unit tests in tx_pool_v2.test.ts: the retain/delete boundary and the checkpoint resolution when the target slot falls in a gap.
  • New getCheckpointsBySlot tests in block_store.test.ts: reverse exact-hit, gap walk-back, before-genesis, multi nearest-first, and the forward direction.
  • fromSlot schema round-trip added to the archiver and aztec-node interface tests.
  • Full tx_pool_v2 (254), block_store (159), and interface (120) suites pass.

Closes A-1274.

…ec-packages into phil/a-1274-tx-pool-finality-retention-margin
The base-branch merge moved the p2p-client setup from server.ts into
createAztecNodeService in factory.ts. Re-apply the prover-node retention floor
(keepFinalizedTxsForSlots >= (proofSubmissionEpochs + 1) epochs) in the new location.
Comment on lines +772 to +777
for (let slot = targetSlot; slot >= 0; slot--) {
const checkpoint = await this.#l2BlockSource.getCheckpointData({ slot: SlotNumber(slot) });
if (checkpoint) {
return BlockNumber(checkpoint.startBlock + checkpoint.blockCount - 1);
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This loop could be expensive to run. We already have checkpoints indexed by slot in the block_store, so we could use a range query with limit=1 to get the previous or next checkpoint given a slot. It just needs to be added to the BlockSource interface.

Comment on lines +766 to +771
const keepSlots = this.#config.keepFinalizedTxsForSlots;
if (keepSlots <= 0) {
return block.globalVariables.blockNumber;
}

const targetSlot = block.getSlot() - keepSlots;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aren't we double-counting slots to keep txs for? The prover wants to keep txs around during their proof submission window, so it needs to keep all txs that were mined in the last proofSubmissionEpochs + 1 from the current slot, not the finalized one.

Keeping them for longer sure doesn't harm, but we could be pruning them much earlier.

… query

Replace the slot-by-slot walk in the tx pool's finalization cutoff with one
reverse range query over the archiver's slot index. Adds a fromSlot/limit/reverse
variant to CheckpointsQuery, backed by BlockStore.getCheckpointsBySlot, and folds
all CheckpointsQuery resolution into getCheckpointsData.

@spalladino spalladino left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! Let's just add a comment about the double-counting, in case we need to be more strict in pruning in the future.

@PhilWindle PhilWindle enabled auto-merge (squash) July 7, 2026 13:03
@PhilWindle PhilWindle merged commit 3aad58e into merge-train/spartan-v5 Jul 7, 2026
12 checks passed
@PhilWindle PhilWindle deleted the phil/a-1274-tx-pool-finality-retention-margin branch July 7, 2026 13:21
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.

2 participants