Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sc-chain-spec: add support for custom host functions #2190

Merged
merged 14 commits into from
Nov 8, 2023
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ where

/// Helper function to build the genesis storage using given json patch and code
fn build_genesis_storage(patch: serde_json::Value, code: &[u8]) -> Storage {
let mut storage = GenesisConfigBuilderRuntimeCaller::new(code)
let mut storage = <GenesisConfigBuilderRuntimeCaller>::new(code)
michalkucharczyk marked this conversation as resolved.
Show resolved Hide resolved
.get_storage_for_patch(patch)
.unwrap();
storage
Expand Down
3 changes: 2 additions & 1 deletion substrate/bin/utils/chain-spec-builder/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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}"))?;
Expand Down
72 changes: 48 additions & 24 deletions substrate/client/chain-spec/src/chain_spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -122,7 +122,10 @@ impl<G: RuntimeGenesis> GenesisSource<G> {
}
}

impl<G: RuntimeGenesis, E> BuildStorage for ChainSpec<G, E> {
impl<G: RuntimeGenesis, E, HF> BuildStorage for ChainSpec<G, E, HF>
where
HF: HostFunctions,
{
fn assimilate_storage(&self, storage: &mut Storage) -> Result<(), String> {
match self.genesis.resolve()? {
#[allow(deprecated)]
Expand Down Expand Up @@ -158,7 +161,7 @@ impl<G: RuntimeGenesis, E> BuildStorage for ChainSpec<G, E> {
json_blob: RuntimeGenesisConfigJson::Config(config),
code,
}) => {
RuntimeCaller::new(&code[..])
RuntimeCaller::<HF>::new(&code[..])
michalkucharczyk marked this conversation as resolved.
Show resolved Hide resolved
.get_storage_for_config(config)?
.assimilate_storage(storage)?;
storage
Expand All @@ -169,7 +172,7 @@ impl<G: RuntimeGenesis, E> BuildStorage for ChainSpec<G, E> {
json_blob: RuntimeGenesisConfigJson::Patch(patch),
code,
}) => {
RuntimeCaller::new(&code[..])
RuntimeCaller::<HF>::new(&code[..])
michalkucharczyk marked this conversation as resolved.
Show resolved Hide resolved
.get_storage_for_patch(patch)?
.assimilate_storage(storage)?;
storage
Expand Down Expand Up @@ -448,23 +451,29 @@ impl<G, E> ChainSpecBuilder<G, E> {
ChainSpec {
client_spec,
genesis: GenesisSource::GenesisBuilderApi(self.genesis_build_action, self.code.into()),
_host_functions: Default::default(),
michalkucharczyk marked this conversation as resolved.
Show resolved Hide resolved
}
}
}

/// A configuration of a chain. Can be used to build a genesis block.
pub struct ChainSpec<G, E = NoExtension> {
pub struct ChainSpec<G, E = NoExtension, HF = sp_io::SubstrateHostFunctions> {
michalkucharczyk marked this conversation as resolved.
Show resolved Hide resolved
client_spec: ClientSpec<E>,
genesis: GenesisSource<G>,
_host_functions: PhantomData<HF>,
}

impl<G, E: Clone> Clone for ChainSpec<G, E> {
impl<G, E: Clone, HF> Clone for ChainSpec<G, E, HF> {
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, HF> ChainSpec<G, E, HF> {
/// A list of bootnode addresses.
pub fn boot_nodes(&self) -> &[MultiaddrWithPeerId] {
&self.client_spec.boot_nodes
Expand Down Expand Up @@ -553,6 +562,7 @@ impl<G, E> ChainSpec<G, E> {
ChainSpec {
client_spec,
genesis: GenesisSource::Factory(Arc::new(constructor), code.into()),
_host_functions: Default::default(),
}
}

Expand All @@ -567,14 +577,18 @@ impl<G, E> ChainSpec<G, E> {
}
}

impl<G: serde::de::DeserializeOwned, E: serde::de::DeserializeOwned> ChainSpec<G, E> {
impl<G: serde::de::DeserializeOwned, E: serde::de::DeserializeOwned, HF> ChainSpec<G, E, HF> {
/// 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`
Expand All @@ -593,7 +607,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(),
})
}
}

Expand All @@ -608,7 +626,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, HF> ChainSpec<G, E, HF>
where
HF: HostFunctions,
{
fn json_container(&self, raw: bool) -> Result<ChainSpecJsonContainer<G, E>, String> {
let raw_genesis = match (raw, self.genesis.resolve()?) {
(
Expand All @@ -618,7 +639,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::<HF>::new(&code[..]).get_storage_for_config(config)?;
michalkucharczyk marked this conversation as resolved.
Show resolved Hide resolved
storage.top.insert(sp_core::storage::well_known_keys::CODE.to_vec(), code);
RawGenesis::from(storage)
},
Expand All @@ -629,7 +651,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::<HF>::new(&code[..]).get_storage_for_patch(patch)?;
michalkucharczyk marked this conversation as resolved.
Show resolved Hide resolved
storage.top.insert(sp_core::storage::well_known_keys::CODE.to_vec(), code);
RawGenesis::from(storage)
},
Expand Down Expand Up @@ -664,10 +687,11 @@ impl<G: RuntimeGenesis, E: serde::Serialize + Clone + 'static> ChainSpec<G, E> {
}
}

impl<G, E> crate::ChainSpec for ChainSpec<G, E>
impl<G, E, HF> crate::ChainSpec for ChainSpec<G, E, HF>
where
G: RuntimeGenesis + 'static,
E: GetExtension + serde::Serialize + Clone + Send + Sync + 'static,
HF: HostFunctions,
{
fn boot_nodes(&self) -> &[MultiaddrWithPeerId] {
ChainSpec::boot_nodes(self)
Expand Down Expand Up @@ -953,7 +977,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(),
)
Expand Down Expand Up @@ -986,7 +1010,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(),
)
Expand Down Expand Up @@ -1033,7 +1057,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(),
)
Expand Down Expand Up @@ -1065,7 +1089,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(),
)
Expand All @@ -1083,7 +1107,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(),
)
Expand Down Expand Up @@ -1139,7 +1163,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(),
)
Expand All @@ -1162,7 +1186,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(),
)
Expand All @@ -1184,7 +1208,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(),
)
Expand Down
20 changes: 11 additions & 9 deletions substrate/client/chain-spec/src/genesis_config_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand All @@ -30,29 +31,30 @@ 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> {
pub struct GenesisConfigBuilderRuntimeCaller<'a, HF = sp_io::SubstrateHostFunctions> {
michalkucharczyk marked this conversation as resolved.
Show resolved Hide resolved
code: Cow<'a, [u8]>,
code_hash: Vec<u8>,
executor: WasmExecutor<sp_io::SubstrateHostFunctions>,
executor: WasmExecutor<HF>,
}

impl<'a> FetchRuntimeCode for GenesisConfigBuilderRuntimeCaller<'a> {
impl<'a, HF> FetchRuntimeCode for GenesisConfigBuilderRuntimeCaller<'a, HF> {
fn fetch_runtime_code(&self) -> Option<Cow<[u8]>> {
Some(self.code.as_ref().into())
}
}

impl<'a> GenesisConfigBuilderRuntimeCaller<'a> {
impl<'a, HF> GenesisConfigBuilderRuntimeCaller<'a, HF>
where
HF: HostFunctions,
{
/// Creates new instance using the provided code blob.
///
/// This code is later referred to as `runtime`.
pub fn new(code: &'a [u8]) -> Self {
GenesisConfigBuilderRuntimeCaller {
code: code.into(),
code_hash: sp_core::blake2_256(code).to_vec(),
executor: WasmExecutor::<sp_io::SubstrateHostFunctions>::builder()
.with_allow_missing_host_functions(true)
.build(),
executor: WasmExecutor::<HF>::builder().with_allow_missing_host_functions(true).build(),
michalkucharczyk marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand Down Expand Up @@ -134,7 +136,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())
michalkucharczyk marked this conversation as resolved.
Show resolved Hide resolved
.get_default_config()
.unwrap();
let expected = r#"{"system":{},"babe":{"authorities":[],"epochConfig":null},"substrateTest":{"authorities":[]},"balances":{"balances":[]}}"#;
Expand All @@ -156,7 +158,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();

Expand Down
Loading