@@ -8,10 +8,10 @@ import { promiseWithResolvers } from '@aztec/foundation/promise';
88import { sleep } from '@aztec/foundation/sleep' ;
99import { DateProvider } from '@aztec/foundation/timer' ;
1010import type { EpochProverFactory } from '@aztec/prover-client' ;
11- import type { ChonkCache , SubTreeResult } from '@aztec/prover-client/orchestrator' ;
12- import type { PublicProcessorFactory } from '@aztec/simulator/server' ;
11+ import type { CheckpointSubTreeOrchestrator , ChonkCache , SubTreeResult } from '@aztec/prover-client/orchestrator' ;
12+ import type { PublicProcessor , PublicProcessorFactory } from '@aztec/simulator/server' ;
1313import { Checkpoint } from '@aztec/stdlib/checkpoint' ;
14- import type { ForkMerkleTreeOperations , ITxProvider } from '@aztec/stdlib/interfaces/server' ;
14+ import type { ForkMerkleTreeOperations , ITxProvider , MerkleTreeWriteOperations } from '@aztec/stdlib/interfaces/server' ;
1515import { BlockHeader , type Tx } from '@aztec/stdlib/tx' ;
1616
1717import { jest } from '@jest/globals' ;
@@ -504,7 +504,8 @@ describe('CheckpointProver', () => {
504504
505505 const prover = makeProver ( ) ;
506506
507- await expect ( prover . whenSubTreeProofsReady ( ) ) . rejects . toThrow ( / d i d n o t c o m p l e t e b l o c k p r o c e s s i n g / ) ;
507+ // The failure the loop hit wins over the early proofs, and is what the promise rejects with.
508+ await expect ( prover . whenSubTreeProofsReady ( ) ) . rejects . toThrow ( / U n a b l e t o g e t m e t a d a t a f o r b l o c k 0 / ) ;
508509 await prover . whenDone ( ) ;
509510 expect ( stop ) . toHaveBeenCalledTimes ( 1 ) ;
510511 expect ( prover . isFailed ( ) ) . toBe ( true ) ;
@@ -515,39 +516,52 @@ describe('CheckpointProver', () => {
515516 // ---------------- data-plane reorg fork fault ----------------
516517
517518 describe ( 'streaming message slicing' , ( ) => {
518- /** Stubs the sub-tree and forks so the execute loop runs with empty-tx blocks, recording per-block messages. */
519- function stubExecution ( ) {
519+ /**
520+ * Stubs the sub-tree, the forks and the public processor so the execute loop runs over empty-tx blocks, and
521+ * exposes the per-block message bundles it hands each of them. `blocksCompleted` resolves once the loop has run
522+ * `expectedBlocks` blocks to completion, which is the point at which every bundle has been handed over.
523+ */
524+ function stubExecution ( expectedBlocks : number ) {
520525 txProvider . getTxsForBlock . mockReset ( ) ;
521526 txProvider . getTxsForBlock . mockResolvedValue ( { txs : [ ] , missingTxs : [ ] } ) ;
522- const startNewBlock = jest . fn ( ( ..._args : unknown [ ] ) => Promise . resolve ( ) ) ;
523- const appendLeaves = jest . fn ( ( ..._args : unknown [ ] ) => Promise . resolve ( ) ) ;
524- const subTree = {
525- getSubTreeResult : ( ) => new Promise < never > ( ( ) => { } ) ,
526- startNewBlock,
527- startChonkVerifierCircuits : ( ) => Promise . resolve ( ) ,
528- addTxs : ( ) => Promise . resolve ( ) ,
529- setBlockCompleted : ( ) => Promise . resolve ( ) ,
530- cancel : ( ) => { } ,
531- stop : ( ) => Promise . resolve ( ) ,
532- } ;
533- proverFactory . createCheckpointSubTreeOrchestrator . mockResolvedValue ( subTree as any ) ;
534- dbProvider . fork . mockResolvedValue ( { appendLeaves, close : ( ) => Promise . resolve ( ) } as any ) ;
535- publicProcessorFactory . create . mockReturnValue ( { process : ( ) => Promise . resolve ( [ [ ] , [ ] ] ) } as any ) ;
536- return { startNewBlock, appendLeaves } ;
527+
528+ const blocksCompleted = promiseWithResolvers < void > ( ) ;
529+ const subTree = mock < CheckpointSubTreeOrchestrator > ( ) ;
530+ // The sub-tree's proofs never land: these tests only exercise the block loop that feeds it.
531+ subTree . getSubTreeResult . mockReturnValue ( new Promise < SubTreeResult > ( ( ) => { } ) ) ;
532+ subTree . setBlockCompleted . mockImplementation ( ( _blockNumber , expectedHeader ) => {
533+ if ( subTree . setBlockCompleted . mock . calls . length >= expectedBlocks ) {
534+ blocksCompleted . resolve ( ) ;
535+ }
536+ return Promise . resolve ( expectedHeader ?? BlockHeader . empty ( ) ) ;
537+ } ) ;
538+ proverFactory . createCheckpointSubTreeOrchestrator . mockResolvedValue ( subTree ) ;
539+
540+ const fork = mock < MerkleTreeWriteOperations > ( ) ;
541+ dbProvider . fork . mockResolvedValue ( fork ) ;
542+
543+ const publicProcessor = mock < PublicProcessor > ( ) ;
544+ publicProcessor . process . mockResolvedValue ( [ [ ] , [ ] , [ ] , [ ] , [ ] ] ) ;
545+ publicProcessorFactory . create . mockReturnValue ( publicProcessor ) ;
546+
547+ const bundlesPassedToSubTree = ( ) => subTree . startNewBlock . mock . calls . map ( ( [ , , , messages ] ) => messages ) ;
548+ const bundlesAppendedToFork = ( ) => fork . appendLeaves . mock . calls . map ( ( [ , leaves ] ) => leaves ) ;
549+ return { blocksCompleted : blocksCompleted . promise , bundlesPassedToSubTree, bundlesAppendedToFork } ;
537550 }
538551
539552 it ( 'slices the checkpoint messages per block by the headers leaf counts' , async ( ) => {
540553 checkpoint = await Checkpoint . random ( CheckpointNumber ( 1 ) , { numBlocks : 3 , txsPerBlock : 0 } ) ;
541554 // The parent consumed 10 messages; the blocks consume 2, 0 and 1 more.
542555 pinConsumedMessageCounts ( checkpoint , [ 12 , 12 , 13 ] ) ;
543556 const messages = [ Fr . random ( ) , Fr . random ( ) , Fr . random ( ) ] ;
544- const { startNewBlock , appendLeaves } = stubExecution ( ) ;
557+ const { blocksCompleted , bundlesPassedToSubTree , bundlesAppendedToFork } = stubExecution ( 3 ) ;
545558
546559 const prover = makeProver ( { previousBlockHeader : makePreviousBlockHeader ( 10 ) , l1ToL2Messages : messages } ) ;
547- await ( prover as any ) . runPromise ;
560+ await blocksCompleted ;
548561
549- expect ( startNewBlock . mock . calls . map ( call => call [ 3 ] ) ) . toEqual ( [ messages . slice ( 0 , 2 ) , [ ] , messages . slice ( 2 ) ] ) ;
550- expect ( appendLeaves . mock . calls . map ( call => call [ 1 ] ) ) . toEqual ( [ messages . slice ( 0 , 2 ) , [ ] , messages . slice ( 2 ) ] ) ;
562+ const expectedBundles = [ messages . slice ( 0 , 2 ) , [ ] , messages . slice ( 2 ) ] ;
563+ expect ( bundlesPassedToSubTree ( ) ) . toEqual ( expectedBundles ) ;
564+ expect ( bundlesAppendedToFork ( ) ) . toEqual ( expectedBundles ) ;
551565 expect ( prover . isFailed ( ) ) . toBe ( false ) ;
552566 prover . cancel ( ) ;
553567 await prover . whenDone ( ) ;
@@ -556,7 +570,7 @@ describe('CheckpointProver', () => {
556570 it ( 'fails the prover when the message list does not cover the blocks leaf count range' , async ( ) => {
557571 checkpoint = await Checkpoint . random ( CheckpointNumber ( 1 ) , { numBlocks : 2 , txsPerBlock : 0 } ) ;
558572 pinConsumedMessageCounts ( checkpoint , [ 12 , 13 ] ) ;
559- const { startNewBlock } = stubExecution ( ) ;
573+ const { bundlesPassedToSubTree } = stubExecution ( 2 ) ;
560574
561575 // The parent consumed 10, the blocks reach 13, but only two messages are supplied.
562576 const prover = makeProver ( {
@@ -567,7 +581,8 @@ describe('CheckpointProver', () => {
567581 await expect ( prover . whenSubTreeProofsReady ( ) ) . rejects . toThrow (
568582 / c o n s u m e d 3 L 1 t o L 2 m e s s a g e s .* b u t 2 w e r e s u p p l i e d / ,
569583 ) ;
570- expect ( startNewBlock ) . not . toHaveBeenCalled ( ) ;
584+ // The mismatch is caught before any block is handed to the sub-tree, so nothing was proven on a bad slice.
585+ expect ( bundlesPassedToSubTree ( ) ) . toEqual ( [ ] ) ;
571586 expect ( prover . isFailed ( ) ) . toBe ( true ) ;
572587 expect ( onFailed ) . toHaveBeenCalledWith ( prover ) ;
573588 await prover . whenDone ( ) ;
@@ -576,16 +591,15 @@ describe('CheckpointProver', () => {
576591 it ( 'fails the prover when a block leaf count falls below its parent' , async ( ) => {
577592 checkpoint = await Checkpoint . random ( CheckpointNumber ( 1 ) , { numBlocks : 2 , txsPerBlock : 0 } ) ;
578593 pinConsumedMessageCounts ( checkpoint , [ 13 , 12 ] ) ;
579- const { startNewBlock } = stubExecution ( ) ;
594+ const messages = [ Fr . random ( ) , Fr . random ( ) ] ;
595+ const { bundlesPassedToSubTree } = stubExecution ( 2 ) ;
580596
581- const prover = makeProver ( {
582- previousBlockHeader : makePreviousBlockHeader ( 10 ) ,
583- l1ToL2Messages : [ Fr . random ( ) , Fr . random ( ) ] ,
584- } ) ;
597+ const prover = makeProver ( { previousBlockHeader : makePreviousBlockHeader ( 10 ) , l1ToL2Messages : messages } ) ;
585598
586599 await expect ( prover . whenSubTreeProofsReady ( ) ) . rejects . toThrow ( / l e a f c o u n t 1 2 i s b e l o w i t s p a r e n t ' s 1 3 / ) ;
587- // The first block was started before the second's count was found to rewind.
588- expect ( startNewBlock ) . toHaveBeenCalledTimes ( 1 ) ;
600+ // The first block was started (claiming all three messages the header says it consumed, of which only two
601+ // exist) before the second block's count was found to rewind.
602+ expect ( bundlesPassedToSubTree ( ) ) . toEqual ( [ messages ] ) ;
589603 expect ( prover . isFailed ( ) ) . toBe ( true ) ;
590604 await prover . whenDone ( ) ;
591605 } ) ;
0 commit comments