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