feat(p2p): retain finalized txs a configurable margin behind finality#24329
Conversation
…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.
…l/a-1274-tx-pool-finality-retention-margin
| 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); | ||
| } | ||
| } |
There was a problem hiding this comment.
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.
| const keepSlots = this.#config.keepFinalizedTxsForSlots; | ||
| if (keepSlots <= 0) { | ||
| return block.globalVariables.blockNumber; | ||
| } | ||
|
|
||
| const targetSlot = block.getSlot() - keepSlots; |
There was a problem hiding this comment.
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
left a comment
There was a problem hiding this comment.
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.
What
Adds
keepFinalizedTxsForSlotsto 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 is0(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
keepFinalizedTxsForSlotsconfig (P2PConfig+ envP2P_KEEP_FINALIZED_TXS_FOR_SLOTS, default0).handleFinalizedBlockresolves 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 theDeletedPoolfinalize 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 of0short-circuits to the previous behaviour.{ fromSlot, limit, reverse }variant onCheckpointsQuery(backed byBlockStore.getCheckpointsBySlot, a one-pass walk of the existing slot index) returns the nearest checkpoint at or before the slot withlimit: 1, reverse: true. AllCheckpointsQueryresolution now lives ingetCheckpointsData.createAztecNodeServicefloorskeepFinalizedTxsForSlotsat(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
tx_pool_v2.test.ts: the retain/delete boundary and the checkpoint resolution when the target slot falls in a gap.getCheckpointsBySlottests inblock_store.test.ts: reverse exact-hit, gap walk-back, before-genesis, multi nearest-first, and the forward direction.fromSlotschema round-trip added to the archiver and aztec-node interface tests.tx_pool_v2(254),block_store(159), and interface (120) suites pass.Closes A-1274.