use crate::{DBProvider, DatabaseProviderRW, ExecutionOutcome};
use alloy_consensus::{TxLegacy, EMPTY_OMMER_ROOT_HASH};
use alloy_primitives::{
b256, hex_literal::hex, map::HashMap, Address, BlockNumber, Bytes, Log, TxKind, B256, U256,
};
use alloy_consensus::Header;
use alloy_eips::eip4895::{Withdrawal, Withdrawals};
use alloy_primitives::PrimitiveSignature as Signature;
use reth_db::tables;
use reth_db_api::{database::Database, models::StoredBlockBodyIndices};
use reth_node_types::NodeTypes;
use reth_primitives::{
Account, BlockBody, Receipt, SealedBlock, SealedBlockWithSenders, SealedHeader, Transaction,
TransactionSigned, TxType,
};
use reth_trie::root::{state_root_unhashed, storage_root_unhashed};
use revm::{db::BundleState, primitives::AccountInfo};
use std::{str::FromStr, sync::LazyLock};
pub fn assert_genesis_block<DB: Database, N: NodeTypes>(
provider: &DatabaseProviderRW<DB, N>,
g: SealedBlock,
) {
let n = g.number;
let h = B256::ZERO;
let tx = provider;
assert_eq!(tx.table::<tables::Headers>().unwrap(), vec![(g.number, g.header.clone().unseal())]);
assert_eq!(tx.table::<tables::HeaderNumbers>().unwrap(), vec![(h, n)]);
assert_eq!(tx.table::<tables::CanonicalHeaders>().unwrap(), vec![(n, h)]);
assert_eq!(
tx.table::<tables::HeaderTerminalDifficulties>().unwrap(),
vec![(n, g.difficulty.into())]
);
assert_eq!(
tx.table::<tables::BlockBodyIndices>().unwrap(),
vec![(0, StoredBlockBodyIndices::default())]
);
assert_eq!(tx.table::<tables::BlockOmmers>().unwrap(), vec![]);
assert_eq!(tx.table::<tables::BlockWithdrawals>().unwrap(), vec![]);
assert_eq!(tx.table::<tables::Transactions>().unwrap(), vec![]);
assert_eq!(tx.table::<tables::TransactionBlocks>().unwrap(), vec![]);
assert_eq!(tx.table::<tables::TransactionHashNumbers>().unwrap(), vec![]);
assert_eq!(tx.table::<tables::Receipts>().unwrap(), vec![]);
assert_eq!(tx.table::<tables::PlainAccountState>().unwrap(), vec![]);
assert_eq!(tx.table::<tables::PlainStorageState>().unwrap(), vec![]);
assert_eq!(tx.table::<tables::AccountsHistory>().unwrap(), vec![]);
assert_eq!(tx.table::<tables::StoragesHistory>().unwrap(), vec![]);
assert_eq!(tx.table::<tables::AccountChangeSets>().unwrap(), vec![]);
assert_eq!(tx.table::<tables::StorageChangeSets>().unwrap(), vec![]);
assert_eq!(tx.table::<tables::HashedAccounts>().unwrap(), vec![]);
assert_eq!(tx.table::<tables::HashedStorages>().unwrap(), vec![]);
assert_eq!(tx.table::<tables::AccountsTrie>().unwrap(), vec![]);
assert_eq!(tx.table::<tables::StoragesTrie>().unwrap(), vec![]);
assert_eq!(tx.table::<tables::TransactionSenders>().unwrap(), vec![]);
}
pub(crate) static TEST_BLOCK: LazyLock<SealedBlock> = LazyLock::new(|| SealedBlock {
header: SealedHeader::new(
Header {
parent_hash: hex!("c86e8cc0310ae7c531c758678ddbfd16fc51c8cef8cec650b032de9869e8b94f")
.into(),
ommers_hash: EMPTY_OMMER_ROOT_HASH,
beneficiary: hex!("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba").into(),
state_root: hex!("50554882fbbda2c2fd93fdc466db9946ea262a67f7a76cc169e714f105ab583d")
.into(),
transactions_root: hex!(
"0967f09ef1dfed20c0eacfaa94d5cd4002eda3242ac47eae68972d07b106d192"
)
.into(),
receipts_root: hex!("e3c8b47fbfc94667ef4cceb17e5cc21e3b1eebd442cebb27f07562b33836290d")
.into(),
difficulty: U256::from(131_072),
number: 1,
gas_limit: 1_000_000,
gas_used: 14_352,
timestamp: 1_000,
..Default::default()
},
hex!("cf7b274520720b50e6a4c3e5c4d553101f44945396827705518ce17cb7219a42").into(),
),
body: BlockBody {
transactions: vec![TransactionSigned::new(
Transaction::Legacy(TxLegacy {
gas_price: 10,
gas_limit: 400_000,
to: TxKind::Call(hex!("095e7baea6a6c7c4c2dfeb977efac326af552d87").into()),
..Default::default()
}),
Signature::new(
U256::from_str(
"51983300959770368863831494747186777928121405155922056726144551509338672451120",
)
.unwrap(),
U256::from_str(
"29056683545955299640297374067888344259176096769870751649153779895496107008675",
)
.unwrap(),
false,
),
b256!("3541dd1d17e76adeb25dcf2b0a9b60a1669219502e58dcf26a2beafbfb550397"),
)],
..Default::default()
},
});
#[derive(Debug)]
pub struct BlockchainTestData {
pub genesis: SealedBlock,
pub blocks: Vec<(SealedBlockWithSenders, ExecutionOutcome)>,
}
impl BlockchainTestData {
pub fn default_from_number(first: BlockNumber) -> Self {
let one = block1(first);
let mut extended_execution_outcome = one.1.clone();
let two = block2(first + 1, one.0.hash(), &extended_execution_outcome);
extended_execution_outcome.extend(two.1.clone());
let three = block3(first + 2, two.0.hash(), &extended_execution_outcome);
extended_execution_outcome.extend(three.1.clone());
let four = block4(first + 3, three.0.hash(), &extended_execution_outcome);
extended_execution_outcome.extend(four.1.clone());
let five = block5(first + 4, four.0.hash(), &extended_execution_outcome);
Self { genesis: genesis(), blocks: vec![one, two, three, four, five] }
}
}
impl Default for BlockchainTestData {
fn default() -> Self {
let one = block1(1);
let mut extended_execution_outcome = one.1.clone();
let two = block2(2, one.0.hash(), &extended_execution_outcome);
extended_execution_outcome.extend(two.1.clone());
let three = block3(3, two.0.hash(), &extended_execution_outcome);
extended_execution_outcome.extend(three.1.clone());
let four = block4(4, three.0.hash(), &extended_execution_outcome);
extended_execution_outcome.extend(four.1.clone());
let five = block5(5, four.0.hash(), &extended_execution_outcome);
Self { genesis: genesis(), blocks: vec![one, two, three, four, five] }
}
}
pub fn genesis() -> SealedBlock {
SealedBlock {
header: SealedHeader::new(
Header { number: 0, difficulty: U256::from(1), ..Default::default() },
B256::ZERO,
),
body: Default::default(),
}
}
fn bundle_state_root(execution_outcome: &ExecutionOutcome) -> B256 {
state_root_unhashed(execution_outcome.bundle_accounts_iter().filter_map(
|(address, account)| {
account.info.as_ref().map(|info| {
(
address,
Account::from(info).into_trie_account(storage_root_unhashed(
account
.storage
.iter()
.filter(|(_, value)| !value.present_value.is_zero())
.map(|(slot, value)| ((*slot).into(), value.present_value)),
)),
)
})
},
))
}
fn block1(number: BlockNumber) -> (SealedBlockWithSenders, ExecutionOutcome) {
let account1: Address = [0x60; 20].into();
let account2: Address = [0x61; 20].into();
let slot = U256::from(5);
let info = AccountInfo { nonce: 1, balance: U256::from(10), ..Default::default() };
let execution_outcome = ExecutionOutcome::new(
BundleState::builder(number..=number)
.state_present_account_info(account1, info.clone())
.revert_account_info(number, account1, Some(None))
.state_present_account_info(account2, info)
.revert_account_info(number, account2, Some(None))
.state_storage(account1, HashMap::from_iter([(slot, (U256::ZERO, U256::from(10)))]))
.build(),
vec![vec![Some(
#[allow(clippy::needless_update)] Receipt {
tx_type: TxType::Eip2930,
success: true,
cumulative_gas_used: 300,
logs: vec![Log::new_unchecked(
Address::new([0x60; 20]),
vec![B256::with_last_byte(1), B256::with_last_byte(2)],
Bytes::default(),
)],
..Default::default()
},
)]]
.into(),
number,
Vec::new(),
);
let state_root = bundle_state_root(&execution_outcome);
assert_eq!(
state_root,
b256!("5d035ccb3e75a9057452ff060b773b213ec1fc353426174068edfc3971a0b6bd")
);
let mut block = TEST_BLOCK.clone();
block.body.withdrawals = Some(Withdrawals::new(vec![Withdrawal::default()]));
let mut header = block.header.clone().unseal();
header.number = number;
header.state_root = state_root;
header.parent_hash = B256::ZERO;
block.header = SealedHeader::seal(header);
(SealedBlockWithSenders { block, senders: vec![Address::new([0x30; 20])] }, execution_outcome)
}
fn block2(
number: BlockNumber,
parent_hash: B256,
prev_execution_outcome: &ExecutionOutcome,
) -> (SealedBlockWithSenders, ExecutionOutcome) {
let account: Address = [0x60; 20].into();
let slot = U256::from(5);
let execution_outcome = ExecutionOutcome::new(
BundleState::builder(number..=number)
.state_present_account_info(
account,
AccountInfo { nonce: 3, balance: U256::from(20), ..Default::default() },
)
.state_storage(account, HashMap::from_iter([(slot, (U256::ZERO, U256::from(15)))]))
.revert_account_info(
number,
account,
Some(Some(AccountInfo { nonce: 1, balance: U256::from(10), ..Default::default() })),
)
.revert_storage(number, account, Vec::from([(slot, U256::from(10))]))
.build(),
vec![vec![Some(
#[allow(clippy::needless_update)] Receipt {
tx_type: TxType::Eip1559,
success: false,
cumulative_gas_used: 400,
logs: vec![Log::new_unchecked(
Address::new([0x61; 20]),
vec![B256::with_last_byte(3), B256::with_last_byte(4)],
Bytes::default(),
)],
..Default::default()
},
)]]
.into(),
number,
Vec::new(),
);
let mut extended = prev_execution_outcome.clone();
extended.extend(execution_outcome.clone());
let state_root = bundle_state_root(&extended);
assert_eq!(
state_root,
b256!("90101a13dd059fa5cca99ed93d1dc23657f63626c5b8f993a2ccbdf7446b64f8")
);
let mut block = TEST_BLOCK.clone();
block.body.withdrawals = Some(Withdrawals::new(vec![Withdrawal::default()]));
let mut header = block.header.clone().unseal();
header.number = number;
header.state_root = state_root;
header.parent_hash = parent_hash;
block.header = SealedHeader::seal(header);
(SealedBlockWithSenders { block, senders: vec![Address::new([0x31; 20])] }, execution_outcome)
}
fn block3(
number: BlockNumber,
parent_hash: B256,
prev_execution_outcome: &ExecutionOutcome,
) -> (SealedBlockWithSenders, ExecutionOutcome) {
let address_range = 1..=20;
let slot_range = 1..=100;
let mut bundle_state_builder = BundleState::builder(number..=number);
for idx in address_range {
let address = Address::with_last_byte(idx);
bundle_state_builder = bundle_state_builder
.state_present_account_info(
address,
AccountInfo { nonce: 1, balance: U256::from(idx), ..Default::default() },
)
.state_storage(
address,
slot_range
.clone()
.map(|slot| (U256::from(slot), (U256::ZERO, U256::from(slot))))
.collect(),
)
.revert_account_info(number, address, Some(None))
.revert_storage(number, address, Vec::new());
}
let execution_outcome = ExecutionOutcome::new(
bundle_state_builder.build(),
vec![vec![Some(
#[allow(clippy::needless_update)] Receipt {
tx_type: TxType::Eip1559,
success: true,
cumulative_gas_used: 400,
logs: vec![Log::new_unchecked(
Address::new([0x61; 20]),
vec![B256::with_last_byte(3), B256::with_last_byte(4)],
Bytes::default(),
)],
..Default::default()
},
)]]
.into(),
number,
Vec::new(),
);
let mut extended = prev_execution_outcome.clone();
extended.extend(execution_outcome.clone());
let state_root = bundle_state_root(&extended);
let mut block = TEST_BLOCK.clone();
block.body.withdrawals = Some(Withdrawals::new(vec![Withdrawal::default()]));
let mut header = block.header.clone().unseal();
header.number = number;
header.state_root = state_root;
header.parent_hash = parent_hash;
block.header = SealedHeader::seal(header);
(SealedBlockWithSenders { block, senders: vec![Address::new([0x31; 20])] }, execution_outcome)
}
fn block4(
number: BlockNumber,
parent_hash: B256,
prev_execution_outcome: &ExecutionOutcome,
) -> (SealedBlockWithSenders, ExecutionOutcome) {
let address_range = 1..=20;
let slot_range = 1..=100;
let mut bundle_state_builder = BundleState::builder(number..=number);
for idx in address_range {
let address = Address::with_last_byte(idx);
bundle_state_builder = if idx % 2 == 0 {
bundle_state_builder
.state_present_account_info(
address,
AccountInfo { nonce: 1, balance: U256::from(idx * 2), ..Default::default() },
)
.state_storage(
address,
slot_range
.clone()
.map(|slot| (U256::from(slot), (U256::from(slot), U256::from(slot * 2))))
.collect(),
)
} else {
bundle_state_builder.state_address(address).state_storage(
address,
slot_range
.clone()
.map(|slot| (U256::from(slot), (U256::from(slot), U256::ZERO)))
.collect(),
)
};
bundle_state_builder = bundle_state_builder
.revert_account_info(
number,
address,
Some(Some(AccountInfo {
nonce: 1,
balance: U256::from(idx),
..Default::default()
})),
)
.revert_storage(
number,
address,
slot_range.clone().map(|slot| (U256::from(slot), U256::from(slot))).collect(),
);
}
let execution_outcome = ExecutionOutcome::new(
bundle_state_builder.build(),
vec![vec![Some(
#[allow(clippy::needless_update)] Receipt {
tx_type: TxType::Eip1559,
success: true,
cumulative_gas_used: 400,
logs: vec![Log::new_unchecked(
Address::new([0x61; 20]),
vec![B256::with_last_byte(3), B256::with_last_byte(4)],
Bytes::default(),
)],
..Default::default()
},
)]]
.into(),
number,
Vec::new(),
);
let mut extended = prev_execution_outcome.clone();
extended.extend(execution_outcome.clone());
let state_root = bundle_state_root(&extended);
let mut block = TEST_BLOCK.clone();
block.body.withdrawals = Some(Withdrawals::new(vec![Withdrawal::default()]));
let mut header = block.header.clone().unseal();
header.number = number;
header.state_root = state_root;
header.parent_hash = parent_hash;
block.header = SealedHeader::seal(header);
(SealedBlockWithSenders { block, senders: vec![Address::new([0x31; 20])] }, execution_outcome)
}
fn block5(
number: BlockNumber,
parent_hash: B256,
prev_execution_outcome: &ExecutionOutcome,
) -> (SealedBlockWithSenders, ExecutionOutcome) {
let address_range = 1..=20;
let slot_range = 1..=100;
let mut bundle_state_builder = BundleState::builder(number..=number);
for idx in address_range {
let address = Address::with_last_byte(idx);
bundle_state_builder = bundle_state_builder
.state_present_account_info(
address,
AccountInfo { nonce: 1, balance: U256::from(idx * 2), ..Default::default() },
)
.state_storage(
address,
slot_range
.clone()
.take(50)
.map(|slot| (U256::from(slot), (U256::from(slot), U256::from(slot * 4))))
.collect(),
);
bundle_state_builder = if idx % 2 == 0 {
bundle_state_builder
.revert_account_info(
number,
address,
Some(Some(AccountInfo {
nonce: 1,
balance: U256::from(idx * 2),
..Default::default()
})),
)
.revert_storage(
number,
address,
slot_range
.clone()
.map(|slot| (U256::from(slot), U256::from(slot * 2)))
.collect(),
)
} else {
bundle_state_builder.revert_address(number, address)
};
}
let execution_outcome = ExecutionOutcome::new(
bundle_state_builder.build(),
vec![vec![Some(
#[allow(clippy::needless_update)] Receipt {
tx_type: TxType::Eip1559,
success: true,
cumulative_gas_used: 400,
logs: vec![Log::new_unchecked(
Address::new([0x61; 20]),
vec![B256::with_last_byte(3), B256::with_last_byte(4)],
Bytes::default(),
)],
..Default::default()
},
)]]
.into(),
number,
Vec::new(),
);
let mut extended = prev_execution_outcome.clone();
extended.extend(execution_outcome.clone());
let state_root = bundle_state_root(&extended);
let mut block = TEST_BLOCK.clone();
block.body.withdrawals = Some(Withdrawals::new(vec![Withdrawal::default()]));
let mut header = block.header.clone().unseal();
header.number = number;
header.state_root = state_root;
header.parent_hash = parent_hash;
block.header = SealedHeader::seal(header);
(SealedBlockWithSenders { block, senders: vec![Address::new([0x31; 20])] }, execution_outcome)
}