anvil_polkadot/substrate_node/
genesis.rs1use crate::{
4 api_server::revive_conversions::ReviveAddress, config::AnvilNodeConfig,
5 substrate_node::service::storage::well_known_keys,
6};
7use alloy_genesis::GenesisAccount;
8use alloy_primitives::{Address, U256};
9use codec::Encode;
10use polkadot_sdk::{
11 pallet_revive::{evm::Account, genesis::ContractData},
12 sc_chain_spec::{BuildGenesisBlock, resolve_state_version_from_wasm},
13 sc_client_api::{BlockImportOperation, backend::Backend},
14 sc_executor::RuntimeVersionOf,
15 sp_blockchain,
16 sp_core::{self, H160, storage::Storage},
17 sp_runtime::{
18 BuildStorage, FixedU128,
19 traits::{Block as BlockT, Hash as HashT, HashingFor, Header as HeaderT},
20 },
21};
22use serde::{Deserialize, Serialize};
23use serde_json::{Value, json};
24use std::{collections::BTreeMap, marker::PhantomData, sync::Arc};
25use substrate_runtime::{WASM_BINARY, constants::NATIVE_TO_ETH_RATIO};
26use subxt_signer::eth::Keypair;
27
28#[derive(Clone, Debug, Default)]
30pub struct GenesisConfig {
31 pub chain_id: u64,
33 pub timestamp: u64,
35 pub alloc: Option<BTreeMap<Address, GenesisAccount>>,
38 pub number: u32,
40 pub base_fee_per_gas: FixedU128,
42 pub genesis_accounts: Vec<Keypair>,
44 pub genesis_balance: U256,
46 pub coinbase: Option<Address>,
48 pub code: Vec<u8>,
50}
51
52impl<'a> From<&'a AnvilNodeConfig> for GenesisConfig {
53 fn from(anvil_config: &'a AnvilNodeConfig) -> Self {
54 Self {
55 chain_id: anvil_config.get_chain_id(),
56 timestamp: anvil_config
58 .get_genesis_timestamp()
59 .checked_mul(1000)
60 .expect("Genesis timestamp overflow"),
61 alloc: anvil_config.genesis.as_ref().map(|g| g.alloc.clone()),
62 number: anvil_config
63 .get_genesis_number()
64 .try_into()
65 .expect("Genesis block number overflow"),
66 base_fee_per_gas: FixedU128::from_rational(
67 anvil_config.get_base_fee(),
68 NATIVE_TO_ETH_RATIO.into(),
69 ),
70 genesis_accounts: anvil_config.genesis_accounts.clone(),
71 genesis_balance: anvil_config.genesis_balance,
72 coinbase: anvil_config.genesis.as_ref().map(|g| g.coinbase),
73 code: WASM_BINARY.expect("Development wasm not available").to_vec(),
74 }
75 }
76}
77
78#[derive(Debug, Clone, Serialize, Deserialize)]
80pub struct ReviveGenesisAccount {
81 pub address: H160,
82 #[serde(default)]
83 pub balance: U256,
84 #[serde(default)]
85 pub nonce: u64,
86 #[serde(flatten, skip_serializing_if = "Option::is_none")]
87 pub contract_data: Option<ContractData>,
88}
89
90impl GenesisConfig {
91 pub fn as_storage_key_value(&self) -> Vec<(Vec<u8>, Vec<u8>)> {
92 let mut aura_authority_id = [0xEE; 32];
93 aura_authority_id[..20].copy_from_slice(
94 self.coinbase.as_ref().map(|addr| addr.0.as_slice()).unwrap_or(&[0; 20]),
95 );
96 let storage = vec![
97 (well_known_keys::CHAIN_ID.to_vec(), self.chain_id.encode()),
98 (well_known_keys::TIMESTAMP.to_vec(), self.timestamp.encode()),
99 (well_known_keys::BLOCK_NUMBER_KEY.to_vec(), self.number.encode()),
100 (well_known_keys::AURA_AUTHORITIES.to_vec(), vec![aura_authority_id].encode()),
101 (sp_core::storage::well_known_keys::CODE.to_vec(), self.code.clone()),
102 ];
103 storage
104 }
105
106 pub fn runtime_genesis_config_patch(&self) -> Value {
107 let mut revive_genesis_accounts: Vec<ReviveGenesisAccount> = self
109 .alloc
110 .clone()
111 .unwrap_or_default()
112 .iter()
113 .map(|(address, account)| {
114 let genesis_address: H160 = ReviveAddress::from(*address).inner();
115 let genesis_balance: U256 = account.balance;
116 let genesis_nonce: u64 = account.nonce.unwrap_or_default();
117 let contract_data: Option<ContractData> = if account.code.is_some() {
118 Some(ContractData {
119 code: account.code.clone().map(|code| code.to_vec()).unwrap_or_default(),
120 storage: account
121 .storage
122 .clone()
123 .map(|storage| {
124 storage
125 .into_iter()
126 .map(|(k, v)| (k.0.into(), v.0.into()))
127 .collect::<BTreeMap<_, _>>()
128 })
129 .unwrap_or_default(),
130 })
131 } else {
132 None
133 };
134
135 ReviveGenesisAccount {
136 address: genesis_address,
137 balance: genesis_balance,
138 nonce: genesis_nonce,
139 contract_data,
140 }
141 })
142 .collect();
143 revive_genesis_accounts.extend(
144 self.genesis_accounts
145 .iter()
146 .map(|key| ReviveGenesisAccount {
147 address: Account::from(key.clone()).address(),
148 balance: self.genesis_balance,
149 nonce: 0,
150 contract_data: None,
151 })
152 .collect::<Vec<_>>(),
153 );
154 json!({
155 "revive": {
156 "accounts": revive_genesis_accounts,
157 "debugSettings": {
158 "allow_unlimited_contract_size": true,
159 "bypass_eip_3607": true,
160 "pvm_logs": true
161 }
162 },
163 "transactionPayment": {
164 "multiplier": self.base_fee_per_gas.into_inner().to_string(),
165 }
166 })
167 }
168}
169
170pub struct DevelopmentGenesisBlockBuilder<Block: BlockT, B, E> {
171 genesis_number: u32,
172 genesis_storage: Storage,
173 commit_genesis_state: bool,
174 backend: Arc<B>,
175 executor: E,
176 _phantom: PhantomData<Block>,
177}
178
179impl<Block: BlockT, B: Backend<Block>, E: RuntimeVersionOf>
180 DevelopmentGenesisBlockBuilder<Block, B, E>
181{
182 pub fn new(
183 genesis_number: u64,
184 build_genesis_storage: &dyn BuildStorage,
185 commit_genesis_state: bool,
186 backend: Arc<B>,
187 executor: E,
188 ) -> sp_blockchain::Result<Self> {
189 let genesis_storage =
190 build_genesis_storage.build_storage().map_err(sp_blockchain::Error::Storage)?;
191 Self::new_with_storage(
192 genesis_number,
193 genesis_storage,
194 commit_genesis_state,
195 backend,
196 executor,
197 )
198 }
199
200 fn new_with_storage(
201 genesis_number: u64,
202 genesis_storage: Storage,
203 commit_genesis_state: bool,
204 backend: Arc<B>,
205 executor: E,
206 ) -> sp_blockchain::Result<Self> {
207 Ok(Self {
208 genesis_number: genesis_number.try_into().map_err(|_| {
209 sp_blockchain::Error::Application(
210 format!(
211 "Genesis number {} is too large for u32 (max: {})",
212 genesis_number,
213 u32::MAX
214 )
215 .into(),
216 )
217 })?,
218 genesis_storage,
219 commit_genesis_state,
220 backend,
221 executor,
222 _phantom: PhantomData::<Block>,
223 })
224 }
225}
226
227impl<Block: BlockT, B: Backend<Block>, E: RuntimeVersionOf> BuildGenesisBlock<Block>
228 for DevelopmentGenesisBlockBuilder<Block, B, E>
229{
230 type BlockImportOperation = <B as Backend<Block>>::BlockImportOperation;
231
232 fn build_genesis_block(self) -> sp_blockchain::Result<(Block, Self::BlockImportOperation)> {
233 let Self {
234 genesis_number,
235 genesis_storage,
236 commit_genesis_state,
237 backend,
238 executor,
239 _phantom,
240 } = self;
241
242 let genesis_state_version =
243 resolve_state_version_from_wasm::<_, HashingFor<Block>>(&genesis_storage, &executor)?;
244 let mut op = backend.begin_operation()?;
245 let state_root =
246 op.set_genesis_state(genesis_storage, commit_genesis_state, genesis_state_version)?;
247 let extrinsics_root = <<<Block as BlockT>::Header as HeaderT>::Hashing as HashT>::trie_root(
248 Vec::new(),
249 genesis_state_version,
250 );
251 let genesis_block = Block::new(
252 <<Block as BlockT>::Header as HeaderT>::new(
253 genesis_number.into(),
254 extrinsics_root,
255 state_root,
256 Default::default(),
257 Default::default(),
258 ),
259 Default::default(),
260 );
261
262 Ok((genesis_block, op))
263 }
264}
265
266#[cfg(test)]
267mod tests {
268 use super::*;
269
270 #[test]
271 fn test_storage_encoding() {
272 let block_number: u32 = 5;
273 let timestamp: u64 = 10;
274 let chain_id: u64 = 42;
275 let authority_id: [u8; 32] = [0xEE; 32];
276 let base_fee_per_gas = FixedU128::from_rational(6_000_000, NATIVE_TO_ETH_RATIO.into());
277 let genesis_config = GenesisConfig {
278 number: block_number,
279 timestamp,
280 chain_id,
281 coinbase: Some(Address::from([0xEE; 20])),
282 base_fee_per_gas,
283 ..Default::default()
284 };
285 let genesis_storage = genesis_config.as_storage_key_value();
286 assert!(
287 genesis_storage
288 .contains(&(well_known_keys::BLOCK_NUMBER_KEY.to_vec(), block_number.encode())),
289 "Block number not found in genesis key-value storage"
290 );
291 assert!(
292 genesis_storage.contains(&(well_known_keys::TIMESTAMP.to_vec(), timestamp.encode())),
293 "Timestamp not found in genesis key-value storage"
294 );
295 assert!(
296 genesis_storage.contains(&(well_known_keys::CHAIN_ID.to_vec(), chain_id.encode())),
297 "Chain id not found in genesis key-value storage"
298 );
299
300 assert!(
301 genesis_storage.contains(&(
302 well_known_keys::AURA_AUTHORITIES.to_vec(),
303 vec![authority_id].encode()
304 )),
305 "Authorities not found in genesis key-value storage"
306 );
307 }
308}