@@ -5,14 +5,16 @@ import { EthAddress } from '@aztec/aztec.js/addresses';
55import { waitForProven } from '@aztec/aztec.js/contracts' ;
66import { generateClaimSecret } from '@aztec/aztec.js/ethereum' ;
77import { Fr } from '@aztec/aztec.js/fields' ;
8- import { RollupCheatCodes } from '@aztec/aztec/testing' ;
8+ import { waitForL1ToL2MessageReady } from '@aztec/aztec.js/messaging' ;
9+ import { EpochTestSettler , RollupCheatCodes } from '@aztec/aztec/testing' ;
910import { FeeAssetHandlerContract , RegistryContract , RollupContract } from '@aztec/ethereum/contracts' ;
1011import { deployRollupForUpgrade } from '@aztec/ethereum/deploy-aztec-l1-contracts' ;
1112import { deployL1Contract } from '@aztec/ethereum/deploy-l1-contract' ;
1213import type { L1ContractAddresses } from '@aztec/ethereum/l1-contract-addresses' ;
1314import { L1TxUtils , createL1TxUtils } from '@aztec/ethereum/l1-tx-utils' ;
1415import type { ExtendedViemWalletClient } from '@aztec/ethereum/types' ;
1516import { CheckpointNumber , EpochNumber , SlotNumber } from '@aztec/foundation/branded-types' ;
17+ import { retryUntil } from '@aztec/foundation/retry' ;
1618import { sleep } from '@aztec/foundation/sleep' ;
1719import {
1820 GovernanceAbi ,
@@ -42,7 +44,6 @@ import { shouldCollectMetrics } from '../fixtures/fixtures.js';
4244import { sendL1ToL2Message } from '../fixtures/l1_to_l2_messaging.js' ;
4345import { ATTESTER_PRIVATE_KEYS_START_INDEX , createNodes , createProverNode } from '../fixtures/setup_p2p_test.js' ;
4446import { setupSharedBlobStorage } from '../fixtures/utils.js' ;
45- import { waitForL1ToL2MessageSeen } from '../shared/wait_for_l1_to_l2_message.js' ;
4647import { TestWallet } from '../test-wallet/test_wallet.js' ;
4748import { P2PNetworkTest , SHORTENED_BLOCK_TIME_CONFIG_NO_PRUNES } from './p2p_network.js' ;
4849
@@ -53,7 +54,7 @@ const BOOT_NODE_UDP_PORT = 4500;
5354const DATA_DIR = fs . mkdtempSync ( path . join ( os . tmpdir ( ) , 'add-rollup-old-' ) ) ;
5455const DATA_DIR_NEW = fs . mkdtempSync ( path . join ( os . tmpdir ( ) , 'add-rollup-new-' ) ) ;
5556
56- jest . setTimeout ( 1000 * 60 * 10 ) ;
57+ jest . setTimeout ( 1000 * 60 * 20 ) ;
5758
5859/**
5960 * This test emulates the addition of a new rollup to the registry and tests that cross-chain messages work.
@@ -67,6 +68,13 @@ describe('e2e_p2p_add_rollup', () => {
6768 let nodes : AztecNodeService [ ] ;
6869 let proverAztecNode : AztecNodeService ;
6970 let l1TxUtils : L1TxUtils ;
71+ // Cheat-code-driven epoch settlers stand in for real prover-node submission. Pipelining
72+ // currently produces a `Root rollup public inputs mismatch` between the prover's recomputed
73+ // checkpoint header hashes and the on-chain log (see pipeline-review.md), so the real prover
74+ // never moves the proven tip and `waitForProven` would hang indefinitely. The settler advances
75+ // the proven tip and writes the outbox out hash via cheat codes once each epoch is complete.
76+ let oldRollupSettler : EpochTestSettler | undefined ;
77+ let newRollupSettler : EpochTestSettler | undefined ;
7078
7179 beforeAll ( async ( ) => {
7280 t = await P2PNetworkTest . create ( {
@@ -80,6 +88,14 @@ describe('e2e_p2p_add_rollup', () => {
8088 ...SHORTENED_BLOCK_TIME_CONFIG_NO_PRUNES ,
8189 listenAddress : '127.0.0.1' ,
8290 governanceProposerRoundSize : 10 ,
91+ enableProposerPipelining : true ,
92+ // Allow validators to build empty checkpoints under pipelining so the chain keeps
93+ // advancing while we wait for L1->L2 messages to land in the next checkpoint's inbox tree.
94+ minTxsPerBlock : 0 ,
95+ // Pipelining starts cycle for checkpoint N+1 during slot N, but the inbox tree for
96+ // checkpoint N is only sealed when checkpoint N is published. inboxLag: 2 sources
97+ // L1->L2 messages from checkpoint N-1 (already sealed), avoiding L1ToL2MessagesNotReadyError.
98+ inboxLag : 2 ,
8399 } ,
84100 startProverNode : false , // Start one later using p2p.
85101 } ) ;
@@ -94,6 +110,8 @@ describe('e2e_p2p_add_rollup', () => {
94110 } ) ;
95111
96112 afterAll ( async ( ) => {
113+ await oldRollupSettler ?. stop ( ) ;
114+ await newRollupSettler ?. stop ( ) ;
97115 await tryStop ( proverAztecNode ) ;
98116 await t . stopNodes ( nodes ) ;
99117 await t . teardown ( ) ;
@@ -260,6 +278,18 @@ describe('e2e_p2p_add_rollup', () => {
260278 shouldCollectMetrics ( ) ,
261279 ) ) ;
262280
281+ // Cheat-code-driven epoch settler so the proven tip and outbox advance without depending on
282+ // the real prover, which currently fails to publish under proposer pipelining due to a
283+ // `Root rollup public inputs mismatch`. See add_rollup.pipeline-review.md.
284+ oldRollupSettler = new EpochTestSettler (
285+ t . ctx . cheatCodes . eth ,
286+ t . ctx . deployL1ContractsValues . l1ContractAddresses . rollupAddress ,
287+ nodes [ 0 ] . getBlockSource ( ) ,
288+ t . logger . createChild ( 'epoch-settler-old' ) ,
289+ { pollingIntervalMs : 200 } ,
290+ ) ;
291+ await oldRollupSettler . start ( ) ;
292+
263293 await sleep ( 4000 ) ;
264294
265295 t . logger . info ( 'Start progressing time to cast votes' ) ;
@@ -307,7 +337,10 @@ describe('e2e_p2p_add_rollup', () => {
307337 } ) ;
308338
309339 const makeMessageConsumable = async ( msgHash : Fr ) => {
310- await waitForL1ToL2MessageSeen ( node , msgHash , { timeoutSeconds : 10 } ) ;
340+ // Wait until the message is ready to be consumed (the rollup has reached the message's checkpoint).
341+ // Using waitForL1ToL2MessageReady rather than isL1ToL2MessageSynced because with `inboxLag > 0`
342+ // a synced message is not yet present in the latest checkpoint's inbox tree.
343+ await waitForL1ToL2MessageReady ( node , msgHash , { timeoutSeconds : 120 } ) ;
311344
312345 const { receipt } = await testContract . methods
313346 . create_l2_to_l1_message_arbitrary_recipient_private ( contentOutFromRollup , ethRecipient )
@@ -572,12 +605,41 @@ describe('e2e_p2p_add_rollup', () => {
572605 shouldCollectMetrics ( ) ,
573606 ) ) ;
574607
608+ // Stop the old-rollup settler and spin up one for the new rollup. Same rationale as above:
609+ // the real prover does not publish proofs under pipelining, so we need cheat-code settlement
610+ // for the bridging step's `waitForProven` to make progress.
611+ await oldRollupSettler ?. stop ( ) ;
612+ oldRollupSettler = undefined ;
613+ newRollupSettler = new EpochTestSettler (
614+ t . ctx . cheatCodes . eth ,
615+ EthAddress . fromString ( newRollup . address ) ,
616+ nodes [ 0 ] . getBlockSource ( ) ,
617+ t . logger . createChild ( 'epoch-settler-new' ) ,
618+ { pollingIntervalMs : 200 } ,
619+ ) ;
620+ await newRollupSettler . start ( ) ;
621+
575622 // wait a bit for peers to discover each other
576623 await sleep ( 4000 ) ;
577624
578625 // The new rollup should have no checkpoints
579626 expect ( await newRollup . getCheckpointNumber ( ) ) . toBe ( CheckpointNumber ( 0 ) ) ;
580627
628+ // Wait for the new rollup to publish its first checkpoint AND for `nodes[0]` to have synced
629+ // it locally, before the second bridging step. The bridge wallet uses
630+ // `syncChainTip: 'checkpointed'`, which falls back to the genesis block when no checkpoint
631+ // exists. After warping ~500 epochs forward, txs anchored at genesis would expire before
632+ // being included. We poll the node's local view (not just the L1 rollup contract) so the PXE
633+ // and the assertion observe the same chain state.
634+ t . logger . info ( `Waiting for new rollup to publish its first checkpoint` ) ;
635+ await retryUntil (
636+ async ( ) => Number ( await nodes [ 0 ] . getCheckpointNumber ( 'checkpointed' ) ) > 0 ,
637+ 'newRollup first checkpoint synced by node' ,
638+ 300 ,
639+ 2 ,
640+ ) ;
641+ t . logger . info ( `New rollup published its first checkpoint` ) ;
642+
581643 // Bridge into and out of the new rollup to ensure that it works.
582644 await bridging (
583645 nodes [ 0 ] ,
0 commit comments