sc_chain_spec/
genesis_config_builder.rs1use codec::{Decode, Encode};
22pub use sc_executor::sp_wasm_interface::HostFunctions;
23use sc_executor::{error::Result, WasmExecutor};
24use serde_json::{from_slice, Value};
25use sp_core::{
26 storage::Storage,
27 traits::{CallContext, CodeExecutor, Externalities, FetchRuntimeCode, RuntimeCode},
28};
29use sp_genesis_builder::{PresetId, Result as BuildResult};
30pub use sp_genesis_builder::{DEV_RUNTIME_PRESET, LOCAL_TESTNET_RUNTIME_PRESET};
31use sp_state_machine::BasicExternalities;
32use std::borrow::Cow;
33
34pub struct GenesisConfigBuilderRuntimeCaller<'a, EHF = ()>
39where
40 EHF: HostFunctions,
41{
42 code: Cow<'a, [u8]>,
43 code_hash: Vec<u8>,
44 executor: WasmExecutor<(sp_io::SubstrateHostFunctions, EHF)>,
45}
46
47impl<'a, EHF> FetchRuntimeCode for GenesisConfigBuilderRuntimeCaller<'a, EHF>
48where
49 EHF: HostFunctions,
50{
51 fn fetch_runtime_code(&self) -> Option<Cow<[u8]>> {
52 Some(self.code.as_ref().into())
53 }
54}
55
56impl<'a, EHF> GenesisConfigBuilderRuntimeCaller<'a, EHF>
57where
58 EHF: HostFunctions,
59{
60 pub fn new(code: &'a [u8]) -> Self {
64 GenesisConfigBuilderRuntimeCaller {
65 code: code.into(),
66 code_hash: sp_crypto_hashing::blake2_256(code).to_vec(),
67 executor: WasmExecutor::<(sp_io::SubstrateHostFunctions, EHF)>::builder()
68 .with_allow_missing_host_functions(true)
69 .build(),
70 }
71 }
72
73 fn call(&self, ext: &mut dyn Externalities, method: &str, data: &[u8]) -> Result<Vec<u8>> {
74 self.executor
75 .call(
76 ext,
77 &RuntimeCode { heap_pages: None, code_fetcher: self, hash: self.code_hash.clone() },
78 method,
79 data,
80 CallContext::Offchain,
81 )
82 .0
83 }
84
85 pub fn get_default_config(&self) -> core::result::Result<Value, String> {
91 self.get_named_preset(None)
92 }
93
94 pub fn get_named_preset(&self, id: Option<&String>) -> core::result::Result<Value, String> {
99 let mut t = BasicExternalities::new_empty();
100 let call_result = self
101 .call(&mut t, "GenesisBuilder_get_preset", &id.encode())
102 .map_err(|e| format!("wasm call error {e}"))?;
103
104 let named_preset = Option::<Vec<u8>>::decode(&mut &call_result[..])
105 .map_err(|e| format!("scale codec error: {e}"))?;
106
107 if let Some(named_preset) = named_preset {
108 Ok(from_slice(&named_preset[..]).expect("returned value is json. qed."))
109 } else {
110 Err(format!("The preset with name {id:?} is not available."))
111 }
112 }
113
114 pub fn get_storage_for_config(&self, config: Value) -> core::result::Result<Storage, String> {
116 let mut ext = BasicExternalities::new_empty();
117
118 let json_pretty_str = serde_json::to_string_pretty(&config)
119 .map_err(|e| format!("json to string failed: {e}"))?;
120
121 let call_result = self
122 .call(&mut ext, "GenesisBuilder_build_state", &json_pretty_str.encode())
123 .map_err(|e| format!("wasm call error {e}"))?;
124
125 BuildResult::decode(&mut &call_result[..])
126 .map_err(|e| format!("scale codec error: {e}"))?
127 .map_err(|e| format!("{e} for blob:\n{}", json_pretty_str))?;
128
129 Ok(ext.into_storages())
130 }
131
132 pub fn get_storage_for_patch(&self, patch: Value) -> core::result::Result<Storage, String> {
153 let mut config = self.get_default_config()?;
154 crate::json_patch::merge(&mut config, patch);
155 self.get_storage_for_config(config)
156 }
157
158 pub fn get_storage_for_named_preset(
159 &self,
160 name: Option<&String>,
161 ) -> core::result::Result<Storage, String> {
162 self.get_storage_for_patch(self.get_named_preset(name)?)
163 }
164
165 pub fn preset_names(&self) -> core::result::Result<Vec<PresetId>, String> {
166 let mut t = BasicExternalities::new_empty();
167 let call_result = self
168 .call(&mut t, "GenesisBuilder_preset_names", &vec![])
169 .map_err(|e| format!("wasm call error {e}"))?;
170
171 let preset_names = Vec::<PresetId>::decode(&mut &call_result[..])
172 .map_err(|e| format!("scale codec error: {e}"))?;
173
174 Ok(preset_names)
175 }
176}
177
178#[cfg(test)]
179mod tests {
180 use super::*;
181 use serde_json::{from_str, json};
182 pub use sp_consensus_babe::{AllowedSlots, BabeEpochConfiguration};
183 pub use sp_genesis_builder::PresetId;
184
185 #[test]
186 fn list_presets_works() {
187 sp_tracing::try_init_simple();
188 let presets =
189 <GenesisConfigBuilderRuntimeCaller>::new(substrate_test_runtime::wasm_binary_unwrap())
190 .preset_names()
191 .unwrap();
192 assert_eq!(presets, vec![PresetId::from("foobar"), PresetId::from("staging"),]);
193 }
194
195 #[test]
196 fn get_default_config_works() {
197 let config =
198 <GenesisConfigBuilderRuntimeCaller>::new(substrate_test_runtime::wasm_binary_unwrap())
199 .get_default_config()
200 .unwrap();
201 let expected = r#"{"babe": {"authorities": [], "epochConfig": {"allowed_slots": "PrimaryAndSecondaryVRFSlots", "c": [1, 4]}}, "balances": {"balances": []}, "substrateTest": {"authorities": []}, "system": {}}"#;
202 assert_eq!(from_str::<Value>(expected).unwrap(), config);
203 }
204
205 #[test]
206 fn get_named_preset_works() {
207 sp_tracing::try_init_simple();
208 let config =
209 <GenesisConfigBuilderRuntimeCaller>::new(substrate_test_runtime::wasm_binary_unwrap())
210 .get_named_preset(Some(&"foobar".to_string()))
211 .unwrap();
212 let expected = r#"{"foo":"bar"}"#;
213 assert_eq!(from_str::<Value>(expected).unwrap(), config);
214 }
215
216 #[test]
217 fn get_storage_for_patch_works() {
218 let patch = json!({
219 "babe": {
220 "epochConfig": {
221 "c": [
222 69,
223 696
224 ],
225 "allowed_slots": "PrimaryAndSecondaryPlainSlots"
226 }
227 },
228 });
229
230 let storage =
231 <GenesisConfigBuilderRuntimeCaller>::new(substrate_test_runtime::wasm_binary_unwrap())
232 .get_storage_for_patch(patch)
233 .unwrap();
234
235 let value: Vec<u8> = storage
237 .top
238 .get(
239 &array_bytes::hex2bytes(
240 "1cb6f36e027abb2091cfb5110ab5087fdc6b171b77304263c292cc3ea5ed31ef",
241 )
242 .unwrap(),
243 )
244 .unwrap()
245 .clone();
246
247 assert_eq!(
248 BabeEpochConfiguration::decode(&mut &value[..]).unwrap(),
249 BabeEpochConfiguration {
250 c: (69, 696),
251 allowed_slots: AllowedSlots::PrimaryAndSecondaryPlainSlots
252 }
253 );
254 }
255}