1use alloy_consensus::{Header, Transaction as _, TxLegacy};
4use alloy_eips::{
5 eip1898::BlockWithParent,
6 eip4895::{Withdrawal, Withdrawals},
7 NumHash,
8};
9use alloy_primitives::{Address, BlockNumber, Bytes, TxKind, B256, U256};
10pub use rand::Rng;
11use rand::{
12 distributions::uniform::SampleRange, rngs::StdRng, seq::SliceRandom, thread_rng, SeedableRng,
13};
14use reth_primitives::{
15 proofs, sign_message, Account, BlockBody, Log, Receipt, SealedBlock, SealedHeader,
16 StorageEntry, Transaction, TransactionSigned,
17};
18use secp256k1::{Keypair, Secp256k1};
19use std::{
20 cmp::{max, min},
21 collections::{hash_map::DefaultHasher, BTreeMap},
22 hash::Hasher,
23 ops::{Range, RangeInclusive},
24};
25
26#[derive(Debug, Default)]
28pub struct BlockParams {
29 pub parent: Option<B256>,
31 pub tx_count: Option<u8>,
33 pub ommers_count: Option<u8>,
35 pub requests_count: Option<u8>,
37 pub withdrawals_count: Option<u8>,
39}
40
41#[derive(Debug)]
43pub struct BlockRangeParams {
44 pub parent: Option<B256>,
46 pub tx_count: Range<u8>,
50 pub requests_count: Option<Range<u8>>,
52 pub withdrawals_count: Option<Range<u8>>,
54}
55
56impl Default for BlockRangeParams {
57 fn default() -> Self {
58 Self {
59 parent: None,
60 tx_count: 0..u8::MAX / 2,
61 requests_count: None,
62 withdrawals_count: None,
63 }
64 }
65}
66
67pub fn rng() -> StdRng {
71 if let Ok(seed) = std::env::var("SEED") {
72 let mut hasher = DefaultHasher::new();
73 hasher.write(seed.as_bytes());
74 StdRng::seed_from_u64(hasher.finish())
75 } else {
76 StdRng::from_rng(thread_rng()).expect("could not build rng")
77 }
78}
79
80pub fn random_header_range<R: Rng>(
87 rng: &mut R,
88 range: Range<u64>,
89 head: B256,
90) -> Vec<SealedHeader> {
91 let mut headers = Vec::with_capacity(range.end.saturating_sub(range.start) as usize);
92 for idx in range {
93 headers.push(random_header(
94 rng,
95 idx,
96 Some(headers.last().map(|h: &SealedHeader| h.hash()).unwrap_or(head)),
97 ));
98 }
99 headers
100}
101
102pub fn random_block_with_parent<R: Rng>(
104 rng: &mut R,
105 number: u64,
106 parent: Option<B256>,
107) -> BlockWithParent {
108 BlockWithParent { parent: parent.unwrap_or_default(), block: NumHash::new(number, rng.gen()) }
109}
110
111pub fn random_header<R: Rng>(rng: &mut R, number: u64, parent: Option<B256>) -> SealedHeader {
115 let header = alloy_consensus::Header {
116 number,
117 nonce: rng.gen(),
118 difficulty: U256::from(rng.gen::<u32>()),
119 parent_hash: parent.unwrap_or_default(),
120 ..Default::default()
121 };
122 SealedHeader::seal(header)
123}
124
125pub fn random_tx<R: Rng>(rng: &mut R) -> Transaction {
132 Transaction::Legacy(TxLegacy {
133 chain_id: Some(1),
134 nonce: rng.gen::<u16>().into(),
135 gas_price: rng.gen::<u16>().into(),
136 gas_limit: rng.gen::<u16>().into(),
137 to: TxKind::Call(rng.gen()),
138 value: U256::from(rng.gen::<u16>()),
139 input: Bytes::default(),
140 })
141}
142
143pub fn random_signed_tx<R: Rng>(rng: &mut R) -> TransactionSigned {
149 let tx = random_tx(rng);
150 sign_tx_with_random_key_pair(rng, tx)
151}
152
153pub fn sign_tx_with_random_key_pair<R: Rng>(rng: &mut R, tx: Transaction) -> TransactionSigned {
155 let secp = Secp256k1::new();
156 let key_pair = Keypair::new(&secp, rng);
157 sign_tx_with_key_pair(key_pair, tx)
158}
159
160pub fn sign_tx_with_key_pair(key_pair: Keypair, tx: Transaction) -> TransactionSigned {
162 let signature =
163 sign_message(B256::from_slice(&key_pair.secret_bytes()[..]), tx.signature_hash()).unwrap();
164
165 TransactionSigned::new_unhashed(tx, signature)
166}
167
168pub fn generate_keys<R: Rng>(rng: &mut R, count: usize) -> Vec<Keypair> {
170 let secp = Secp256k1::new();
171 (0..count).map(|_| Keypair::new(&secp, rng)).collect()
172}
173
174pub fn random_block<R: Rng>(rng: &mut R, number: u64, block_params: BlockParams) -> SealedBlock {
189 let tx_count = block_params.tx_count.unwrap_or_else(|| rng.gen::<u8>());
191 let transactions: Vec<TransactionSigned> =
192 (0..tx_count).map(|_| random_signed_tx(rng)).collect();
193 let total_gas = transactions.iter().fold(0, |sum, tx| sum + tx.transaction.gas_limit());
194
195 let ommers_count = block_params.ommers_count.unwrap_or_else(|| rng.gen_range(0..2));
197 let ommers = (0..ommers_count)
198 .map(|_| random_header(rng, number, block_params.parent).unseal())
199 .collect::<Vec<_>>();
200
201 let transactions_root = proofs::calculate_transaction_root(&transactions);
203 let ommers_hash = proofs::calculate_ommers_root(&ommers);
204
205 let withdrawals = block_params.withdrawals_count.map(|count| {
206 (0..count)
207 .map(|i| Withdrawal {
208 amount: rng.gen(),
209 index: i.into(),
210 validator_index: i.into(),
211 address: rng.gen(),
212 })
213 .collect::<Vec<_>>()
214 });
215 let withdrawals_root = withdrawals.as_ref().map(|w| proofs::calculate_withdrawals_root(w));
216
217 let header = Header {
218 parent_hash: block_params.parent.unwrap_or_default(),
219 number,
220 gas_used: total_gas,
221 gas_limit: total_gas,
222 transactions_root,
223 ommers_hash,
224 base_fee_per_gas: Some(rng.gen()),
225 requests_hash: None,
227 withdrawals_root,
228 ..Default::default()
229 };
230
231 SealedBlock {
232 header: SealedHeader::seal(header),
233 body: BlockBody { transactions, ommers, withdrawals: withdrawals.map(Withdrawals::new) },
234 }
235}
236
237pub fn random_block_range<R: Rng>(
244 rng: &mut R,
245 block_numbers: RangeInclusive<BlockNumber>,
246 block_range_params: BlockRangeParams,
247) -> Vec<SealedBlock> {
248 let mut blocks =
249 Vec::with_capacity(block_numbers.end().saturating_sub(*block_numbers.start()) as usize);
250 for idx in block_numbers {
251 let tx_count = block_range_params.tx_count.clone().sample_single(rng);
252 let requests_count =
253 block_range_params.requests_count.clone().map(|r| r.sample_single(rng));
254 let withdrawals_count =
255 block_range_params.withdrawals_count.clone().map(|r| r.sample_single(rng));
256 let parent = block_range_params.parent.unwrap_or_default();
257 blocks.push(random_block(
258 rng,
259 idx,
260 BlockParams {
261 parent: Some(
262 blocks.last().map(|block: &SealedBlock| block.header.hash()).unwrap_or(parent),
263 ),
264 tx_count: Some(tx_count),
265 ommers_count: None,
266 requests_count,
267 withdrawals_count,
268 },
269 ));
270 }
271 blocks
272}
273
274pub type ChangeSet = Vec<(Address, Account, Vec<StorageEntry>)>;
276type AccountState = (Account, Vec<StorageEntry>);
277
278pub fn random_changeset_range<'a, R: Rng, IBlk, IAcc>(
283 rng: &mut R,
284 blocks: IBlk,
285 accounts: IAcc,
286 n_storage_changes: Range<u64>,
287 key_range: Range<u64>,
288) -> (Vec<ChangeSet>, BTreeMap<Address, AccountState>)
289where
290 IBlk: IntoIterator<Item = &'a SealedBlock>,
291 IAcc: IntoIterator<Item = (Address, (Account, Vec<StorageEntry>))>,
292{
293 let mut state: BTreeMap<_, _> = accounts
294 .into_iter()
295 .map(|(addr, (acc, st))| {
296 (
297 addr,
298 (
299 acc,
300 st.into_iter()
301 .map(|e| (e.key, (e.value, e.is_private)))
302 .collect::<BTreeMap<_, _>>(),
303 ),
304 )
305 })
306 .collect();
307
308 let valid_addresses = state.keys().copied().collect::<Vec<_>>();
309
310 let mut changesets = Vec::new();
311
312 for _block in blocks {
313 let mut changeset = Vec::new();
314 let (from, to, mut transfer, new_entries) = random_account_change(
315 rng,
316 &valid_addresses,
317 n_storage_changes.clone(),
318 key_range.clone(),
319 );
320
321 let (prev_from, _) = state.get_mut(&from).unwrap();
323 changeset.push((from, *prev_from, Vec::new()));
324
325 transfer = max(min(transfer, prev_from.balance), U256::from(1));
326 prev_from.balance = prev_from.balance.wrapping_sub(transfer);
327
328 let (prev_to, storage): &mut (Account, BTreeMap<B256, (U256, bool)>) =
330 state.get_mut(&to).unwrap();
331
332 let mut old_entries: Vec<_> = new_entries
333 .into_iter()
334 .filter_map(|entry| {
335 let old = if entry.value.is_zero() {
336 let old = storage.remove(&entry.key);
337 if matches!(old, Some((U256::ZERO, false))) {
338 return None;
339 }
340 old
341 } else {
342 storage.insert(entry.key, (entry.value, entry.is_private))
343 };
344 match old {
345 Some((old_value, old_is_private)) => {
346 return Some(StorageEntry {
347 value: old_value,
348 is_private: old_is_private,
349 ..entry
350 });
351 }
352 None => {
353 return Some(StorageEntry { value: U256::ZERO, is_private: false, ..entry });
354 }
355 }
356 })
357 .collect();
358 old_entries.sort_by_key(|entry| entry.key);
359
360 changeset.push((to, *prev_to, old_entries));
361
362 changeset.sort_by_key(|(address, _, _)| *address);
363
364 prev_to.balance = prev_to.balance.wrapping_add(transfer);
365
366 changesets.push(changeset);
367 }
368
369 let final_state = state
370 .into_iter()
371 .map(|(addr, (acc, storage))| {
372 (addr, (acc, storage.into_iter().map(|v| v.into()).collect()))
373 })
374 .collect();
375 (changesets, final_state)
376}
377
378pub fn random_account_change<R: Rng>(
382 rng: &mut R,
383 valid_addresses: &[Address],
384 n_storage_changes: Range<u64>,
385 key_range: Range<u64>,
386) -> (Address, Address, U256, Vec<StorageEntry>) {
387 let mut addresses = valid_addresses.choose_multiple(rng, 2).copied();
388
389 let addr_from = addresses.next().unwrap_or_else(Address::random);
390 let addr_to = addresses.next().unwrap_or_else(Address::random);
391
392 let balance_change = U256::from(rng.gen::<u64>());
393
394 let storage_changes = if n_storage_changes.is_empty() {
395 Vec::new()
396 } else {
397 (0..n_storage_changes.sample_single(rng))
398 .map(|_| random_storage_entry(rng, key_range.clone()))
399 .collect()
400 };
401
402 (addr_from, addr_to, balance_change, storage_changes)
403}
404
405pub fn random_storage_entry<R: Rng>(rng: &mut R, key_range: Range<u64>) -> StorageEntry {
407 let key = B256::new({
408 let n = key_range.sample_single(rng);
409 let mut m = [0u8; 32];
410 m[24..32].copy_from_slice(&n.to_be_bytes());
411 m
412 });
413 let value = U256::from(rng.gen::<u64>());
414
415 StorageEntry { key, value, ..Default::default() }
416}
417
418pub fn random_eoa_account<R: Rng>(rng: &mut R) -> (Address, Account) {
420 let nonce: u64 = rng.gen();
421 let balance = U256::from(rng.gen::<u32>());
422 let addr = rng.gen();
423
424 (addr, Account { nonce, balance, bytecode_hash: None })
425}
426
427pub fn random_eoa_accounts<R: Rng>(rng: &mut R, accounts_num: usize) -> Vec<(Address, Account)> {
429 let mut accounts = Vec::with_capacity(accounts_num);
430 for _ in 0..accounts_num {
431 accounts.push(random_eoa_account(rng))
432 }
433 accounts
434}
435
436pub fn random_contract_account_range<R: Rng>(
438 rng: &mut R,
439 acc_range: &mut Range<u64>,
440) -> Vec<(Address, Account)> {
441 let mut accounts = Vec::with_capacity(acc_range.end.saturating_sub(acc_range.start) as usize);
442 for _ in acc_range {
443 let (address, eoa_account) = random_eoa_account(rng);
444 let account = Account { bytecode_hash: Some(rng.gen()), ..eoa_account };
446 accounts.push((address, account))
447 }
448 accounts
449}
450
451pub fn random_receipt<R: Rng>(
453 rng: &mut R,
454 transaction: &TransactionSigned,
455 logs_count: Option<u8>,
456) -> Receipt {
457 let success = rng.gen::<bool>();
458 let logs_count = logs_count.unwrap_or_else(|| rng.gen::<u8>());
459 #[allow(clippy::needless_update)] Receipt {
461 tx_type: transaction.tx_type(),
462 success,
463 cumulative_gas_used: rng.gen_range(0..=transaction.gas_limit()),
464 logs: if success {
465 (0..logs_count).map(|_| random_log(rng, None, None)).collect()
466 } else {
467 vec![]
468 },
469 ..Default::default()
470 }
471}
472
473pub fn random_log<R: Rng>(rng: &mut R, address: Option<Address>, topics_count: Option<u8>) -> Log {
475 let data_byte_count = rng.gen::<u8>() as usize;
476 let topics_count = topics_count.unwrap_or_else(|| rng.gen()) as usize;
477 Log::new_unchecked(
478 address.unwrap_or_else(|| rng.gen()),
479 std::iter::repeat_with(|| rng.gen()).take(topics_count).collect(),
480 std::iter::repeat_with(|| rng.gen()).take(data_byte_count).collect::<Vec<_>>().into(),
481 )
482}
483
484#[cfg(test)]
485mod tests {
486 use super::*;
487 use alloy_consensus::TxEip1559;
488 use alloy_eips::eip2930::AccessList;
489 use alloy_primitives::{hex, PrimitiveSignature as Signature};
490 use reth_primitives::public_key_to_address;
491 use reth_primitives_traits::SignedTransaction;
492 use std::str::FromStr;
493
494 #[test]
495 fn test_sign_message() {
496 let secp = Secp256k1::new();
497
498 let tx = Transaction::Eip1559(TxEip1559 {
499 chain_id: 1,
500 nonce: 0x42,
501 gas_limit: 44386,
502 to: TxKind::Call(hex!("6069a6c32cf691f5982febae4faf8a6f3ab2f0f6").into()),
503 value: U256::from(0_u64),
504 input: hex!("a22cb4650000000000000000000000005eee75727d804a2b13038928d36f8b188945a57a0000000000000000000000000000000000000000000000000000000000000000").into(),
505 max_fee_per_gas: 0x4a817c800,
506 max_priority_fee_per_gas: 0x3b9aca00,
507 access_list: AccessList::default(),
508 });
509 let signature_hash = tx.signature_hash();
510
511 for _ in 0..100 {
512 let key_pair = Keypair::new(&secp, &mut rand::thread_rng());
513
514 let signature =
515 sign_message(B256::from_slice(&key_pair.secret_bytes()[..]), signature_hash)
516 .unwrap();
517
518 let signed = TransactionSigned::new_unhashed(tx.clone(), signature);
519 let recovered = signed.recover_signer().unwrap();
520
521 let expected = public_key_to_address(key_pair.public_key());
522 assert_eq!(recovered, expected);
523 }
524 }
525
526 #[test]
527 fn test_sign_eip_155() {
528 let transaction = Transaction::Legacy(TxLegacy {
530 chain_id: Some(1),
531 nonce: 9,
532 gas_price: 20 * 10_u128.pow(9),
533 gas_limit: 21000,
534 to: TxKind::Call(hex!("3535353535353535353535353535353535353535").into()),
535 value: U256::from(10_u128.pow(18)),
536 input: Bytes::default(),
537 });
538
539 let hash = transaction.signature_hash();
545 let expected =
546 B256::from_str("daf5a779ae972f972197303d7b574746c7ef83eadac0f2791ad23db92e4c8e53")
547 .unwrap();
548 assert_eq!(expected, hash);
549
550 let secret =
551 B256::from_str("4646464646464646464646464646464646464646464646464646464646464646")
552 .unwrap();
553 let signature = sign_message(secret, hash).unwrap();
554
555 let expected = Signature::new(
556 U256::from_str(
557 "18515461264373351373200002665853028612451056578545711640558177340181847433846",
558 )
559 .unwrap(),
560 U256::from_str(
561 "46948507304638947509940763649030358759909902576025900602547168820602576006531",
562 )
563 .unwrap(),
564 false,
565 );
566 assert_eq!(expected, signature);
567 }
568}