test: add TestBlockBuilder (#9925)

This commit is contained in:
Federico Gimenez
2024-07-31 13:50:53 +02:00
committed by GitHub
parent a4c3f9c198
commit f9ed57d6a6
4 changed files with 299 additions and 232 deletions

View File

@ -20,183 +20,211 @@ use std::{
};
use tokio::sync::broadcast::{self, Sender};
/// Generates a random `SealedBlockWithSenders`.
pub fn generate_random_block(
number: BlockNumber,
parent_hash: B256,
chain_spec: &ChainSpec,
signer: Address,
signer_balance: &mut U256,
nonce: &mut u64,
) -> SealedBlockWithSenders {
let mut rng = thread_rng();
let single_tx_cost = U256::from(EIP1559_INITIAL_BASE_FEE * 21_000);
let mock_tx = |nonce: u64| -> TransactionSignedEcRecovered {
TransactionSigned::from_transaction_and_signature(
Transaction::Eip1559(TxEip1559 {
chain_id: chain_spec.chain.id(),
nonce,
gas_limit: 21_000,
to: Address::random().into(),
max_fee_per_gas: EIP1559_INITIAL_BASE_FEE as u128,
max_priority_fee_per_gas: 1,
..Default::default()
}),
Signature::default(),
)
.with_signer(signer)
};
let num_txs = rng.gen_range(0..5);
let signer_balance_decrease = single_tx_cost * U256::from(num_txs);
let transactions: Vec<TransactionSignedEcRecovered> = (0..num_txs)
.map(|_| {
let tx = mock_tx(*nonce);
*nonce += 1;
*signer_balance -= signer_balance_decrease;
tx
})
.collect();
let receipts = transactions
.iter()
.enumerate()
.map(|(idx, tx)| {
Receipt {
tx_type: tx.tx_type(),
success: true,
cumulative_gas_used: (idx as u64 + 1) * 21_000,
..Default::default()
}
.with_bloom()
})
.collect::<Vec<_>>();
let initial_signer_balance = U256::from(10).pow(U256::from(18));
let header = Header {
number,
parent_hash,
gas_used: transactions.len() as u64 * 21_000,
gas_limit: chain_spec.max_gas_limit,
mix_hash: B256::random(),
base_fee_per_gas: Some(EIP1559_INITIAL_BASE_FEE),
transactions_root: calculate_transaction_root(&transactions),
receipts_root: calculate_receipt_root(&receipts),
beneficiary: Address::random(),
state_root: state_root_unhashed(HashMap::from([(
signer,
(
AccountInfo {
balance: initial_signer_balance - signer_balance_decrease,
nonce: num_txs,
..Default::default()
},
EMPTY_ROOT_HASH,
),
)])),
..Default::default()
};
let block = SealedBlock {
header: header.seal_slow(),
body: transactions.into_iter().map(|tx| tx.into_signed()).collect(),
ommers: Vec::new(),
withdrawals: None,
requests: None,
};
SealedBlockWithSenders::new(block, vec![signer; num_txs as usize]).unwrap()
/// Functionality to build blocks for tests and help with assertions about
/// their execution.
#[derive(Debug)]
pub struct TestBlockBuilder {
/// The account that signs all the block's transactions.
pub signer: Address,
/// Keeps track of signer's account info after execution, will be updated in
/// methods related to block execution.
pub signer_execute_account_info: AccountInfo,
/// Keeps track of signer's nonce, will be updated in methods related
/// to block execution.
pub signer_build_account_info: AccountInfo,
/// Chain spec of the blocks generated by this builder
pub chain_spec: ChainSpec,
}
/// Creates a fork chain with the given base block.
pub fn create_fork(
base_block: &SealedBlock,
length: u64,
chain_spec: &ChainSpec,
signer: Address,
initial_signer_balance: U256,
) -> Vec<SealedBlockWithSenders> {
let mut fork = Vec::with_capacity(length as usize);
let mut parent = base_block.clone();
let mut signer_balance = initial_signer_balance;
let mut nonce = 0;
impl Default for TestBlockBuilder {
fn default() -> Self {
let initial_account_info = AccountInfo::from_balance(U256::from(10).pow(U256::from(18)));
Self {
chain_spec: ChainSpec::default(),
signer: Address::random(),
signer_execute_account_info: initial_account_info.clone(),
signer_build_account_info: initial_account_info,
}
}
}
for _ in 0..length {
let block = generate_random_block(
parent.number + 1,
parent.hash(),
chain_spec,
signer,
&mut signer_balance,
&mut nonce,
);
parent = block.block.clone();
fork.push(block);
impl TestBlockBuilder {
/// Signer setter.
pub const fn with_signer(mut self, signer: Address) -> Self {
self.signer = signer;
self
}
fork
/// Chainspec setter.
pub fn with_chain_spec(mut self, chain_spec: ChainSpec) -> Self {
self.chain_spec = chain_spec;
self
}
/// Gas cost of a single transaction generated by the block builder.
pub fn single_tx_cost() -> U256 {
U256::from(EIP1559_INITIAL_BASE_FEE * 21_000)
}
/// Generates a random `SealedBlockWithSenders`.
pub fn generate_random_block(
&mut self,
number: BlockNumber,
parent_hash: B256,
) -> SealedBlockWithSenders {
let mut rng = thread_rng();
let mock_tx = |nonce: u64| -> TransactionSignedEcRecovered {
TransactionSigned::from_transaction_and_signature(
Transaction::Eip1559(TxEip1559 {
chain_id: self.chain_spec.chain.id(),
nonce,
gas_limit: 21_000,
to: Address::random().into(),
max_fee_per_gas: EIP1559_INITIAL_BASE_FEE as u128,
max_priority_fee_per_gas: 1,
..Default::default()
}),
Signature::default(),
)
.with_signer(self.signer)
};
let num_txs = rng.gen_range(0..5);
let signer_balance_decrease = Self::single_tx_cost() * U256::from(num_txs);
let transactions: Vec<TransactionSignedEcRecovered> = (0..num_txs)
.map(|_| {
let tx = mock_tx(self.signer_build_account_info.nonce);
self.signer_build_account_info.nonce += 1;
self.signer_build_account_info.balance -= signer_balance_decrease;
tx
})
.collect();
let receipts = transactions
.iter()
.enumerate()
.map(|(idx, tx)| {
Receipt {
tx_type: tx.tx_type(),
success: true,
cumulative_gas_used: (idx as u64 + 1) * 21_000,
..Default::default()
}
.with_bloom()
})
.collect::<Vec<_>>();
let initial_signer_balance = U256::from(10).pow(U256::from(18));
let header = Header {
number,
parent_hash,
gas_used: transactions.len() as u64 * 21_000,
gas_limit: self.chain_spec.max_gas_limit,
mix_hash: B256::random(),
base_fee_per_gas: Some(EIP1559_INITIAL_BASE_FEE),
transactions_root: calculate_transaction_root(&transactions),
receipts_root: calculate_receipt_root(&receipts),
beneficiary: Address::random(),
state_root: state_root_unhashed(HashMap::from([(
self.signer,
(
AccountInfo {
balance: initial_signer_balance - signer_balance_decrease,
nonce: num_txs,
..Default::default()
},
EMPTY_ROOT_HASH,
),
)])),
..Default::default()
};
let block = SealedBlock {
header: header.seal_slow(),
body: transactions.into_iter().map(|tx| tx.into_signed()).collect(),
ommers: Vec::new(),
withdrawals: None,
requests: None,
};
SealedBlockWithSenders::new(block, vec![self.signer; num_txs as usize]).unwrap()
}
/// Creates a fork chain with the given base block.
pub fn create_fork(
&mut self,
base_block: &SealedBlock,
length: u64,
) -> Vec<SealedBlockWithSenders> {
let mut fork = Vec::with_capacity(length as usize);
let mut parent = base_block.clone();
for _ in 0..length {
let block = self.generate_random_block(parent.number + 1, parent.hash());
parent = block.block.clone();
fork.push(block);
}
fork
}
fn get_executed_block(
&mut self,
block_number: BlockNumber,
receipts: Receipts,
parent_hash: B256,
) -> ExecutedBlock {
let block_with_senders = self.generate_random_block(block_number, parent_hash);
ExecutedBlock::new(
Arc::new(block_with_senders.block.clone()),
Arc::new(block_with_senders.senders),
Arc::new(ExecutionOutcome::new(
BundleState::default(),
receipts,
block_number,
vec![Requests::default()],
)),
Arc::new(HashedPostState::default()),
Arc::new(TrieUpdates::default()),
)
}
/// Generates an `ExecutedBlock` that includes the given `Receipts`.
pub fn get_executed_block_with_receipts(
&mut self,
receipts: Receipts,
parent_hash: B256,
) -> ExecutedBlock {
let number = rand::thread_rng().gen::<u64>();
self.get_executed_block(number, receipts, parent_hash)
}
/// Generates an `ExecutedBlock` with the given `BlockNumber`.
pub fn get_executed_block_with_number(
&mut self,
block_number: BlockNumber,
parent_hash: B256,
) -> ExecutedBlock {
self.get_executed_block(block_number, Receipts { receipt_vec: vec![vec![]] }, parent_hash)
}
/// Generates a range of executed blocks with ascending block numbers.
pub fn get_executed_blocks(
&mut self,
range: Range<u64>,
) -> impl Iterator<Item = ExecutedBlock> + '_ {
let mut parent_hash = B256::default();
range.map(move |number| {
let current_parent_hash = parent_hash;
let block = self.get_executed_block_with_number(number, current_parent_hash);
parent_hash = block.block.hash();
block
})
}
}
fn get_executed_block(
block_number: BlockNumber,
receipts: Receipts,
parent_hash: B256,
) -> ExecutedBlock {
let chain_spec = ChainSpec::default();
let signer = Address::random();
let mut signer_balance = U256::from(1_000_000_000_000_000_000u64);
let mut nonce = 0;
let block_with_senders = generate_random_block(
block_number,
parent_hash,
&chain_spec,
signer,
&mut signer_balance,
&mut nonce,
);
ExecutedBlock::new(
Arc::new(block_with_senders.block.clone()),
Arc::new(block_with_senders.senders),
Arc::new(ExecutionOutcome::new(
BundleState::default(),
receipts,
block_number,
vec![Requests::default()],
)),
Arc::new(HashedPostState::default()),
Arc::new(TrieUpdates::default()),
)
}
/// Generates an `ExecutedBlock` that includes the given `Receipts`.
pub fn get_executed_block_with_receipts(receipts: Receipts, parent_hash: B256) -> ExecutedBlock {
let number = rand::thread_rng().gen::<u64>();
get_executed_block(number, receipts, parent_hash)
}
/// Generates an `ExecutedBlock` with the given `BlockNumber`.
pub fn get_executed_block_with_number(
block_number: BlockNumber,
parent_hash: B256,
) -> ExecutedBlock {
get_executed_block(block_number, Receipts { receipt_vec: vec![vec![]] }, parent_hash)
}
/// Generates a range of executed blocks with ascending block numbers.
pub fn get_executed_blocks(range: Range<u64>) -> impl Iterator<Item = ExecutedBlock> {
let mut parent_hash = B256::default();
range.map(move |number| {
let current_parent_hash = parent_hash;
let block = get_executed_block_with_number(number, current_parent_hash);
parent_hash = block.block.hash();
block
})
}
/// A test `ChainEventSubscriptions`
#[derive(Clone, Debug, Default)]
pub struct TestCanonStateSubscriptions {