frame_benchmarking_cli/shared/
genesis_state.rs1use crate::overhead::command::ParachainExtension;
19use sc_chain_spec::{ChainSpec, GenericChainSpec, GenesisConfigBuilderRuntimeCaller};
20use sc_cli::Result;
21use serde_json::Value;
22use sp_storage::{well_known_keys::CODE, Storage};
23use sp_wasm_interface::HostFunctions;
24use std::{borrow::Cow, path::PathBuf};
25
26const ERROR_CANNOT_BUILD_GENESIS: &str = "The runtime returned \
28an error when trying to build the genesis storage. Please ensure that all pallets \
29define a genesis config that can be built. This can be tested with: \
30https://github.com/paritytech/polkadot-sdk/pull/3412";
31
32pub const WARN_SPEC_GENESIS_CTOR: &'static str = "Using the chain spec instead of the runtime to \
34generate the genesis state is deprecated. Please remove the `--chain`/`--dev`/`--local` argument, \
35point `--runtime` to your runtime blob and set `--genesis-builder=runtime`. This warning may \
36become a hard error any time after December 2024.";
37
38pub enum SpecGenesisSource {
40 Runtime(String),
42 SpecJson,
44 None,
46}
47
48pub enum GenesisStateHandler {
50 ChainSpec(Box<dyn ChainSpec>, SpecGenesisSource),
51 Runtime(Vec<u8>, Option<String>),
52}
53
54impl GenesisStateHandler {
55 pub fn build_storage<HF: HostFunctions>(
60 &self,
61 json_patcher: Option<Box<dyn FnOnce(Value) -> Value + 'static>>,
62 ) -> Result<Storage> {
63 match self {
64 GenesisStateHandler::ChainSpec(chain_spec, source) => match source {
65 SpecGenesisSource::Runtime(preset) => {
66 let mut storage = chain_spec.build_storage()?;
67 let code_bytes = storage
68 .top
69 .remove(CODE)
70 .ok_or("chain spec genesis does not contain code")?;
71 genesis_from_code::<HF>(code_bytes.as_slice(), preset, json_patcher)
72 },
73 SpecGenesisSource::SpecJson => chain_spec
74 .build_storage()
75 .map_err(|e| format!("{ERROR_CANNOT_BUILD_GENESIS}\nError: {e}").into()),
76 SpecGenesisSource::None => Ok(Storage::default()),
77 },
78 GenesisStateHandler::Runtime(code_bytes, Some(preset)) =>
79 genesis_from_code::<HF>(code_bytes.as_slice(), preset, json_patcher),
80 GenesisStateHandler::Runtime(_, None) => Ok(Storage::default()),
81 }
82 }
83
84 pub fn get_code_bytes(&self) -> Result<Cow<[u8]>> {
86 match self {
87 GenesisStateHandler::ChainSpec(chain_spec, _) => {
88 let mut storage = chain_spec.build_storage()?;
89 storage
90 .top
91 .remove(CODE)
92 .map(|code| Cow::from(code))
93 .ok_or("chain spec genesis does not contain code".into())
94 },
95 GenesisStateHandler::Runtime(code_bytes, _) => Ok(code_bytes.into()),
96 }
97 }
98}
99
100pub fn chain_spec_from_path<HF: HostFunctions>(
101 chain: PathBuf,
102) -> Result<(Box<dyn ChainSpec>, Option<u32>)> {
103 let spec = GenericChainSpec::<ParachainExtension, HF>::from_json_file(chain)
104 .map_err(|e| format!("Unable to load chain spec: {:?}", e))?;
105
106 let para_id_from_chain_spec = spec.extensions().para_id;
107 Ok((Box::new(spec), para_id_from_chain_spec))
108}
109
110fn genesis_from_code<EHF: HostFunctions>(
111 code: &[u8],
112 genesis_builder_preset: &String,
113 storage_patcher: Option<Box<dyn FnOnce(Value) -> Value>>,
114) -> Result<Storage> {
115 let genesis_config_caller = GenesisConfigBuilderRuntimeCaller::<(
116 sp_io::SubstrateHostFunctions,
117 frame_benchmarking::benchmarking::HostFunctions,
118 EHF,
119 )>::new(code);
120
121 let mut preset_json = genesis_config_caller.get_named_preset(Some(genesis_builder_preset))?;
122 if let Some(patcher) = storage_patcher {
123 preset_json = patcher(preset_json);
124 }
125
126 let mut storage =
127 genesis_config_caller.get_storage_for_patch(preset_json).inspect_err(|e| {
128 let presets = genesis_config_caller.preset_names().unwrap_or_default();
129 log::error!(
130 "Please pick one of the available presets with \
131 `--genesis-builder-preset=<PRESET>`. Available presets ({}): {:?}. Error: {:?}",
132 presets.len(),
133 presets,
134 e
135 );
136 })?;
137
138 storage.top.insert(CODE.into(), code.into());
139
140 Ok(storage)
141}