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};
19use secp256k1::{Keypair, Secp256k1};
20use std::{
21 cmp::{max, min},
22 collections::BTreeMap,
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 rng_with_seed(seed.as_bytes())
73 } else {
74 StdRng::from_rng(&mut rand::rng())
75 }
76}
77
78pub fn rng_with_seed(seed: &[u8]) -> StdRng {
80 let mut seed_bytes = [0u8; 32];
81 seed_bytes[..seed.len().min(32)].copy_from_slice(seed);
82 StdRng::from_seed(seed_bytes)
83}
84
85pub fn random_header_range<R: Rng>(
92 rng: &mut R,
93 range: Range<u64>,
94 head: B256,
95) -> Vec<SealedHeader> {
96 let mut headers = Vec::with_capacity(range.end.saturating_sub(range.start) as usize);
97 for idx in range {
98 headers.push(random_header(
99 rng,
100 idx,
101 Some(headers.last().map(|h: &SealedHeader| h.hash()).unwrap_or(head)),
102 ));
103 }
104 headers
105}
106
107pub fn random_block_with_parent<R: Rng>(
109 rng: &mut R,
110 number: u64,
111 parent: Option<B256>,
112) -> BlockWithParent {
113 BlockWithParent {
114 parent: parent.unwrap_or_default(),
115 block: NumHash::new(number, rng.random()),
116 }
117}
118
119pub fn random_header<R: Rng>(rng: &mut R, number: u64, parent: Option<B256>) -> SealedHeader {
123 let header = alloy_consensus::Header {
124 number,
125 nonce: B64::random(),
126 difficulty: U256::from(rng.random::<u32>()),
127 parent_hash: parent.unwrap_or_default(),
128 ..Default::default()
129 };
130 SealedHeader::seal_slow(header)
131}
132
133pub fn random_tx<R: Rng>(rng: &mut R) -> Transaction {
140 Transaction::Legacy(TxLegacy {
141 chain_id: Some(1),
142 nonce: rng.random::<u16>().into(),
143 gas_price: rng.random::<u16>().into(),
144 gas_limit: rng.random::<u16>().into(),
145 to: TxKind::Call(Address::random()),
146 value: U256::from(rng.random::<u16>()),
147 input: Bytes::default(),
148 })
149}
150
151pub fn random_signed_tx<R: Rng>(rng: &mut R) -> TransactionSigned {
157 let tx = random_tx(rng);
158 sign_tx_with_random_key_pair(rng, tx)
159}
160
161pub fn sign_tx_with_random_key_pair<R: Rng>(_rng: &mut R, tx: Transaction) -> TransactionSigned {
163 let secp = Secp256k1::new();
164 let key_pair = Keypair::new(&secp, &mut rand_08::thread_rng());
166 sign_tx_with_key_pair(key_pair, tx)
167}
168
169pub fn sign_tx_with_key_pair(key_pair: Keypair, tx: Transaction) -> TransactionSigned {
171 let signature =
172 sign_message(B256::from_slice(&key_pair.secret_bytes()[..]), tx.signature_hash()).unwrap();
173
174 tx.into_signed(signature).into()
175}
176
177pub fn generate_key<R: Rng>(_rng: &mut R) -> Keypair {
179 let secp = Secp256k1::new();
180 Keypair::new(&secp, &mut rand_08::thread_rng())
181}
182
183pub fn generate_keys<R: Rng>(_rng: &mut R, count: usize) -> Vec<Keypair> {
185 let secp = Secp256k1::new();
186 (0..count).map(|_| Keypair::new(&secp, &mut rand_08::thread_rng())).collect()
188}
189
190pub fn random_block<R: Rng>(
205 rng: &mut R,
206 number: u64,
207 block_params: BlockParams,
208) -> SealedBlock<Block> {
209 let tx_count = block_params.tx_count.unwrap_or_else(|| rng.random::<u8>());
211 let transactions: Vec<TransactionSigned> =
212 (0..tx_count).map(|_| random_signed_tx(rng)).collect();
213 let total_gas = transactions.iter().fold(0, |sum, tx| sum + tx.gas_limit());
214
215 let ommers_count = block_params.ommers_count.unwrap_or_else(|| rng.random_range(0..2));
217 let ommers = (0..ommers_count)
218 .map(|_| random_header(rng, number, block_params.parent).unseal())
219 .collect::<Vec<_>>();
220
221 let transactions_root = proofs::calculate_transaction_root(&transactions);
223 let ommers_hash = proofs::calculate_ommers_root(&ommers);
224
225 let withdrawals = block_params.withdrawals_count.map(|count| {
226 (0..count)
227 .map(|i| Withdrawal {
228 amount: rng.random(),
229 index: i.into(),
230 validator_index: i.into(),
231 address: Address::random(),
232 })
233 .collect::<Vec<_>>()
234 });
235 let withdrawals_root = withdrawals.as_ref().map(|w| proofs::calculate_withdrawals_root(w));
236
237 let header = Header {
238 parent_hash: block_params.parent.unwrap_or_default(),
239 number,
240 gas_used: total_gas,
241 gas_limit: total_gas,
242 transactions_root,
243 ommers_hash,
244 base_fee_per_gas: Some(rng.random()),
245 requests_hash: None,
247 withdrawals_root,
248 ..Default::default()
249 };
250
251 Block {
252 header,
253 body: BlockBody { transactions, ommers, withdrawals: withdrawals.map(Withdrawals::new) },
254 }
255 .seal_slow()
256}
257
258pub fn random_block_range<R: Rng>(
265 rng: &mut R,
266 block_numbers: RangeInclusive<BlockNumber>,
267 block_range_params: BlockRangeParams,
268) -> Vec<SealedBlock<Block>> {
269 let mut blocks =
270 Vec::with_capacity(block_numbers.end().saturating_sub(*block_numbers.start()) as usize);
271 for idx in block_numbers {
272 let tx_count = block_range_params.tx_count.clone().sample_single(rng).unwrap();
273 let requests_count =
274 block_range_params.requests_count.clone().map(|r| r.sample_single(rng).unwrap());
275 let withdrawals_count =
276 block_range_params.withdrawals_count.clone().map(|r| r.sample_single(rng).unwrap());
277 let parent = block_range_params.parent.unwrap_or_default();
278 blocks.push(random_block(
279 rng,
280 idx,
281 BlockParams {
282 parent: Some(
283 blocks.last().map(|block: &SealedBlock<Block>| block.hash()).unwrap_or(parent),
284 ),
285 tx_count: Some(tx_count),
286 ommers_count: None,
287 requests_count,
288 withdrawals_count,
289 },
290 ));
291 }
292 blocks
293}
294
295pub type ChangeSet = Vec<(Address, Account, Vec<StorageEntry>)>;
297type AccountState = (Account, Vec<StorageEntry>);
298
299pub fn random_changeset_range<'a, R: Rng, IBlk, IAcc>(
304 rng: &mut R,
305 blocks: IBlk,
306 accounts: IAcc,
307 n_storage_changes: Range<u64>,
308 key_range: Range<u64>,
309) -> (Vec<ChangeSet>, BTreeMap<Address, AccountState>)
310where
311 IBlk: IntoIterator<Item = &'a SealedBlock<Block>>,
312 IAcc: IntoIterator<Item = (Address, (Account, Vec<StorageEntry>))>,
313{
314 let mut state: BTreeMap<_, _> = accounts
315 .into_iter()
316 .map(|(addr, (acc, st))| {
317 (
318 addr,
319 (
320 acc,
321 st.into_iter()
322 .map(|e| (e.key, (e.value, e.is_private)))
323 .collect::<BTreeMap<_, _>>(),
324 ),
325 )
326 })
327 .collect();
328
329 let valid_addresses = state.keys().copied().collect::<Vec<_>>();
330
331 let mut changesets = Vec::new();
332
333 for _block in blocks {
334 let mut changeset = Vec::new();
335 let (from, to, mut transfer, new_entries) = random_account_change(
336 rng,
337 &valid_addresses,
338 n_storage_changes.clone(),
339 key_range.clone(),
340 );
341
342 let (prev_from, _) = state.get_mut(&from).unwrap();
344 changeset.push((from, *prev_from, Vec::new()));
345
346 transfer = max(min(transfer, prev_from.balance), U256::from(1));
347 prev_from.balance = prev_from.balance.wrapping_sub(transfer);
348
349 let (prev_to, storage): &mut (Account, BTreeMap<B256, (U256, bool)>) =
351 state.get_mut(&to).unwrap();
352
353 let mut old_entries: Vec<_> = new_entries
354 .into_iter()
355 .filter_map(|entry| {
356 let old = if entry.value.is_zero() {
357 let old = storage.remove(&entry.key);
358 if matches!(old, Some((U256::ZERO, false))) {
359 return None;
360 }
361 old
362 } else {
363 storage.insert(entry.key, (entry.value, entry.is_private))
364 };
365 match old {
366 Some((old_value, old_is_private)) => {
367 return Some(StorageEntry {
368 value: old_value,
369 is_private: old_is_private,
370 ..entry
371 });
372 }
373 None => {
374 return Some(StorageEntry { value: U256::ZERO, is_private: false, ..entry });
375 }
376 }
377 })
378 .collect();
379 old_entries.sort_by_key(|entry| entry.key);
380
381 changeset.push((to, *prev_to, old_entries));
382
383 changeset.sort_by_key(|(address, _, _)| *address);
384
385 prev_to.balance = prev_to.balance.wrapping_add(transfer);
386
387 changesets.push(changeset);
388 }
389
390 let final_state = state
391 .into_iter()
392 .map(|(addr, (acc, storage))| {
393 (addr, (acc, storage.into_iter().map(|v| v.into()).collect()))
394 })
395 .collect();
396 (changesets, final_state)
397}
398
399pub fn random_account_change<R: Rng>(
403 rng: &mut R,
404 valid_addresses: &[Address],
405 n_storage_changes: Range<u64>,
406 key_range: Range<u64>,
407) -> (Address, Address, U256, Vec<StorageEntry>) {
408 use rand::prelude::IndexedRandom;
409 let mut addresses = valid_addresses.choose_multiple(rng, 2).copied();
410
411 let addr_from = addresses.next().unwrap_or_else(Address::random);
412 let addr_to = addresses.next().unwrap_or_else(Address::random);
413
414 let balance_change = U256::from(rng.random::<u64>());
415
416 let storage_changes = if n_storage_changes.is_empty() {
417 Vec::new()
418 } else {
419 (0..n_storage_changes.sample_single(rng).unwrap())
420 .map(|_| random_storage_entry(rng, key_range.clone()))
421 .collect()
422 };
423
424 (addr_from, addr_to, balance_change, storage_changes)
425}
426
427pub fn random_storage_entry<R: Rng>(rng: &mut R, key_range: Range<u64>) -> StorageEntry {
429 let key = B256::new({
430 let n = key_range.sample_single(rng).unwrap();
431 let mut m = [0u8; 32];
432 m[24..32].copy_from_slice(&n.to_be_bytes());
433 m
434 });
435 let value = U256::from(rng.random::<u64>());
436
437 StorageEntry { key, value, ..Default::default() }
438}
439
440pub fn random_eoa_account<R: Rng>(rng: &mut R) -> (Address, Account) {
442 let nonce: u64 = rng.random();
443 let balance = U256::from(rng.random::<u32>());
444 let addr = Address::random();
445
446 (addr, Account { nonce, balance, bytecode_hash: None })
447}
448
449pub fn random_eoa_accounts<R: Rng>(rng: &mut R, accounts_num: usize) -> Vec<(Address, Account)> {
451 let mut accounts = Vec::with_capacity(accounts_num);
452 for _ in 0..accounts_num {
453 accounts.push(random_eoa_account(rng))
454 }
455 accounts
456}
457
458pub fn random_contract_account_range<R: Rng>(
460 rng: &mut R,
461 acc_range: &mut Range<u64>,
462) -> Vec<(Address, Account)> {
463 let mut accounts = Vec::with_capacity(acc_range.end.saturating_sub(acc_range.start) as usize);
464 for _ in acc_range {
465 let (address, eoa_account) = random_eoa_account(rng);
466 let account = Account { bytecode_hash: Some(B256::random()), ..eoa_account };
468 accounts.push((address, account))
469 }
470 accounts
471}
472
473pub fn random_receipt<R: Rng>(
475 rng: &mut R,
476 transaction: &TransactionSigned,
477 logs_count: Option<u8>,
478) -> Receipt {
479 let success = rng.random::<bool>();
480 let logs_count = logs_count.unwrap_or_else(|| rng.random::<u8>());
481 #[expect(clippy::needless_update)] Receipt {
483 tx_type: transaction.tx_type(),
484 success,
485 cumulative_gas_used: rng.random_range(0..=transaction.gas_limit()),
486 logs: if success {
487 (0..logs_count).map(|_| random_log(rng, None, None)).collect()
488 } else {
489 vec![]
490 },
491 ..Default::default()
492 }
493}
494
495pub fn random_log<R: Rng>(rng: &mut R, address: Option<Address>, topics_count: Option<u8>) -> Log {
497 let data_byte_count = rng.random::<u8>() as usize;
498 let topics_count = topics_count.unwrap_or_else(|| rng.random()) as usize;
499 Log::new_unchecked(
500 address.unwrap_or_else(|| Address::random()),
501 std::iter::repeat_with(|| B256::random()).take(topics_count).collect(),
502 std::iter::repeat_with(|| rng.random()).take(data_byte_count).collect::<Vec<_>>().into(),
503 )
504}
505
506#[cfg(test)]
507mod tests {
508 use super::*;
509 use alloy_consensus::TxEip1559;
510 use alloy_eips::eip2930::AccessList;
511 use alloy_primitives::{hex, Signature};
512 use reth_primitives_traits::{
513 crypto::secp256k1::{public_key_to_address, sign_message},
514 SignerRecoverable,
515 };
516 use std::str::FromStr;
517
518 #[test]
519 fn test_sign_message() {
520 let secp = Secp256k1::new();
521
522 let tx = Transaction::Eip1559(TxEip1559 {
523 chain_id: 1,
524 nonce: 0x42,
525 gas_limit: 44386,
526 to: TxKind::Call(hex!("6069a6c32cf691f5982febae4faf8a6f3ab2f0f6").into()),
527 value: U256::from(0_u64),
528 input: hex!("a22cb4650000000000000000000000005eee75727d804a2b13038928d36f8b188945a57a0000000000000000000000000000000000000000000000000000000000000000").into(),
529 max_fee_per_gas: 0x4a817c800,
530 max_priority_fee_per_gas: 0x3b9aca00,
531 access_list: AccessList::default(),
532 });
533 let signature_hash = tx.signature_hash();
534
535 for _ in 0..100 {
536 let key_pair = Keypair::new(&secp, &mut rand_08::thread_rng());
537
538 let signature =
539 sign_message(B256::from_slice(&key_pair.secret_bytes()[..]), signature_hash)
540 .unwrap();
541
542 let signed: TransactionSigned = tx.clone().into_signed(signature).into();
543 let recovered = signed.recover_signer().unwrap();
544
545 let expected = public_key_to_address(key_pair.public_key());
546 assert_eq!(recovered, expected);
547 }
548 }
549
550 #[test]
551 fn test_sign_eip_155() {
552 let transaction = Transaction::Legacy(TxLegacy {
554 chain_id: Some(1),
555 nonce: 9,
556 gas_price: 20 * 10_u128.pow(9),
557 gas_limit: 21000,
558 to: TxKind::Call(hex!("3535353535353535353535353535353535353535").into()),
559 value: U256::from(10_u128.pow(18)),
560 input: Bytes::default(),
561 });
562
563 let hash = transaction.signature_hash();
569 let expected =
570 B256::from_str("daf5a779ae972f972197303d7b574746c7ef83eadac0f2791ad23db92e4c8e53")
571 .unwrap();
572 assert_eq!(expected, hash);
573
574 let secret =
575 B256::from_str("4646464646464646464646464646464646464646464646464646464646464646")
576 .unwrap();
577 let signature = sign_message(secret, hash).unwrap();
578
579 let expected = Signature::new(
580 U256::from_str(
581 "18515461264373351373200002665853028612451056578545711640558177340181847433846",
582 )
583 .unwrap(),
584 U256::from_str(
585 "46948507304638947509940763649030358759909902576025900602547168820602576006531",
586 )
587 .unwrap(),
588 false,
589 );
590 assert_eq!(expected, signature);
591 }
592}