@@ -10,7 +10,7 @@ import { waitForTx } from '@aztec/aztec.js/node';
1010import { RollupContract } from '@aztec/ethereum/contracts' ;
1111import type { Operator } from '@aztec/ethereum/deploy-aztec-l1-contracts' ;
1212import { asyncMap } from '@aztec/foundation/async-map' ;
13- import { CheckpointNumber } from '@aztec/foundation/branded-types' ;
13+ import { CheckpointNumber , SlotNumber } from '@aztec/foundation/branded-types' ;
1414import { times , timesAsync } from '@aztec/foundation/collection' ;
1515import { SecretValue } from '@aztec/foundation/config' ;
1616import { retryUntil } from '@aztec/foundation/retry' ;
@@ -363,4 +363,76 @@ describe('e2e_epochs/epochs_mbps', () => {
363363
364364 await assertMultipleBlocksPerSlot ( 2 , logger ) ;
365365 } ) ;
366+
367+ it ( 'builds multiple blocks per slot and non-validator re-executes and stores proposed multi-block slots' , async ( ) => {
368+ await setupTest ( { syncChainTip : 'proposed' , minTxsPerBlock : 1 , maxTxsPerBlock : 1 } ) ;
369+
370+ logger . warn ( `Creating non-validator node` ) ;
371+ const nonValidatorNode = await test . createNonValidatorNode ( {
372+ alwaysReexecuteBlockProposals : true ,
373+ skipPushProposedBlocksToArchiver : false ,
374+ } ) ;
375+
376+ await Promise . all ( nodes . map ( n => n . getSequencer ( ) ! . start ( ) ) ) ;
377+ logger . warn ( `Started all sequencers` ) ;
378+
379+ logger . warn ( `Pre-proving ${ TX_COUNT / 2 } transactions` ) ;
380+ const txs = await timesAsync ( TX_COUNT / 2 , i => {
381+ const nullifier = new Fr ( i + 100 ) ;
382+ return proveInteraction ( context . wallet , contract . methods . emit_nullifier ( nullifier ) , { from } ) ;
383+ } ) ;
384+ logger . warn ( `Pre-proved ${ txs . length } transactions` ) ;
385+
386+ const sentTxHashes = await Promise . all ( txs . map ( tx => tx . send ( { wait : NO_WAIT } ) ) ) ;
387+ logger . warn ( `Sent ${ sentTxHashes . length } transactions` ) ;
388+
389+ const nonValidatorArchiver = nonValidatorNode . getBlockSource ( ) ;
390+
391+ let multiBlockSlotNumber : number | undefined ;
392+ let checkpointedBlockNumber : number | undefined ;
393+ await retryUntil (
394+ async ( ) => {
395+ const tips = await nonValidatorArchiver . getL2Tips ( ) ;
396+ if ( tips . proposed . number <= tips . checkpointed . block . number ) {
397+ return false ;
398+ }
399+ const header = await nonValidatorArchiver . getBlockHeader ( tips . proposed . number ) ;
400+ if ( ! header ) {
401+ return false ;
402+ }
403+ const blocksInSlot = await nonValidatorArchiver . getBlocksForSlot ( header . globalVariables . slotNumber ) ;
404+ if ( blocksInSlot . length < 2 ) {
405+ return false ;
406+ }
407+ multiBlockSlotNumber = header . globalVariables . slotNumber ;
408+ checkpointedBlockNumber = tips . checkpointed . block . number ;
409+ return true ;
410+ } ,
411+ 'non-validator node to store multi-block proposed slot' ,
412+ test . L2_SLOT_DURATION_IN_S * 5 ,
413+ 0.5 ,
414+ ) ;
415+
416+ // ensure the proposed multi-block slot has valid effects
417+ expect ( multiBlockSlotNumber ) . toBeDefined ( ) ;
418+ const blocksInSlot = await nonValidatorArchiver . getBlocksForSlot ( SlotNumber ( multiBlockSlotNumber ! ) ) ;
419+ expect ( blocksInSlot . length ) . toBeGreaterThanOrEqual ( 2 ) ;
420+ expect ( checkpointedBlockNumber ) . toBeDefined ( ) ;
421+ expect ( blocksInSlot . every ( block => block . number > checkpointedBlockNumber ! ) ) . toBe ( true ) ; // ensure the block is proposed
422+ const txHashesInSlot = blocksInSlot . flatMap ( block => block . body . txEffects . map ( effect => effect . txHash ) ) ;
423+ expect ( txHashesInSlot . length ) . toBeGreaterThan ( 0 ) ;
424+ const effectsInSlot = await Promise . all ( txHashesInSlot . map ( txHash => nonValidatorArchiver . getTxEffect ( txHash ) ) ) ;
425+ expect ( effectsInSlot . every ( effect => effect !== undefined ) ) . toBe ( true ) ;
426+
427+ const maxBlockNumberInSlot = Math . max ( ...blocksInSlot . map ( block => block . number ) ) ;
428+ await retryUntil (
429+ async ( ) => {
430+ const tips = await nonValidatorArchiver . getL2Tips ( ) ;
431+ return tips . checkpointed . block . number >= maxBlockNumberInSlot ;
432+ } ,
433+ 'non-validator node to sync checkpointed block' ,
434+ test . L2_SLOT_DURATION_IN_S * 5 ,
435+ 0.5 ,
436+ ) ;
437+ } ) ;
366438} ) ;
0 commit comments