GenesisConfig presets for runtime (#2714)

The runtime now can provide a number of predefined presets of
`RuntimeGenesisConfig` struct. This presets are intended to be used in
different deployments, e.g.: `local`, `staging`, etc, and should be
included into the corresponding chain-specs.

Having `GenesisConfig` presets in runtime allows to fully decouple node
from runtime types (the problem is described in #1984).

**Summary of changes:**
- The `GenesisBuilder` API was adjusted to enable this functionality
(and provide better naming - #150):
   ```rust
    fn preset_names() -> Vec<PresetId>;
fn get_preset(id: Option<PresetId>) -> Option<serde_json::Value>;
//`None` means default
    fn build_state(value: serde_json::Value);
    pub struct PresetId(Vec<u8>);
   ```

- **Breaking change**: Old `create_default_config` method was removed,
`build_config` was renamed to `build_state`. As a consequence a node
won't be able to interact with genesis config for older runtimes. The
cleanup was made for sake of API simplicity. Also IMO maintaining
compatibility with old API is not so crucial.
- Reference implementation was provided for `substrate-test-runtime` and
`rococo` runtimes. For rococo new
[`genesis_configs_presets`](https://github.com/paritytech/polkadot-sdk/blob/3b41d66b97c5ff0ec4a1989da5ffd8b9f3f588e3/polkadot/runtime/rococo/src/genesis_config_presets.rs#L530)
module was added and is used in `GenesisBuilder`
[_presets-related_](https://github.com/paritytech/polkadot-sdk/blob/3b41d66b97c5ff0ec4a1989da5ffd8b9f3f588e3/polkadot/runtime/rococo/src/lib.rs#L2462-L2485)
methods.

- The `chain-spec-builder` util was also improved and allows to
([_doc_](https://github.com/paritytech/polkadot-sdk/blob/3b41d66b97c5ff0ec4a1989da5ffd8b9f3f588e3/substrate/bin/utils/chain-spec-builder/src/lib.rs#L19)):
   - list presets provided by given runtime (`list-presets`),
- display preset or default config provided by the runtime
(`display-preset`),
   - build chain-spec using named preset (`create ... named-preset`),


- The `ChainSpecBuilder` is extended with
[`with_genesis_config_preset_name`](https://github.com/paritytech/polkadot-sdk/blob/3b41d66b97c5ff0ec4a1989da5ffd8b9f3f588e3/substrate/client/chain-spec/src/chain_spec.rs#L447)
method which allows to build chain-spec using named preset provided by
the runtime. Sample usage on the node side
[here](https://github.com/paritytech/polkadot-sdk/blob/2caffaae803e08a3d5b46c860e8016da023ff4ce/polkadot/node/service/src/chain_spec.rs#L404).

Implementation of #1984.
fixes: #150
part of: #25

---------

Co-authored-by: Sebastian Kunert <skunert49@gmail.com>
Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
This commit is contained in:
Michal Kucharczyk
2024-04-04 20:30:54 +02:00
committed by GitHub
parent 9d052b7e09
commit f910a15c1c
47 changed files with 1460 additions and 661 deletions
+1
View File
@@ -58,6 +58,7 @@ sp-api = { path = "../../../primitives/api" }
sp-core = { path = "../../../primitives/core" }
sp-runtime = { path = "../../../primitives/runtime" }
sp-timestamp = { path = "../../../primitives/timestamp" }
sp-genesis-builder = { path = "../../../primitives/genesis-builder" }
sp-inherents = { path = "../../../primitives/inherents" }
sp-keyring = { path = "../../../primitives/keyring" }
sp-keystore = { path = "../../../primitives/keystore" }
+10 -4
View File
@@ -838,10 +838,16 @@ fn should_import_block_with_test_client() {
#[test]
fn default_config_as_json_works() {
let mut t = new_test_ext(compact_code_unwrap());
let r = executor_call(&mut t, "GenesisBuilder_create_default_config", &vec![])
.0
.unwrap();
let r = Vec::<u8>::decode(&mut &r[..]).unwrap();
let r = executor_call(
&mut t,
"GenesisBuilder_get_preset",
&None::<&sp_genesis_builder::PresetId>.encode(),
)
.0
.unwrap();
let r = Option::<Vec<u8>>::decode(&mut &r[..])
.unwrap()
.expect("default config is there");
let json = String::from_utf8(r.into()).expect("returned value is json. qed.");
let expected = include_str!("res/default_genesis_config.json").to_string();
+9 -5
View File
@@ -31,7 +31,7 @@ use frame_support::{
derive_impl,
dispatch::DispatchClass,
dynamic_params::{dynamic_pallet_params, dynamic_params},
genesis_builder_helper::{build_config, create_default_config},
genesis_builder_helper::{build_state, get_preset},
instances::{Instance1, Instance2},
ord_parameter_types,
pallet_prelude::Get,
@@ -3194,12 +3194,16 @@ impl_runtime_apis! {
}
impl sp_genesis_builder::GenesisBuilder<Block> for Runtime {
fn create_default_config() -> Vec<u8> {
create_default_config::<RuntimeGenesisConfig>()
fn build_state(config: Vec<u8>) -> sp_genesis_builder::Result {
build_state::<RuntimeGenesisConfig>(config)
}
fn build_config(config: Vec<u8>) -> sp_genesis_builder::Result {
build_config::<RuntimeGenesisConfig>(config)
fn get_preset(id: &Option<sp_genesis_builder::PresetId>) -> Option<Vec<u8>> {
get_preset::<RuntimeGenesisConfig>(id, |_| None)
}
fn preset_names() -> Vec<sp_genesis_builder::PresetId> {
vec![]
}
}
}
@@ -1,6 +1,6 @@
[package]
name = "staging-chain-spec-builder"
version = "2.0.0"
version = "3.0.0"
authors.workspace = true
edition.workspace = true
build = "build.rs"
@@ -18,14 +18,24 @@
use chain_spec_builder::{
generate_chain_spec_for_runtime, ChainSpecBuilder, ChainSpecBuilderCmd, ConvertToRawCmd,
UpdateCodeCmd, VerifyCmd,
DisplayPresetCmd, ListPresetsCmd, UpdateCodeCmd, VerifyCmd,
};
use clap::Parser;
use sc_chain_spec::{update_code_in_json_chain_spec, GenericChainSpec};
use sc_chain_spec::{
update_code_in_json_chain_spec, GenericChainSpec, GenesisConfigBuilderRuntimeCaller,
};
use staging_chain_spec_builder as chain_spec_builder;
use std::fs;
fn main() -> Result<(), String> {
//avoid error message escaping
fn main() {
match inner_main() {
Err(e) => eprintln!("{}", format!("{e}")),
_ => {},
}
}
fn inner_main() -> Result<(), String> {
sp_tracing::try_init_simple();
let builder = ChainSpecBuilder::parse();
@@ -71,6 +81,36 @@ fn main() -> Result<(), String> {
let _ = serde_json::from_str::<serde_json::Value>(&chain_spec.as_json(true)?)
.map_err(|e| format!("Conversion to json failed: {e}"))?;
},
ChainSpecBuilderCmd::ListPresets(ListPresetsCmd { runtime_wasm_path }) => {
let code = fs::read(runtime_wasm_path.as_path())
.map_err(|e| format!("wasm blob shall be readable {e}"))?;
let caller: GenesisConfigBuilderRuntimeCaller =
GenesisConfigBuilderRuntimeCaller::new(&code[..]);
let presets = caller
.preset_names()
.map_err(|e| format!("getting default config from runtime should work: {e}"))?;
let presets: Vec<String> = presets
.into_iter()
.map(|preset| {
String::from(
TryInto::<&str>::try_into(&preset)
.unwrap_or_else(|_| "cannot display preset id")
.to_string(),
)
})
.collect();
println!("{presets:#?}");
},
ChainSpecBuilderCmd::DisplayPreset(DisplayPresetCmd { runtime_wasm_path, preset_name }) => {
let code = fs::read(runtime_wasm_path.as_path())
.map_err(|e| format!("wasm blob shall be readable {e}"))?;
let caller: GenesisConfigBuilderRuntimeCaller =
GenesisConfigBuilderRuntimeCaller::new(&code[..]);
let preset = caller
.get_named_preset(preset_name.as_ref())
.map_err(|e| format!("getting default config from runtime should work: {e}"))?;
println!("{preset}");
},
};
Ok(())
}
@@ -28,51 +28,84 @@
//! See [`ChainSpecBuilderCmd`] for a list of available commands.
//!
//! ## Typical use-cases.
//! ##### Get default config from runtime.
//! ##### Generate chains-spec using default config from runtime.
//!
//! Query the default genesis config from the provided `runtime.wasm` and use it in the chain
//! spec. The tool allows specifying where to write the chain spec, and optionally also where the
//! write the default genesis state config (which is `/dev/stdout` in the following example):
//! ```text
//! chain-spec-builder --chain_spec_path ./my_chain_spec.json create -r runtime.wasm default /dev/stdout
//! Query the default genesis config from the provided `runtime.wasm` and use it in the chain
//! spec.
//! ```bash
//! chain-spec-builder create -r runtime.wasm default
//! ```
//!
//! _Note:_ [`GenesisBuilder::create_default_config`][sp-genesis-builder-create] runtime function is
//!
//! _Note:_ [`GenesisBuilder::get_preset`][sp-genesis-builder-get-preset] runtime function is
//! called.
//!
//!
//! ##### Display the runtime's default `GenesisConfig`
//!
//! Displays the content of the runtime's default `GenesisConfig`
//! ```bash
//! chain-spec-builder display-preset -r runtime.wasm
//! ```
//!
//! _Note:_ [`GenesisBuilder::get_preset`][sp-genesis-builder-get-preset] runtime function is called.
//!
//! ##### Display the `GenesisConfig` preset with given name
//!
//! Displays the content of the `GenesisConfig` preset for given name
//! ```bash
//! chain-spec-builder display-preset -r runtime.wasm -p "staging"
//! ```
//!
//! _Note:_ [`GenesisBuilder::get_preset`][sp-genesis-builder-get-preset] runtime function is called.
//!
//! ##### List the names of `GenesisConfig` presets provided by runtime.
//!
//! Displays the names of the presets of `GenesisConfigs` provided by runtime.
//! ```bash
//! chain-spec-builder list-presets -r runtime.wasm
//! ```
//!
//! _Note:_ [`GenesisBuilder::preset_names`][sp-genesis-builder-list] runtime function is called.
//!
//! ##### Generate chain spec using runtime provided genesis config preset.
//!
//! Patch the runtime's default genesis config with the named preset provided by the runtime and generate the plain
//! version of chain spec:
//! ```bash
//! chain-spec-builder create -r runtime.wasm named-preset "staging"
//! ```
//!
//! _Note:_ [`GenesisBuilder::get_preset`][sp-genesis-builder-get-preset] and [`GenesisBuilder::build_state`][sp-genesis-builder-build] runtime functions are called.
//!
//! ##### Generate raw storage chain spec using genesis config patch.
//!
//! Patch the runtime's default genesis config with provided `patch.json` and generate raw
//! storage (`-s`) version of chain spec:
//!
//! ```bash
//! chain-spec-builder create -s -r runtime.wasm patch patch.json
//! ```
//!
//! _Note:_ [`GenesisBuilder::build_config`][sp-genesis-builder-build] runtime function is called.
//!
//! _Note:_ [`GenesisBuilder::build_state`][sp-genesis-builder-build] runtime function is called.
//!
//! ##### Generate raw storage chain spec using full genesis config.
//!
//! Build the chain spec using provided full genesis config json file. No defaults will be used:
//!
//! ```bash
//! chain-spec-builder create -s -r runtime.wasm full full-genesis-config.json
//! ```
//!
//! _Note_: [`GenesisBuilder::build_config`][sp-genesis-builder-build] runtime function is called.
//!
//! _Note_: [`GenesisBuilder::build_state`][sp-genesis-builder-build] runtime function is called.
//!
//! ##### Generate human readable chain spec using provided genesis config patch.
//! ```bash
//! chain-spec-builder create -r runtime.wasm patch patch.json
//! ```
//!
//!
//! ##### Generate human readable chain spec using provided full genesis config.
//!
//! ```bash
//! chain-spec-builder create -r runtime.wasm full full-genesis-config.json
//! ```
//!
//!
//! ##### Extra tools.
//! The `chain-spec-builder` provides also some extra utilities: [`VerifyCmd`], [`ConvertToRawCmd`],
//! [`UpdateCodeCmd`].
@@ -80,8 +113,9 @@
//! [`sc-chain-spec`]: ../sc_chain_spec/index.html
//! [`node-cli`]: ../node_cli/index.html
//! [`sp-genesis-builder`]: ../sp_genesis_builder/index.html
//! [sp-genesis-builder-create]: ../sp_genesis_builder/trait.GenesisBuilder.html#method.create_default_config
//! [sp-genesis-builder-build]: ../sp_genesis_builder/trait.GenesisBuilder.html#method.build_config
//! [sp-genesis-builder-build]: ../sp_genesis_builder/trait.GenesisBuilder.html#method.build_state
//! [sp-genesis-builder-list]: ../sp_genesis_builder/trait.GenesisBuilder.html#method.preset_names
//! [sp-genesis-builder-get-preset]: ../sp_genesis_builder/trait.GenesisBuilder.html#method.get_preset
use std::{fs, path::PathBuf};
@@ -107,6 +141,8 @@ pub enum ChainSpecBuilderCmd {
Verify(VerifyCmd),
UpdateCode(UpdateCodeCmd),
ConvertToRaw(ConvertToRawCmd),
ListPresets(ListPresetsCmd),
DisplayPreset(DisplayPresetCmd),
}
/// Create a new chain spec by interacting with the provided runtime wasm blob.
@@ -137,6 +173,7 @@ enum GenesisBuildAction {
Patch(PatchCmd),
Full(FullCmd),
Default(DefaultCmd),
NamedPreset(NamedPresetCmd),
}
/// Patches the runtime's default genesis config with provided patch.
@@ -157,10 +194,12 @@ struct FullCmd {
/// default genesis config may not be valid. For some runtimes initial values should be added there
/// (e.g. session keys, babe epoch).
#[derive(Parser, Debug, Clone)]
struct DefaultCmd {
/// If provided stores the default genesis config json file at given path (in addition to
/// chain-spec).
default_config_path: Option<PathBuf>,
struct DefaultCmd {}
/// Uses named preset provided by runtime to build the chains spec.
#[derive(Parser, Debug, Clone)]
struct NamedPresetCmd {
preset_name: String,
}
/// Updates the code in the provided input chain spec.
@@ -182,6 +221,25 @@ pub struct ConvertToRawCmd {
pub input_chain_spec: PathBuf,
}
/// Lists available presets
#[derive(Parser, Debug, Clone)]
pub struct ListPresetsCmd {
/// The path to runtime wasm blob.
#[arg(long, short)]
pub runtime_wasm_path: PathBuf,
}
/// Displays given preset
#[derive(Parser, Debug, Clone)]
pub struct DisplayPresetCmd {
/// The path to runtime wasm blob.
#[arg(long, short)]
pub runtime_wasm_path: PathBuf,
/// Preset to be displayed. If none is given default will be displayed.
#[arg(long, short)]
pub preset_name: Option<String>,
}
/// Verifies the provided input chain spec.
///
/// Silently checks if given input chain spec can be converted to raw. It allows to check if all
@@ -204,6 +262,8 @@ pub fn generate_chain_spec_for_runtime(cmd: &CreateCmd) -> Result<String, String
.with_chain_type(sc_chain_spec::ChainType::Live);
let builder = match cmd.action {
GenesisBuildAction::NamedPreset(NamedPresetCmd { ref preset_name }) =>
builder.with_genesis_config_preset_name(&preset_name),
GenesisBuildAction::Patch(PatchCmd { ref patch_path }) => {
let patch = fs::read(patch_path.as_path())
.map_err(|e| format!("patch file {patch_path:?} shall be readable: {e}"))?;
@@ -218,16 +278,12 @@ pub fn generate_chain_spec_for_runtime(cmd: &CreateCmd) -> Result<String, String
|e| format!("config file {config_path:?} shall contain a valid json: {e}"),
)?)
},
GenesisBuildAction::Default(DefaultCmd { ref default_config_path }) => {
GenesisBuildAction::Default(DefaultCmd {}) => {
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}"))?;
default_config_path.clone().map(|path| {
fs::write(path.as_path(), serde_json::to_string_pretty(&default_config).unwrap())
.map_err(|err| err.to_string())
});
builder.with_genesis_config(default_config)
},
};
+2 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "sc-chain-spec"
version = "27.0.0"
version = "28.0.0"
authors.workspace = true
edition.workspace = true
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
@@ -33,6 +33,7 @@ sp-genesis-builder = { path = "../../primitives/genesis-builder" }
sp-runtime = { path = "../../primitives/runtime" }
sp-state-machine = { path = "../../primitives/state-machine" }
log = { workspace = true }
sp-tracing = { path = "../../primitives/tracing" }
array-bytes = { version = "6.1" }
docify = "0.2.8"
@@ -0,0 +1,114 @@
Invalid JSON blob: unknown field `babex`, expected one of `system`, `babe`, `substrateTest`, `balances` at line 2 column 9 for blob:
{
"babex": {
"authorities": [
[
"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
1
],
[
"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty",
1
],
[
"5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y",
1
]
],
"epochConfig": {
"allowed_slots": "PrimaryAndSecondaryPlainSlots",
"c": [
3,
10
]
}
},
"balances": {
"balances": [
[
"5D34dL5prEUaGNQtPPZ3yN5Y6BnkfXunKXXz6fo7ZJbLwRRH",
100000000000000000
],
[
"5GBNeWRhZc2jXu7D55rBimKYDk8PGk8itRYFTPfC8RJLKG5o",
100000000000000000
],
[
"5Dfis6XL8J2P6JHUnUtArnFWndn62SydeP8ee8sG2ky9nfm9",
100000000000000000
],
[
"5F4H97f7nQovyrbiq4ZetaaviNwThSVcFobcA5aGab6167dK",
100000000000000000
],
[
"5DiDShBWa1fQx6gLzpf3SFBhMinCoyvHM1BWjPNsmXS8hkrW",
100000000000000000
],
[
"5EFb84yH9tpcFuiKUcsmdoF7xeeY3ajG1ZLQimxQoFt9HMKR",
100000000000000000
],
[
"5DZLHESsfGrJ5YzT3HuRPXsSNb589xQ4Unubh1mYLodzKdVY",
100000000000000000
],
[
"5GHJzqvG6tXnngCpG7B12qjUvbo5e4e9z8Xjidk3CQZHxTPZ",
100000000000000000
],
[
"5CUnSsgAyLND3bxxnfNhgWXSe9Wn676JzLpGLgyJv858qhoX",
100000000000000000
],
[
"5CVKn7HAZW1Ky4r7Vkgsr7VEW88C2sHgUNDiwHY9Ct2hjU8q",
100000000000000000
],
[
"5H673aukQ4PeDe1U2nuv1bi32xDEziimh3PZz7hDdYUB7TNz",
100000000000000000
],
[
"5HTe9L15LJryjUAt1jZXZCBPnzbbGnpvFwbjE3NwCWaAqovf",
100000000000000000
],
[
"5D7LFzGpMwHPyDBavkRbWSKWTtJhCaPPZ379wWLT23bJwXJz",
100000000000000000
],
[
"5CLepMARnEgtVR1EkUuJVUvKh97gzergpSxUU3yKGx1v6EwC",
100000000000000000
],
[
"5Chb2UhfvZpmjjEziHbFbotM4quX32ZscRV6QJBt1rUKzz51",
100000000000000000
],
[
"5HmRp3i3ZZk7xsAvbi8hyXVP6whSMnBJGebVC4FsiZVhx52e",
100000000000000000
],
[
"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
100000000000000000
],
[
"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty",
100000000000000000
],
[
"5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y",
100000000000000000
]
]
},
"substrateTest": {
"authorities": [
"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty",
"5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y"
]
},
"system": {}
}
@@ -0,0 +1,35 @@
{
"name": "TestName",
"id": "test_id",
"chainType": "Local",
"bootNodes": [],
"telemetryEndpoints": null,
"protocolId": null,
"properties": null,
"codeSubstitutes": {},
"genesis": {
"runtimeGenesis": {
"patch": {
"balances": {
"balances": [
[
"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty",
1000000000000000
],
[
"5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y",
1000000000000000
]
]
},
"substrateTest": {
"authorities": [
"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
"5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL"
]
}
},
"code": "0x0"
}
}
}
+63 -14
View File
@@ -40,15 +40,26 @@ use std::{
sync::Arc,
};
#[derive(Serialize, Deserialize, Clone)]
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
enum GenesisBuildAction {
enum GenesisBuildAction<EHF> {
Patch(json::Value),
Full(json::Value),
NamedPreset(String, PhantomData<EHF>),
}
impl<EHF> Clone for GenesisBuildAction<EHF> {
fn clone(&self) -> Self {
match self {
Self::Patch(ref p) => Self::Patch(p.clone()),
Self::Full(ref f) => Self::Full(f.clone()),
Self::NamedPreset(ref p, _) => Self::NamedPreset(p.clone(), Default::default()),
}
}
}
#[allow(deprecated)]
enum GenesisSource<G> {
enum GenesisSource<G, EHF> {
File(PathBuf),
Binary(Cow<'static, [u8]>),
/// factory function + code
@@ -56,10 +67,10 @@ enum GenesisSource<G> {
Factory(Arc<dyn Fn() -> G + Send + Sync>, Vec<u8>),
Storage(Storage),
/// build action + code
GenesisBuilderApi(GenesisBuildAction, Vec<u8>),
GenesisBuilderApi(GenesisBuildAction<EHF>, Vec<u8>),
}
impl<G> Clone for GenesisSource<G> {
impl<G, EHF> Clone for GenesisSource<G, EHF> {
fn clone(&self) -> Self {
match *self {
Self::File(ref path) => Self::File(path.clone()),
@@ -71,7 +82,7 @@ impl<G> Clone for GenesisSource<G> {
}
}
impl<G: RuntimeGenesis> GenesisSource<G> {
impl<G: RuntimeGenesis, EHF: HostFunctions> GenesisSource<G, EHF> {
fn resolve(&self) -> Result<Genesis<G>, String> {
/// helper container for deserializing genesis from the JSON file (ChainSpec JSON file is
/// also supported here)
@@ -118,6 +129,13 @@ impl<G: RuntimeGenesis> GenesisSource<G> {
json_blob: RuntimeGenesisConfigJson::Patch(patch.clone()),
code: code.clone(),
})),
Self::GenesisBuilderApi(GenesisBuildAction::NamedPreset(name, _), code) => {
let patch = RuntimeCaller::<EHF>::new(&code[..]).get_named_preset(Some(name))?;
Ok(Genesis::RuntimeGenesis(RuntimeGenesisInner {
json_blob: RuntimeGenesisConfigJson::Patch(patch),
code: code.clone(),
}))
},
}
}
}
@@ -331,13 +349,13 @@ pub struct ChainSpecBuilder<G, E = NoExtension, EHF = ()> {
name: String,
id: String,
chain_type: ChainType,
genesis_build_action: GenesisBuildAction,
genesis_build_action: GenesisBuildAction<EHF>,
boot_nodes: Option<Vec<MultiaddrWithPeerId>>,
telemetry_endpoints: Option<TelemetryEndpoints>,
protocol_id: Option<String>,
fork_id: Option<String>,
properties: Option<Properties>,
_genesis: PhantomData<(G, EHF)>,
_genesis: PhantomData<G>,
}
impl<G, E, EHF> ChainSpecBuilder<G, E, EHF> {
@@ -425,6 +443,13 @@ impl<G, E, EHF> ChainSpecBuilder<G, E, EHF> {
self
}
/// Sets the name of runtime-provided JSON patch for runtime's GenesisConfig.
pub fn with_genesis_config_preset_name(mut self, name: &str) -> Self {
self.genesis_build_action =
GenesisBuildAction::NamedPreset(name.to_string(), Default::default());
self
}
/// Sets the full runtime's GenesisConfig JSON.
pub fn with_genesis_config(mut self, config: json::Value) -> Self {
self.genesis_build_action = GenesisBuildAction::Full(config);
@@ -463,7 +488,7 @@ impl<G, E, EHF> ChainSpecBuilder<G, E, EHF> {
/// 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>,
genesis: GenesisSource<G, EHF>,
_host_functions: PhantomData<EHF>,
}
@@ -1009,7 +1034,30 @@ mod tests {
assert!(raw_chain_spec.is_ok());
}
#[docify::export]
#[test]
fn generate_chain_spec_with_named_preset_works() {
sp_tracing::try_init_simple();
let output: ChainSpec<()> = ChainSpec::builder(
substrate_test_runtime::wasm_binary_unwrap().into(),
Default::default(),
)
.with_name("TestName")
.with_id("test_id")
.with_chain_type(ChainType::Local)
.with_genesis_config_preset_name("staging")
.build();
let actual = output.as_json(false).unwrap();
let expected =
from_str::<Value>(include_str!("../res/substrate_test_runtime_from_named_preset.json"))
.unwrap();
//wasm blob may change overtime so let's zero it. Also ensure it is there:
let actual = zeroize_code_key_in_json(false, actual.as_str());
assert_eq!(actual, expected);
}
#[test]
fn generate_chain_spec_with_patch_works() {
let output = ChainSpec::<()>::builder(
@@ -1089,6 +1137,8 @@ mod tests {
#[test]
fn chain_spec_as_json_fails_with_invalid_config() {
let expected_error_message =
include_str!("../res/chain_spec_as_json_fails_with_invalid_config.err");
let j =
include_str!("../../../test-utils/runtime/res/default_genesis_config_invalid_2.json");
let output = ChainSpec::<()>::builder(
@@ -1101,10 +1151,9 @@ mod tests {
.with_genesis_config(from_str(j).unwrap())
.build();
assert_eq!(
output.as_json(true),
Err("Invalid JSON blob: unknown field `babex`, expected one of `system`, `babe`, `substrateTest`, `balances` at line 1 column 8".to_string())
);
let result = output.as_json(true);
assert_eq!(result.err().unwrap(), expected_error_message);
}
#[test]
@@ -26,7 +26,7 @@ use sp_core::{
storage::Storage,
traits::{CallContext, CodeExecutor, Externalities, FetchRuntimeCode, RuntimeCode},
};
use sp_genesis_builder::Result as BuildResult;
use sp_genesis_builder::{PresetId, Result as BuildResult};
use sp_state_machine::BasicExternalities;
use std::borrow::Cow;
@@ -84,30 +84,46 @@ where
/// Returns a json representation of the default `RuntimeGenesisConfig` provided by the
/// `runtime`.
///
/// Calls [`GenesisBuilder::create_default_config`](sp_genesis_builder::GenesisBuilder::create_default_config) in the `runtime`.
/// Calls [`GenesisBuilder::get_preset`](sp_genesis_builder::GenesisBuilder::get_preset) in the
/// `runtime` with `None` argument.
pub fn get_default_config(&self) -> core::result::Result<Value, String> {
let mut t = BasicExternalities::new_empty();
let call_result = self
.call(&mut t, "GenesisBuilder_create_default_config", &[])
.map_err(|e| format!("wasm call error {e}"))?;
let default_config = Vec::<u8>::decode(&mut &call_result[..])
.map_err(|e| format!("scale codec error: {e}"))?;
Ok(from_slice(&default_config[..]).expect("returned value is json. qed."))
self.get_named_preset(None)
}
/// Builds `RuntimeGenesisConfig` from given json blob and returns the genesis state.
/// Returns a JSON blob representation of the builtin `GenesisConfig` identified by `id`.
///
/// Calls [`GenesisBuilder::build_config`](sp_genesis_builder::GenesisBuilder::build_config)
/// Calls [`GenesisBuilder::get_preset`](sp_genesis_builder::GenesisBuilder::get_preset)
/// provided by the `runtime`.
pub fn get_named_preset(&self, id: Option<&String>) -> core::result::Result<Value, String> {
let mut t = BasicExternalities::new_empty();
let call_result = self
.call(&mut t, "GenesisBuilder_get_preset", &id.encode())
.map_err(|e| format!("wasm call error {e}"))?;
let named_preset = Option::<Vec<u8>>::decode(&mut &call_result[..])
.map_err(|e| format!("scale codec error: {e}"))?;
if let Some(named_preset) = named_preset {
Ok(from_slice(&named_preset[..]).expect("returned value is json. qed."))
} else {
Err(format!("The preset with name {id:?} is not available."))
}
}
/// Calls [`sp_genesis_builder::GenesisBuilder::build_state`] provided by runtime.
pub fn get_storage_for_config(&self, config: Value) -> core::result::Result<Storage, String> {
let mut ext = BasicExternalities::new_empty();
let json_pretty_str = serde_json::to_string_pretty(&config)
.map_err(|e| format!("json to string failed: {e}"))?;
let call_result = self
.call(&mut ext, "GenesisBuilder_build_config", &config.to_string().encode())
.call(&mut ext, "GenesisBuilder_build_state", &json_pretty_str.encode())
.map_err(|e| format!("wasm call error {e}"))?;
BuildResult::decode(&mut &call_result[..])
.map_err(|e| format!("scale codec error: {e}"))??;
.map_err(|e| format!("scale codec error: {e}"))?
.map_err(|e| format!("{e} for blob:\n{}", json_pretty_str))?;
Ok(ext.into_storages())
}
@@ -137,6 +153,25 @@ where
crate::json_patch::merge(&mut config, patch);
self.get_storage_for_config(config)
}
pub fn get_storage_for_named_preset(
&self,
name: Option<&String>,
) -> core::result::Result<Storage, String> {
self.get_storage_for_patch(self.get_named_preset(name)?)
}
pub fn preset_names(&self) -> core::result::Result<Vec<PresetId>, String> {
let mut t = BasicExternalities::new_empty();
let call_result = self
.call(&mut t, "GenesisBuilder_preset_names", &vec![])
.map_err(|e| format!("wasm call error {e}"))?;
let preset_names = Vec::<PresetId>::decode(&mut &call_result[..])
.map_err(|e| format!("scale codec error: {e}"))?;
Ok(preset_names)
}
}
#[cfg(test)]
@@ -144,6 +179,17 @@ mod tests {
use super::*;
use serde_json::{from_str, json};
pub use sp_consensus_babe::{AllowedSlots, BabeEpochConfiguration};
pub use sp_genesis_builder::PresetId;
#[test]
fn list_presets_works() {
sp_tracing::try_init_simple();
let presets =
<GenesisConfigBuilderRuntimeCaller>::new(substrate_test_runtime::wasm_binary_unwrap())
.preset_names()
.unwrap();
assert_eq!(presets, vec![PresetId::from("foobar"), PresetId::from("staging"),]);
}
#[test]
fn get_default_config_works() {
@@ -155,6 +201,17 @@ mod tests {
assert_eq!(from_str::<Value>(expected).unwrap(), config);
}
#[test]
fn get_named_preset_works() {
sp_tracing::try_init_simple();
let config =
<GenesisConfigBuilderRuntimeCaller>::new(substrate_test_runtime::wasm_binary_unwrap())
.get_named_preset(Some(&"foobar".to_string()))
.unwrap();
let expected = r#"{"foo":"bar"}"#;
assert_eq!(from_str::<Value>(expected).unwrap(), config);
}
#[test]
fn get_storage_for_patch_works() {
let patch = json!({
+2 -2
View File
@@ -181,7 +181,7 @@
//! the node needs to interact with the runtime.
//!
//! This interaction involves passing the runtime genesis config JSON blob to the runtime using the
//! [`sp_genesis_builder::GenesisBuilder::build_config`] function. During this operation, the
//! [`sp_genesis_builder::GenesisBuilder::build_state`] function. During this operation, the
//! runtime converts the JSON representation of the genesis config into [`sp_io::storage`] items. It
//! is a crucial step for computing the storage root hash, which is a key component in determining
//! the genesis hash.
@@ -325,7 +325,7 @@ mod chain_spec;
mod extension;
mod genesis_block;
mod genesis_config_builder;
mod json_patch;
pub mod json_patch;
pub use self::{
chain_spec::{
@@ -19,26 +19,39 @@
//!
//! Provides common logic. For more info refer to [`sp_genesis_builder::GenesisBuilder`].
extern crate alloc;
use alloc::vec::Vec;
use frame_support::traits::BuildGenesisConfig;
use sp_genesis_builder::Result as BuildResult;
use sp_genesis_builder::{PresetId, Result as BuildResult};
use sp_runtime::format_runtime_string;
/// Get the default `GenesisConfig` as a JSON blob. For more info refer to
/// [`sp_genesis_builder::GenesisBuilder::create_default_config`]
pub fn create_default_config<GC>() -> sp_std::vec::Vec<u8>
where
GC: BuildGenesisConfig + Default,
{
serde_json::to_string(&GC::default())
.expect("serialization to json is expected to work. qed.")
.into_bytes()
}
/// Build `GenesisConfig` from a JSON blob not using any defaults and store it in the storage. For
/// more info refer to [`sp_genesis_builder::GenesisBuilder::build_config`].
pub fn build_config<GC: BuildGenesisConfig>(json: sp_std::vec::Vec<u8>) -> BuildResult {
/// more info refer to [`sp_genesis_builder::GenesisBuilder::build_state`].
pub fn build_state<GC: BuildGenesisConfig>(json: Vec<u8>) -> BuildResult {
let gc = serde_json::from_slice::<GC>(&json)
.map_err(|e| format_runtime_string!("Invalid JSON blob: {}", e))?;
<GC as BuildGenesisConfig>::build(&gc);
Ok(())
}
/// Get the default `GenesisConfig` as a JSON blob if `name` is None.
///
/// Query of named presets is delegetaed to provided `preset_for_name` closure. For more info refer
/// to [`sp_genesis_builder::GenesisBuilder::get_preset`].
pub fn get_preset<GC>(
name: &Option<PresetId>,
preset_for_name: impl FnOnce(&sp_genesis_builder::PresetId) -> Option<sp_std::vec::Vec<u8>>,
) -> Option<Vec<u8>>
where
GC: BuildGenesisConfig + Default,
{
name.as_ref().map_or(
Some(
serde_json::to_string(&GC::default())
.expect("serialization to json is expected to work. qed.")
.into_bytes(),
),
preset_for_name,
)
}
@@ -1,6 +1,6 @@
[package]
name = "sp-genesis-builder"
version = "0.7.0"
version = "0.8.0"
authors.workspace = true
edition.workspace = true
license = "Apache-2.0"
@@ -16,10 +16,19 @@ workspace = true
targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["bytes"] }
scale-info = { version = "2.10.0", default-features = false, features = ["derive"] }
sp-api = { path = "../api", default-features = false }
sp-runtime = { path = "../runtime", default-features = false }
serde_json = { features = ["alloc", "arbitrary_precision"], workspace = true }
[features]
default = ["std"]
std = ["serde_json/std", "sp-api/std", "sp-runtime/std"]
std = [
"codec/std",
"scale-info/std",
"serde_json/std",
"sp-api/std",
"sp-runtime/std",
]
+64 -20
View File
@@ -19,39 +19,83 @@
//! Substrate genesis config builder
//!
//! This Runtime API allows to construct `RuntimeGenesisConfig`, in particular:
//! - serialize the runtime default `RuntimeGenesisConfig` struct into json format,
//! - put the RuntimeGenesisConfig struct into the storage. Internally this operation calls
//! `GenesisBuild::build` function for all runtime pallets, which is typically provided by
//! pallet's author.
//! - deserialize the `RuntimeGenesisConfig` from given json blob and put `RuntimeGenesisConfig`
//! into the state storage. Allows to build customized configuration.
//! For FRAME based runtimes, this runtime interface provides means to interact with
//! `RuntimeGenesisConfig`. Runtime provides a default `RuntimeGenesisConfig` structure in a form of
//! the JSON blob.
//!
//! Providing externalities with empty storage and putting `RuntimeGenesisConfig` into storage
//! allows to catch and build the raw storage of `RuntimeGenesisConfig` which is the foundation for
//! genesis block.
//! For non-FRAME runtimes this interface is intended to build genesis state of the runtime basing
//! on some input arbitrary bytes array. This documentation uses term `RuntimeGenesisConfig`, which
//! for non-FRAME runtimes may be understood as the runtime-side entity representing initial runtime
//! configuration. The representation of the preset is an arbitrary `Vec<u8>` and does not
//! necessarily have to represent a JSON blob.
//!
//! The runtime may provide a number of partial predefined `RuntimeGenesisConfig` configurations in
//! the form of patches which shall be applied on top of the default `RuntimeGenesisConfig`. The
//! patch is a JSON blob, which essentially comprises the list of key-value pairs that are to be
//! customized in the default runtime genesis config. These predefined configurations are referred
//! to as presets.
//!
//! This allows the runtime to provide a number of predefined configs (e.g. for different
//! testnets or development) without neccessity to leak the runtime types outside the itself (e.g.
//! node or chain-spec related tools).
//!
//! This Runtime API allows to interact with `RuntimeGenesisConfig`, in particular:
//! - provide the list of available preset names,
//! - provide a number of named presets of `RuntimeGenesisConfig`,
//! - provide a JSON represention of the default `RuntimeGenesisConfig` (by simply serializing the
//! default `RuntimeGenesisConfig` struct into JSON format),
//! - deserialize the full `RuntimeGenesisConfig` from given JSON blob and put the resulting
//! `RuntimeGenesisConfig` structure into the state storage creating the initial runtime's state.
//! Allows to build customized genesis. This operation internally calls `GenesisBuild::build`
//! function for all runtime pallets.
//!
//! Providing externalities with an empty storage and putting `RuntimeGenesisConfig` into storage
//! (by calling `build_state`) allows to construct the raw storage of `RuntimeGenesisConfig`
//! which is the foundation for genesis block.
extern crate alloc;
use alloc::vec::Vec;
/// The result type alias, used in build methods. `Err` contains formatted error message.
pub type Result = core::result::Result<(), sp_runtime::RuntimeString>;
/// The type representing preset ID.
pub type PresetId = sp_runtime::RuntimeString;
sp_api::decl_runtime_apis! {
/// API to interact with RuntimeGenesisConfig for the runtime
pub trait GenesisBuilder {
/// Creates the default `RuntimeGenesisConfig` and returns it as a JSON blob.
/// Build `RuntimeGenesisConfig` from a JSON blob not using any defaults and store it in the
/// storage.
///
/// This function instantiates the default `RuntimeGenesisConfig` struct for the runtime and serializes it into a JSON
/// blob. It returns a `Vec<u8>` containing the JSON representation of the default `RuntimeGenesisConfig`.
fn create_default_config() -> alloc::vec::Vec<u8>;
/// In the case of a FRAME-based runtime, this function deserializes the full `RuntimeGenesisConfig` from the given JSON blob and
/// puts it into the storage. If the provided JSON blob is incorrect or incomplete or the
/// deserialization fails, an error is returned.
///
/// Please note that provided JSON blob must contain all `RuntimeGenesisConfig` fields, no
/// defaults will be used.
fn build_state(json: Vec<u8>) -> Result;
/// Build `RuntimeGenesisConfig` from a JSON blob not using any defaults and store it in the storage.
/// Returns a JSON blob representation of the built-in `RuntimeGenesisConfig` identified by
/// `id`.
///
/// This function deserializes the full `RuntimeGenesisConfig` from the given JSON blob and puts it into the storage.
/// If the provided JSON blob is incorrect or incomplete or the deserialization fails, an error is returned.
/// It is recommended to log any errors encountered during the process.
/// If `id` is `None` the function returns JSON blob representation of the default
/// `RuntimeGenesisConfig` struct of the runtime. Implementation must provide default
/// `RuntimeGenesisConfig`.
///
/// Please note that provided json blob must contain all `RuntimeGenesisConfig` fields, no defaults will be used.
fn build_config(json: alloc::vec::Vec<u8>) -> Result;
/// Otherwise function returns a JSON representation of the built-in, named
/// `RuntimeGenesisConfig` preset identified by `id`, or `None` if such preset does not
/// exists. Returned `Vec<u8>` contains bytes of JSON blob (patch) which comprises a list of
/// (potentially nested) key-value pairs that are intended for customizing the default
/// runtime genesis config. The patch shall be merged (rfc7386) with the JSON representation
/// of the default `RuntimeGenesisConfig` to create a comprehensive genesis config that can
/// be used in `build_state` method.
fn get_preset(id: &Option<PresetId>) -> Option<Vec<u8>>;
/// Returns a list of identifiers for available builtin `RuntimeGenesisConfig` presets.
///
/// The presets from the list can be queried with [`GenesisBuilder::get_preset`] method. If
/// no named presets are provided by the runtime the list is empty.
fn preset_names() -> Vec<PresetId>;
}
}
+2
View File
@@ -50,7 +50,9 @@ sp-externalities = { path = "../../primitives/externalities", default-features =
# 3rd party
array-bytes = { version = "6.1", optional = true }
serde_json = { workspace = true, features = ["alloc"] }
log = { workspace = true }
hex-literal = { version = "0.4.1" }
[dev-dependencies]
futures = "0.3.30"
+99 -26
View File
@@ -27,11 +27,14 @@ pub mod extrinsic;
pub mod genesismap;
pub mod substrate_test_pallet;
use alloc::boxed::Box;
#[cfg(not(feature = "std"))]
use alloc::{vec, vec::Vec};
use codec::{Decode, Encode};
use frame_support::{
construct_runtime, derive_impl,
dispatch::DispatchClass,
genesis_builder_helper::{build_config, create_default_config},
genesis_builder_helper::{build_state, get_preset},
parameter_types,
traits::{ConstU32, ConstU64},
weights::{
@@ -44,10 +47,8 @@ use frame_system::{
CheckNonce, CheckWeight,
};
use scale_info::TypeInfo;
use alloc::boxed::Box;
#[cfg(not(feature = "std"))]
use alloc::{vec, vec::Vec};
use sp_application_crypto::Ss58Codec;
use sp_keyring::AccountKeyring;
use sp_application_crypto::{ecdsa, ed25519, sr25519, RuntimeAppPublic};
use sp_core::{OpaqueMetadata, RuntimeDebug};
@@ -57,8 +58,10 @@ use sp_trie::{
};
use trie_db::{Trie, TrieMut};
use serde_json::json;
use sp_api::{decl_runtime_apis, impl_runtime_apis};
pub use sp_core::hash::H256;
use sp_genesis_builder::PresetId;
use sp_inherents::{CheckInherentsResult, InherentData};
use sp_runtime::{
create_runtime_str, impl_opaque_keys,
@@ -93,7 +96,7 @@ pub mod wasm_binary_logging_disabled {
#[cfg(feature = "std")]
pub fn wasm_binary_unwrap() -> &'static [u8] {
WASM_BINARY.expect(
"Development wasm binary is not available. Testing is only supported with the flag \
"Development wasm binary is not available. Testing is only supported with the flag
disabled.",
)
}
@@ -102,7 +105,7 @@ pub fn wasm_binary_unwrap() -> &'static [u8] {
#[cfg(feature = "std")]
pub fn wasm_binary_logging_disabled_unwrap() -> &'static [u8] {
wasm_binary_logging_disabled::WASM_BINARY.expect(
"Development wasm binary is not available. Testing is only supported with the flag \
"Development wasm binary is not available. Testing is only supported with the flag
disabled.",
)
}
@@ -725,12 +728,42 @@ impl_runtime_apis! {
}
impl sp_genesis_builder::GenesisBuilder<Block> for Runtime {
fn create_default_config() -> Vec<u8> {
create_default_config::<RuntimeGenesisConfig>()
fn build_state(config: Vec<u8>) -> sp_genesis_builder::Result {
build_state::<RuntimeGenesisConfig>(config)
}
fn build_config(config: Vec<u8>) -> sp_genesis_builder::Result {
build_config::<RuntimeGenesisConfig>(config)
fn get_preset(name: &Option<PresetId>) -> Option<Vec<u8>> {
get_preset::<RuntimeGenesisConfig>(name, |name| {
let patch = match name.try_into() {
Ok("staging") => {
let endowed_accounts: Vec<AccountId> = vec![
AccountKeyring::Bob.public().into(),
AccountKeyring::Charlie.public().into(),
];
json!({
"balances": {
"balances": endowed_accounts.into_iter().map(|k| (k, 10 * currency::DOLLARS)).collect::<Vec<_>>(),
},
"substrateTest": {
"authorities": [
AccountKeyring::Alice.public().to_ss58check(),
AccountKeyring::Ferdie.public().to_ss58check()
],
}
})
},
Ok("foobar") => json!({"foo":"bar"}),
_ => return None,
};
Some(serde_json::to_string(&patch)
.expect("serialization to json is expected to work. qed.")
.into_bytes())
})
}
fn preset_names() -> Vec<PresetId> {
vec![PresetId::from("foobar"), PresetId::from("staging")]
}
}
}
@@ -836,7 +869,6 @@ fn test_witness(proof: StorageProof, root: crate::Hash) {
pub mod storage_key_generator {
use super::*;
use sp_core::Pair;
use sp_keyring::AccountKeyring;
/// Generate hex string without prefix
pub(super) fn hex<T>(x: T) -> String
@@ -1026,7 +1058,6 @@ mod tests {
use sp_api::{ApiExt, ProvideRuntimeApi};
use sp_consensus::BlockOrigin;
use sp_core::{storage::well_known_keys::HEAP_PAGES, traits::CallContext};
use sp_keyring::AccountKeyring;
use sp_runtime::{
traits::{Hash as _, SignedExtension},
transaction_validity::{InvalidTransaction, ValidTransaction},
@@ -1177,7 +1208,7 @@ mod tests {
fn check_substrate_check_signed_extension_works() {
sp_tracing::try_init_simple();
new_test_ext().execute_with(|| {
let x = sp_keyring::AccountKeyring::Alice.into();
let x = AccountKeyring::Alice.into();
let info = DispatchInfo::default();
let len = 0_usize;
assert_eq!(
@@ -1242,7 +1273,7 @@ mod tests {
let default_minimal_json = r#"{"system":{},"babe":{"authorities":[],"epochConfig":{"c": [ 3, 10 ],"allowed_slots":"PrimaryAndSecondaryPlainSlots"}},"substrateTest":{"authorities":[]},"balances":{"balances":[]}}"#;
let mut t = BasicExternalities::new_empty();
executor_call(&mut t, "GenesisBuilder_build_config", &default_minimal_json.encode())
executor_call(&mut t, "GenesisBuilder_build_state", &default_minimal_json.encode())
.unwrap();
let mut keys = t.into_storages().top.keys().cloned().map(hex).collect::<Vec<String>>();
@@ -1290,21 +1321,60 @@ mod tests {
fn default_config_as_json_works() {
sp_tracing::try_init_simple();
let mut t = BasicExternalities::new_empty();
let r = executor_call(&mut t, "GenesisBuilder_create_default_config", &vec![]).unwrap();
let r = Vec::<u8>::decode(&mut &r[..]).unwrap();
let r = executor_call(&mut t, "GenesisBuilder_get_preset", &None::<&PresetId>.encode())
.unwrap();
let r = Option::<Vec<u8>>::decode(&mut &r[..])
.unwrap()
.expect("default config is there");
let json = String::from_utf8(r.into()).expect("returned value is json. qed.");
let expected = r#"{"system":{},"babe":{"authorities":[],"epochConfig":{"c":[1,4],"allowed_slots":"PrimaryAndSecondaryVRFSlots"}},"substrateTest":{"authorities":[]},"balances":{"balances":[]}}"#;
assert_eq!(expected.to_string(), json);
}
#[test]
fn preset_names_listing_works() {
sp_tracing::try_init_simple();
let mut t = BasicExternalities::new_empty();
let r = executor_call(&mut t, "GenesisBuilder_preset_names", &vec![]).unwrap();
let r = Vec::<PresetId>::decode(&mut &r[..]).unwrap();
assert_eq!(r, vec![PresetId::from("foobar"), PresetId::from("staging"),]);
log::info!("r: {:#?}", r);
}
#[test]
fn named_config_works() {
sp_tracing::try_init_simple();
let f = |cfg_name: &str, expected: &str| {
let mut t = BasicExternalities::new_empty();
let name = cfg_name.to_string();
let r = executor_call(
&mut t,
"GenesisBuilder_get_preset",
&Some(name.as_bytes()).encode(),
)
.unwrap();
let r = Option::<Vec<u8>>::decode(&mut &r[..]).unwrap();
let json =
String::from_utf8(r.unwrap().into()).expect("returned value is json. qed.");
log::info!("json: {:#?}", json);
assert_eq!(expected.to_string(), json);
};
f("foobar", r#"{"foo":"bar"}"#);
f(
"staging",
r#"{"balances":{"balances":[["5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty",1000000000000000],["5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y",1000000000000000]]},"substrateTest":{"authorities":["5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY","5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL"]}}"#,
);
}
#[test]
fn build_config_from_json_works() {
sp_tracing::try_init_simple();
let j = include_str!("../res/default_genesis_config.json");
let mut t = BasicExternalities::new_empty();
let r = executor_call(&mut t, "GenesisBuilder_build_config", &j.encode()).unwrap();
let r = executor_call(&mut t, "GenesisBuilder_build_state", &j.encode()).unwrap();
let r = BuildResult::decode(&mut &r[..]);
assert!(r.is_ok());
@@ -1323,7 +1393,7 @@ mod tests {
sp_tracing::try_init_simple();
let j = include_str!("../res/default_genesis_config_invalid.json");
let mut t = BasicExternalities::new_empty();
let r = executor_call(&mut t, "GenesisBuilder_build_config", &j.encode()).unwrap();
let r = executor_call(&mut t, "GenesisBuilder_build_state", &j.encode()).unwrap();
let r = BuildResult::decode(&mut &r[..]).unwrap();
log::info!("result: {:#?}", r);
assert_eq!(r, Err(
@@ -1338,7 +1408,7 @@ mod tests {
sp_tracing::try_init_simple();
let j = include_str!("../res/default_genesis_config_invalid_2.json");
let mut t = BasicExternalities::new_empty();
let r = executor_call(&mut t, "GenesisBuilder_build_config", &j.encode()).unwrap();
let r = executor_call(&mut t, "GenesisBuilder_build_state", &j.encode()).unwrap();
let r = BuildResult::decode(&mut &r[..]).unwrap();
assert_eq!(r, Err(
sp_runtime::RuntimeString::Owned(
@@ -1353,7 +1423,7 @@ mod tests {
let j = include_str!("../res/default_genesis_config_incomplete.json");
let mut t = BasicExternalities::new_empty();
let r = executor_call(&mut t, "GenesisBuilder_build_config", &j.encode()).unwrap();
let r = executor_call(&mut t, "GenesisBuilder_build_state", &j.encode()).unwrap();
let r =
core::result::Result::<(), sp_runtime::RuntimeString>::decode(&mut &r[..]).unwrap();
assert_eq!(
@@ -1388,8 +1458,11 @@ mod tests {
sp_tracing::try_init_simple();
let mut t = BasicExternalities::new_empty();
let r = executor_call(&mut t, "GenesisBuilder_create_default_config", &vec![]).unwrap();
let r = Vec::<u8>::decode(&mut &r[..]).unwrap();
let r = executor_call(&mut t, "GenesisBuilder_get_preset", &None::<&PresetId>.encode())
.unwrap();
let r = Option::<Vec<u8>>::decode(&mut &r[..])
.unwrap()
.expect("default config is there");
let mut default_config: serde_json::Value =
serde_json::from_slice(&r[..]).expect("returned value is json. qed.");
@@ -1418,7 +1491,7 @@ mod tests {
let mut t = BasicExternalities::new_empty();
executor_call(
&mut t,
"GenesisBuilder_build_config",
"GenesisBuilder_build_state",
&default_config.to_string().encode(),
)
.unwrap();
@@ -1436,8 +1509,8 @@ mod tests {
let authority_key_vec =
Vec::<sp_core::sr25519::Public>::decode(&mut &value[..]).unwrap();
assert_eq!(authority_key_vec.len(), 2);
assert_eq!(authority_key_vec[0], sp_keyring::AccountKeyring::Ferdie.public());
assert_eq!(authority_key_vec[1], sp_keyring::AccountKeyring::Alice.public());
assert_eq!(authority_key_vec[0], AccountKeyring::Ferdie.public());
assert_eq!(authority_key_vec[1], AccountKeyring::Alice.public());
//Babe|Authorities
let value: Vec<u8> = get_from_storage(