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