anvil_polkadot/substrate_node/
chain_spec.rs1use 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#[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 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
143struct 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 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}