1use alloy_consensus::{Header, SignableTransaction, Transaction as _, TxLegacy};
6use alloy_eips::{
7 eip1898::BlockWithParent,
8 eip4895::{Withdrawal, Withdrawals},
9 NumHash,
10};
11use alloy_primitives::{Address, BlockNumber, Bytes, TxKind, B256, B64, U256};
12pub use rand::Rng;
13use rand::{distr::uniform::SampleRange, rngs::StdRng, SeedableRng};
14use reth_ethereum_primitives::{Block, BlockBody, Receipt, Transaction, TransactionSigned};
15use reth_primitives_traits::{
16 crypto::secp256k1::sign_message, proofs, Account, Block as _, Log, SealedBlock, SealedHeader,
17 StorageEntry,
18};
19
20use secp256k1::{Keypair, Secp256k1};
21use std::{
22 cmp::{max, min},
23 collections::BTreeMap,
24 ops::{Range, RangeInclusive},
25};
26
27#[derive(Debug, Default)]
29pub struct BlockParams {
30 pub parent: Option<B256>,
32 pub tx_count: Option<u8>,
34 pub ommers_count: Option<u8>,
36 pub requests_count: Option<u8>,
38 pub withdrawals_count: Option<u8>,
40}
41
42#[derive(Debug)]
44pub struct BlockRangeParams {
45 pub parent: Option<B256>,
47 pub tx_count: Range<u8>,
51 pub requests_count: Option<Range<u8>>,
53 pub withdrawals_count: Option<Range<u8>>,
55}
56
57impl Default for BlockRangeParams {
58 fn default() -> Self {
59 Self {
60 parent: None,
61 tx_count: 0..u8::MAX / 2,
62 requests_count: None,
63 withdrawals_count: None,
64 }
65 }
66}
67
68pub fn rng() -> StdRng {
72 if let Ok(seed) = std::env::var("SEED") {
73 rng_with_seed(seed.as_bytes())
74 } else {
75 StdRng::from_rng(&mut rand::rng())
76 }
77}
78
79pub fn rng_with_seed(seed: &[u8]) -> StdRng {
81 let mut seed_bytes = [0u8; 32];
82 seed_bytes[..seed.len().min(32)].copy_from_slice(seed);
83 StdRng::from_seed(seed_bytes)
84}
85
86pub fn random_header_range<R: Rng>(
93 rng: &mut R,
94 range: Range<u64>,
95 head: B256,
96) -> Vec<SealedHeader> {
97 let mut headers = Vec::with_capacity(range.end.saturating_sub(range.start) as usize);
98 for idx in range {
99 headers.push(random_header(
100 rng,
101 idx,
102 Some(headers.last().map(|h: &SealedHeader| h.hash()).unwrap_or(head)),
103 ));
104 }
105 headers
106}
107
108pub fn random_block_with_parent<R: Rng>(
110 rng: &mut R,
111 number: u64,
112 parent: Option<B256>,
113) -> BlockWithParent {
114 BlockWithParent {
115 parent: parent.unwrap_or_default(),
116 block: NumHash::new(number, rng.random()),
117 }
118}
119
120pub fn random_header<R: Rng>(rng: &mut R, number: u64, parent: Option<B256>) -> SealedHeader {
124 let header = alloy_consensus::Header {
125 number,
126 nonce: B64::random(),
127 difficulty: U256::from(rng.random::<u32>()),
128 parent_hash: parent.unwrap_or_default(),
129 ..Default::default()
130 };
131 SealedHeader::seal_slow(header)
132}
133
134pub fn random_tx<R: Rng>(rng: &mut R) -> Transaction {
141 Transaction::Legacy(TxLegacy {
142 chain_id: Some(1),
143 nonce: rng.random::<u16>().into(),
144 gas_price: rng.random::<u16>().into(),
145 gas_limit: rng.random::<u16>().into(),
146 to: TxKind::Call(Address::random()),
147 value: U256::from(rng.random::<u16>()),
148 input: Bytes::default(),
149 })
150}
151
152pub fn random_signed_tx<R: Rng>(rng: &mut R) -> TransactionSigned {
158 let tx = random_tx(rng);
159 sign_tx_with_random_key_pair(rng, tx)
160}
161
162pub fn sign_tx_with_random_key_pair<R: Rng>(_rng: &mut R, tx: Transaction) -> TransactionSigned {
164 let secp = Secp256k1::new();
165 let key_pair = Keypair::new(&secp, &mut rand_08::thread_rng());
167 sign_tx_with_key_pair(key_pair, tx)
168}
169
170pub fn sign_tx_with_key_pair(key_pair: Keypair, tx: Transaction) -> TransactionSigned {
172 let signature =
173 sign_message(B256::from_slice(&key_pair.secret_bytes()[..]), tx.signature_hash()).unwrap();
174
175 TransactionSigned::new_unhashed(tx, signature)
176}
177
178pub fn generate_key<R: Rng>(_rng: &mut R) -> Keypair {
180 let secp = Secp256k1::new();
181 Keypair::new(&secp, &mut rand_08::thread_rng())
182}
183
184pub fn generate_keys<R: Rng>(_rng: &mut R, count: usize) -> Vec<Keypair> {
186 let secp = Secp256k1::new();
187 (0..count).map(|_| Keypair::new(&secp, &mut rand_08::thread_rng())).collect()
189}
190
191pub fn random_block<R: Rng>(
206 rng: &mut R,
207 number: u64,
208 block_params: BlockParams,
209) -> SealedBlock<Block> {
210 let tx_count = block_params.tx_count.unwrap_or_else(|| rng.random::<u8>());
212 let transactions: Vec<TransactionSigned> =
213 (0..tx_count).map(|_| random_signed_tx(rng)).collect();
214 let total_gas = transactions.iter().fold(0, |sum, tx| sum + tx.transaction().gas_limit());
215
216 let ommers_count = block_params.ommers_count.unwrap_or_else(|| rng.random_range(0..2));
218 let ommers = (0..ommers_count)
219 .map(|_| random_header(rng, number, block_params.parent).unseal())
220 .collect::<Vec<_>>();
221
222 let transactions_root = proofs::calculate_transaction_root(&transactions);
224 let ommers_hash = proofs::calculate_ommers_root(&ommers);
225
226 let withdrawals = block_params.withdrawals_count.map(|count| {
227 (0..count)
228 .map(|i| Withdrawal {
229 amount: rng.random(),
230 index: i.into(),
231 validator_index: i.into(),
232 address: Address::random(),
233 })
234 .collect::<Vec<_>>()
235 });
236 let withdrawals_root = withdrawals.as_ref().map(|w| proofs::calculate_withdrawals_root(w));
237
238 let header = Header {
239 parent_hash: block_params.parent.unwrap_or_default(),
240 number,
241 gas_used: total_gas,
242 gas_limit: total_gas,
243 transactions_root,
244 ommers_hash,
245 base_fee_per_gas: Some(rng.random()),
246 requests_hash: None,
248 withdrawals_root,
249 ..Default::default()
250 };
251
252 Block {
253 header,
254 body: BlockBody { transactions, ommers, withdrawals: withdrawals.map(Withdrawals::new) },
255 }
256 .seal_slow()
257}
258
259pub fn random_block_range<R: Rng>(
266 rng: &mut R,
267 block_numbers: RangeInclusive<BlockNumber>,
268 block_range_params: BlockRangeParams,
269) -> Vec<SealedBlock<Block>> {
270 let mut blocks =
271 Vec::with_capacity(block_numbers.end().saturating_sub(*block_numbers.start()) as usize);
272 for idx in block_numbers {
273 let tx_count = block_range_params.tx_count.clone().sample_single(rng).unwrap();
274 let requests_count =
275 block_range_params.requests_count.clone().map(|r| r.sample_single(rng).unwrap());
276 let withdrawals_count =
277 block_range_params.withdrawals_count.clone().map(|r| r.sample_single(rng).unwrap());
278 let parent = block_range_params.parent.unwrap_or_default();
279 blocks.push(random_block(
280 rng,
281 idx,
282 BlockParams {
283 parent: Some(
284 blocks.last().map(|block: &SealedBlock<Block>| block.hash()).unwrap_or(parent),
285 ),
286 tx_count: Some(tx_count),
287 ommers_count: None,
288 requests_count,
289 withdrawals_count,
290 },
291 ));
292 }
293 blocks
294}
295
296pub type ChangeSet = Vec<(Address, Account, Vec<StorageEntry>)>;
298type AccountState = (Account, Vec<StorageEntry>);
299
300pub fn random_changeset_range<'a, R: Rng, IBlk, IAcc>(
305 rng: &mut R,
306 blocks: IBlk,
307 accounts: IAcc,
308 n_storage_changes: Range<u64>,
309 key_range: Range<u64>,
310) -> (Vec<ChangeSet>, BTreeMap<Address, AccountState>)
311where
312 IBlk: IntoIterator<Item = &'a SealedBlock<Block>>,
313 IAcc: IntoIterator<Item = (Address, (Account, Vec<StorageEntry>))>,
314{
315 let mut state: BTreeMap<_, _> = accounts
316 .into_iter()
317 .map(|(addr, (acc, st))| (addr, (acc, st.into_iter().map(|e| (e.key, e.value)).collect())))
318 .collect();
319
320 let valid_addresses = state.keys().copied().collect::<Vec<_>>();
321
322 let mut changesets = Vec::new();
323
324 for _block in blocks {
325 let mut changeset = Vec::new();
326 let (from, to, mut transfer, new_entries) = random_account_change(
327 rng,
328 &valid_addresses,
329 n_storage_changes.clone(),
330 key_range.clone(),
331 );
332
333 let (prev_from, _) = state.get_mut(&from).unwrap();
335 changeset.push((from, *prev_from, Vec::new()));
336
337 transfer = max(min(transfer, prev_from.balance), U256::from(1));
338 prev_from.balance = prev_from.balance.wrapping_sub(transfer);
339
340 let (prev_to, storage): &mut (Account, BTreeMap<B256, U256>) = state.get_mut(&to).unwrap();
342
343 let mut old_entries: Vec<_> = new_entries
344 .into_iter()
345 .filter_map(|entry| {
346 let old = if entry.value.is_zero() {
347 let old = storage.remove(&entry.key);
348 if matches!(old, Some(U256::ZERO)) {
349 return None
350 }
351 old
352 } else {
353 storage.insert(entry.key, entry.value)
354 };
355 Some(StorageEntry { value: old.unwrap_or(U256::ZERO), ..entry })
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 use rand::prelude::IndexedRandom;
388 let mut addresses = valid_addresses.choose_multiple(rng, 2).copied();
389
390 let addr_from = addresses.next().unwrap_or_else(Address::random);
391 let addr_to = addresses.next().unwrap_or_else(Address::random);
392
393 let balance_change = U256::from(rng.random::<u64>());
394
395 let storage_changes = if n_storage_changes.is_empty() {
396 Vec::new()
397 } else {
398 (0..n_storage_changes.sample_single(rng).unwrap())
399 .map(|_| random_storage_entry(rng, key_range.clone()))
400 .collect()
401 };
402
403 (addr_from, addr_to, balance_change, storage_changes)
404}
405
406pub fn random_storage_entry<R: Rng>(rng: &mut R, key_range: Range<u64>) -> StorageEntry {
408 let key = B256::new({
409 let n = key_range.sample_single(rng).unwrap();
410 let mut m = [0u8; 32];
411 m[24..32].copy_from_slice(&n.to_be_bytes());
412 m
413 });
414 let value = U256::from(rng.random::<u64>());
415
416 StorageEntry { key, value }
417}
418
419pub fn random_eoa_account<R: Rng>(rng: &mut R) -> (Address, Account) {
421 let nonce: u64 = rng.random();
422 let balance = U256::from(rng.random::<u32>());
423 let addr = Address::random();
424
425 (addr, Account { nonce, balance, bytecode_hash: None })
426}
427
428pub fn random_eoa_accounts<R: Rng>(rng: &mut R, accounts_num: usize) -> Vec<(Address, Account)> {
430 let mut accounts = Vec::with_capacity(accounts_num);
431 for _ in 0..accounts_num {
432 accounts.push(random_eoa_account(rng))
433 }
434 accounts
435}
436
437pub fn random_contract_account_range<R: Rng>(
439 rng: &mut R,
440 acc_range: &mut Range<u64>,
441) -> Vec<(Address, Account)> {
442 let mut accounts = Vec::with_capacity(acc_range.end.saturating_sub(acc_range.start) as usize);
443 for _ in acc_range {
444 let (address, eoa_account) = random_eoa_account(rng);
445 let account = Account { bytecode_hash: Some(B256::random()), ..eoa_account };
447 accounts.push((address, account))
448 }
449 accounts
450}
451
452pub fn random_receipt<R: Rng>(
454 rng: &mut R,
455 transaction: &TransactionSigned,
456 logs_count: Option<u8>,
457) -> Receipt {
458 let success = rng.random::<bool>();
459 let logs_count = logs_count.unwrap_or_else(|| rng.random::<u8>());
460 #[expect(clippy::needless_update)] Receipt {
462 tx_type: transaction.tx_type(),
463 success,
464 cumulative_gas_used: rng.random_range(0..=transaction.gas_limit()),
465 logs: if success {
466 (0..logs_count).map(|_| random_log(rng, None, None)).collect()
467 } else {
468 vec![]
469 },
470 ..Default::default()
471 }
472}
473
474pub fn random_log<R: Rng>(rng: &mut R, address: Option<Address>, topics_count: Option<u8>) -> Log {
476 let data_byte_count = rng.random::<u8>() as usize;
477 let topics_count = topics_count.unwrap_or_else(|| rng.random()) as usize;
478 Log::new_unchecked(
479 address.unwrap_or_else(|| Address::random()),
480 std::iter::repeat_with(|| B256::random()).take(topics_count).collect(),
481 std::iter::repeat_with(|| rng.random()).take(data_byte_count).collect::<Vec<_>>().into(),
482 )
483}
484
485#[cfg(test)]
486mod tests {
487 use super::*;
488 use alloy_consensus::TxEip1559;
489 use alloy_eips::eip2930::AccessList;
490 use alloy_primitives::{hex, Signature};
491 use reth_primitives_traits::{
492 crypto::secp256k1::{public_key_to_address, sign_message},
493 SignedTransaction,
494 };
495 use std::str::FromStr;
496
497 #[test]
498 fn test_sign_message() {
499 let secp = Secp256k1::new();
500
501 let tx = Transaction::Eip1559(TxEip1559 {
502 chain_id: 1,
503 nonce: 0x42,
504 gas_limit: 44386,
505 to: TxKind::Call(hex!("6069a6c32cf691f5982febae4faf8a6f3ab2f0f6").into()),
506 value: U256::from(0_u64),
507 input: hex!("a22cb4650000000000000000000000005eee75727d804a2b13038928d36f8b188945a57a0000000000000000000000000000000000000000000000000000000000000000").into(),
508 max_fee_per_gas: 0x4a817c800,
509 max_priority_fee_per_gas: 0x3b9aca00,
510 access_list: AccessList::default(),
511 });
512 let signature_hash = tx.signature_hash();
513
514 for _ in 0..100 {
515 let key_pair = Keypair::new(&secp, &mut rand_08::thread_rng());
516
517 let signature =
518 sign_message(B256::from_slice(&key_pair.secret_bytes()[..]), signature_hash)
519 .unwrap();
520
521 let signed = TransactionSigned::new_unhashed(tx.clone(), signature);
522 let recovered = signed.recover_signer().unwrap();
523
524 let expected = public_key_to_address(key_pair.public_key());
525 assert_eq!(recovered, expected);
526 }
527 }
528
529 #[test]
530 fn test_sign_eip_155() {
531 let transaction = Transaction::Legacy(TxLegacy {
533 chain_id: Some(1),
534 nonce: 9,
535 gas_price: 20 * 10_u128.pow(9),
536 gas_limit: 21000,
537 to: TxKind::Call(hex!("3535353535353535353535353535353535353535").into()),
538 value: U256::from(10_u128.pow(18)),
539 input: Bytes::default(),
540 });
541
542 let hash = transaction.signature_hash();
548 let expected =
549 B256::from_str("daf5a779ae972f972197303d7b574746c7ef83eadac0f2791ad23db92e4c8e53")
550 .unwrap();
551 assert_eq!(expected, hash);
552
553 let secret =
554 B256::from_str("4646464646464646464646464646464646464646464646464646464646464646")
555 .unwrap();
556 let signature = sign_message(secret, hash).unwrap();
557
558 let expected = Signature::new(
559 U256::from_str(
560 "18515461264373351373200002665853028612451056578545711640558177340181847433846",
561 )
562 .unwrap(),
563 U256::from_str(
564 "46948507304638947509940763649030358759909902576025900602547168820602576006531",
565 )
566 .unwrap(),
567 false,
568 );
569 assert_eq!(expected, signature);
570 }
571}