use crate::overhead::command::ParachainExtension;
use sc_chain_spec::{ChainSpec, GenericChainSpec, GenesisConfigBuilderRuntimeCaller};
use sc_cli::Result;
use serde_json::Value;
use sp_storage::{well_known_keys::CODE, Storage};
use sp_wasm_interface::HostFunctions;
use std::{borrow::Cow, path::PathBuf};
const ERROR_CANNOT_BUILD_GENESIS: &str = "The runtime returned \
an error when trying to build the genesis storage. Please ensure that all pallets \
define a genesis config that can be built. This can be tested with: \
https://github.com/paritytech/polkadot-sdk/pull/3412";
pub const WARN_SPEC_GENESIS_CTOR: &'static str = "Using the chain spec instead of the runtime to \
generate the genesis state is deprecated. Please remove the `--chain`/`--dev`/`--local` argument, \
point `--runtime` to your runtime blob and set `--genesis-builder=runtime`. This warning may \
become a hard error any time after December 2024.";
pub enum SpecGenesisSource {
Runtime(String),
SpecJson,
None,
}
pub enum GenesisStateHandler {
ChainSpec(Box<dyn ChainSpec>, SpecGenesisSource),
Runtime(Vec<u8>, Option<String>),
}
impl GenesisStateHandler {
pub fn build_storage<HF: HostFunctions>(
&self,
json_patcher: Option<Box<dyn FnOnce(Value) -> Value + 'static>>,
) -> Result<Storage> {
match self {
GenesisStateHandler::ChainSpec(chain_spec, source) => match source {
SpecGenesisSource::Runtime(preset) => {
let mut storage = chain_spec.build_storage()?;
let code_bytes = storage
.top
.remove(CODE)
.ok_or("chain spec genesis does not contain code")?;
genesis_from_code::<HF>(code_bytes.as_slice(), preset, json_patcher)
},
SpecGenesisSource::SpecJson => chain_spec
.build_storage()
.map_err(|e| format!("{ERROR_CANNOT_BUILD_GENESIS}\nError: {e}").into()),
SpecGenesisSource::None => Ok(Storage::default()),
},
GenesisStateHandler::Runtime(code_bytes, Some(preset)) =>
genesis_from_code::<HF>(code_bytes.as_slice(), preset, json_patcher),
GenesisStateHandler::Runtime(_, None) => Ok(Storage::default()),
}
}
pub fn get_code_bytes(&self) -> Result<Cow<[u8]>> {
match self {
GenesisStateHandler::ChainSpec(chain_spec, _) => {
let mut storage = chain_spec.build_storage()?;
storage
.top
.remove(CODE)
.map(|code| Cow::from(code))
.ok_or("chain spec genesis does not contain code".into())
},
GenesisStateHandler::Runtime(code_bytes, _) => Ok(code_bytes.into()),
}
}
}
pub fn chain_spec_from_path<HF: HostFunctions>(
chain: PathBuf,
) -> Result<(Box<dyn ChainSpec>, Option<u32>)> {
let spec = GenericChainSpec::<ParachainExtension, HF>::from_json_file(chain)
.map_err(|e| format!("Unable to load chain spec: {:?}", e))?;
let para_id_from_chain_spec = spec.extensions().para_id;
Ok((Box::new(spec), para_id_from_chain_spec))
}
fn genesis_from_code<EHF: HostFunctions>(
code: &[u8],
genesis_builder_preset: &String,
storage_patcher: Option<Box<dyn FnOnce(Value) -> Value>>,
) -> Result<Storage> {
let genesis_config_caller = GenesisConfigBuilderRuntimeCaller::<(
sp_io::SubstrateHostFunctions,
frame_benchmarking::benchmarking::HostFunctions,
EHF,
)>::new(code);
let mut preset_json = genesis_config_caller.get_named_preset(Some(genesis_builder_preset))?;
if let Some(patcher) = storage_patcher {
preset_json = patcher(preset_json);
}
let mut storage =
genesis_config_caller.get_storage_for_patch(preset_json).inspect_err(|e| {
let presets = genesis_config_caller.preset_names().unwrap_or_default();
log::error!(
"Please pick one of the available presets with \
`--genesis-builder-preset=<PRESET>`. Available presets ({}): {:?}. Error: {:?}",
presets.len(),
presets,
e
);
})?;
storage.top.insert(CODE.into(), code.into());
Ok(storage)
}