Skip to main content

anvil_polkadot/substrate_node/
genesis.rs

1//! Genesis settings
2
3use 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/// Genesis settings
29#[derive(Clone, Debug, Default)]
30pub struct GenesisConfig {
31    /// The chain id of the Substrate chain.
32    pub chain_id: u64,
33    /// The initial timestamp for the genesis block in milliseconds
34    pub timestamp: u64,
35    /// All accounts that should be initialised at genesis with their info.
36    /// Populated from user provided JSON.
37    pub alloc: Option<BTreeMap<Address, GenesisAccount>>,
38    /// The initial number for the genesis block
39    pub number: u32,
40    /// The genesis header base fee
41    pub base_fee_per_gas: FixedU128,
42    /// Signer accounts from account_generator
43    pub genesis_accounts: Vec<Keypair>,
44    /// Signers accounts balance
45    pub genesis_balance: U256,
46    /// Coinbase address
47    pub coinbase: Option<Address>,
48    /// Substrate runtime code
49    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            // Anvil genesis timestamp is in seconds, while Substrate timestamp is in milliseconds.
57            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/// Used to provide genesis accounts to pallet-revive
79#[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        // Relies on ReviveGenesisAccount type from pallet-revive
108        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}