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