1//! Helps create a custom genesis alloc by making it easy to add funded accounts with known
2//! signers to the genesis block.
34use alloy_genesis::GenesisAccount;
5use alloy_primitives::{Address, Bytes, B256, U256};
6use reth_primitives_traits::crypto::secp256k1::public_key_to_address;
7use secp256k1::{
8 rand::{thread_rng, RngCore},
9 Keypair, Secp256k1,
10};
11use std::{
12 collections::{hash_map::Entry, BTreeMap, HashMap},
13fmt,
14};
1516/// This helps create a custom genesis alloc by making it easy to add funded accounts with known
17/// signers to the genesis block.
18///
19/// # Example
20/// ```
21/// # use alloy_primitives::{Address, U256, hex, Bytes};
22/// # use reth_testing_utils::GenesisAllocator;
23/// # use std::str::FromStr;
24/// let mut allocator = GenesisAllocator::default();
25///
26/// // This will add a genesis account to the alloc builder, with the provided balance. The
27/// // signer for the account will be returned.
28/// let (_signer, _addr) = allocator.new_funded_account(U256::from(100_000_000_000_000_000u128));
29///
30/// // You can also provide code for the account.
31/// let code = Bytes::from_str("0x1234").unwrap();
32/// let (_second_signer, _second_addr) =
33/// allocator.new_funded_account_with_code(U256::from(100_000_000_000_000_000u128), code);
34///
35/// // You can also add an account with a specific address.
36/// // This will not return a signer, since the address is provided by the user and the signer
37/// // may be unknown.
38/// let addr = "0Ac1dF02185025F65202660F8167210A80dD5086".parse::<Address>().unwrap();
39/// allocator.add_funded_account_with_address(addr, U256::from(100_000_000_000_000_000u128));
40///
41/// // Once you're done adding accounts, you can build the alloc.
42/// let alloc = allocator.build();
43/// ```
44pub struct GenesisAllocator<'a> {
45/// The genesis alloc to be built.
46alloc: HashMap<Address, GenesisAccount>,
47/// The rng to use for generating key pairs.
48rng: Box<dyn RngCore + 'a>,
49}
5051impl<'a> GenesisAllocator<'a> {
52/// Initialize a new alloc builder with the provided rng.
53pub fn new_with_rng<R>(rng: &'a mut R) -> Self
54where
55R: RngCore,
56 {
57Self { alloc: HashMap::default(), rng: Box::new(rng) }
58 }
5960/// Use the provided rng for generating key pairs.
61pub fn with_rng<R>(mut self, rng: &'a mut R) -> Self
62where
63R: RngCore + std::fmt::Debug,
64 {
65self.rng = Box::new(rng);
66self67}
6869/// Add a funded account to the genesis alloc.
70 ///
71 /// Returns the key pair for the account and the account's address.
72pub fn new_funded_account(&mut self, balance: U256) -> (Keypair, Address) {
73let secp = Secp256k1::new();
74let pair = Keypair::new(&secp, &mut self.rng);
75let address = public_key_to_address(pair.public_key());
7677self.alloc.insert(address, GenesisAccount::default().with_balance(balance));
7879 (pair, address)
80 }
8182/// Add a funded account to the genesis alloc with the provided code.
83 ///
84 /// Returns the key pair for the account and the account's address.
85pub fn new_funded_account_with_code(
86&mut self,
87 balance: U256,
88 code: Bytes,
89 ) -> (Keypair, Address) {
90let secp = Secp256k1::new();
91let pair = Keypair::new(&secp, &mut self.rng);
92let address = public_key_to_address(pair.public_key());
9394self.alloc
95 .insert(address, GenesisAccount::default().with_balance(balance).with_code(Some(code)));
9697 (pair, address)
98 }
99100/// Adds a funded account to the genesis alloc with the provided storage.
101 ///
102 /// Returns the key pair for the account and the account's address.
103pub fn new_funded_account_with_storage(
104&mut self,
105 balance: U256,
106 storage: BTreeMap<B256, B256>,
107 ) -> (Keypair, Address) {
108let secp = Secp256k1::new();
109let pair = Keypair::new(&secp, &mut self.rng);
110let address = public_key_to_address(pair.public_key());
111112self.alloc.insert(
113address,
114GenesisAccount::default().with_balance(balance).with_storage(Some(storage)),
115 );
116117 (pair, address)
118 }
119120/// Adds an account with code and storage to the genesis alloc.
121 ///
122 /// Returns the key pair for the account and the account's address.
123pub fn new_account_with_code_and_storage(
124&mut self,
125 code: Bytes,
126 storage: BTreeMap<B256, B256>,
127 ) -> (Keypair, Address) {
128let secp = Secp256k1::new();
129let pair = Keypair::new(&secp, &mut self.rng);
130let address = public_key_to_address(pair.public_key());
131132self.alloc.insert(
133address,
134GenesisAccount::default().with_code(Some(code)).with_storage(Some(storage)),
135 );
136137 (pair, address)
138 }
139140/// Adds an account with code to the genesis alloc.
141 ///
142 /// Returns the key pair for the account and the account's address.
143pub fn new_account_with_code(&mut self, code: Bytes) -> (Keypair, Address) {
144let secp = Secp256k1::new();
145let pair = Keypair::new(&secp, &mut self.rng);
146let address = public_key_to_address(pair.public_key());
147148self.alloc.insert(address, GenesisAccount::default().with_code(Some(code)));
149150 (pair, address)
151 }
152153/// Add a funded account to the genesis alloc with the provided address.
154 ///
155 /// Neither the key pair nor the account will be returned, since the address is provided by
156 /// the user and the signer may be unknown.
157pub fn add_funded_account_with_address(&mut self, address: Address, balance: U256) {
158self.alloc.insert(address, GenesisAccount::default().with_balance(balance));
159 }
160161/// Adds the given [`GenesisAccount`] to the genesis alloc.
162 ///
163 /// Returns the key pair for the account and the account's address.
164pub fn add_account(&mut self, account: GenesisAccount) -> Address {
165let secp = Secp256k1::new();
166let pair = Keypair::new(&secp, &mut self.rng);
167let address = public_key_to_address(pair.public_key());
168169self.alloc.insert(address, account);
170171address172 }
173174/// Gets the account for the provided address.
175 ///
176 /// If it does not exist, this returns `None`.
177pub fn get_account(&self, address: &Address) -> Option<&GenesisAccount> {
178self.alloc.get(address)
179 }
180181/// Gets a mutable version of the account for the provided address, if it exists.
182pub fn get_account_mut(&mut self, address: &Address) -> Option<&mut GenesisAccount> {
183self.alloc.get_mut(address)
184 }
185186/// Gets an [Entry] for the provided address.
187pub fn account_entry(&mut self, address: Address) -> Entry<'_, Address, GenesisAccount> {
188self.alloc.entry(address)
189 }
190191/// Build the genesis alloc.
192pub fn build(self) -> HashMap<Address, GenesisAccount> {
193self.alloc
194 }
195}
196197impl Defaultfor GenesisAllocator<'_> {
198fn default() -> Self {
199Self { alloc: HashMap::default(), rng: Box::new(thread_rng()) }
200 }
201}
202203impl fmt::Debugfor GenesisAllocator<'_> {
204fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
205f.debug_struct("GenesisAllocator").field("alloc", &self.alloc).finish_non_exhaustive()
206 }
207}