Skip to main content

anvil_polkadot/substrate_node/
chain_spec.rs

1use crate::substrate_node::genesis::GenesisConfig;
2use codec::{Decode, Encode};
3use polkadot_sdk::{
4    sc_chain_spec::{ChainSpec, GetExtension, json_patch},
5    sc_executor,
6    sc_executor::HostFunctions,
7    sc_network::config::MultiaddrWithPeerId,
8    sc_service::{ChainType, GenericChainSpec, Properties},
9    sc_telemetry::TelemetryEndpoints,
10    sp_core::{
11        storage::Storage,
12        traits::{CallContext, CodeExecutor, Externalities, FetchRuntimeCode, RuntimeCode},
13    },
14    sp_genesis_builder::Result as BuildResult,
15    sp_io::{self, hashing::blake2_256},
16    sp_runtime::BuildStorage,
17    sp_state_machine::BasicExternalities,
18};
19use serde_json::Value;
20use std::borrow::Cow;
21
22pub fn development_chain_spec(
23    genesis_config: GenesisConfig,
24) -> Result<DevelopmentChainSpec, String> {
25    let inner = GenericChainSpec::builder(&genesis_config.code, Default::default())
26        .with_name("Development")
27        .with_id("dev")
28        .with_chain_type(ChainType::Development)
29        .with_properties(props())
30        .build();
31    Ok(DevelopmentChainSpec { inner, genesis_config })
32}
33
34/// This is a wrapper around the general Substrate ChainSpec type that allows manual changes to the
35/// genesis block.
36#[derive(Clone)]
37pub struct DevelopmentChainSpec<E = Option<()>, EHF = ()> {
38    inner: GenericChainSpec<E, EHF>,
39    genesis_config: GenesisConfig,
40}
41
42impl<E, EHF> BuildStorage for DevelopmentChainSpec<E, EHF>
43where
44    EHF: HostFunctions,
45    GenericChainSpec<E, EHF>: BuildStorage,
46{
47    fn assimilate_storage(&self, storage: &mut Storage) -> Result<(), String> {
48        storage.top.extend(self.genesis_config.as_storage_key_value());
49
50        // We need to initialise the storage used when calling into the runtime for the genesis
51        // config, so that the customised items (like block number and timestamp) will be
52        // seen even in the code that processes the genesis config patch.
53        let temp_storage = storage.clone();
54
55        GenesisBuilderRuntimeCaller::<EHF>::new(&self.genesis_config.code[..])
56            .get_storage_for_patch(
57                self.genesis_config.runtime_genesis_config_patch(),
58                temp_storage,
59            )?
60            .assimilate_storage(storage)?;
61
62        Ok(())
63    }
64}
65
66impl<E, EHF> ChainSpec for DevelopmentChainSpec<E, EHF>
67where
68    E: GetExtension + serde::Serialize + Clone + Send + Sync + 'static,
69    EHF: HostFunctions,
70{
71    fn boot_nodes(&self) -> &[MultiaddrWithPeerId] {
72        self.inner.boot_nodes()
73    }
74
75    fn name(&self) -> &str {
76        self.inner.name()
77    }
78
79    fn id(&self) -> &str {
80        self.inner.id()
81    }
82
83    fn chain_type(&self) -> ChainType {
84        self.inner.chain_type()
85    }
86
87    fn telemetry_endpoints(&self) -> &Option<TelemetryEndpoints> {
88        self.inner.telemetry_endpoints()
89    }
90
91    fn protocol_id(&self) -> Option<&str> {
92        self.inner.protocol_id()
93    }
94
95    fn fork_id(&self) -> Option<&str> {
96        self.inner.fork_id()
97    }
98
99    fn properties(&self) -> Properties {
100        self.inner.properties()
101    }
102
103    fn add_boot_node(&mut self, addr: MultiaddrWithPeerId) {
104        self.inner.add_boot_node(addr)
105    }
106
107    fn extensions(&self) -> &dyn GetExtension {
108        self.inner.extensions() as &dyn GetExtension
109    }
110
111    fn extensions_mut(&mut self) -> &mut dyn GetExtension {
112        self.inner.extensions_mut() as &mut dyn GetExtension
113    }
114
115    fn as_json(&self, raw: bool) -> Result<String, String> {
116        self.inner.as_json(raw)
117    }
118
119    fn as_storage_builder(&self) -> &dyn BuildStorage {
120        self
121    }
122
123    fn cloned_box(&self) -> Box<dyn ChainSpec> {
124        Box::new(Self { inner: self.inner.clone(), genesis_config: self.genesis_config.clone() })
125    }
126
127    fn set_storage(&mut self, storage: Storage) {
128        self.inner.set_storage(storage);
129    }
130
131    fn code_substitutes(&self) -> std::collections::BTreeMap<String, Vec<u8>> {
132        self.inner.code_substitutes()
133    }
134}
135
136fn props() -> Properties {
137    let mut properties = Properties::new();
138    properties.insert("tokenDecimals".to_string(), 12.into());
139    properties.insert("tokenSymbol".to_string(), "DOT".into());
140    properties
141}
142
143// This mostly copies the upstream `GenesisConfigBuilderRuntimeCaller`, but with the ability of
144// injecting genesis state even before the genesis config builders in the runtime are run via
145// `GenesisBuilder_build_state`
146struct GenesisBuilderRuntimeCaller<'a, EHF = ()>
147where
148    EHF: HostFunctions,
149{
150    code: Cow<'a, [u8]>,
151    code_hash: Vec<u8>,
152    executor: sc_executor::WasmExecutor<(sp_io::SubstrateHostFunctions, EHF)>,
153}
154
155impl<'a, EHF> FetchRuntimeCode for GenesisBuilderRuntimeCaller<'a, EHF>
156where
157    EHF: HostFunctions,
158{
159    fn fetch_runtime_code(&self) -> Option<Cow<'_, [u8]>> {
160        Some(self.code.as_ref().into())
161    }
162}
163
164impl<'a, EHF> GenesisBuilderRuntimeCaller<'a, EHF>
165where
166    EHF: HostFunctions,
167{
168    fn new(code: &'a [u8]) -> Self {
169        GenesisBuilderRuntimeCaller {
170            code: code.into(),
171            code_hash: blake2_256(code).to_vec(),
172            executor: sc_executor::WasmExecutor::<(sp_io::SubstrateHostFunctions, EHF)>::builder()
173                .with_allow_missing_host_functions(true)
174                .build(),
175        }
176    }
177
178    fn get_storage_for_patch(
179        &self,
180        patch: Value,
181        genesis_storage: Storage,
182    ) -> core::result::Result<Storage, String> {
183        let mut config = self.get_named_preset(None)?;
184        json_patch::merge(&mut config, patch);
185        self.get_storage_for_config(config, genesis_storage)
186    }
187
188    fn call(
189        &self,
190        ext: &mut dyn Externalities,
191        method: &str,
192        data: &[u8],
193    ) -> sc_executor::error::Result<Vec<u8>> {
194        self.executor
195            .call(
196                ext,
197                &RuntimeCode { heap_pages: None, code_fetcher: self, hash: self.code_hash.clone() },
198                method,
199                data,
200                CallContext::Offchain,
201            )
202            .0
203    }
204
205    fn get_named_preset(&self, id: Option<&String>) -> core::result::Result<Value, String> {
206        let mut t = BasicExternalities::new_empty();
207        let call_result = self
208            .call(&mut t, "GenesisBuilder_get_preset", &id.encode())
209            .map_err(|e| format!("wasm call error {e}"))?;
210
211        let named_preset = Option::<Vec<u8>>::decode(&mut &call_result[..])
212            .map_err(|e| format!("scale codec error: {e}"))?;
213
214        if let Some(named_preset) = named_preset {
215            Ok(serde_json::from_slice(&named_preset[..]).expect("returned value is json. qed."))
216        } else {
217            Err(format!("The preset with name {id:?} is not available."))
218        }
219    }
220
221    fn get_storage_for_config(
222        &self,
223        config: Value,
224        genesis_storage: Storage,
225    ) -> core::result::Result<Storage, String> {
226        // This is the key difference compared to the upstream variant, we don't initialise the
227        // storage as empty.
228        let mut ext = BasicExternalities::new(genesis_storage);
229
230        let json_pretty_str = serde_json::to_string_pretty(&config)
231            .map_err(|e| format!("json to string failed: {e}"))?;
232
233        let call_result = self
234            .call(&mut ext, "GenesisBuilder_build_state", &json_pretty_str.encode())
235            .map_err(|e| format!("wasm call error {e}"))?;
236
237        BuildResult::decode(&mut &call_result[..])
238            .map_err(|e| format!("scale codec error: {e}"))?
239            .map_err(|e| format!("{e} for blob:\n{json_pretty_str}"))?;
240
241        Ok(ext.into_storages())
242    }
243}