@@ -46,6 +46,7 @@ use ethrex_storage::EngineType;
4646use ethrex_storage:: Store ;
4747use ethrex_storage_rollup:: StoreRollup ;
4848use ethrex_vm:: { BlockExecutionResult , Evm } ;
49+ use rand:: Rng ;
4950use serde:: Serialize ;
5051use std:: {
5152 collections:: { BTreeMap , HashMap } ,
@@ -270,16 +271,50 @@ impl L1Committer {
270271 ) ?;
271272 let first_block_to_commit = last_block + 1 ;
272273
274+ // We need to guarantee that the checkpoint path is new
275+ // to avoid causing a lock error under rocksdb feature.
276+ let rand_suffix: u32 = rand:: thread_rng ( ) . r#gen ( ) ;
277+ let one_time_checkpoint_path = self
278+ . checkpoints_dir
279+ . join ( format ! ( "temp_checkpoint_{batch_to_commit}_{rand_suffix}" ) ) ;
280+
281+ // For re-execution we need to use a checkpoint to the previous state
282+ // (i.e. checkpoint of the state to the latest block from the previous
283+ // batch, or the state of the genesis if this is the first batch).
284+ // We already have this initial checkpoint as part of the L1Committer
285+ // struct, but we need to create a one-time copy of it because
286+ // we still need to use the current checkpoint store later for witness
287+ // generation.
288+
289+ let ( one_time_checkpoint_store, one_time_checkpoint_blockchain) = self
290+ . create_checkpoint ( & self . current_checkpoint_store , & one_time_checkpoint_path)
291+ . await ?;
292+
273293 // Try to prepare batch
294+ let result = self
295+ . prepare_batch_from_block (
296+ * last_block,
297+ batch_to_commit,
298+ one_time_checkpoint_store,
299+ one_time_checkpoint_blockchain,
300+ )
301+ . await ;
302+
303+ if one_time_checkpoint_path. exists ( ) {
304+ let _ = remove_dir_all ( & one_time_checkpoint_path) . inspect_err ( |e| {
305+ error ! (
306+ "Failed to remove one-time checkpoint directory at path {one_time_checkpoint_path:?}. Should be removed manually. Error: {}" , e. to_string( )
307+ )
308+ } ) ;
309+ }
310+
274311 let (
275312 blobs_bundle,
276313 new_state_root,
277314 message_hashes,
278315 privileged_transactions_hash,
279316 last_block_of_batch,
280- ) = self
281- . prepare_batch_from_block ( * last_block, batch_to_commit)
282- . await ?;
317+ ) = result?;
283318
284319 if * last_block == last_block_of_batch {
285320 debug ! ( "No new blocks to commit, skipping" ) ;
@@ -370,6 +405,8 @@ impl L1Committer {
370405 & mut self ,
371406 mut last_added_block_number : BlockNumber ,
372407 batch_number : u64 ,
408+ one_time_checkpoint_store : Store ,
409+ one_time_checkpoint_blockchain : Arc < Blockchain > ,
373410 ) -> Result < ( BlobsBundle , H256 , Vec < H256 > , H256 , BlockNumber ) , CommitterError > {
374411 let first_block_of_batch = last_added_block_number + 1 ;
375412 let mut blobs_bundle = BlobsBundle :: default ( ) ;
@@ -391,21 +428,6 @@ impl L1Committer {
391428
392429 info ! ( "Preparing state diff from block {first_block_of_batch}, {batch_number}" ) ;
393430
394- let one_time_checkpoint_path = self
395- . checkpoints_dir
396- . join ( format ! ( "temp_checkpoint_batch_{batch_number}" ) ) ;
397-
398- // For re-execution we need to use a checkpoint to the previous state
399- // (i.e. checkpoint of the state to the latest block from the previous
400- // batch, or the state of the genesis if this is the first batch).
401- // We already have this initial checkpoint as part of the L1Committer
402- // struct, but we need to create a one-time copy of it because
403- // we still need to use the current checkpoint store later for witness
404- // generation.
405- let ( one_time_checkpoint_store, one_time_checkpoint_blockchain) = self
406- . create_checkpoint ( & self . current_checkpoint_store , & one_time_checkpoint_path)
407- . await ?;
408-
409431 loop {
410432 let block_to_commit_number = last_added_block_number + 1 ;
411433
@@ -657,12 +679,6 @@ impl L1Committer {
657679 let privileged_transactions_hash =
658680 compute_privileged_transactions_hash ( privileged_transactions_hashes) ?;
659681
660- remove_dir_all ( & one_time_checkpoint_path) . map_err ( |e| {
661- CommitterError :: FailedToCreateCheckpoint ( format ! (
662- "Failed to remove one-time checkpoint directory {one_time_checkpoint_path:?}: {e}"
663- ) )
664- } ) ?;
665-
666682 Ok ( (
667683 blobs_bundle,
668684 new_state_root,
@@ -683,11 +699,30 @@ impl L1Committer {
683699 )
684700 . await ?;
685701
686- let batch_witness = self
687- . current_checkpoint_blockchain
702+ let rand_suffix: u32 = rand:: thread_rng ( ) . r#gen ( ) ;
703+ let one_time_checkpoint_path = self . checkpoints_dir . join ( format ! (
704+ "temp_checkpoint_witness_{}_{rand_suffix}" ,
705+ batch. number
706+ ) ) ;
707+ // We need to create a one-time checkpoint copy because if witness generation fails the checkpoint would be modified
708+ let ( _, one_time_checkpoint_blockchain) = self
709+ . create_checkpoint ( & self . current_checkpoint_store , & one_time_checkpoint_path)
710+ . await ?;
711+
712+ let result = one_time_checkpoint_blockchain
688713 . generate_witness_for_blocks_with_fee_configs ( & blocks, Some ( & fee_configs) )
689714 . await
690- . map_err ( CommitterError :: FailedToGenerateBatchWitness ) ?;
715+ . map_err ( CommitterError :: FailedToGenerateBatchWitness ) ;
716+
717+ if one_time_checkpoint_path. exists ( ) {
718+ let _ = remove_dir_all ( & one_time_checkpoint_path) . inspect_err ( |e| {
719+ error ! (
720+ "Failed to remove one-time checkpoint directory at path {one_time_checkpoint_path:?}. Should be removed manually. Error: {}" , e. to_string( )
721+ )
722+ } ) ;
723+ }
724+
725+ let batch_witness = result?;
691726
692727 // We still need to differentiate the validium case because for validium
693728 // we are generating the BlobsBundle with BlobsBundle::default which
@@ -752,7 +787,7 @@ impl L1Committer {
752787 // We need to skip checkpoint creation if the directory already exists.
753788 // Sometimes the commit_next_batch task is retried after a failure, and in
754789 // that case we would try to create a checkpoint again at the same path,
755- // causing an lock error under rocksdb feature.
790+ // causing a lock error under rocksdb feature.
756791 if new_checkpoint_path. exists ( ) {
757792 debug ! ( "Checkpoint at path {new_checkpoint_path:?} already exists, skipping creation" ) ;
758793 return Ok ( ( ) ) ;
@@ -775,7 +810,7 @@ impl L1Committer {
775810 /// 1. Creates a checkpoint of the provided store at the specified path.
776811 /// 2. Initializes a new store and blockchain for the checkpoint.
777812 /// 3. Regenerates the head state in the checkpoint store.
778- /// 4. Validates that the checkpoint store's head block number and latest block match those of the original store .
813+ /// 4. TODO: Validates that the checkpoint contains the needed state root .
779814 async fn create_checkpoint (
780815 & self ,
781816 checkpointee : & Store ,
@@ -803,54 +838,8 @@ impl L1Committer {
803838 self . blockchain . options . clone ( ) ,
804839 ) ) ;
805840
806- let checkpoint_head_block_number = checkpoint_store. get_latest_block_number ( ) . await ?;
807-
808- let db_head_block_number = checkpointee. get_latest_block_number ( ) . await ?;
809-
810- if checkpoint_head_block_number != db_head_block_number {
811- return Err ( CommitterError :: FailedToCreateCheckpoint (
812- "checkpoint store head block number does not match main store head block number before regeneration" . to_string ( ) ,
813- ) ) ;
814- }
815-
816841 regenerate_head_state ( & checkpoint_store, & checkpoint_blockchain) . await ?;
817842
818- let checkpoint_latest_block_number = checkpoint_store. get_latest_block_number ( ) . await ?;
819-
820- let db_latest_block_number = checkpointee. get_latest_block_number ( ) . await ?;
821-
822- let checkpoint_latest_block = checkpoint_store
823- . get_block_by_number ( checkpoint_latest_block_number)
824- . await ?
825- . ok_or ( CommitterError :: FailedToCreateCheckpoint (
826- "latest block not found in checkpoint store" . to_string ( ) ,
827- ) ) ?;
828-
829- let db_latest_block = checkpointee
830- . get_block_by_number ( db_latest_block_number)
831- . await ?
832- . ok_or ( CommitterError :: FailedToCreateCheckpoint (
833- "latest block not found in main store" . to_string ( ) ,
834- ) ) ?;
835-
836- if !checkpoint_store. has_state_root ( checkpoint_latest_block. header . state_root ) ? {
837- return Err ( CommitterError :: FailedToCreateCheckpoint (
838- "checkpoint store state is not regenerated properly" . to_string ( ) ,
839- ) ) ;
840- }
841-
842- if checkpoint_latest_block_number != db_head_block_number {
843- return Err ( CommitterError :: FailedToCreateCheckpoint (
844- "checkpoint store latest block number does not match main store head block number after regeneration" . to_string ( ) ,
845- ) ) ;
846- }
847-
848- if checkpoint_latest_block. hash ( ) != db_latest_block. hash ( ) {
849- return Err ( CommitterError :: FailedToCreateCheckpoint (
850- "checkpoint store latest block hash does not match main store latest block hash after regeneration" . to_string ( ) ,
851- ) ) ;
852- }
853-
854843 Ok ( ( checkpoint_store, checkpoint_blockchain) )
855844 }
856845
0 commit comments