mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-11 09:31:12 +00:00
sc-chain-spec: add support for custom host functions (#2190)
Genesis building in runtime may involve calling some custom host functions. This PR allows to pass `HostFunctions` into the `ChainSpec` struct, which in turn are passed to `WasmExecutor`. The `ChainSpec` now has extended host functions type parameter: ``` pub struct ChainSpec<G, E = NoExtension, EHF = ()> ``` which will be combined with the default set (`sp_io::SubstrateHostFunctions`) in an instance of `WasmExecutor` used to build the genesis config. Fix for #2188 --------- Co-authored-by: Davide Galassi <davxy@datawok.net> Co-authored-by: Bastian Köcher <git@kchr.de>
This commit is contained in:
committed by
GitHub
parent
ffa0e30e58
commit
b8acc57c50
@@ -338,7 +338,8 @@ pub fn generate_chain_spec_for_runtime(cmd: &RuntimeCmd) -> Result<String, Strin
|
||||
)?)
|
||||
},
|
||||
GenesisBuildAction::Default(DefaultCmd { ref default_config_path }) => {
|
||||
let caller = GenesisConfigBuilderRuntimeCaller::new(&code[..]);
|
||||
let caller: GenesisConfigBuilderRuntimeCaller =
|
||||
GenesisConfigBuilderRuntimeCaller::new(&code[..]);
|
||||
let default_config = caller
|
||||
.get_default_config()
|
||||
.map_err(|e| format!("getting default config from runtime should work: {e}"))?;
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
//! Substrate chain configurations.
|
||||
#![warn(missing_docs)]
|
||||
use crate::{
|
||||
extension::GetExtension, ChainType, GenesisConfigBuilderRuntimeCaller as RuntimeCaller,
|
||||
Properties, RuntimeGenesis,
|
||||
extension::GetExtension, genesis_config_builder::HostFunctions, ChainType,
|
||||
GenesisConfigBuilderRuntimeCaller as RuntimeCaller, Properties, RuntimeGenesis,
|
||||
};
|
||||
use sc_network::config::MultiaddrWithPeerId;
|
||||
use sc_telemetry::TelemetryEndpoints;
|
||||
@@ -122,7 +122,10 @@ impl<G: RuntimeGenesis> GenesisSource<G> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<G: RuntimeGenesis, E> BuildStorage for ChainSpec<G, E> {
|
||||
impl<G: RuntimeGenesis, E, EHF> BuildStorage for ChainSpec<G, E, EHF>
|
||||
where
|
||||
EHF: HostFunctions,
|
||||
{
|
||||
fn assimilate_storage(&self, storage: &mut Storage) -> Result<(), String> {
|
||||
match self.genesis.resolve()? {
|
||||
#[allow(deprecated)]
|
||||
@@ -158,7 +161,7 @@ impl<G: RuntimeGenesis, E> BuildStorage for ChainSpec<G, E> {
|
||||
json_blob: RuntimeGenesisConfigJson::Config(config),
|
||||
code,
|
||||
}) => {
|
||||
RuntimeCaller::new(&code[..])
|
||||
RuntimeCaller::<EHF>::new(&code[..])
|
||||
.get_storage_for_config(config)?
|
||||
.assimilate_storage(storage)?;
|
||||
storage
|
||||
@@ -169,7 +172,7 @@ impl<G: RuntimeGenesis, E> BuildStorage for ChainSpec<G, E> {
|
||||
json_blob: RuntimeGenesisConfigJson::Patch(patch),
|
||||
code,
|
||||
}) => {
|
||||
RuntimeCaller::new(&code[..])
|
||||
RuntimeCaller::<EHF>::new(&code[..])
|
||||
.get_storage_for_patch(patch)?
|
||||
.assimilate_storage(storage)?;
|
||||
storage
|
||||
@@ -322,7 +325,7 @@ struct ClientSpec<E> {
|
||||
pub type NoExtension = Option<()>;
|
||||
|
||||
/// Builder for creating [`ChainSpec`] instances.
|
||||
pub struct ChainSpecBuilder<G, E = NoExtension> {
|
||||
pub struct ChainSpecBuilder<G, E = NoExtension, EHF = ()> {
|
||||
code: Vec<u8>,
|
||||
extensions: E,
|
||||
name: String,
|
||||
@@ -334,10 +337,10 @@ pub struct ChainSpecBuilder<G, E = NoExtension> {
|
||||
protocol_id: Option<String>,
|
||||
fork_id: Option<String>,
|
||||
properties: Option<Properties>,
|
||||
_genesis: PhantomData<G>,
|
||||
_genesis: PhantomData<(G, EHF)>,
|
||||
}
|
||||
|
||||
impl<G, E> ChainSpecBuilder<G, E> {
|
||||
impl<G, E, EHF> ChainSpecBuilder<G, E, EHF> {
|
||||
/// Creates a new builder instance with no defaults.
|
||||
pub fn new(code: &[u8], extensions: E) -> Self {
|
||||
Self {
|
||||
@@ -429,7 +432,7 @@ impl<G, E> ChainSpecBuilder<G, E> {
|
||||
}
|
||||
|
||||
/// Builds a [`ChainSpec`] instance using the provided settings.
|
||||
pub fn build(self) -> ChainSpec<G, E> {
|
||||
pub fn build(self) -> ChainSpec<G, E, EHF> {
|
||||
let client_spec = ClientSpec {
|
||||
name: self.name,
|
||||
id: self.id,
|
||||
@@ -448,23 +451,33 @@ impl<G, E> ChainSpecBuilder<G, E> {
|
||||
ChainSpec {
|
||||
client_spec,
|
||||
genesis: GenesisSource::GenesisBuilderApi(self.genesis_build_action, self.code.into()),
|
||||
_host_functions: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A configuration of a chain. Can be used to build a genesis block.
|
||||
pub struct ChainSpec<G, E = NoExtension> {
|
||||
///
|
||||
/// The chain spec is generic over the native `RuntimeGenesisConfig` struct (`G`). It is also
|
||||
/// possible to parametrize chain spec over the extended host functions (EHF). It should be use if
|
||||
/// runtime is using the non-standard host function during genesis state creation.
|
||||
pub struct ChainSpec<G, E = NoExtension, EHF = ()> {
|
||||
client_spec: ClientSpec<E>,
|
||||
genesis: GenesisSource<G>,
|
||||
_host_functions: PhantomData<EHF>,
|
||||
}
|
||||
|
||||
impl<G, E: Clone> Clone for ChainSpec<G, E> {
|
||||
impl<G, E: Clone, EHF> Clone for ChainSpec<G, E, EHF> {
|
||||
fn clone(&self) -> Self {
|
||||
ChainSpec { client_spec: self.client_spec.clone(), genesis: self.genesis.clone() }
|
||||
ChainSpec {
|
||||
client_spec: self.client_spec.clone(),
|
||||
genesis: self.genesis.clone(),
|
||||
_host_functions: self._host_functions,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<G, E> ChainSpec<G, E> {
|
||||
impl<G, E, EHF> ChainSpec<G, E, EHF> {
|
||||
/// A list of bootnode addresses.
|
||||
pub fn boot_nodes(&self) -> &[MultiaddrWithPeerId] {
|
||||
&self.client_spec.boot_nodes
|
||||
@@ -553,6 +566,7 @@ impl<G, E> ChainSpec<G, E> {
|
||||
ChainSpec {
|
||||
client_spec,
|
||||
genesis: GenesisSource::Factory(Arc::new(constructor), code.into()),
|
||||
_host_functions: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -562,19 +576,23 @@ impl<G, E> ChainSpec<G, E> {
|
||||
}
|
||||
|
||||
/// Provides a `ChainSpec` builder.
|
||||
pub fn builder(code: &[u8], extensions: E) -> ChainSpecBuilder<G, E> {
|
||||
pub fn builder(code: &[u8], extensions: E) -> ChainSpecBuilder<G, E, EHF> {
|
||||
ChainSpecBuilder::new(code, extensions)
|
||||
}
|
||||
}
|
||||
|
||||
impl<G: serde::de::DeserializeOwned, E: serde::de::DeserializeOwned> ChainSpec<G, E> {
|
||||
impl<G: serde::de::DeserializeOwned, E: serde::de::DeserializeOwned, EHF> ChainSpec<G, E, EHF> {
|
||||
/// Parse json content into a `ChainSpec`
|
||||
pub fn from_json_bytes(json: impl Into<Cow<'static, [u8]>>) -> Result<Self, String> {
|
||||
let json = json.into();
|
||||
let client_spec = json::from_slice(json.as_ref())
|
||||
.map_err(|e| format!("Error parsing spec file: {}", e))?;
|
||||
|
||||
Ok(ChainSpec { client_spec, genesis: GenesisSource::Binary(json) })
|
||||
Ok(ChainSpec {
|
||||
client_spec,
|
||||
genesis: GenesisSource::Binary(json),
|
||||
_host_functions: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Parse json file into a `ChainSpec`
|
||||
@@ -593,7 +611,11 @@ impl<G: serde::de::DeserializeOwned, E: serde::de::DeserializeOwned> ChainSpec<G
|
||||
let client_spec =
|
||||
json::from_slice(&bytes).map_err(|e| format!("Error parsing spec file: {}", e))?;
|
||||
|
||||
Ok(ChainSpec { client_spec, genesis: GenesisSource::File(path) })
|
||||
Ok(ChainSpec {
|
||||
client_spec,
|
||||
genesis: GenesisSource::File(path),
|
||||
_host_functions: Default::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -608,7 +630,10 @@ struct ChainSpecJsonContainer<G, E> {
|
||||
genesis: Genesis<G>,
|
||||
}
|
||||
|
||||
impl<G: RuntimeGenesis, E: serde::Serialize + Clone + 'static> ChainSpec<G, E> {
|
||||
impl<G: RuntimeGenesis, E: serde::Serialize + Clone + 'static, EHF> ChainSpec<G, E, EHF>
|
||||
where
|
||||
EHF: HostFunctions,
|
||||
{
|
||||
fn json_container(&self, raw: bool) -> Result<ChainSpecJsonContainer<G, E>, String> {
|
||||
let raw_genesis = match (raw, self.genesis.resolve()?) {
|
||||
(
|
||||
@@ -618,7 +643,8 @@ impl<G: RuntimeGenesis, E: serde::Serialize + Clone + 'static> ChainSpec<G, E> {
|
||||
code,
|
||||
}),
|
||||
) => {
|
||||
let mut storage = RuntimeCaller::new(&code[..]).get_storage_for_config(config)?;
|
||||
let mut storage =
|
||||
RuntimeCaller::<EHF>::new(&code[..]).get_storage_for_config(config)?;
|
||||
storage.top.insert(sp_core::storage::well_known_keys::CODE.to_vec(), code);
|
||||
RawGenesis::from(storage)
|
||||
},
|
||||
@@ -629,7 +655,8 @@ impl<G: RuntimeGenesis, E: serde::Serialize + Clone + 'static> ChainSpec<G, E> {
|
||||
code,
|
||||
}),
|
||||
) => {
|
||||
let mut storage = RuntimeCaller::new(&code[..]).get_storage_for_patch(patch)?;
|
||||
let mut storage =
|
||||
RuntimeCaller::<EHF>::new(&code[..]).get_storage_for_patch(patch)?;
|
||||
storage.top.insert(sp_core::storage::well_known_keys::CODE.to_vec(), code);
|
||||
RawGenesis::from(storage)
|
||||
},
|
||||
@@ -664,10 +691,11 @@ impl<G: RuntimeGenesis, E: serde::Serialize + Clone + 'static> ChainSpec<G, E> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<G, E> crate::ChainSpec for ChainSpec<G, E>
|
||||
impl<G, E, EHF> crate::ChainSpec for ChainSpec<G, E, EHF>
|
||||
where
|
||||
G: RuntimeGenesis + 'static,
|
||||
E: GetExtension + serde::Serialize + Clone + Send + Sync + 'static,
|
||||
EHF: HostFunctions,
|
||||
{
|
||||
fn boot_nodes(&self) -> &[MultiaddrWithPeerId] {
|
||||
ChainSpec::boot_nodes(self)
|
||||
@@ -953,7 +981,7 @@ mod tests {
|
||||
#[docify::export]
|
||||
#[test]
|
||||
fn build_chain_spec_with_patch_works() {
|
||||
let output: ChainSpec<()> = ChainSpec::builder(
|
||||
let output = ChainSpec::<()>::builder(
|
||||
substrate_test_runtime::wasm_binary_unwrap().into(),
|
||||
Default::default(),
|
||||
)
|
||||
@@ -986,7 +1014,7 @@ mod tests {
|
||||
#[docify::export]
|
||||
#[test]
|
||||
fn generate_chain_spec_with_patch_works() {
|
||||
let output: ChainSpec<()> = ChainSpec::builder(
|
||||
let output = ChainSpec::<()>::builder(
|
||||
substrate_test_runtime::wasm_binary_unwrap().into(),
|
||||
Default::default(),
|
||||
)
|
||||
@@ -1033,7 +1061,7 @@ mod tests {
|
||||
#[test]
|
||||
fn generate_chain_spec_with_full_config_works() {
|
||||
let j = include_str!("../../../test-utils/runtime/res/default_genesis_config.json");
|
||||
let output: ChainSpec<()> = ChainSpec::builder(
|
||||
let output = ChainSpec::<()>::builder(
|
||||
substrate_test_runtime::wasm_binary_unwrap().into(),
|
||||
Default::default(),
|
||||
)
|
||||
@@ -1065,7 +1093,7 @@ mod tests {
|
||||
fn chain_spec_as_json_fails_with_invalid_config() {
|
||||
let j =
|
||||
include_str!("../../../test-utils/runtime/res/default_genesis_config_invalid_2.json");
|
||||
let output: ChainSpec<()> = ChainSpec::builder(
|
||||
let output = ChainSpec::<()>::builder(
|
||||
substrate_test_runtime::wasm_binary_unwrap().into(),
|
||||
Default::default(),
|
||||
)
|
||||
@@ -1083,7 +1111,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn chain_spec_as_json_fails_with_invalid_patch() {
|
||||
let output: ChainSpec<()> = ChainSpec::builder(
|
||||
let output = ChainSpec::<()>::builder(
|
||||
substrate_test_runtime::wasm_binary_unwrap().into(),
|
||||
Default::default(),
|
||||
)
|
||||
@@ -1139,7 +1167,7 @@ mod tests {
|
||||
#[test]
|
||||
fn update_code_works_with_runtime_genesis_config() {
|
||||
let j = include_str!("../../../test-utils/runtime/res/default_genesis_config.json");
|
||||
let chain_spec: ChainSpec<()> = ChainSpec::builder(
|
||||
let chain_spec = ChainSpec::<()>::builder(
|
||||
substrate_test_runtime::wasm_binary_unwrap().into(),
|
||||
Default::default(),
|
||||
)
|
||||
@@ -1162,7 +1190,7 @@ mod tests {
|
||||
#[test]
|
||||
fn update_code_works_for_raw() {
|
||||
let j = include_str!("../../../test-utils/runtime/res/default_genesis_config.json");
|
||||
let chain_spec: ChainSpec<()> = ChainSpec::builder(
|
||||
let chain_spec = ChainSpec::<()>::builder(
|
||||
substrate_test_runtime::wasm_binary_unwrap().into(),
|
||||
Default::default(),
|
||||
)
|
||||
@@ -1184,7 +1212,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn update_code_works_with_runtime_genesis_patch() {
|
||||
let chain_spec: ChainSpec<()> = ChainSpec::builder(
|
||||
let chain_spec = ChainSpec::<()>::builder(
|
||||
substrate_test_runtime::wasm_binary_unwrap().into(),
|
||||
Default::default(),
|
||||
)
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
//! A helper module for calling the GenesisBuilder API from arbitrary runtime wasm blobs.
|
||||
|
||||
use codec::{Decode, Encode};
|
||||
pub use sc_executor::sp_wasm_interface::HostFunctions;
|
||||
use sc_executor::{error::Result, WasmExecutor};
|
||||
use serde_json::{from_slice, Value};
|
||||
use sp_core::{
|
||||
@@ -30,19 +31,31 @@ use sp_state_machine::BasicExternalities;
|
||||
use std::borrow::Cow;
|
||||
|
||||
/// A utility that facilitates calling the GenesisBuilder API from the runtime wasm code blob.
|
||||
pub struct GenesisConfigBuilderRuntimeCaller<'a> {
|
||||
///
|
||||
/// `EHF` type allows to specify the extended host function required for building runtime's genesis
|
||||
/// config. The type will be compbined with default `sp_io::SubstrateHostFunctions`.
|
||||
pub struct GenesisConfigBuilderRuntimeCaller<'a, EHF = ()>
|
||||
where
|
||||
EHF: HostFunctions,
|
||||
{
|
||||
code: Cow<'a, [u8]>,
|
||||
code_hash: Vec<u8>,
|
||||
executor: WasmExecutor<sp_io::SubstrateHostFunctions>,
|
||||
executor: WasmExecutor<(sp_io::SubstrateHostFunctions, EHF)>,
|
||||
}
|
||||
|
||||
impl<'a> FetchRuntimeCode for GenesisConfigBuilderRuntimeCaller<'a> {
|
||||
impl<'a, EHF> FetchRuntimeCode for GenesisConfigBuilderRuntimeCaller<'a, EHF>
|
||||
where
|
||||
EHF: HostFunctions,
|
||||
{
|
||||
fn fetch_runtime_code(&self) -> Option<Cow<[u8]>> {
|
||||
Some(self.code.as_ref().into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> GenesisConfigBuilderRuntimeCaller<'a> {
|
||||
impl<'a, EHF> GenesisConfigBuilderRuntimeCaller<'a, EHF>
|
||||
where
|
||||
EHF: HostFunctions,
|
||||
{
|
||||
/// Creates new instance using the provided code blob.
|
||||
///
|
||||
/// This code is later referred to as `runtime`.
|
||||
@@ -50,7 +63,7 @@ impl<'a> GenesisConfigBuilderRuntimeCaller<'a> {
|
||||
GenesisConfigBuilderRuntimeCaller {
|
||||
code: code.into(),
|
||||
code_hash: sp_core::blake2_256(code).to_vec(),
|
||||
executor: WasmExecutor::<sp_io::SubstrateHostFunctions>::builder()
|
||||
executor: WasmExecutor::<(sp_io::SubstrateHostFunctions, EHF)>::builder()
|
||||
.with_allow_missing_host_functions(true)
|
||||
.build(),
|
||||
}
|
||||
@@ -134,7 +147,7 @@ mod tests {
|
||||
#[test]
|
||||
fn get_default_config_works() {
|
||||
let config =
|
||||
GenesisConfigBuilderRuntimeCaller::new(substrate_test_runtime::wasm_binary_unwrap())
|
||||
<GenesisConfigBuilderRuntimeCaller>::new(substrate_test_runtime::wasm_binary_unwrap())
|
||||
.get_default_config()
|
||||
.unwrap();
|
||||
let expected = r#"{"system":{},"babe":{"authorities":[],"epochConfig":null},"substrateTest":{"authorities":[]},"balances":{"balances":[]}}"#;
|
||||
@@ -156,7 +169,7 @@ mod tests {
|
||||
});
|
||||
|
||||
let storage =
|
||||
GenesisConfigBuilderRuntimeCaller::new(substrate_test_runtime::wasm_binary_unwrap())
|
||||
<GenesisConfigBuilderRuntimeCaller>::new(substrate_test_runtime::wasm_binary_unwrap())
|
||||
.get_storage_for_patch(patch)
|
||||
.unwrap();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user