1use crate::{DBProvider, DatabaseProviderRW, ExecutionOutcome};
3use alloy_consensus::{TxLegacy, EMPTY_OMMER_ROOT_HASH};
4use alloy_primitives::{
5 b256, hex_literal::hex, map::HashMap, Address, BlockNumber, Bytes, Log, TxKind, B256, U256,
6};
7
8use alloy_consensus::Header;
9use alloy_eips::eip4895::{Withdrawal, Withdrawals};
10use alloy_primitives::Signature;
11use reth_db_api::{database::Database, models::StoredBlockBodyIndices, tables};
12use reth_ethereum_primitives::{BlockBody, Receipt, Transaction, TransactionSigned, TxType};
13use reth_node_types::NodeTypes;
14use reth_primitives_traits::{Account, RecoveredBlock, SealedBlock, SealedHeader};
15use reth_trie::root::{state_root_unhashed, storage_root_unhashed};
16use revm_database::BundleState;
17use revm_state::{AccountInfo, FlaggedStorage};
18use std::{str::FromStr, sync::LazyLock};
19
20pub fn assert_genesis_block<DB: Database, N: NodeTypes>(
22 provider: &DatabaseProviderRW<DB, N>,
23 g: SealedBlock<reth_ethereum_primitives::Block>,
24) {
25 let n = g.number;
26 let h = B256::ZERO;
27 let tx = provider;
28
29 assert_eq!(tx.table::<tables::Headers>().unwrap(), vec![(g.number, g.header().clone())]);
31
32 assert_eq!(tx.table::<tables::HeaderNumbers>().unwrap(), vec![(h, n)]);
33 assert_eq!(tx.table::<tables::CanonicalHeaders>().unwrap(), vec![(n, h)]);
34 assert_eq!(
35 tx.table::<tables::HeaderTerminalDifficulties>().unwrap(),
36 vec![(n, g.difficulty.into())]
37 );
38 assert_eq!(
39 tx.table::<tables::BlockBodyIndices>().unwrap(),
40 vec![(0, StoredBlockBodyIndices::default())]
41 );
42 assert_eq!(tx.table::<tables::BlockOmmers>().unwrap(), vec![]);
43 assert_eq!(tx.table::<tables::BlockWithdrawals>().unwrap(), vec![]);
44 assert_eq!(tx.table::<tables::Transactions>().unwrap(), vec![]);
45 assert_eq!(tx.table::<tables::TransactionBlocks>().unwrap(), vec![]);
46 assert_eq!(tx.table::<tables::TransactionHashNumbers>().unwrap(), vec![]);
47 assert_eq!(tx.table::<tables::Receipts>().unwrap(), vec![]);
48 assert_eq!(tx.table::<tables::PlainAccountState>().unwrap(), vec![]);
49 assert_eq!(tx.table::<tables::PlainStorageState>().unwrap(), vec![]);
50 assert_eq!(tx.table::<tables::AccountsHistory>().unwrap(), vec![]);
51 assert_eq!(tx.table::<tables::StoragesHistory>().unwrap(), vec![]);
52 assert_eq!(tx.table::<tables::AccountChangeSets>().unwrap(), vec![]);
55 assert_eq!(tx.table::<tables::StorageChangeSets>().unwrap(), vec![]);
56 assert_eq!(tx.table::<tables::HashedAccounts>().unwrap(), vec![]);
57 assert_eq!(tx.table::<tables::HashedStorages>().unwrap(), vec![]);
58 assert_eq!(tx.table::<tables::AccountsTrie>().unwrap(), vec![]);
59 assert_eq!(tx.table::<tables::StoragesTrie>().unwrap(), vec![]);
60 assert_eq!(tx.table::<tables::TransactionSenders>().unwrap(), vec![]);
61 }
63
64pub(crate) static TEST_BLOCK: LazyLock<SealedBlock<reth_ethereum_primitives::Block>> =
65 LazyLock::new(|| {
66 SealedBlock::from_sealed_parts(
67 SealedHeader::new(
68 Header {
69 parent_hash: hex!(
70 "c86e8cc0310ae7c531c758678ddbfd16fc51c8cef8cec650b032de9869e8b94f"
71 )
72 .into(),
73 ommers_hash: EMPTY_OMMER_ROOT_HASH,
74 beneficiary: hex!("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba").into(),
75 state_root: hex!(
76 "50554882fbbda2c2fd93fdc466db9946ea262a67f7a76cc169e714f105ab583d"
77 )
78 .into(),
79 transactions_root: hex!(
80 "0967f09ef1dfed20c0eacfaa94d5cd4002eda3242ac47eae68972d07b106d192"
81 )
82 .into(),
83 receipts_root: hex!(
84 "e3c8b47fbfc94667ef4cceb17e5cc21e3b1eebd442cebb27f07562b33836290d"
85 )
86 .into(),
87 difficulty: U256::from(131_072),
88 number: 1,
89 gas_limit: 1_000_000,
90 gas_used: 14_352,
91 timestamp: 1_000,
92 ..Default::default()
93 },
94 hex!("cf7b274520720b50e6a4c3e5c4d553101f44945396827705518ce17cb7219a42").into(),
95 ),
96 BlockBody {
97 transactions: vec![TransactionSigned::new_unhashed(
98 Transaction::Legacy(TxLegacy {
99 gas_price: 10,
100 gas_limit: 400_000,
101 to: TxKind::Call(hex!("095e7baea6a6c7c4c2dfeb977efac326af552d87").into()),
102 ..Default::default()
103 }),
104 Signature::new(
105 U256::from_str(
106 "51983300959770368863831494747186777928121405155922056726144551509338672451120",
107 )
108 .unwrap(),
109 U256::from_str(
110 "29056683545955299640297374067888344259176096769870751649153779895496107008675",
111 )
112 .unwrap(),
113 false,
114 )
115 )],
116 ..Default::default()
117 },
118 )
119 });
120
121#[derive(Debug)]
124pub struct BlockchainTestData {
125 pub genesis: SealedBlock<reth_ethereum_primitives::Block>,
127 pub blocks: Vec<(RecoveredBlock<reth_ethereum_primitives::Block>, ExecutionOutcome)>,
129}
130
131impl BlockchainTestData {
132 pub fn default_from_number(first: BlockNumber) -> Self {
134 let one = block1(first);
135 let mut extended_execution_outcome = one.1.clone();
136 let two = block2(first + 1, one.0.hash(), &extended_execution_outcome);
137 extended_execution_outcome.extend(two.1.clone());
138 let three = block3(first + 2, two.0.hash(), &extended_execution_outcome);
139 extended_execution_outcome.extend(three.1.clone());
140 let four = block4(first + 3, three.0.hash(), &extended_execution_outcome);
141 extended_execution_outcome.extend(four.1.clone());
142 let five = block5(first + 4, four.0.hash(), &extended_execution_outcome);
143 Self { genesis: genesis(), blocks: vec![one, two, three, four, five] }
144 }
145}
146
147impl Default for BlockchainTestData {
148 fn default() -> Self {
149 let one = block1(1);
150 let mut extended_execution_outcome = one.1.clone();
151 let two = block2(2, one.0.hash(), &extended_execution_outcome);
152 extended_execution_outcome.extend(two.1.clone());
153 let three = block3(3, two.0.hash(), &extended_execution_outcome);
154 extended_execution_outcome.extend(three.1.clone());
155 let four = block4(4, three.0.hash(), &extended_execution_outcome);
156 extended_execution_outcome.extend(four.1.clone());
157 let five = block5(5, four.0.hash(), &extended_execution_outcome);
158 Self { genesis: genesis(), blocks: vec![one, two, three, four, five] }
159 }
160}
161
162pub fn genesis() -> SealedBlock<reth_ethereum_primitives::Block> {
164 SealedBlock::from_sealed_parts(
165 SealedHeader::new(
166 Header { number: 0, difficulty: U256::from(1), ..Default::default() },
167 B256::ZERO,
168 ),
169 Default::default(),
170 )
171}
172
173fn bundle_state_root(execution_outcome: &ExecutionOutcome) -> B256 {
174 state_root_unhashed(execution_outcome.bundle_accounts_iter().filter_map(
175 |(address, account)| {
176 account.info.as_ref().map(|info| {
177 (
178 address,
179 Account::from(info).into_trie_account(storage_root_unhashed(
180 account
181 .storage
182 .iter()
183 .filter(|(_, value)| !value.present_value.is_zero())
184 .map(|(slot, value)| {
185 (
186 (*slot).into(),
187 (value.present_value.value, value.present_value.is_private),
188 )
189 }),
190 )),
191 )
192 })
193 },
194 ))
195}
196
197fn block1(
199 number: BlockNumber,
200) -> (RecoveredBlock<reth_ethereum_primitives::Block>, ExecutionOutcome) {
201 let account1: Address = [0x60; 20].into();
203 let account2: Address = [0x61; 20].into();
204 let slot = U256::from(5);
205 let info = AccountInfo { nonce: 1, balance: U256::from(10), ..Default::default() };
206
207 let execution_outcome = ExecutionOutcome::new(
208 BundleState::builder(number..=number)
209 .state_present_account_info(account1, info.clone())
210 .revert_account_info(number, account1, Some(None))
211 .state_present_account_info(account2, info)
212 .revert_account_info(number, account2, Some(None))
213 .state_storage(
214 account1,
215 HashMap::from_iter([(
216 slot,
217 (FlaggedStorage::ZERO, FlaggedStorage::new_from_value(10)),
218 )]),
219 )
220 .build(),
221 vec![vec![Receipt {
222 tx_type: TxType::Eip2930,
223 success: true,
224 cumulative_gas_used: 300,
225 logs: vec![Log::new_unchecked(
226 Address::new([0x60; 20]),
227 vec![B256::with_last_byte(1), B256::with_last_byte(2)],
228 Bytes::default(),
229 )],
230 }]],
231 number,
232 Vec::new(),
233 );
234
235 let state_root = bundle_state_root(&execution_outcome);
236 assert_eq!(
237 state_root,
238 b256!("0x5d035ccb3e75a9057452ff060b773b213ec1fc353426174068edfc3971a0b6bd")
239 );
240
241 let (mut header, mut body) = TEST_BLOCK.clone().split_header_body();
242 body.withdrawals = Some(Withdrawals::new(vec![Withdrawal::default()]));
243 header.number = number;
244 header.state_root = state_root;
245 header.parent_hash = B256::ZERO;
246 let block = SealedBlock::seal_parts(header, body);
247
248 (RecoveredBlock::new_sealed(block, vec![Address::new([0x30; 20])]), execution_outcome)
249}
250
251fn block2(
253 number: BlockNumber,
254 parent_hash: B256,
255 prev_execution_outcome: &ExecutionOutcome,
256) -> (RecoveredBlock<reth_ethereum_primitives::Block>, ExecutionOutcome) {
257 let account: Address = [0x60; 20].into();
259 let slot = U256::from(5);
260
261 let execution_outcome = ExecutionOutcome::new(
262 BundleState::builder(number..=number)
263 .state_present_account_info(
264 account,
265 AccountInfo { nonce: 3, balance: U256::from(20), ..Default::default() },
266 )
267 .state_storage(
268 account,
269 HashMap::from_iter([(
270 slot,
271 (FlaggedStorage::ZERO, FlaggedStorage::new_from_value(15)),
272 )]),
273 )
274 .revert_account_info(
275 number,
276 account,
277 Some(Some(AccountInfo { nonce: 1, balance: U256::from(10), ..Default::default() })),
278 )
279 .revert_storage(
280 number,
281 account,
282 Vec::from([(slot, FlaggedStorage::new_from_value(10))]),
283 )
284 .build(),
285 vec![vec![Receipt {
286 tx_type: TxType::Eip1559,
287 success: false,
288 cumulative_gas_used: 400,
289 logs: vec![Log::new_unchecked(
290 Address::new([0x61; 20]),
291 vec![B256::with_last_byte(3), B256::with_last_byte(4)],
292 Bytes::default(),
293 )],
294 }]],
295 number,
296 Vec::new(),
297 );
298
299 let mut extended = prev_execution_outcome.clone();
300 extended.extend(execution_outcome.clone());
301 let state_root = bundle_state_root(&extended);
302 assert_eq!(
303 state_root,
304 b256!("0x90101a13dd059fa5cca99ed93d1dc23657f63626c5b8f993a2ccbdf7446b64f8")
305 );
306
307 let (mut header, mut body) = TEST_BLOCK.clone().split_header_body();
308
309 body.withdrawals = Some(Withdrawals::new(vec![Withdrawal::default()]));
310 header.number = number;
311 header.state_root = state_root;
312 header.parent_hash = parent_hash;
314 let block = SealedBlock::seal_parts(header, body);
315
316 (RecoveredBlock::new_sealed(block, vec![Address::new([0x31; 20])]), execution_outcome)
317}
318
319fn block3(
321 number: BlockNumber,
322 parent_hash: B256,
323 prev_execution_outcome: &ExecutionOutcome,
324) -> (RecoveredBlock<reth_ethereum_primitives::Block>, ExecutionOutcome) {
325 let address_range = 1..=20;
326 let slot_range = 1..=100;
327
328 let mut bundle_state_builder = BundleState::builder(number..=number);
329 for idx in address_range {
330 let address = Address::with_last_byte(idx);
331 bundle_state_builder = bundle_state_builder
332 .state_present_account_info(
333 address,
334 AccountInfo { nonce: 1, balance: U256::from(idx), ..Default::default() },
335 )
336 .state_storage(
337 address,
338 HashMap::from_iter(slot_range.clone().map(|slot| {
339 (U256::from(slot), (FlaggedStorage::ZERO, FlaggedStorage::new_from_value(slot)))
340 })),
341 )
342 .revert_account_info(number, address, Some(None))
343 .revert_storage(number, address, Vec::new());
344 }
345 let execution_outcome = ExecutionOutcome::new(
346 bundle_state_builder.build(),
347 vec![vec![Receipt {
348 tx_type: TxType::Eip1559,
349 success: true,
350 cumulative_gas_used: 400,
351 logs: vec![Log::new_unchecked(
352 Address::new([0x61; 20]),
353 vec![B256::with_last_byte(3), B256::with_last_byte(4)],
354 Bytes::default(),
355 )],
356 }]],
357 number,
358 Vec::new(),
359 );
360
361 let mut extended = prev_execution_outcome.clone();
362 extended.extend(execution_outcome.clone());
363 let state_root = bundle_state_root(&extended);
364
365 let (mut header, mut body) = TEST_BLOCK.clone().split_header_body();
366 body.withdrawals = Some(Withdrawals::new(vec![Withdrawal::default()]));
367 header.number = number;
368 header.state_root = state_root;
369 header.parent_hash = parent_hash;
371 let block = SealedBlock::seal_parts(header, body);
372
373 (RecoveredBlock::new_sealed(block, vec![Address::new([0x31; 20])]), execution_outcome)
374}
375
376fn block4(
378 number: BlockNumber,
379 parent_hash: B256,
380 prev_execution_outcome: &ExecutionOutcome,
381) -> (RecoveredBlock<reth_ethereum_primitives::Block>, ExecutionOutcome) {
382 let address_range = 1..=20;
383 let slot_range = 1..=100;
384
385 let mut bundle_state_builder = BundleState::builder(number..=number);
386 for idx in address_range {
387 let address = Address::with_last_byte(idx);
388 bundle_state_builder = if idx % 2 == 0 {
390 bundle_state_builder
391 .state_present_account_info(
392 address,
393 AccountInfo { nonce: 1, balance: U256::from(idx * 2), ..Default::default() },
394 )
395 .state_storage(
396 address,
397 HashMap::from_iter(slot_range.clone().map(|slot| {
398 (
399 U256::from(slot),
400 (
401 FlaggedStorage::new_from_value(slot),
402 FlaggedStorage::new_from_value(slot * 2),
403 ),
404 )
405 })),
406 )
407 } else {
408 bundle_state_builder.state_address(address).state_storage(
409 address,
410 HashMap::from_iter(slot_range.clone().map(|slot| {
411 (U256::from(slot), (FlaggedStorage::new_from_value(slot), FlaggedStorage::ZERO))
412 })),
413 )
414 };
415 bundle_state_builder = bundle_state_builder
417 .revert_account_info(
418 number,
419 address,
420 Some(Some(AccountInfo {
421 nonce: 1,
422 balance: U256::from(idx),
423 ..Default::default()
424 })),
425 )
426 .revert_storage(
427 number,
428 address,
429 Vec::from_iter(
430 slot_range
431 .clone()
432 .map(|slot| (U256::from(slot), FlaggedStorage::new_from_value(slot))),
433 ),
434 );
435 }
436 let execution_outcome = ExecutionOutcome::new(
437 bundle_state_builder.build(),
438 vec![vec![Receipt {
439 tx_type: TxType::Eip1559,
440 success: true,
441 cumulative_gas_used: 400,
442 logs: vec![Log::new_unchecked(
443 Address::new([0x61; 20]),
444 vec![B256::with_last_byte(3), B256::with_last_byte(4)],
445 Bytes::default(),
446 )],
447 }]],
448 number,
449 Vec::new(),
450 );
451
452 let mut extended = prev_execution_outcome.clone();
453 extended.extend(execution_outcome.clone());
454 let state_root = bundle_state_root(&extended);
455
456 let (mut header, mut body) = TEST_BLOCK.clone().split_header_body();
457 body.withdrawals = Some(Withdrawals::new(vec![Withdrawal::default()]));
458 header.number = number;
459 header.state_root = state_root;
460 header.parent_hash = parent_hash;
462 let block = SealedBlock::seal_parts(header, body);
463
464 (RecoveredBlock::new_sealed(block, vec![Address::new([0x31; 20])]), execution_outcome)
465}
466
467fn block5(
469 number: BlockNumber,
470 parent_hash: B256,
471 prev_execution_outcome: &ExecutionOutcome,
472) -> (RecoveredBlock<reth_ethereum_primitives::Block>, ExecutionOutcome) {
473 let address_range = 1..=20;
474 let slot_range = 1..=100;
475
476 let mut bundle_state_builder = BundleState::builder(number..=number);
477 for idx in address_range {
478 let address = Address::with_last_byte(idx);
479 bundle_state_builder = bundle_state_builder
481 .state_present_account_info(
482 address,
483 AccountInfo { nonce: 1, balance: U256::from(idx * 2), ..Default::default() },
484 )
485 .state_storage(
486 address,
487 HashMap::from_iter(slot_range.clone().take(50).map(|slot| {
488 (
489 U256::from(slot),
490 (
491 FlaggedStorage::new_from_value(slot),
492 FlaggedStorage::new_from_value(slot * 4),
493 ),
494 )
495 })),
496 );
497 bundle_state_builder =
498 if idx % 2 == 0 {
499 bundle_state_builder
500 .revert_account_info(
501 number,
502 address,
503 Some(Some(AccountInfo {
504 nonce: 1,
505 balance: U256::from(idx * 2),
506 ..Default::default()
507 })),
508 )
509 .revert_storage(
510 number,
511 address,
512 Vec::from_iter(slot_range.clone().map(|slot| {
513 (U256::from(slot), FlaggedStorage::new_from_value(slot * 2))
514 })),
515 )
516 } else {
517 bundle_state_builder.revert_address(number, address)
518 };
519 }
520 let execution_outcome = ExecutionOutcome::new(
521 bundle_state_builder.build(),
522 vec![vec![Receipt {
523 tx_type: TxType::Eip1559,
524 success: true,
525 cumulative_gas_used: 400,
526 logs: vec![Log::new_unchecked(
527 Address::new([0x61; 20]),
528 vec![B256::with_last_byte(3), B256::with_last_byte(4)],
529 Bytes::default(),
530 )],
531 }]],
532 number,
533 Vec::new(),
534 );
535
536 let mut extended = prev_execution_outcome.clone();
537 extended.extend(execution_outcome.clone());
538 let state_root = bundle_state_root(&extended);
539
540 let (mut header, mut body) = TEST_BLOCK.clone().split_header_body();
541 body.withdrawals = Some(Withdrawals::new(vec![Withdrawal::default()]));
542 header.number = number;
543 header.state_root = state_root;
544 header.parent_hash = parent_hash;
546 let block = SealedBlock::seal_parts(header, body);
547
548 (RecoveredBlock::new_sealed(block, vec![Address::new([0x31; 20])]), execution_outcome)
549}