@@ -4,7 +4,7 @@ import type { Logger } from '@aztec/aztec.js/log';
44import { tryRmDir } from '@aztec/foundation/fs' ;
55import { promiseWithResolvers } from '@aztec/foundation/promise' ;
66import { sleep } from '@aztec/foundation/sleep' ;
7- import { downloadEpochProvingJob , rerunEpochProvingJob } from '@aztec/prover-node' ;
7+ import { downloadEpochProvingJob , rerunCheckpointProvingJob , rerunEpochProvingJob } from '@aztec/prover-node' ;
88import type { TestProverNode } from '@aztec/prover-node/test' ;
99
1010import { jest } from '@jest/globals' ;
@@ -61,11 +61,11 @@ describe('single-node/proving/upload_failed_proof', () => {
6161 await tryRmDir ( rerunDownloadDir , logger ) ;
6262 } ) ;
6363
64- // Makes the prover's top-tree prove always throw (v5 uses the session's topTreeProveOverride hook;
65- // pre-v5 it patched finalizeEpoch), intercepts tryUploadSessionFailure (pre-v5 tryUploadEpochFailure)
66- // to capture the upload URL, then waits for epoch 1 to start and for the upload to complete. Tears
67- // down the live context, downloads the proving job data, and re-runs it via rerunEpochProvingJob with
68- // fake proofs on a fresh config.
64+ // Makes the prover's top-tree prove always throw. Because every checkpoint prover still succeeds, the
65+ // session fails on its own account (state 'failed'), which the session manager treats as a genuine,
66+ // race-free failure and uploads a post-mortem eagerly via tryUploadEpochFailure(sessionId, checkpoints).
67+ // Intercepts that to capture the upload URL, then tears down the live context, downloads the proving
68+ // job data, and re-runs it via rerunEpochProvingJob with fake proofs on a fresh config.
6969 it ( 'uploads failed proving job state and re-runs it on a fresh instance' , async ( ) => {
7070 // Make initial prover node fail to prove, via the session's top-tree-prove hook.
7171 const proverNode = test . proverNodes [ 0 ] . getProverNode ( ) as TestProverNode ;
@@ -77,19 +77,20 @@ describe('single-node/proving/upload_failed_proof', () => {
7777 } ,
7878 } ) ;
7979
80- // And track when the epoch failure upload is complete
80+ // Track when the epoch failure upload is complete. It fires eagerly when a full session fails with
81+ // healthy provers (a top-tree failure here).
8182 const { promise : epochUploaded , resolve : onEpochUploaded } = promiseWithResolvers < string > ( ) ;
82- const origTryUploadEpochFailure = proverNode . tryUploadSessionFailure . bind ( proverNode ) ;
83- proverNode . tryUploadSessionFailure = async ( session : any ) => {
84- const url = await origTryUploadEpochFailure ( session ) ;
83+ const origTryUploadEpochFailure = proverNode . tryUploadEpochFailure . bind ( proverNode ) ;
84+ proverNode . tryUploadEpochFailure = async ( id : any , checkpoints : any ) => {
85+ const url = await origTryUploadEpochFailure ( id , checkpoints ) ;
8586 if ( url !== undefined ) {
8687 onEpochUploaded ( url ) ;
8788 }
8889 return url ;
8990 } ;
9091
91- // Warp to the start of epoch one so prover node starts proving epoch 0,
92- // and wait for the data to be uploaded to the remote file store
92+ // Warp to the start of epoch one so the prover node starts proving, fails at the top tree, and
93+ // uploads the failed proving-job data to the remote file store.
9394 await test . warpToEpochStart ( 1 ) ;
9495 const epochUploadUrl = await epochUploaded ;
9596
@@ -122,4 +123,62 @@ describe('single-node/proving/upload_failed_proof', () => {
122123
123124 logger . info ( `Test succeeded` ) ;
124125 } ) ;
126+
127+ // Same shape as above, one level down: forces a single checkpoint's sub-tree to fail (every checkpoint
128+ // prover fails, via the test hook), which uploads that checkpoint's proving data on its own. Intercepts
129+ // tryUploadCheckpointFailure for the URL, then downloads and re-proves just that checkpoint via
130+ // rerunCheckpointProvingJob (no epoch top-tree / L1 submit).
131+ it ( 'uploads failed checkpoint proving state and re-proves it on a fresh instance' , async ( ) => {
132+ const proverNode = test . proverNodes [ 0 ] . getProverNode ( ) as TestProverNode ;
133+ proverNode . setCheckpointHooks ( {
134+ checkpointProveOverride : async ( ) => {
135+ await sleep ( 1000 ) ;
136+ logger . warn ( `Triggering error on checkpoint sub-tree prove` ) ;
137+ throw new Error ( `Fake error while proving checkpoint` ) ;
138+ } ,
139+ } ) ;
140+
141+ // The checkpoint post-mortem upload fires eagerly when a CheckpointProver's block proofs reject.
142+ const { promise : checkpointUploaded , resolve : onCheckpointUploaded } = promiseWithResolvers < string > ( ) ;
143+ const origTryUploadCheckpointFailure = proverNode . tryUploadCheckpointFailure . bind ( proverNode ) ;
144+ proverNode . tryUploadCheckpointFailure = async ( prover : any ) => {
145+ const url = await origTryUploadCheckpointFailure ( prover ) ;
146+ if ( url !== undefined ) {
147+ onCheckpointUploaded ( url ) ;
148+ }
149+ return url ;
150+ } ;
151+
152+ // Warp so the prover node starts registering checkpoints; the first one's sub-tree fails and uploads.
153+ await test . warpToEpochStart ( 1 ) ;
154+ const checkpointUploadUrl = await checkpointUploaded ;
155+
156+ await test . teardown ( ) ;
157+
158+ const rerunDownloadPath = join ( rerunDownloadDir , 'data.bin' ) ;
159+ logger . warn ( `Downloading checkpoint proving job data and state` , { rerunDataDir, rerunDownloadPath } ) ;
160+ await downloadEpochProvingJob ( checkpointUploadUrl , logger , {
161+ dataDirectory : rerunDataDir ,
162+ jobDataDownloadPath : rerunDownloadPath ,
163+ } ) ;
164+
165+ logger . warn ( `Rerunning checkpoint proving job from ${ rerunDownloadPath } ` ) ;
166+ await rerunCheckpointProvingJob (
167+ rerunDownloadPath ,
168+ logger ,
169+ {
170+ ...config ,
171+ realProofs : false ,
172+ dataStoreMapSizeKb : 1024 * 1024 ,
173+ dataDirectory : rerunDataDir ,
174+ proverAgentCount : 2 ,
175+ proverId : EthAddress . random ( ) ,
176+ ...( await getACVMConfig ( logger ) ) ,
177+ ...( await getBBConfig ( logger ) ) ,
178+ } ,
179+ context . genesis ,
180+ ) ;
181+
182+ logger . info ( `Test succeeded` ) ;
183+ } ) ;
125184} ) ;
0 commit comments