@@ -201,13 +201,25 @@ impl Blockchain {
201201 blocks : & [ Block ] ,
202202 fee_configs : Option < & [ FeeConfig ] > ,
203203 ) -> Result < ExecutionWitness , ChainError > {
204- let first_block_header = blocks
204+ let first_block_header = & blocks
205205 . first ( )
206206 . ok_or ( ChainError :: WitnessGeneration (
207207 "Empty block batch" . to_string ( ) ,
208208 ) ) ?
209- . header
210- . clone ( ) ;
209+ . header ;
210+
211+ // Later on, we need to access block hashes by number. To avoid needing
212+ // to apply fork choice for each block, we cache them here.
213+ let mut block_hashes_map: BTreeMap < u64 , H256 > = blocks
214+ . iter ( )
215+ . cloned ( )
216+ . map ( |block| ( block. header . number , block. hash ( ) ) )
217+ . collect ( ) ;
218+
219+ block_hashes_map. insert (
220+ first_block_header. number . saturating_sub ( 1 ) ,
221+ first_block_header. parent_hash ,
222+ ) ;
211223
212224 // Get state at previous block
213225 let trie = self
@@ -216,7 +228,17 @@ impl Blockchain {
216228 . map_err ( |_| ChainError :: ParentStateNotFound ) ?
217229 . ok_or ( ChainError :: ParentStateNotFound ) ?;
218230
219- let ( state_trie_witness, mut trie) = TrieLogger :: open_trie ( trie) ;
231+ let ( mut current_trie_witness, mut trie) = TrieLogger :: open_trie ( trie) ;
232+
233+ // For each block, a new TrieLogger will be opened, each containing the
234+ // witness accessed during the block execution. We need to accumulate
235+ // all the nodes accessed during the entire batch execution.
236+ let mut accumulated_state_trie_witness = current_trie_witness
237+ . lock ( )
238+ . map_err ( |_| {
239+ ChainError :: WitnessGeneration ( "Failed to lock state trie witness" . to_string ( ) )
240+ } ) ?
241+ . clone ( ) ;
220242
221243 let mut touched_account_storage_slots = BTreeMap :: new ( ) ;
222244 // This will become the state trie + storage trie
@@ -232,10 +254,18 @@ impl Blockchain {
232254
233255 for ( i, block) in blocks. iter ( ) . enumerate ( ) {
234256 let parent_hash = block. header . parent_hash ;
257+
258+ // This assumes that the user has the necessary state stored already,
259+ // so if the user only has the state previous to the first block, it
260+ // will fail in the second iteration of this for loop. To ensure this,
261+ // doesn't fail, later in this function we store the new state after
262+ // re-execution.
235263 let vm_db: DynVmDatabase =
236264 Box :: new ( StoreVmDatabase :: new ( self . storage . clone ( ) , parent_hash) ) ;
265+
237266 let logger = Arc :: new ( DatabaseLogger :: new ( Arc :: new ( Mutex :: new ( Box :: new ( vm_db) ) ) ) ) ;
238- let mut vm = match & self . options . r#type {
267+
268+ let mut vm = match self . options . r#type {
239269 BlockchainType :: L1 => Evm :: new_from_db_for_l1 ( logger. clone ( ) ) ,
240270 BlockchainType :: L2 ( _) => {
241271 let l2_config = match fee_configs {
@@ -253,7 +283,8 @@ impl Blockchain {
253283 } ;
254284
255285 // Re-execute block with logger
256- vm. execute_block ( block) ?;
286+ let execution_result = vm. execute_block ( block) ?;
287+
257288 // Gather account updates
258289 let account_updates = vm. get_state_transitions ( ) ?;
259290
@@ -276,7 +307,9 @@ impl Blockchain {
276307 ChainError :: WitnessGeneration ( "Failed to get block hashes" . to_string ( ) )
277308 } ) ?
278309 . clone ( ) ;
310+
279311 block_hashes. extend ( logger_block_hashes) ;
312+
280313 // Access all the accounts needed for withdrawals
281314 if let Some ( withdrawals) = block. body . withdrawals . as_ref ( ) {
282315 for withdrawal in withdrawals {
@@ -320,6 +353,7 @@ impl Blockchain {
320353 used_storage_tries. insert ( * account, ( storage_trie_witness, storage_trie) ) ;
321354 }
322355 }
356+
323357 // Store all the accessed evm bytecodes
324358 for code_hash in logger
325359 . code_accessed
@@ -342,14 +376,21 @@ impl Blockchain {
342376 }
343377
344378 // Apply account updates to the trie recording all the necessary nodes to do so
345- let ( updated_trie , storage_tries_after_update ) = self
379+ let ( storage_tries_after_update , account_updates_list ) = self
346380 . storage
347381 . apply_account_updates_from_trie_with_witness (
348382 trie,
349383 & account_updates,
350384 used_storage_tries,
351385 )
352386 . await ?;
387+
388+ // We cannot ensure that the users of this function have the necessary
389+ // state stored, so in order for it to not assume anything, we update
390+ // the storage with the new state after re-execution
391+ self . store_block ( block. clone ( ) , account_updates_list, execution_result)
392+ . await ?;
393+
353394 for ( address, ( witness, _storage_trie) ) in storage_tries_after_update {
354395 let mut witness = witness. lock ( ) . map_err ( |_| {
355396 ChainError :: WitnessGeneration ( "Failed to lock storage trie witness" . to_string ( ) )
@@ -359,15 +400,40 @@ impl Blockchain {
359400 used_trie_nodes. extend_from_slice ( & witness) ;
360401 touched_account_storage_slots. entry ( address) . or_default ( ) ;
361402 }
403+
404+ let ( new_state_trie_witness, updated_trie) = TrieLogger :: open_trie (
405+ self . storage
406+ . state_trie (
407+ block_hashes_map
408+ . get ( & block. header . number )
409+ . ok_or ( ChainError :: WitnessGeneration (
410+ "Block hash not found for witness generation" . to_string ( ) ,
411+ ) ) ?
412+ . to_owned ( ) ,
413+ )
414+ . map_err ( |_| ChainError :: ParentStateNotFound ) ?
415+ . ok_or ( ChainError :: ParentStateNotFound ) ?,
416+ ) ;
417+
418+ // Use the updated state trie for the next block
362419 trie = updated_trie;
420+
421+ for state_trie_witness in current_trie_witness
422+ . lock ( )
423+ . map_err ( |_| {
424+ ChainError :: WitnessGeneration ( "Failed to lock state trie witness" . to_string ( ) )
425+ } ) ?
426+ . iter ( )
427+ {
428+ accumulated_state_trie_witness. insert ( state_trie_witness. clone ( ) ) ;
429+ }
430+
431+ current_trie_witness = new_state_trie_witness;
363432 }
364433
365- // Get the witness for the state trie
366- let mut state_trie_witness = state_trie_witness. lock ( ) . map_err ( |_| {
367- ChainError :: WitnessGeneration ( "Failed to lock state trie witness" . to_string ( ) )
368- } ) ?;
369- let state_trie_witness = std:: mem:: take ( & mut * state_trie_witness) ;
370- used_trie_nodes. extend_from_slice ( & Vec :: from_iter ( state_trie_witness. into_iter ( ) ) ) ;
434+ used_trie_nodes
435+ . extend_from_slice ( & Vec :: from_iter ( accumulated_state_trie_witness. into_iter ( ) ) ) ;
436+
371437 // If the witness is empty at least try to store the root
372438 if used_trie_nodes. is_empty ( )
373439 && let Some ( root) = root_node
@@ -376,6 +442,7 @@ impl Blockchain {
376442 }
377443
378444 let mut needed_block_numbers = block_hashes. keys ( ) . collect :: < Vec < _ > > ( ) ;
445+
379446 needed_block_numbers. sort ( ) ;
380447
381448 // Last needed block header for the witness is the parent of the last block we need to execute
@@ -385,17 +452,26 @@ impl Blockchain {
385452 . header
386453 . number
387454 . saturating_sub ( 1 ) ;
455+
388456 // The first block number we need is either the parent of the first block number or the earliest block number used by BLOCKHASH
389457 let mut first_needed_block_number = first_block_header. number . saturating_sub ( 1 ) ;
458+
390459 if let Some ( block_number_from_logger) = needed_block_numbers. first ( )
391460 && * * block_number_from_logger < first_needed_block_number
392461 {
393462 first_needed_block_number = * * block_number_from_logger;
394463 }
464+
395465 let mut block_headers_bytes = Vec :: new ( ) ;
466+
396467 for block_number in first_needed_block_number..=last_needed_block_number {
397- let header = self . storage . get_block_header ( block_number) ?. ok_or (
398- ChainError :: WitnessGeneration ( "Failed to get block header" . to_string ( ) ) ,
468+ let hash = block_hashes_map
469+ . get ( & block_number)
470+ . ok_or ( ChainError :: WitnessGeneration ( format ! (
471+ "Failed to get block {block_number} hash"
472+ ) ) ) ?;
473+ let header = self . storage . get_block_header_by_hash ( * hash) ?. ok_or (
474+ ChainError :: WitnessGeneration ( format ! ( "Failed to get block {block_number} header" ) ) ,
399475 ) ?;
400476 block_headers_bytes. push ( header. encode_to_vec ( ) ) ;
401477 }
0 commit comments