1// This file is part of Substrate.
23// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
56// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
1011// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
1516// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
1819//! A helper module for calling the GenesisBuilder API from arbitrary runtime wasm blobs.
2021use 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, Resultas BuildResult};
30pub use sp_genesis_builder::{DEV_RUNTIME_PRESET, LOCAL_TESTNET_RUNTIME_PRESET};
31use sp_state_machine::BasicExternalities;
32use std::borrow::Cow;
3334/// A utility that facilitates calling the GenesisBuilder API from the runtime wasm code blob.
35///
36/// `EHF` type allows to specify the extended host function required for building runtime's genesis
37/// config. The type will be combined with default `sp_io::SubstrateHostFunctions`.
38pub struct GenesisConfigBuilderRuntimeCaller<'a, EHF = ()>
39where
40EHF: HostFunctions,
41{
42 code: Cow<'a, [u8]>,
43 code_hash: Vec<u8>,
44 executor: WasmExecutor<(sp_io::SubstrateHostFunctions, EHF)>,
45}
4647impl<'a, EHF> FetchRuntimeCode for GenesisConfigBuilderRuntimeCaller<'a, EHF>
48where
49EHF: HostFunctions,
50{
51fn fetch_runtime_code(&self) -> Option<Cow<[u8]>> {
52Some(self.code.as_ref().into())
53 }
54}
5556impl<'a, EHF> GenesisConfigBuilderRuntimeCaller<'a, EHF>
57where
58EHF: HostFunctions,
59{
60/// Creates new instance using the provided code blob.
61 ///
62 /// This code is later referred to as `runtime`.
63pub 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 }
7273fn call(&self, ext: &mut dyn Externalities, method: &str, data: &[u8]) -> Result<Vec<u8>> {
74self.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}
8485/// Returns a json representation of the default `RuntimeGenesisConfig` provided by the
86 /// `runtime`.
87 ///
88 /// Calls [`GenesisBuilder::get_preset`](sp_genesis_builder::GenesisBuilder::get_preset) in the
89 /// `runtime` with `None` argument.
90pub fn get_default_config(&self) -> core::result::Result<Value, String> {
91self.get_named_preset(None)
92 }
9394/// Returns a JSON blob representation of the builtin `GenesisConfig` identified by `id`.
95 ///
96 /// Calls [`GenesisBuilder::get_preset`](sp_genesis_builder::GenesisBuilder::get_preset)
97 /// provided by the `runtime`.
98pub fn get_named_preset(&self, id: Option<&String>) -> core::result::Result<Value, String> {
99let mut t = BasicExternalities::new_empty();
100let call_result = self
101.call(&mut t, "GenesisBuilder_get_preset", &id.encode())
102 .map_err(|e| format!("wasm call error {e}"))?;
103104let named_preset = Option::<Vec<u8>>::decode(&mut &call_result[..])
105 .map_err(|e| format!("scale codec error: {e}"))?;
106107if let Some(named_preset) = named_preset {
108Ok(from_slice(&named_preset[..]).expect("returned value is json. qed."))
109 } else {
110Err(format!("The preset with name {id:?} is not available."))
111 }
112 }
113114/// Calls [`sp_genesis_builder::GenesisBuilder::build_state`] provided by runtime.
115pub fn get_storage_for_config(&self, config: Value) -> core::result::Result<Storage, String> {
116let mut ext = BasicExternalities::new_empty();
117118let json_pretty_str = serde_json::to_string_pretty(&config)
119 .map_err(|e| format!("json to string failed: {e}"))?;
120121let call_result = self
122.call(&mut ext, "GenesisBuilder_build_state", &json_pretty_str.encode())
123 .map_err(|e| format!("wasm call error {e}"))?;
124125 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))?;
128129Ok(ext.into_storages())
130 }
131132/// Creates the genesis state by patching the default `RuntimeGenesisConfig`.
133 ///
134 /// This function generates the `RuntimeGenesisConfig` for the runtime by applying a provided
135 /// JSON patch. The patch modifies the default `RuntimeGenesisConfig` allowing customization of
136 /// the specific keys. The resulting `RuntimeGenesisConfig` is then deserialized from the
137 /// patched JSON representation and stored in the storage.
138 ///
139 /// If the provided JSON patch is incorrect or the deserialization fails the error will be
140 /// returned.
141 ///
142 /// The patching process modifies the default `RuntimeGenesisConfig` according to the following
143 /// rules:
144 /// 1. Existing keys in the default configuration will be overridden by the corresponding values
145 /// in the patch (also applies to `null` values).
146 /// 2. If a key exists in the patch but not in the default configuration, it will be added to
147 /// the resulting `RuntimeGenesisConfig`.
148 ///
149 /// Please note that the patch may contain full `RuntimeGenesisConfig`.
150pub fn get_storage_for_patch(&self, patch: Value) -> core::result::Result<Storage, String> {
151let mut config = self.get_default_config()?;
152crate::json_patch::merge(&mut config, patch);
153self.get_storage_for_config(config)
154 }
155156pub fn get_storage_for_named_preset(
157&self,
158 name: Option<&String>,
159 ) -> core::result::Result<Storage, String> {
160self.get_storage_for_patch(self.get_named_preset(name)?)
161 }
162163pub fn preset_names(&self) -> core::result::Result<Vec<PresetId>, String> {
164let mut t = BasicExternalities::new_empty();
165let call_result = self
166.call(&mut t, "GenesisBuilder_preset_names", &vec![])
167 .map_err(|e| format!("wasm call error {e}"))?;
168169let preset_names = Vec::<PresetId>::decode(&mut &call_result[..])
170 .map_err(|e| format!("scale codec error: {e}"))?;
171172Ok(preset_names)
173 }
174}
175176#[cfg(test)]
177mod tests {
178use super::*;
179use serde_json::{from_str, json};
180pub use sp_consensus_babe::{AllowedSlots, BabeEpochConfiguration};
181pub use sp_genesis_builder::PresetId;
182183#[test]
184fn list_presets_works() {
185 sp_tracing::try_init_simple();
186let presets =
187 <GenesisConfigBuilderRuntimeCaller>::new(substrate_test_runtime::wasm_binary_unwrap())
188 .preset_names()
189 .unwrap();
190assert_eq!(presets, vec![PresetId::from("foobar"), PresetId::from("staging"),]);
191 }
192193#[test]
194fn get_default_config_works() {
195let config =
196 <GenesisConfigBuilderRuntimeCaller>::new(substrate_test_runtime::wasm_binary_unwrap())
197 .get_default_config()
198 .unwrap();
199let expected = r#"{"babe": {"authorities": [], "epochConfig": {"allowed_slots": "PrimaryAndSecondaryVRFSlots", "c": [1, 4]}}, "balances": {"balances": [], "devAccounts": null}, "substrateTest": {"authorities": []}, "system": {}}"#;
200assert_eq!(from_str::<Value>(expected).unwrap(), config);
201 }
202203#[test]
204fn get_named_preset_works() {
205 sp_tracing::try_init_simple();
206let config =
207 <GenesisConfigBuilderRuntimeCaller>::new(substrate_test_runtime::wasm_binary_unwrap())
208 .get_named_preset(Some(&"foobar".to_string()))
209 .unwrap();
210let expected = r#"{"foo":"bar"}"#;
211assert_eq!(from_str::<Value>(expected).unwrap(), config);
212 }
213214#[test]
215fn get_storage_for_patch_works() {
216let patch = json!({
217"babe": {
218"epochConfig": {
219"c": [
22069,
221696
222],
223"allowed_slots": "PrimaryAndSecondaryPlainSlots"
224}
225 },
226 });
227228let storage =
229 <GenesisConfigBuilderRuntimeCaller>::new(substrate_test_runtime::wasm_binary_unwrap())
230 .get_storage_for_patch(patch)
231 .unwrap();
232233//Babe|Authorities
234let value: Vec<u8> = storage
235 .top
236 .get(
237&array_bytes::hex2bytes(
238"1cb6f36e027abb2091cfb5110ab5087fdc6b171b77304263c292cc3ea5ed31ef",
239 )
240 .unwrap(),
241 )
242 .unwrap()
243 .clone();
244245assert_eq!(
246 BabeEpochConfiguration::decode(&mut &value[..]).unwrap(),
247 BabeEpochConfiguration {
248 c: (69, 696),
249 allowed_slots: AllowedSlots::PrimaryAndSecondaryPlainSlots
250 }
251 );
252 }
253}