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;
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)| ((*slot).into(), value.present_value)),
185 )),
186 )
187 })
188 },
189 ))
190}
191
192fn block1(
194 number: BlockNumber,
195) -> (RecoveredBlock<reth_ethereum_primitives::Block>, ExecutionOutcome) {
196 let account1: Address = [0x60; 20].into();
198 let account2: Address = [0x61; 20].into();
199 let slot = U256::from(5);
200 let info = AccountInfo { nonce: 1, balance: U256::from(10), ..Default::default() };
201
202 let execution_outcome = ExecutionOutcome::new(
203 BundleState::builder(number..=number)
204 .state_present_account_info(account1, info.clone())
205 .revert_account_info(number, account1, Some(None))
206 .state_present_account_info(account2, info)
207 .revert_account_info(number, account2, Some(None))
208 .state_storage(account1, HashMap::from_iter([(slot, (U256::ZERO, U256::from(10)))]))
209 .build(),
210 vec![vec![Receipt {
211 tx_type: TxType::Eip2930,
212 success: true,
213 cumulative_gas_used: 300,
214 logs: vec![Log::new_unchecked(
215 Address::new([0x60; 20]),
216 vec![B256::with_last_byte(1), B256::with_last_byte(2)],
217 Bytes::default(),
218 )],
219 }]],
220 number,
221 Vec::new(),
222 );
223
224 let state_root = bundle_state_root(&execution_outcome);
225 assert_eq!(
226 state_root,
227 b256!("0x5d035ccb3e75a9057452ff060b773b213ec1fc353426174068edfc3971a0b6bd")
228 );
229
230 let (mut header, mut body) = TEST_BLOCK.clone().split_header_body();
231 body.withdrawals = Some(Withdrawals::new(vec![Withdrawal::default()]));
232 header.number = number;
233 header.state_root = state_root;
234 header.parent_hash = B256::ZERO;
235 let block = SealedBlock::seal_parts(header, body);
236
237 (RecoveredBlock::new_sealed(block, vec![Address::new([0x30; 20])]), execution_outcome)
238}
239
240fn block2(
242 number: BlockNumber,
243 parent_hash: B256,
244 prev_execution_outcome: &ExecutionOutcome,
245) -> (RecoveredBlock<reth_ethereum_primitives::Block>, ExecutionOutcome) {
246 let account: Address = [0x60; 20].into();
248 let slot = U256::from(5);
249
250 let execution_outcome = ExecutionOutcome::new(
251 BundleState::builder(number..=number)
252 .state_present_account_info(
253 account,
254 AccountInfo { nonce: 3, balance: U256::from(20), ..Default::default() },
255 )
256 .state_storage(account, HashMap::from_iter([(slot, (U256::ZERO, U256::from(15)))]))
257 .revert_account_info(
258 number,
259 account,
260 Some(Some(AccountInfo { nonce: 1, balance: U256::from(10), ..Default::default() })),
261 )
262 .revert_storage(number, account, Vec::from([(slot, U256::from(10))]))
263 .build(),
264 vec![vec![Receipt {
265 tx_type: TxType::Eip1559,
266 success: false,
267 cumulative_gas_used: 400,
268 logs: vec![Log::new_unchecked(
269 Address::new([0x61; 20]),
270 vec![B256::with_last_byte(3), B256::with_last_byte(4)],
271 Bytes::default(),
272 )],
273 }]],
274 number,
275 Vec::new(),
276 );
277
278 let mut extended = prev_execution_outcome.clone();
279 extended.extend(execution_outcome.clone());
280 let state_root = bundle_state_root(&extended);
281 assert_eq!(
282 state_root,
283 b256!("0x90101a13dd059fa5cca99ed93d1dc23657f63626c5b8f993a2ccbdf7446b64f8")
284 );
285
286 let (mut header, mut body) = TEST_BLOCK.clone().split_header_body();
287
288 body.withdrawals = Some(Withdrawals::new(vec![Withdrawal::default()]));
289 header.number = number;
290 header.state_root = state_root;
291 header.parent_hash = parent_hash;
293 let block = SealedBlock::seal_parts(header, body);
294
295 (RecoveredBlock::new_sealed(block, vec![Address::new([0x31; 20])]), execution_outcome)
296}
297
298fn block3(
300 number: BlockNumber,
301 parent_hash: B256,
302 prev_execution_outcome: &ExecutionOutcome,
303) -> (RecoveredBlock<reth_ethereum_primitives::Block>, ExecutionOutcome) {
304 let address_range = 1..=20;
305 let slot_range = 1..=100;
306
307 let mut bundle_state_builder = BundleState::builder(number..=number);
308 for idx in address_range {
309 let address = Address::with_last_byte(idx);
310 bundle_state_builder = bundle_state_builder
311 .state_present_account_info(
312 address,
313 AccountInfo { nonce: 1, balance: U256::from(idx), ..Default::default() },
314 )
315 .state_storage(
316 address,
317 slot_range
318 .clone()
319 .map(|slot| (U256::from(slot), (U256::ZERO, U256::from(slot))))
320 .collect(),
321 )
322 .revert_account_info(number, address, Some(None))
323 .revert_storage(number, address, Vec::new());
324 }
325 let execution_outcome = ExecutionOutcome::new(
326 bundle_state_builder.build(),
327 vec![vec![Receipt {
328 tx_type: TxType::Eip1559,
329 success: true,
330 cumulative_gas_used: 400,
331 logs: vec![Log::new_unchecked(
332 Address::new([0x61; 20]),
333 vec![B256::with_last_byte(3), B256::with_last_byte(4)],
334 Bytes::default(),
335 )],
336 }]],
337 number,
338 Vec::new(),
339 );
340
341 let mut extended = prev_execution_outcome.clone();
342 extended.extend(execution_outcome.clone());
343 let state_root = bundle_state_root(&extended);
344
345 let (mut header, mut body) = TEST_BLOCK.clone().split_header_body();
346 body.withdrawals = Some(Withdrawals::new(vec![Withdrawal::default()]));
347 header.number = number;
348 header.state_root = state_root;
349 header.parent_hash = parent_hash;
351 let block = SealedBlock::seal_parts(header, body);
352
353 (RecoveredBlock::new_sealed(block, vec![Address::new([0x31; 20])]), execution_outcome)
354}
355
356fn block4(
358 number: BlockNumber,
359 parent_hash: B256,
360 prev_execution_outcome: &ExecutionOutcome,
361) -> (RecoveredBlock<reth_ethereum_primitives::Block>, ExecutionOutcome) {
362 let address_range = 1..=20;
363 let slot_range = 1..=100;
364
365 let mut bundle_state_builder = BundleState::builder(number..=number);
366 for idx in address_range {
367 let address = Address::with_last_byte(idx);
368 bundle_state_builder = if idx % 2 == 0 {
370 bundle_state_builder
371 .state_present_account_info(
372 address,
373 AccountInfo { nonce: 1, balance: U256::from(idx * 2), ..Default::default() },
374 )
375 .state_storage(
376 address,
377 slot_range
378 .clone()
379 .map(|slot| (U256::from(slot), (U256::from(slot), U256::from(slot * 2))))
380 .collect(),
381 )
382 } else {
383 bundle_state_builder.state_address(address).state_storage(
384 address,
385 slot_range
386 .clone()
387 .map(|slot| (U256::from(slot), (U256::from(slot), U256::ZERO)))
388 .collect(),
389 )
390 };
391 bundle_state_builder = bundle_state_builder
393 .revert_account_info(
394 number,
395 address,
396 Some(Some(AccountInfo {
397 nonce: 1,
398 balance: U256::from(idx),
399 ..Default::default()
400 })),
401 )
402 .revert_storage(
403 number,
404 address,
405 slot_range.clone().map(|slot| (U256::from(slot), U256::from(slot))).collect(),
406 );
407 }
408 let execution_outcome = ExecutionOutcome::new(
409 bundle_state_builder.build(),
410 vec![vec![Receipt {
411 tx_type: TxType::Eip1559,
412 success: true,
413 cumulative_gas_used: 400,
414 logs: vec![Log::new_unchecked(
415 Address::new([0x61; 20]),
416 vec![B256::with_last_byte(3), B256::with_last_byte(4)],
417 Bytes::default(),
418 )],
419 }]],
420 number,
421 Vec::new(),
422 );
423
424 let mut extended = prev_execution_outcome.clone();
425 extended.extend(execution_outcome.clone());
426 let state_root = bundle_state_root(&extended);
427
428 let (mut header, mut body) = TEST_BLOCK.clone().split_header_body();
429 body.withdrawals = Some(Withdrawals::new(vec![Withdrawal::default()]));
430 header.number = number;
431 header.state_root = state_root;
432 header.parent_hash = parent_hash;
434 let block = SealedBlock::seal_parts(header, body);
435
436 (RecoveredBlock::new_sealed(block, vec![Address::new([0x31; 20])]), execution_outcome)
437}
438
439fn block5(
441 number: BlockNumber,
442 parent_hash: B256,
443 prev_execution_outcome: &ExecutionOutcome,
444) -> (RecoveredBlock<reth_ethereum_primitives::Block>, ExecutionOutcome) {
445 let address_range = 1..=20;
446 let slot_range = 1..=100;
447
448 let mut bundle_state_builder = BundleState::builder(number..=number);
449 for idx in address_range {
450 let address = Address::with_last_byte(idx);
451 bundle_state_builder = bundle_state_builder
453 .state_present_account_info(
454 address,
455 AccountInfo { nonce: 1, balance: U256::from(idx * 2), ..Default::default() },
456 )
457 .state_storage(
458 address,
459 slot_range
460 .clone()
461 .take(50)
462 .map(|slot| (U256::from(slot), (U256::from(slot), U256::from(slot * 4))))
463 .collect(),
464 );
465 bundle_state_builder = if idx % 2 == 0 {
466 bundle_state_builder
467 .revert_account_info(
468 number,
469 address,
470 Some(Some(AccountInfo {
471 nonce: 1,
472 balance: U256::from(idx * 2),
473 ..Default::default()
474 })),
475 )
476 .revert_storage(
477 number,
478 address,
479 slot_range
480 .clone()
481 .map(|slot| (U256::from(slot), U256::from(slot * 2)))
482 .collect(),
483 )
484 } else {
485 bundle_state_builder.revert_address(number, address)
486 };
487 }
488 let execution_outcome = ExecutionOutcome::new(
489 bundle_state_builder.build(),
490 vec![vec![Receipt {
491 tx_type: TxType::Eip1559,
492 success: true,
493 cumulative_gas_used: 400,
494 logs: vec![Log::new_unchecked(
495 Address::new([0x61; 20]),
496 vec![B256::with_last_byte(3), B256::with_last_byte(4)],
497 Bytes::default(),
498 )],
499 }]],
500 number,
501 Vec::new(),
502 );
503
504 let mut extended = prev_execution_outcome.clone();
505 extended.extend(execution_outcome.clone());
506 let state_root = bundle_state_root(&extended);
507
508 let (mut header, mut body) = TEST_BLOCK.clone().split_header_body();
509 body.withdrawals = Some(Withdrawals::new(vec![Withdrawal::default()]));
510 header.number = number;
511 header.state_root = state_root;
512 header.parent_hash = parent_hash;
514 let block = SealedBlock::seal_parts(header, body);
515
516 (RecoveredBlock::new_sealed(block, vec![Address::new([0x31; 20])]), execution_outcome)
517}