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