@@ -10,15 +10,15 @@ use error::MempoolError;
10
10
use error:: { ChainError , InvalidBlockError } ;
11
11
use ethrex_common:: constants:: { GAS_PER_BLOB , MIN_BASE_FEE_PER_BLOB_GAS } ;
12
12
use ethrex_common:: types:: requests:: { compute_requests_hash, EncodedRequests , Requests } ;
13
- use ethrex_common:: types:: BlobsBundle ;
14
13
use ethrex_common:: types:: MempoolTransaction ;
15
14
use ethrex_common:: types:: {
16
15
compute_receipts_root, validate_block_header, validate_cancun_header_fields,
17
16
validate_prague_header_fields, validate_pre_cancun_header_fields, Block , BlockHash ,
18
17
BlockHeader , BlockNumber , ChainConfig , EIP4844Transaction , Receipt , Transaction ,
19
18
} ;
19
+ use ethrex_common:: types:: { BlobsBundle , Fork } ;
20
20
21
- use ethrex_common:: { Address , H160 , H256 } ;
21
+ use ethrex_common:: { Address , H256 } ;
22
22
use mempool:: Mempool ;
23
23
use std:: collections:: HashMap ;
24
24
use std:: { ops:: Div , time:: Instant } ;
@@ -62,14 +62,18 @@ impl Blockchain {
62
62
}
63
63
64
64
/// Executes a block withing a new vm instance and state
65
- async fn execute_block ( & self , block : & Block ) -> Result < BlockExecutionResult , ChainError > {
65
+ async fn execute_block (
66
+ & self ,
67
+ block : & Block ,
68
+ ) -> Result < ( BlockExecutionResult , Vec < AccountUpdate > ) , ChainError > {
66
69
// Validate if it can be the new head and find the parent
67
70
let Ok ( parent_header) = find_parent_header ( & block. header , & self . storage ) else {
68
71
// If the parent is not present, we store it as pending.
69
72
self . storage . add_pending_block ( block. clone ( ) ) . await ?;
70
73
return Err ( ChainError :: ParentNotFound ) ;
71
74
} ;
72
75
let chain_config = self . storage . get_chain_config ( ) ?;
76
+ let fork = chain_config. fork ( block. header . timestamp ) ;
73
77
74
78
// Validate the block pre-execution
75
79
validate_block ( block, & parent_header, & chain_config) ?;
@@ -80,13 +84,14 @@ impl Blockchain {
80
84
block. header . parent_hash ,
81
85
) ;
82
86
let execution_result = vm. execute_block ( block) ?;
87
+ let account_updates = vm. get_state_transitions ( fork) ?;
83
88
84
89
// Validate execution went alright
85
90
validate_gas_used ( & execution_result. receipts , & block. header ) ?;
86
91
validate_receipts_root ( & block. header , & execution_result. receipts ) ?;
87
92
validate_requests_hash ( & block. header , & chain_config, & execution_result. requests ) ?;
88
93
89
- Ok ( execution_result)
94
+ Ok ( ( execution_result, account_updates ) )
90
95
}
91
96
92
97
/// Executes a block from a given vm instance an does not clear its state
@@ -114,11 +119,12 @@ impl Blockchain {
114
119
& self ,
115
120
block : & Block ,
116
121
execution_result : BlockExecutionResult ,
122
+ account_updates : & [ AccountUpdate ] ,
117
123
) -> Result < ( ) , ChainError > {
118
124
// Apply the account updates over the last block's state and compute the new state root
119
125
let new_state_root = self
120
126
. storage
121
- . apply_account_updates ( block. header . parent_hash , & execution_result . account_updates )
127
+ . apply_account_updates ( block. header . parent_hash , account_updates)
122
128
. await ?
123
129
. ok_or ( ChainError :: ParentStateNotFound ) ?;
124
130
@@ -137,9 +143,9 @@ impl Blockchain {
137
143
138
144
pub async fn add_block ( & self , block : & Block ) -> Result < ( ) , ChainError > {
139
145
let since = Instant :: now ( ) ;
140
- let res = self . execute_block ( block) . await ?;
146
+ let ( res, updates ) = self . execute_block ( block) . await ?;
141
147
let executed = Instant :: now ( ) ;
142
- let result = self . store_block ( block, res) . await ;
148
+ let result = self . store_block ( block, res, & updates ) . await ;
143
149
let stored = Instant :: now ( ) ;
144
150
Self :: print_add_block_logs ( block, since, executed, stored) ;
145
151
result
@@ -197,6 +203,8 @@ impl Blockchain {
197
203
. storage
198
204
. get_chain_config ( )
199
205
. map_err ( |e| ( e. into ( ) , None ) ) ?;
206
+ let fork = chain_config. fork ( first_block_header. timestamp ) ;
207
+
200
208
let mut vm = Evm :: new (
201
209
self . evm_engine ,
202
210
self . storage . clone ( ) ,
@@ -205,12 +213,20 @@ impl Blockchain {
205
213
206
214
let blocks_len = blocks. len ( ) ;
207
215
let mut all_receipts: HashMap < BlockHash , Vec < Receipt > > = HashMap :: new ( ) ;
208
- let mut all_account_updates: HashMap < H160 , AccountUpdate > = HashMap :: new ( ) ;
209
216
let mut total_gas_used = 0 ;
210
217
let mut transactions_count = 0 ;
211
218
212
219
let interval = Instant :: now ( ) ;
213
220
for ( i, block) in blocks. iter ( ) . enumerate ( ) {
221
+ if is_crossing_spuriousdragon ( fork, chain_config. fork ( block. header . timestamp ) ) {
222
+ return Err ( (
223
+ ChainError :: Custom ( "Crossing fork boundary in bulk mode" . into ( ) ) ,
224
+ Some ( BatchBlockProcessingFailure {
225
+ last_valid_hash,
226
+ failed_block_hash : block. hash ( ) ,
227
+ } ) ,
228
+ ) ) ;
229
+ }
214
230
// for the first block, we need to query the store
215
231
let parent_header = if i == 0 {
216
232
let Ok ( parent_header) = find_parent_header ( & block. header , & self . storage ) else {
@@ -228,11 +244,12 @@ impl Blockchain {
228
244
blocks[ i - 1 ] . header . clone ( )
229
245
} ;
230
246
231
- let BlockExecutionResult {
232
- receipts,
233
- account_updates,
234
- ..
235
- } = match self . execute_block_from_state ( & parent_header, block, & chain_config, & mut vm) {
247
+ let BlockExecutionResult { receipts, .. } = match self . execute_block_from_state (
248
+ & parent_header,
249
+ block,
250
+ & chain_config,
251
+ & mut vm,
252
+ ) {
236
253
Ok ( result) => result,
237
254
Err ( err) => {
238
255
return Err ( (
@@ -245,44 +262,24 @@ impl Blockchain {
245
262
}
246
263
} ;
247
264
248
- // Merge account updates
249
- for account_update in account_updates {
250
- let Some ( cache) = all_account_updates. get_mut ( & account_update. address ) else {
251
- all_account_updates. insert ( account_update. address , account_update) ;
252
- continue ;
253
- } ;
254
-
255
- cache. removed = account_update. removed ;
256
- if let Some ( code) = account_update. code {
257
- cache. code = Some ( code) ;
258
- } ;
259
-
260
- if let Some ( info) = account_update. info {
261
- cache. info = Some ( info) ;
262
- }
263
-
264
- for ( k, v) in account_update. added_storage . into_iter ( ) {
265
- cache. added_storage . insert ( k, v) ;
266
- }
267
- }
268
-
269
265
last_valid_hash = block. hash ( ) ;
270
266
total_gas_used += block. header . gas_used ;
271
267
transactions_count += block. body . transactions . len ( ) ;
272
268
all_receipts. insert ( block. hash ( ) , receipts) ;
273
269
}
274
270
271
+ let account_updates = vm
272
+ . get_state_transitions ( fork)
273
+ . map_err ( |err| ( ChainError :: EvmError ( err) , None ) ) ?;
274
+
275
275
let Some ( last_block) = blocks. last ( ) else {
276
276
return Err ( ( ChainError :: Custom ( "Last block not found" . into ( ) ) , None ) ) ;
277
277
} ;
278
278
279
279
// Apply the account updates over all blocks and compute the new state root
280
280
let new_state_root = self
281
281
. storage
282
- . apply_account_updates (
283
- first_block_header. parent_hash ,
284
- & all_account_updates. into_values ( ) . collect :: < Vec < _ > > ( ) ,
285
- )
282
+ . apply_account_updates ( first_block_header. parent_hash , & account_updates)
286
283
. await
287
284
. map_err ( |e| ( e. into ( ) , None ) ) ?
288
285
. ok_or ( ( ChainError :: ParentStateNotFound , None ) ) ?;
@@ -653,5 +650,15 @@ fn get_total_blob_gas(tx: &EIP4844Transaction) -> u64 {
653
650
GAS_PER_BLOB * tx. blob_versioned_hashes . len ( ) as u64
654
651
}
655
652
653
+ fn is_crossing_spuriousdragon ( from : Fork , to : Fork ) -> bool {
654
+ if from >= Fork :: SpuriousDragon {
655
+ return false ;
656
+ }
657
+ if to < Fork :: SpuriousDragon {
658
+ return false ;
659
+ }
660
+ from != to
661
+ }
662
+
656
663
#[ cfg( test) ]
657
664
mod tests { }
0 commit comments