reth_provider/test_utils/
blocks.rs

1//! Dummy blocks and data for tests
2use 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
20/// Assert genesis block
21pub 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    // check if all tables are empty
30    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    // TODO check after this gets done: https://github.com/paradigmxyz/reth/issues/1588
53    // Bytecodes are not reverted assert_eq!(tx.table::<tables::Bytecodes>().unwrap(), vec![]);
54    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    // StageCheckpoints is not updated in tests
62}
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/// Test chain with genesis, blocks, execution results
122/// that have valid changesets.
123#[derive(Debug)]
124pub struct BlockchainTestData {
125    /// Genesis
126    pub genesis: SealedBlock<reth_ethereum_primitives::Block>,
127    /// Blocks with its execution result
128    pub blocks: Vec<(RecoveredBlock<reth_ethereum_primitives::Block>, ExecutionOutcome)>,
129}
130
131impl BlockchainTestData {
132    /// Create test data with two blocks that are connected, specifying their block numbers.
133    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
162/// Genesis block
163pub 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
197/// Block one that points to genesis
198fn block1(
199    number: BlockNumber,
200) -> (RecoveredBlock<reth_ethereum_primitives::Block>, ExecutionOutcome) {
201    // block changes
202    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
251/// Block two that points to block 1
252fn block2(
253    number: BlockNumber,
254    parent_hash: B256,
255    prev_execution_outcome: &ExecutionOutcome,
256) -> (RecoveredBlock<reth_ethereum_primitives::Block>, ExecutionOutcome) {
257    // block changes
258    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    // parent_hash points to block1 hash
313    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
319/// Block three that points to block 2
320fn 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    // parent_hash points to block1 hash
370    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
376/// Block four that points to block 3
377fn 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        // increase balance for every even account and destroy every odd
389        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        // record previous account info
416        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    // parent_hash points to block1 hash
461    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
467/// Block five that points to block 4
468fn 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        // update every even account and recreate every odd only with half of slots
480        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    // parent_hash points to block1 hash
545    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}