Skip to main content

reth_testing_utils/
genesis_allocator.rs

1//! Helps create a custom genesis alloc by making it easy to add funded accounts with known
2//! signers to the genesis block.
3
4use alloy_genesis::GenesisAccount;
5use alloy_primitives::{
6    map::{AddressMap, Entry},
7    Address, Bytes, B256, U256,
8};
9use secp256k1::{
10    rand::{rng, RngCore},
11    Keypair,
12};
13use std::{collections::BTreeMap, fmt};
14
15/// This helps create a custom genesis alloc by making it easy to add funded accounts with known
16/// signers to the genesis block.
17///
18/// # Example
19/// ```
20/// # use alloy_primitives::{Address, U256, hex, Bytes};
21/// # use reth_testing_utils::GenesisAllocator;
22/// # use std::str::FromStr;
23/// let mut allocator = GenesisAllocator::default();
24///
25/// // This will add a genesis account to the alloc builder, with the provided balance. The
26/// // signer for the account will be returned.
27/// let (_signer, _addr) = allocator.new_funded_account(U256::from(100_000_000_000_000_000u128));
28///
29/// // You can also provide code for the account.
30/// let code = Bytes::from_str("0x1234").unwrap();
31/// let (_second_signer, _second_addr) =
32///     allocator.new_funded_account_with_code(U256::from(100_000_000_000_000_000u128), code);
33///
34/// // You can also add an account with a specific address.
35/// // This will not return a signer, since the address is provided by the user and the signer
36/// // may be unknown.
37/// let addr = "0Ac1dF02185025F65202660F8167210A80dD5086".parse::<Address>().unwrap();
38/// allocator.add_funded_account_with_address(addr, U256::from(100_000_000_000_000_000u128));
39///
40/// // Once you're done adding accounts, you can build the alloc.
41/// let alloc = allocator.build();
42/// ```
43pub struct GenesisAllocator<'a> {
44    /// The genesis alloc to be built.
45    alloc: AddressMap<GenesisAccount>,
46    /// The rng to use for generating key pairs.
47    rng: Box<dyn RngCore + 'a>,
48}
49
50impl<'a> GenesisAllocator<'a> {
51    /// Initialize a new alloc builder with the provided rng.
52    pub fn new_with_rng<R>(rng: &'a mut R) -> Self
53    where
54        R: RngCore,
55    {
56        Self { alloc: AddressMap::default(), rng: Box::new(rng) }
57    }
58
59    /// Use the provided rng for generating key pairs.
60    pub fn with_rng<R>(mut self, rng: &'a mut R) -> Self
61    where
62        R: RngCore + std::fmt::Debug,
63    {
64        self.rng = Box::new(rng);
65        self
66    }
67
68    /// Add a funded account to the genesis alloc.
69    ///
70    /// Returns the key pair for the account and the account's address.
71    pub fn new_funded_account(&mut self, balance: U256) -> (Keypair, Address) {
72        let pair = Keypair::new(&mut self.rng);
73        let address =
74            Address::from_raw_public_key(&pair.public_key().serialize_uncompressed()[1..]);
75
76        self.alloc.insert(address, GenesisAccount::default().with_balance(balance));
77
78        (pair, address)
79    }
80
81    /// Add a funded account to the genesis alloc with the provided code.
82    ///
83    /// Returns the key pair for the account and the account's address.
84    pub fn new_funded_account_with_code(
85        &mut self,
86        balance: U256,
87        code: Bytes,
88    ) -> (Keypair, Address) {
89        let pair = Keypair::new(&mut self.rng);
90        let address =
91            Address::from_raw_public_key(&pair.public_key().serialize_uncompressed()[1..]);
92
93        self.alloc
94            .insert(address, GenesisAccount::default().with_balance(balance).with_code(Some(code)));
95
96        (pair, address)
97    }
98
99    /// Adds a funded account to the genesis alloc with the provided storage.
100    ///
101    /// Returns the key pair for the account and the account's address.
102    pub fn new_funded_account_with_storage(
103        &mut self,
104        balance: U256,
105        storage: BTreeMap<B256, B256>,
106    ) -> (Keypair, Address) {
107        let pair = Keypair::new(&mut self.rng);
108        let address =
109            Address::from_raw_public_key(&pair.public_key().serialize_uncompressed()[1..]);
110
111        self.alloc.insert(
112            address,
113            GenesisAccount::default().with_balance(balance).with_storage(Some(storage)),
114        );
115
116        (pair, address)
117    }
118
119    /// Adds an account with code and storage to the genesis alloc.
120    ///
121    /// Returns the key pair for the account and the account's address.
122    pub fn new_account_with_code_and_storage(
123        &mut self,
124        code: Bytes,
125        storage: BTreeMap<B256, B256>,
126    ) -> (Keypair, Address) {
127        let pair = Keypair::new(&mut self.rng);
128        let address =
129            Address::from_raw_public_key(&pair.public_key().serialize_uncompressed()[1..]);
130
131        self.alloc.insert(
132            address,
133            GenesisAccount::default().with_code(Some(code)).with_storage(Some(storage)),
134        );
135
136        (pair, address)
137    }
138
139    /// Adds an account with code to the genesis alloc.
140    ///
141    /// Returns the key pair for the account and the account's address.
142    pub fn new_account_with_code(&mut self, code: Bytes) -> (Keypair, Address) {
143        let pair = Keypair::new(&mut self.rng);
144        let address =
145            Address::from_raw_public_key(&pair.public_key().serialize_uncompressed()[1..]);
146
147        self.alloc.insert(address, GenesisAccount::default().with_code(Some(code)));
148
149        (pair, address)
150    }
151
152    /// Add a funded account to the genesis alloc with the provided address.
153    ///
154    /// Neither the key pair nor the account will be returned, since the address is provided by
155    /// the user and the signer may be unknown.
156    pub fn add_funded_account_with_address(&mut self, address: Address, balance: U256) {
157        self.alloc.insert(address, GenesisAccount::default().with_balance(balance));
158    }
159
160    /// Adds the given [`GenesisAccount`] to the genesis alloc.
161    ///
162    /// Returns the key pair for the account and the account's address.
163    pub fn add_account(&mut self, account: GenesisAccount) -> Address {
164        let pair = Keypair::new(&mut self.rng);
165        let address =
166            Address::from_raw_public_key(&pair.public_key().serialize_uncompressed()[1..]);
167
168        self.alloc.insert(address, account);
169
170        address
171    }
172
173    /// Gets the account for the provided address.
174    ///
175    /// If it does not exist, this returns `None`.
176    pub fn get_account(&self, address: &Address) -> Option<&GenesisAccount> {
177        self.alloc.get(address)
178    }
179
180    /// Gets a mutable version of the account for the provided address, if it exists.
181    pub fn get_account_mut(&mut self, address: &Address) -> Option<&mut GenesisAccount> {
182        self.alloc.get_mut(address)
183    }
184
185    /// Gets an [Entry] for the provided address.
186    pub fn account_entry(&mut self, address: Address) -> Entry<'_, Address, GenesisAccount> {
187        self.alloc.entry(address)
188    }
189
190    /// Build the genesis alloc.
191    pub fn build(self) -> AddressMap<GenesisAccount> {
192        self.alloc
193    }
194}
195
196impl Default for GenesisAllocator<'_> {
197    fn default() -> Self {
198        Self { alloc: AddressMap::default(), rng: Box::new(rng()) }
199    }
200}
201
202impl fmt::Debug for GenesisAllocator<'_> {
203    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
204        f.debug_struct("GenesisAllocator").field("alloc", &self.alloc).finish_non_exhaustive()
205    }
206}