From 5a94d54bc7ea21760fd452d239b7945707dd1b18 Mon Sep 17 00:00:00 2001 From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Date: Sat, 1 Jun 2024 10:50:37 +0200 Subject: [PATCH 01/15] initial work --- Cargo.lock | 25 ++++ Cargo.toml | 1 + docs/sdk/Cargo.toml | 4 + .../src/reference_docs/chain_spec_genesis.rs | 65 +++++++++- .../chain_spec_runtime/Cargo.toml | 65 ++++++++++ .../chain_spec_runtime/build.rs | 23 ++++ .../chain_spec_runtime/src/lib.rs | 27 ++++ .../chain_spec_runtime/src/pallets.rs | 109 ++++++++++++++++ .../chain_spec_runtime/src/presets.rs | 95 ++++++++++++++ .../chain_spec_runtime/src/runtime.rs | 120 ++++++++++++++++++ 10 files changed, 532 insertions(+), 2 deletions(-) create mode 100644 docs/sdk/src/reference_docs/chain_spec_runtime/Cargo.toml create mode 100644 docs/sdk/src/reference_docs/chain_spec_runtime/build.rs create mode 100644 docs/sdk/src/reference_docs/chain_spec_runtime/src/lib.rs create mode 100644 docs/sdk/src/reference_docs/chain_spec_runtime/src/pallets.rs create mode 100644 docs/sdk/src/reference_docs/chain_spec_runtime/src/presets.rs create mode 100644 docs/sdk/src/reference_docs/chain_spec_runtime/src/runtime.rs diff --git a/Cargo.lock b/Cargo.lock index e50baf6e6687..67d881a9a164 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2509,6 +2509,30 @@ dependencies = [ "zeroize", ] +[[package]] +name = "chain-spec-guide-runtime" +version = "0.0.0" +dependencies = [ + "docify", + "pallet-balances", + "pallet-sudo", + "pallet-timestamp", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "parity-scale-codec", + "polkadot-sdk-frame", + "scale-info", + "serde", + "serde_json", + "sp-application-crypto", + "sp-core", + "sp-genesis-builder", + "sp-keyring", + "sp-runtime", + "sp-std 14.0.0", + "substrate-wasm-builder", +] + [[package]] name = "chrono" version = "0.4.31" @@ -14235,6 +14259,7 @@ dependencies = [ name = "polkadot-sdk-docs" version = "0.0.1" dependencies = [ + "chain-spec-guide-runtime", "cumulus-client-service", "cumulus-pallet-aura-ext", "cumulus-pallet-parachain-system", diff --git a/Cargo.toml b/Cargo.toml index d6099e420f91..2b2a1cdc17d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -144,6 +144,7 @@ members = [ "cumulus/test/service", "cumulus/xcm/xcm-emulator", "docs/sdk", + "docs/sdk/src/reference_docs/chain_spec_runtime", "polkadot", "polkadot/cli", "polkadot/core-primitives", diff --git a/docs/sdk/Cargo.toml b/docs/sdk/Cargo.toml index a8c873be556c..2cb8d49fc5a7 100644 --- a/docs/sdk/Cargo.toml +++ b/docs/sdk/Cargo.toml @@ -102,3 +102,7 @@ sp-version = { path = "../../substrate/primitives/version" } # XCM xcm = { package = "staging-xcm", path = "../../polkadot/xcm" } xcm-docs = { path = "../../polkadot/xcm/docs" } + +# runtime guides +chain-spec-guide-runtime = { path = "./src/reference_docs/chain_spec_runtime" } + diff --git a/docs/sdk/src/reference_docs/chain_spec_genesis.rs b/docs/sdk/src/reference_docs/chain_spec_genesis.rs index 2ac51a91f2de..31bab1851743 100644 --- a/docs/sdk/src/reference_docs/chain_spec_genesis.rs +++ b/docs/sdk/src/reference_docs/chain_spec_genesis.rs @@ -1,4 +1,65 @@ //! Chain spec and genesis build. //! -//! What is chain-spec. -//! What is genesis state and how to build it. +//! # What is chain-spec. +//! +//! # What is genesis state and how to build it. +//! +//! ## `GenesisConfig` for `pallet` +//! +//! pallet_bar GenesisConfig: +#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/pallets.rs", pallet_bar_GenesisConfig)] +//! pallet_bar: +#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/pallets.rs", pallet_bar)] + +//! pallet_bar: +#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/pallets.rs", pallet_bar_build)] + +//! GenesisConfig may contain also more complicated types including nested structs or enums. +//! GenesisConfig for `pallet_foo +#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/pallets.rs", pallet_foo_GenesisConfig)] + +//! pallet_foo build function. It uses fields of GenesisConfig to generate storage. Please not that +//! fields of `GenesisConfig` may not be directly mapped to storage items: +#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/pallets.rs", pallet_foo_build)] + +//! The runtime genesis config struct consists of configs for every pallet. For sample runtime used +//! in this guide, it consists of `SystemConfig`, `BarConfig` and `FooConfig`. This structure was +//! automatically generated by macro and it can be sneaked peaked here: [`RuntimeGenesisConfig`]. + +//! Runtime exposes a dedicated runtime API for interacting with its genesis config: +//! [`sp-genesis-builder::GenesisBuilder`]. Implementation shall be provided withing +//! `impl_runtime_apis` macro, typically making use of some helpers provided: [`build_state`], +//! ['get_preset']. +//! Typically implementation of [`sp-genesis-builder::GenesisBuilder`] is as follows: +#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/runtime.rs", runtime_impl)] + +//! Please note that two functions are customized `preset_names` and `get_preset`. The first one +//! just provdes a vec of names of supported presets, while the latter one delegats the call to +//! function that maps the name to preset: [`chain_spec_guide_runtime::presets::get_builtin_preset`] +#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", get_builtin_preset)] + +//! Internally presets can be provided in a number of ways: +//! - json in string form: +#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", preset_1)] +//! - json using runtime types to serialize values: +#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", preset_2)] +#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", preset_3)] +//! It is worth noting that preset does not have to be the full `RuntimeGenesisConfig`, in that +//! sense that it does not have to contain all the keys of the struct. The preset is acutally a json +//! patch that will be merged with default value of `RuntimeGenesisConfig`. + +//! # Interacting with the tool +//! +//! # JSON representation +//! The json representation of runtime genesis config: +//! +//! # Raw genesis storage. +//! The json representation of runtime genesis storage: +//! What are hashes? How to decipher them? +//! +//! +//! [`RuntimeGenesisConfig`]: +//! chain_spec_guide_runtime::runtime::RuntimeGenesisConfig +//! [`impl_runtime_apis`]: frame::runtime::prelude::impl_runtime_apis +//! [`build_state`]: frame_support::genesis_builder_helper::build_state +//! [`get_preset`]: frame_support::genesis_builder_helper::get_preset diff --git a/docs/sdk/src/reference_docs/chain_spec_runtime/Cargo.toml b/docs/sdk/src/reference_docs/chain_spec_runtime/Cargo.toml new file mode 100644 index 000000000000..36ebc13c9767 --- /dev/null +++ b/docs/sdk/src/reference_docs/chain_spec_runtime/Cargo.toml @@ -0,0 +1,65 @@ +[package] +name = "chain-spec-guide-runtime" +description = "A minimal runtime for chain spec guide" +version = "0.0.0" +license = "MIT-0" +authors.workspace = true +homepage.workspace = true +repository.workspace = true +edition.workspace = true +publish = false + +[dependencies] +docify = "0.2.8" +parity-scale-codec = { version = "3.6.12", default-features = false } +scale-info = { version = "2.6.0", default-features = false } +serde = { workspace = true, default-features = false, feature = ["derive"] } +serde_json = { workspace = true } + +# this is a frame-based runtime, thus importing `frame` with runtime feature enabled. +frame = { package = "polkadot-sdk-frame", path = "../../../../../substrate/frame", default-features = false, features = [ + "experimental", + "runtime", +] } + +# pallets that we want to use +pallet-balances = { path = "../../../../../substrate/frame/balances", default-features = false } +pallet-sudo = { path = "../../../../../substrate/frame/sudo", default-features = false } +pallet-timestamp = { path = "../../../../../substrate/frame/timestamp", default-features = false } +pallet-transaction-payment = { path = "../../../../../substrate/frame/transaction-payment", default-features = false } +pallet-transaction-payment-rpc-runtime-api = { path = "../../../../../substrate/frame/transaction-payment/rpc/runtime-api", default-features = false } + +# genesis builder that allows us to interact with runtime genesis config +sp-genesis-builder = { path = "../../../../../substrate/primitives/genesis-builder", default-features = false } +sp-runtime = { path = "../../../../../substrate/primitives/runtime", default-features = false, features = ["serde"] } +sp-core = { path = "../../../../../substrate/primitives/core", default-features = false } +sp-std = { path = "../../../../../substrate/primitives/std", default-features = false } +sp-keyring = { path = "../../../../../substrate/primitives/keyring", default-features = false } +sp-application-crypto = { path = "../../../../../substrate/primitives/application-crypto", default-features = false, features = ["serde"] } + +[build-dependencies] +substrate-wasm-builder = { path = "../../../../../substrate/utils/wasm-builder", optional = true } + +[features] +default = ["std"] +std = [ + "parity-scale-codec/std", + "scale-info/std", + + "frame/std", + + "pallet-balances/std", + "pallet-sudo/std", + "pallet-timestamp/std", + "pallet-transaction-payment-rpc-runtime-api/std", + "pallet-transaction-payment/std", + + "sp-genesis-builder/std", + "sp-runtime/std", + "sp-core/std", + "sp-std/std", + "sp-keyring/std", + "sp-application-crypto/std", + + "substrate-wasm-builder", +] diff --git a/docs/sdk/src/reference_docs/chain_spec_runtime/build.rs b/docs/sdk/src/reference_docs/chain_spec_runtime/build.rs new file mode 100644 index 000000000000..e6f92757e225 --- /dev/null +++ b/docs/sdk/src/reference_docs/chain_spec_runtime/build.rs @@ -0,0 +1,23 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +fn main() { + #[cfg(feature = "std")] + { + substrate_wasm_builder::WasmBuilder::build_using_defaults(); + } +} diff --git a/docs/sdk/src/reference_docs/chain_spec_runtime/src/lib.rs b/docs/sdk/src/reference_docs/chain_spec_runtime/src/lib.rs new file mode 100644 index 000000000000..4606104fb968 --- /dev/null +++ b/docs/sdk/src/reference_docs/chain_spec_runtime/src/lib.rs @@ -0,0 +1,27 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![cfg_attr(not(feature = "std"), no_std)] + +//! A minimal runtime that shows runtime genesis state. + +pub mod pallets; +pub mod presets; +pub mod runtime; + +#[cfg(feature = "std")] +pub use runtime::{WASM_BINARY, WASM_BINARY_BLOATY}; diff --git a/docs/sdk/src/reference_docs/chain_spec_runtime/src/pallets.rs b/docs/sdk/src/reference_docs/chain_spec_runtime/src/pallets.rs new file mode 100644 index 000000000000..5d0f644c7a59 --- /dev/null +++ b/docs/sdk/src/reference_docs/chain_spec_runtime/src/pallets.rs @@ -0,0 +1,109 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use frame::prelude::*; + +#[docify::export] +#[frame::pallet(dev_mode)] +pub mod pallet_bar { + use super::*; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::storage] + pub(super) type InitialAccount = StorageValue; + + #[pallet::genesis_config] + #[derive(DefaultNoBound)] + #[docify::export(pallet_bar_GenesisConfig)] + pub struct GenesisConfig { + pub initial_account: Option, + } + + #[pallet::genesis_build] + impl BuildGenesisConfig for GenesisConfig { + #[docify::export(pallet_bar_build)] + fn build(&self) { + InitialAccount::::set(self.initial_account.clone()); + } + } +} + +#[derive(Default, serde::Serialize, serde::Deserialize)] +pub struct SomeFooData1 { + pub a: u8, + pub b: u8, +} + +#[derive(Default, serde::Serialize, serde::Deserialize)] +pub struct SomeFooData2 { + #[serde(default, with = "sp_core::bytes")] + pub v: Vec, +} + +#[derive(Default, serde::Serialize, serde::Deserialize)] +pub enum FooEnum { + #[default] + Data0, + Data1(SomeFooData1), + Data2(SomeFooData2), +} + +#[docify::export] +#[frame::pallet(dev_mode)] +pub mod pallet_foo { + use super::*; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::storage] + pub type ProcessedEnumValue = StorageValue; + #[pallet::storage] + pub type SomeInteger = StorageValue; + + #[docify::export(pallet_foo_GenesisConfig)] + #[pallet::genesis_config] + #[derive(DefaultNoBound)] + pub struct GenesisConfig { + pub some_integer: u32, + pub some_enum: FooEnum, + #[serde(skip)] + _phantom: PhantomData, + } + + #[pallet::genesis_build] + impl BuildGenesisConfig for GenesisConfig { + #[docify::export(pallet_foo_build)] + fn build(&self) { + let v: u64 = match &self.some_enum { + FooEnum::Data0 => 0, + FooEnum::Data1(v) => (v.a + v.b).into(), + FooEnum::Data2(v) => v.v.iter().map(|v| *v as u64).sum(), + }; + ProcessedEnumValue::::set(Some(v)); + SomeInteger::::set(Some(self.some_integer)); + } + } +} diff --git a/docs/sdk/src/reference_docs/chain_spec_runtime/src/presets.rs b/docs/sdk/src/reference_docs/chain_spec_runtime/src/presets.rs new file mode 100644 index 000000000000..4bc614870e65 --- /dev/null +++ b/docs/sdk/src/reference_docs/chain_spec_runtime/src/presets.rs @@ -0,0 +1,95 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Presets for demo runtime. + +use crate::pallets::{FooEnum, SomeFooData1, SomeFooData2}; +use serde_json::{json, to_string, Value}; +use sp_application_crypto::Ss58Codec; +use sp_keyring::AccountKeyring; +use sp_std::vec; + +pub const PRESET_1: &str = "preset_1"; +pub const PRESET_2: &str = "preset_2"; +pub const PRESET_3: &str = "preset_3"; + +#[docify::export] +fn preset_1() -> Value { + serde_json::json!({ + "bar": { + "initialAccount": "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL", + }, + "foo": { + "someEnum": { + "Data2": { + "v": "0x0c0f" + } + }, + "someInteger": 100 + }, + }) +} + +#[docify::export] +fn preset_2() -> Value { + serde_json::json!({ + "bar": { + "initialAccount": AccountKeyring::Ferdie.public().to_ss58check(), + }, + "foo": { + "someEnum": FooEnum::Data2(SomeFooData2 { v: vec![12,16] }), + "someInteger": 200 + }, + }) +} + +#[docify::export] +fn preset_3() -> Value { + json!({ + "bar": { + "initialAccount": AccountKeyring::Alice.public().to_ss58check(), + }, + "foo": { + "someEnum": FooEnum::Data1( + SomeFooData1 { + a: 12, + b: 16 + } + ), + "someInteger": 300 + }, + }) +} + +/// Provides a json representation of preset identified by given `id`. +/// +/// If no preset with given `id` exits `None` is returned. +#[docify::export] +pub fn get_builtin_preset(id: &sp_genesis_builder::PresetId) -> Option> { + let preset = match id.try_into() { + Ok(PRESET_1) => preset_1(), + Ok(PRESET_2) => preset_2(), + Ok(PRESET_3) => preset_3(), + _ => return None, + }; + + Some( + to_string(&preset) + .expect("serialization to json is expected to work. qed.") + .into_bytes(), + ) +} diff --git a/docs/sdk/src/reference_docs/chain_spec_runtime/src/runtime.rs b/docs/sdk/src/reference_docs/chain_spec_runtime/src/runtime.rs new file mode 100644 index 000000000000..da91e76e7005 --- /dev/null +++ b/docs/sdk/src/reference_docs/chain_spec_runtime/src/runtime.rs @@ -0,0 +1,120 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! A minimal runtime that shows runtime genesis state. + +// Make the WASM binary available. +#[cfg(feature = "std")] +include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); + +use crate::{ + pallets::{pallet_bar, pallet_foo}, + presets::*, +}; +use frame::{ + deps::frame_support::{ + genesis_builder_helper::{build_state, get_preset}, + runtime, + }, + prelude::*, + runtime::{ + apis::{self, impl_runtime_apis, ExtrinsicInclusionMode}, + prelude::*, + }, +}; +use sp_genesis_builder::PresetId; + +/// The runtime version. +#[runtime_version] +pub const VERSION: RuntimeVersion = RuntimeVersion { + spec_name: create_runtime_str!("minimal-template-runtime"), + impl_name: create_runtime_str!("minimal-template-runtime"), + authoring_version: 1, + spec_version: 0, + impl_version: 1, + apis: RUNTIME_API_VERSIONS, + transaction_version: 1, + state_version: 1, +}; + +/// The signed extensions that are added to the runtime. +type SignedExtra = (); + +// Composes the runtime by adding all the used pallets and deriving necessary types. +#[runtime] +mod runtime { + /// The main runtime type. + #[runtime::runtime] + #[runtime::derive(RuntimeCall, RuntimeEvent, RuntimeError, RuntimeOrigin, RuntimeTask)] + pub struct Runtime; + + /// Mandatory system pallet that should always be included in a FRAME runtime. + #[runtime::pallet_index(0)] + pub type System = frame_system; + + /// Sample pallet 1 + #[runtime::pallet_index(1)] + pub type Bar = pallet_bar; + + /// Sample pallet 2 + #[runtime::pallet_index(2)] + pub type Foo = pallet_foo; +} + +parameter_types! { + pub const Version: RuntimeVersion = VERSION; +} + +/// Implements the types required for the system pallet. +#[derive_impl(frame_system::config_preludes::SolochainDefaultConfig)] +impl frame_system::Config for Runtime { + type Block = Block; + type Version = Version; +} + +impl pallet_bar::Config for Runtime {} +impl pallet_foo::Config for Runtime {} + +type Block = frame::runtime::types_common::BlockOf; +type Header = HeaderFor; + +#[docify::export(runtime_impl)] +impl_runtime_apis! { + impl sp_genesis_builder::GenesisBuilder for Runtime { + fn build_state(config: Vec) -> sp_genesis_builder::Result { + build_state::(config) + } + + fn get_preset(id: &Option) -> Option> { + get_preset::(id, get_builtin_preset) + } + + fn preset_names() -> Vec { + vec![ + PresetId::from(PRESET_1), + PresetId::from(PRESET_2), + PresetId::from(PRESET_3) + ] + } + } + + impl apis::Core for Runtime { + fn version() -> RuntimeVersion { VERSION } + fn execute_block(_: Block) { } + fn initialize_block(_: &Header) -> ExtrinsicInclusionMode { ExtrinsicInclusionMode::default() } + } +} From 2c122e2346404b89412d0b90cc16f18026177d0d Mon Sep 17 00:00:00 2001 From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Date: Mon, 3 Jun 2024 14:23:02 +0200 Subject: [PATCH 02/15] chain-spec: added docs for genesis config, genesis_build and presets --- Cargo.lock | 11 +- docs/sdk/Cargo.toml | 3 +- .../src/reference_docs/chain_spec_genesis.rs | 90 ++++++++++---- .../chain_spec_runtime/Cargo.toml | 3 +- .../chain_spec_runtime/src/pallets.rs | 4 +- .../chain_spec_runtime/src/presets.rs | 19 ++- .../chain_spec_runtime/src/runtime.rs | 3 +- .../tests/chain_spec_builder_tests.rs | 114 ++++++++++++++++++ .../bin/utils/chain-spec-builder/Cargo.toml | 2 +- .../bin/utils/chain-spec-builder/bin/main.rs | 2 +- substrate/client/chain-spec/src/lib.rs | 12 +- 11 files changed, 225 insertions(+), 38 deletions(-) create mode 100644 docs/sdk/src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs diff --git a/Cargo.lock b/Cargo.lock index 67d881a9a164..6428994b15fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2514,6 +2514,7 @@ name = "chain-spec-guide-runtime" version = "0.0.0" dependencies = [ "docify", + "indoc", "pallet-balances", "pallet-sudo", "pallet-timestamp", @@ -6910,6 +6911,12 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "indoc" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" + [[package]] name = "inout" version = "0.1.3" @@ -14296,6 +14303,7 @@ dependencies = [ "parity-scale-codec", "polkadot-sdk", "polkadot-sdk-frame", + "sc-chain-spec", "sc-cli", "sc-client-db", "sc-consensus-aura", @@ -14314,6 +14322,7 @@ dependencies = [ "sp-api", "sp-arithmetic", "sp-core", + "sp-genesis-builder", "sp-io", "sp-keyring", "sp-offchain", @@ -20561,7 +20570,7 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "staging-chain-spec-builder" -version = "1.6.0" +version = "1.6.1" dependencies = [ "clap 4.5.3", "log", diff --git a/docs/sdk/Cargo.toml b/docs/sdk/Cargo.toml index 2cb8d49fc5a7..a0953896356d 100644 --- a/docs/sdk/Cargo.toml +++ b/docs/sdk/Cargo.toml @@ -55,6 +55,7 @@ sc-consensus-manual-seal = { path = "../../substrate/client/consensus/manual-sea sc-consensus-pow = { path = "../../substrate/client/consensus/pow" } sc-executor = { path = "../../substrate/client/executor" } sc-service = { path = "../../substrate/client/service" } +sc-chain-spec = { path = "../../substrate/client/chain-spec" } substrate-wasm-builder = { path = "../../substrate/utils/wasm-builder" } @@ -90,6 +91,7 @@ sp-core = { path = "../../substrate/primitives/core" } sp-keyring = { path = "../../substrate/primitives/keyring" } sp-runtime = { path = "../../substrate/primitives/runtime" } sp-arithmetic = { path = "../../substrate/primitives/arithmetic" } +sp-genesis-builder = { path = "../../substrate/primitives/genesis-builder" } # Misc pallet dependencies pallet-referenda = { path = "../../substrate/frame/referenda" } @@ -105,4 +107,3 @@ xcm-docs = { path = "../../polkadot/xcm/docs" } # runtime guides chain-spec-guide-runtime = { path = "./src/reference_docs/chain_spec_runtime" } - diff --git a/docs/sdk/src/reference_docs/chain_spec_genesis.rs b/docs/sdk/src/reference_docs/chain_spec_genesis.rs index 31bab1851743..b29081e1b2ce 100644 --- a/docs/sdk/src/reference_docs/chain_spec_genesis.rs +++ b/docs/sdk/src/reference_docs/chain_spec_genesis.rs @@ -2,35 +2,58 @@ //! //! # What is chain-spec. //! -//! # What is genesis state and how to build it. +//! Chain specicfication file defines the set of properties that are required to run the node as +//! part of the chain. The chain specification consists of two main parts: +//! - initial state of the runtime, +//! - network / logical properties of the chain, the most important property being the list of +//! bootnodes. +//! +//! This document describes how initial state is handled in pallets and runtime, and how to interact +//! with runtime in order to build genesis sate. +//! +//! For more informations on chain specification and its properties refer to +//! [`sc_chain_spec#from-initial-state-to-raw-genesis`]. +//! +//! The intial genesis state can be prvovided in the following formats: +//! - full, +//! - patch, +//! - raw, +//! +//! Each of the format is explained in [_chain-spec-format_][`sc_chain_spec#chain-spec-formats`]. +//! The //! //! ## `GenesisConfig` for `pallet` //! -//! pallet_bar GenesisConfig: +//! Every frame pallet may have its initial state which is defined by the `GenesisConfig` internal +//! struct. It is a regular rust struct, annotated with [`pallet::genesis_config`] attribute. #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/pallets.rs", pallet_bar_GenesisConfig)] -//! pallet_bar: + +//! The struct shall be defined within the pallet `mod`, as in the following code: #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/pallets.rs", pallet_bar)] -//! pallet_bar: +//! The initial state conveyed in `GenesisConfig` struct is transformed into state storage items by +//! means of the [`BuildGenesisConfig`] trait wich shall be implemented for pallet's `GenesisConfig` +//! struct. The [`pallet::genesis_build`] attribute shall be attached to the `impl` block: #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/pallets.rs", pallet_bar_build)] -//! GenesisConfig may contain also more complicated types including nested structs or enums. -//! GenesisConfig for `pallet_foo +//! GenesisConfig may contain also more complicated types including nested structs or enums, as in +//! the example for `pallet_foo`: #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/pallets.rs", pallet_foo_GenesisConfig)] -//! pallet_foo build function. It uses fields of GenesisConfig to generate storage. Please not that -//! fields of `GenesisConfig` may not be directly mapped to storage items: +//! Please not that fields of `GenesisConfig` may not be directly mapped to storage items: #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/pallets.rs", pallet_foo_build)] -//! The runtime genesis config struct consists of configs for every pallet. For sample runtime used -//! in this guide, it consists of `SystemConfig`, `BarConfig` and `FooConfig`. This structure was -//! automatically generated by macro and it can be sneaked peaked here: [`RuntimeGenesisConfig`]. - +//! ## `GenesisConfig` for `runtimes` +//! The runtime genesis config struct consists of configs for every pallet. For [_demonstration +//! runtime_][`chain_spec_guide_runtime`] used in this guide, it consists of `SystemConfig`, +//! `BarConfig` and `FooConfig`. This structure was automatically generated by macro and it can be +//! sneaked peaked here: [`RuntimeGenesisConfig`]. +//! //! Runtime exposes a dedicated runtime API for interacting with its genesis config: -//! [`sp-genesis-builder::GenesisBuilder`]. Implementation shall be provided withing -//! `impl_runtime_apis` macro, typically making use of some helpers provided: [`build_state`], -//! ['get_preset']. -//! Typically implementation of [`sp-genesis-builder::GenesisBuilder`] is as follows: +//! [`sp_genesis_builder::GenesisBuilder`]. Implementation shall be provided within +//! [`sp_api::impl_runtime_apis`] macro, typically making use of some helpers provided: +//! [`build_state`], [`get_preset`]. +//! Usual implementation of [`sp_genesis_builder::GenesisBuilder`] is as follows: #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/runtime.rs", runtime_impl)] //! Please note that two functions are customized `preset_names` and `get_preset`. The first one @@ -38,6 +61,12 @@ //! function that maps the name to preset: [`chain_spec_guide_runtime::presets::get_builtin_preset`] #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", get_builtin_preset)] +//! ## Genesis state presets for `runtime` +//! Rutime may provide many flavors of initial genesis state. This may be useful for predefined +//! testing networks, local development, or CI integration tests. Predefined genesis state may +//! contain a list of pre-funded accounts, predefined authorithies for consensue, sudo key and many +//! others useful for testing. + //! Internally presets can be provided in a number of ways: //! - json in string form: #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", preset_1)] @@ -46,20 +75,33 @@ #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", preset_3)] //! It is worth noting that preset does not have to be the full `RuntimeGenesisConfig`, in that //! sense that it does not have to contain all the keys of the struct. The preset is acutally a json -//! patch that will be merged with default value of `RuntimeGenesisConfig`. +//! patch that will be merged with default value of `RuntimeGenesisConfig`. Following example +//! illustrates runtime genesis config patch: +#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", preset_4)] //! # Interacting with the tool //! -//! # JSON representation -//! The json representation of runtime genesis config: -//! -//! # Raw genesis storage. -//! The json representation of runtime genesis storage: -//! What are hashes? How to decipher them? -//! +//! The [`chain_spec_builder`] util allows to interact with runtime in order to list or display +//! presets and build the chain specification file. It is possible to use the tool with the +//! [_demonstration runtime_][`chain_spec_guide_runtime`]. To build required packages just run the +//! following command: +//! ``` +//! cargo build -p staging-chain-spec-builder -p chain-spec-guide-runtime --release +//! ``` +//! Here are some examples in the form of rust tests: +//! ### Listing available presets names: +#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs", list_presets)] +//! ### Displaying preset with given name +#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs", get_preset)] +//! ### Building chain-spec using given preset +#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs", generate_chain_spec)] //! //! [`RuntimeGenesisConfig`]: //! chain_spec_guide_runtime::runtime::RuntimeGenesisConfig //! [`impl_runtime_apis`]: frame::runtime::prelude::impl_runtime_apis //! [`build_state`]: frame_support::genesis_builder_helper::build_state //! [`get_preset`]: frame_support::genesis_builder_helper::get_preset +//! [`pallet::genesis_build`]: frame_support::pallet_macros::genesis_build +//! [`pallet::genesis_config`]: frame_support::pallet_macros::genesis_config +//! [`BuildGenesisConfig`]: frame_support::traits::BuildGenesisConfig +//! [`chain_spec_builder`]: ../../../staging_chain_spec_builder/index.html diff --git a/docs/sdk/src/reference_docs/chain_spec_runtime/Cargo.toml b/docs/sdk/src/reference_docs/chain_spec_runtime/Cargo.toml index 36ebc13c9767..13c5ba8933d7 100644 --- a/docs/sdk/src/reference_docs/chain_spec_runtime/Cargo.toml +++ b/docs/sdk/src/reference_docs/chain_spec_runtime/Cargo.toml @@ -14,7 +14,7 @@ docify = "0.2.8" parity-scale-codec = { version = "3.6.12", default-features = false } scale-info = { version = "2.6.0", default-features = false } serde = { workspace = true, default-features = false, feature = ["derive"] } -serde_json = { workspace = true } +serde_json.workspace = true # this is a frame-based runtime, thus importing `frame` with runtime feature enabled. frame = { package = "polkadot-sdk-frame", path = "../../../../../substrate/frame", default-features = false, features = [ @@ -36,6 +36,7 @@ sp-core = { path = "../../../../../substrate/primitives/core", default-features sp-std = { path = "../../../../../substrate/primitives/std", default-features = false } sp-keyring = { path = "../../../../../substrate/primitives/keyring", default-features = false } sp-application-crypto = { path = "../../../../../substrate/primitives/application-crypto", default-features = false, features = ["serde"] } +indoc = "2.0.5" [build-dependencies] substrate-wasm-builder = { path = "../../../../../substrate/utils/wasm-builder", optional = true } diff --git a/docs/sdk/src/reference_docs/chain_spec_runtime/src/pallets.rs b/docs/sdk/src/reference_docs/chain_spec_runtime/src/pallets.rs index 5d0f644c7a59..ff931b60f5cc 100644 --- a/docs/sdk/src/reference_docs/chain_spec_runtime/src/pallets.rs +++ b/docs/sdk/src/reference_docs/chain_spec_runtime/src/pallets.rs @@ -39,8 +39,8 @@ pub mod pallet_bar { } #[pallet::genesis_build] + #[docify::export(pallet_bar_build)] impl BuildGenesisConfig for GenesisConfig { - #[docify::export(pallet_bar_build)] fn build(&self) { InitialAccount::::set(self.initial_account.clone()); } @@ -94,8 +94,8 @@ pub mod pallet_foo { } #[pallet::genesis_build] + #[docify::export(pallet_foo_build)] impl BuildGenesisConfig for GenesisConfig { - #[docify::export(pallet_foo_build)] fn build(&self) { let v: u64 = match &self.some_enum { FooEnum::Data0 => 0, diff --git a/docs/sdk/src/reference_docs/chain_spec_runtime/src/presets.rs b/docs/sdk/src/reference_docs/chain_spec_runtime/src/presets.rs index 4bc614870e65..18b06ddf1f1c 100644 --- a/docs/sdk/src/reference_docs/chain_spec_runtime/src/presets.rs +++ b/docs/sdk/src/reference_docs/chain_spec_runtime/src/presets.rs @@ -26,10 +26,11 @@ use sp_std::vec; pub const PRESET_1: &str = "preset_1"; pub const PRESET_2: &str = "preset_2"; pub const PRESET_3: &str = "preset_3"; +pub const PRESET_4: &str = "preset_4"; #[docify::export] fn preset_1() -> Value { - serde_json::json!({ + json!({ "bar": { "initialAccount": "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL", }, @@ -46,7 +47,7 @@ fn preset_1() -> Value { #[docify::export] fn preset_2() -> Value { - serde_json::json!({ + json!({ "bar": { "initialAccount": AccountKeyring::Ferdie.public().to_ss58check(), }, @@ -75,6 +76,19 @@ fn preset_3() -> Value { }) } +#[docify::export] +fn preset_4() -> Value { + json!({ + "foo": { + "someEnum": { + "Data2": { + "v": "0x0c0f" + } + }, + }, + }) +} + /// Provides a json representation of preset identified by given `id`. /// /// If no preset with given `id` exits `None` is returned. @@ -84,6 +98,7 @@ pub fn get_builtin_preset(id: &sp_genesis_builder::PresetId) -> Option preset_1(), Ok(PRESET_2) => preset_2(), Ok(PRESET_3) => preset_3(), + Ok(PRESET_4) => preset_4(), _ => return None, }; diff --git a/docs/sdk/src/reference_docs/chain_spec_runtime/src/runtime.rs b/docs/sdk/src/reference_docs/chain_spec_runtime/src/runtime.rs index da91e76e7005..2c11a25e8c2f 100644 --- a/docs/sdk/src/reference_docs/chain_spec_runtime/src/runtime.rs +++ b/docs/sdk/src/reference_docs/chain_spec_runtime/src/runtime.rs @@ -107,7 +107,8 @@ impl_runtime_apis! { vec![ PresetId::from(PRESET_1), PresetId::from(PRESET_2), - PresetId::from(PRESET_3) + PresetId::from(PRESET_3), + PresetId::from(PRESET_4) ] } } diff --git a/docs/sdk/src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs b/docs/sdk/src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs new file mode 100644 index 000000000000..7359cce6d039 --- /dev/null +++ b/docs/sdk/src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs @@ -0,0 +1,114 @@ +use serde_json::{json, Value}; +use std::{process::Command, str}; + +const WASM_FILE_PATH: &str = + "../../../../../target/release/wbuild/chain-spec-guide-runtime/chain_spec_guide_runtime.wasm"; + +const CHAIN_SPEC_BUILDER_PATH: &str = "../../../../../target/release/chain-spec-builder"; + +#[test] +#[docify::export] +fn list_presets() { + let output = Command::new(CHAIN_SPEC_BUILDER_PATH) + .arg("list-presets") + .arg("-r") + .arg(WASM_FILE_PATH) + .output() + .expect("Failed to execute command"); + + let output: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap(); + + let expected_output = json!({ + "presets":[ + "preset_1", + "preset_2", + "preset_3", + "preset_4" + ] + }); + assert_eq!(output, expected_output, "Output did not match expected"); +} + +#[test] +#[docify::export] +fn get_preset() { + let output = Command::new(CHAIN_SPEC_BUILDER_PATH) + .arg("display-preset") + .arg("-r") + .arg(WASM_FILE_PATH) + .arg("-p") + .arg("preset_1") + .output() + .expect("Failed to execute command"); + + let output: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap(); + + //note: copy of chain_spec_guide_runtime::preset_1 + let expected_output = json!({ + "bar": { + "initialAccount": "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL", + }, + "foo": { + "someEnum": { + "Data2": { + "v": "0x0c0f" + } + }, + "someInteger": 100 + }, + }); + assert_eq!(output, expected_output, "Output did not match expected"); +} + +#[test] +#[docify::export] +fn generate_chain_spec() { + let output = Command::new(CHAIN_SPEC_BUILDER_PATH) + .arg("-c") + .arg("/dev/stdout") + .arg("create") + .arg("-r") + .arg(WASM_FILE_PATH) + .arg("named-preset") + .arg("preset_1") + .output() + .expect("Failed to execute command"); + + let mut output: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap(); + + //remove code field for better readability + if let Some(code) = output["genesis"]["runtimeGenesis"].as_object_mut().unwrap().get_mut("code") + { + *code = Value::String("0x123".to_string()); + } + + let expected_output = json!({ + "name": "Custom", + "id": "custom", + "chainType": "Live", + "bootNodes": [], + "telemetryEndpoints": null, + "protocolId": null, + "properties": null, + "codeSubstitutes": {}, + "genesis": { + "runtimeGenesis": { + "code": "0x123", + "patch": { + "bar": { + "initialAccount": "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL" + }, + "foo": { + "someEnum": { + "Data2": { + "v": "0x0c0f" + } + }, + "someInteger": 100 + } + } + } + } + }); + assert_eq!(output, expected_output, "Output did not match expected"); +} diff --git a/substrate/bin/utils/chain-spec-builder/Cargo.toml b/substrate/bin/utils/chain-spec-builder/Cargo.toml index de06bbb3fff6..88585649acfe 100644 --- a/substrate/bin/utils/chain-spec-builder/Cargo.toml +++ b/substrate/bin/utils/chain-spec-builder/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "staging-chain-spec-builder" -version = "1.6.0" +version = "1.6.1" authors.workspace = true edition.workspace = true build = "build.rs" diff --git a/substrate/bin/utils/chain-spec-builder/bin/main.rs b/substrate/bin/utils/chain-spec-builder/bin/main.rs index 8d6425a46c77..18da3c30691b 100644 --- a/substrate/bin/utils/chain-spec-builder/bin/main.rs +++ b/substrate/bin/utils/chain-spec-builder/bin/main.rs @@ -99,7 +99,7 @@ fn inner_main() -> Result<(), String> { ) }) .collect(); - println!("{presets:#?}"); + println!("{}", serde_json::json!({"presets":presets}).to_string()); }, ChainSpecBuilderCmd::DisplayPreset(DisplayPresetCmd { runtime_wasm_path, preset_name }) => { let code = fs::read(runtime_wasm_path.as_path()) diff --git a/substrate/client/chain-spec/src/lib.rs b/substrate/client/chain-spec/src/lib.rs index 653c3c618b77..b59ad68610ec 100644 --- a/substrate/client/chain-spec/src/lib.rs +++ b/substrate/client/chain-spec/src/lib.rs @@ -123,7 +123,10 @@ //! As the compiled WASM blob of the runtime code is stored in the chain's state, the initial //! runtime must also be provided within the chain specification. //! -//! In essence, the most important formats of genesis initial state are: +//! # `chain-spec` formats +//! +//! In essence, the most important formats of genesis initial state in chain specification files +//! are: //! //! //! @@ -135,14 +138,14 @@ //! //! //! //! //! From 61e95a0a25a4200a352d1891192b109eeddf8405 Mon Sep 17 00:00:00 2001 From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Date: Mon, 3 Jun 2024 14:24:53 +0200 Subject: [PATCH 03/15] todo removed --- docs/sdk/src/reference_docs/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/sdk/src/reference_docs/mod.rs b/docs/sdk/src/reference_docs/mod.rs index 6fa25bf36e1b..e50690b50212 100644 --- a/docs/sdk/src/reference_docs/mod.rs +++ b/docs/sdk/src/reference_docs/mod.rs @@ -76,7 +76,6 @@ pub mod frame_benchmarking_weight; pub mod frame_tokens; /// Learn about chain specification file and the genesis state of the blockchain. -// TODO: @michalkucharczyk https://github.com/paritytech/polkadot-sdk-docs/issues/51 pub mod chain_spec_genesis; /// Learn about all the memory limitations of the WASM runtime when it comes to memory usage. From ec44a9c87275c23b15733a7cc659ef4b97b479f6 Mon Sep 17 00:00:00 2001 From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Date: Mon, 3 Jun 2024 14:30:45 +0200 Subject: [PATCH 04/15] taplo happy now --- .../chain_spec_runtime/Cargo.toml | 42 ++++++++++--------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/docs/sdk/src/reference_docs/chain_spec_runtime/Cargo.toml b/docs/sdk/src/reference_docs/chain_spec_runtime/Cargo.toml index 13c5ba8933d7..dcd7d94be205 100644 --- a/docs/sdk/src/reference_docs/chain_spec_runtime/Cargo.toml +++ b/docs/sdk/src/reference_docs/chain_spec_runtime/Cargo.toml @@ -18,8 +18,8 @@ serde_json.workspace = true # this is a frame-based runtime, thus importing `frame` with runtime feature enabled. frame = { package = "polkadot-sdk-frame", path = "../../../../../substrate/frame", default-features = false, features = [ - "experimental", - "runtime", + "experimental", + "runtime", ] } # pallets that we want to use @@ -31,11 +31,15 @@ pallet-transaction-payment-rpc-runtime-api = { path = "../../../../../substrate/ # genesis builder that allows us to interact with runtime genesis config sp-genesis-builder = { path = "../../../../../substrate/primitives/genesis-builder", default-features = false } -sp-runtime = { path = "../../../../../substrate/primitives/runtime", default-features = false, features = ["serde"] } +sp-runtime = { path = "../../../../../substrate/primitives/runtime", default-features = false, features = [ + "serde", +] } sp-core = { path = "../../../../../substrate/primitives/core", default-features = false } sp-std = { path = "../../../../../substrate/primitives/std", default-features = false } sp-keyring = { path = "../../../../../substrate/primitives/keyring", default-features = false } -sp-application-crypto = { path = "../../../../../substrate/primitives/application-crypto", default-features = false, features = ["serde"] } +sp-application-crypto = { path = "../../../../../substrate/primitives/application-crypto", default-features = false, features = [ + "serde", +] } indoc = "2.0.5" [build-dependencies] @@ -44,23 +48,23 @@ substrate-wasm-builder = { path = "../../../../../substrate/utils/wasm-builder", [features] default = ["std"] std = [ - "parity-scale-codec/std", - "scale-info/std", + "parity-scale-codec/std", + "scale-info/std", - "frame/std", + "frame/std", - "pallet-balances/std", - "pallet-sudo/std", - "pallet-timestamp/std", - "pallet-transaction-payment-rpc-runtime-api/std", - "pallet-transaction-payment/std", + "pallet-balances/std", + "pallet-sudo/std", + "pallet-timestamp/std", + "pallet-transaction-payment-rpc-runtime-api/std", + "pallet-transaction-payment/std", - "sp-genesis-builder/std", - "sp-runtime/std", - "sp-core/std", - "sp-std/std", - "sp-keyring/std", - "sp-application-crypto/std", + "sp-genesis-builder/std", + "sp-runtime/std", + "sp-core/std", + "sp-std/std", + "sp-keyring/std", + "sp-application-crypto/std", - "substrate-wasm-builder", + "substrate-wasm-builder", ] From 712f187970724f8a2365e5355c7ad3b0739bf690 Mon Sep 17 00:00:00 2001 From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Date: Mon, 3 Jun 2024 15:47:08 +0200 Subject: [PATCH 05/15] workaround for lack of dev-deps to binaries --- Cargo.lock | 9 +-------- .../chain_spec_runtime/Cargo.toml | 13 +++++------- .../tests/chain_spec_builder_tests.rs | 20 ++++++++++++++++--- 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6428994b15fd..056fe36c9c7e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2514,7 +2514,6 @@ name = "chain-spec-guide-runtime" version = "0.0.0" dependencies = [ "docify", - "indoc", "pallet-balances", "pallet-sudo", "pallet-timestamp", @@ -2523,7 +2522,6 @@ dependencies = [ "parity-scale-codec", "polkadot-sdk-frame", "scale-info", - "serde", "serde_json", "sp-application-crypto", "sp-core", @@ -2531,6 +2529,7 @@ dependencies = [ "sp-keyring", "sp-runtime", "sp-std 14.0.0", + "staging-chain-spec-builder", "substrate-wasm-builder", ] @@ -6911,12 +6910,6 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "indoc" -version = "2.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" - [[package]] name = "inout" version = "0.1.3" diff --git a/docs/sdk/src/reference_docs/chain_spec_runtime/Cargo.toml b/docs/sdk/src/reference_docs/chain_spec_runtime/Cargo.toml index dcd7d94be205..0d8e97f6ea52 100644 --- a/docs/sdk/src/reference_docs/chain_spec_runtime/Cargo.toml +++ b/docs/sdk/src/reference_docs/chain_spec_runtime/Cargo.toml @@ -13,7 +13,6 @@ publish = false docify = "0.2.8" parity-scale-codec = { version = "3.6.12", default-features = false } scale-info = { version = "2.6.0", default-features = false } -serde = { workspace = true, default-features = false, feature = ["derive"] } serde_json.workspace = true # this is a frame-based runtime, thus importing `frame` with runtime feature enabled. @@ -31,20 +30,18 @@ pallet-transaction-payment-rpc-runtime-api = { path = "../../../../../substrate/ # genesis builder that allows us to interact with runtime genesis config sp-genesis-builder = { path = "../../../../../substrate/primitives/genesis-builder", default-features = false } -sp-runtime = { path = "../../../../../substrate/primitives/runtime", default-features = false, features = [ - "serde", -] } +sp-runtime = { path = "../../../../../substrate/primitives/runtime", default-features = false, features = [ "serde" ] } sp-core = { path = "../../../../../substrate/primitives/core", default-features = false } sp-std = { path = "../../../../../substrate/primitives/std", default-features = false } sp-keyring = { path = "../../../../../substrate/primitives/keyring", default-features = false } -sp-application-crypto = { path = "../../../../../substrate/primitives/application-crypto", default-features = false, features = [ - "serde", -] } -indoc = "2.0.5" +sp-application-crypto = { path = "../../../../../substrate/primitives/application-crypto", default-features = false, features = [ "serde" ] } [build-dependencies] substrate-wasm-builder = { path = "../../../../../substrate/utils/wasm-builder", optional = true } +[dev-dependencies] +chain-spec-builder = { package = "staging-chain-spec-builder", path = "../../../../../substrate/bin/utils/chain-spec-builder" } + [features] default = ["std"] std = [ diff --git a/docs/sdk/src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs b/docs/sdk/src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs index 7359cce6d039..06190c55de01 100644 --- a/docs/sdk/src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs +++ b/docs/sdk/src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs @@ -6,10 +6,24 @@ const WASM_FILE_PATH: &str = const CHAIN_SPEC_BUILDER_PATH: &str = "../../../../../target/release/chain-spec-builder"; +fn get_chain_spec_builder_path() -> &'static str { + // dev-dependencies do not build binary. So let's do shitty work-around here: + let _ = std::process::Command::new("cargo") + .arg("build") + .arg("--release") + .arg("-p") + .arg("staging-chain-spec-builder") + .arg("--bin") + .arg("chain-spec-builder") + .status() + .expect("Failed to execute command"); + CHAIN_SPEC_BUILDER_PATH +} + #[test] #[docify::export] fn list_presets() { - let output = Command::new(CHAIN_SPEC_BUILDER_PATH) + let output = Command::new(get_chain_spec_builder_path()) .arg("list-presets") .arg("-r") .arg(WASM_FILE_PATH) @@ -32,7 +46,7 @@ fn list_presets() { #[test] #[docify::export] fn get_preset() { - let output = Command::new(CHAIN_SPEC_BUILDER_PATH) + let output = Command::new(get_chain_spec_builder_path()) .arg("display-preset") .arg("-r") .arg(WASM_FILE_PATH) @@ -63,7 +77,7 @@ fn get_preset() { #[test] #[docify::export] fn generate_chain_spec() { - let output = Command::new(CHAIN_SPEC_BUILDER_PATH) + let output = Command::new(get_chain_spec_builder_path()) .arg("-c") .arg("/dev/stdout") .arg("create") From 1aea1638bff7317afed99d3528053650fedc3974 Mon Sep 17 00:00:00 2001 From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Date: Mon, 3 Jun 2024 15:51:51 +0200 Subject: [PATCH 06/15] Apply suggestions from code review Co-authored-by: Alexandru Vasile <60601340+lexnv@users.noreply.github.com> --- docs/sdk/src/reference_docs/chain_spec_genesis.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/sdk/src/reference_docs/chain_spec_genesis.rs b/docs/sdk/src/reference_docs/chain_spec_genesis.rs index b29081e1b2ce..55ccc574dff6 100644 --- a/docs/sdk/src/reference_docs/chain_spec_genesis.rs +++ b/docs/sdk/src/reference_docs/chain_spec_genesis.rs @@ -14,13 +14,13 @@ //! For more informations on chain specification and its properties refer to //! [`sc_chain_spec#from-initial-state-to-raw-genesis`]. //! -//! The intial genesis state can be prvovided in the following formats: +//! The initial genesis state can be provided in the following formats: //! - full, //! - patch, //! - raw, //! //! Each of the format is explained in [_chain-spec-format_][`sc_chain_spec#chain-spec-formats`]. -//! The +//! //! //! ## `GenesisConfig` for `pallet` //! @@ -40,10 +40,11 @@ //! the example for `pallet_foo`: #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/pallets.rs", pallet_foo_GenesisConfig)] -//! Please not that fields of `GenesisConfig` may not be directly mapped to storage items: +//! Please note that fields of `GenesisConfig` may not be directly mapped to storage items: #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/pallets.rs", pallet_foo_build)] //! ## `GenesisConfig` for `runtimes` + //! The runtime genesis config struct consists of configs for every pallet. For [_demonstration //! runtime_][`chain_spec_guide_runtime`] used in this guide, it consists of `SystemConfig`, //! `BarConfig` and `FooConfig`. This structure was automatically generated by macro and it can be @@ -62,6 +63,7 @@ #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", get_builtin_preset)] //! ## Genesis state presets for `runtime` + //! Rutime may provide many flavors of initial genesis state. This may be useful for predefined //! testing networks, local development, or CI integration tests. Predefined genesis state may //! contain a list of pre-funded accounts, predefined authorithies for consensue, sudo key and many From 9c45864e19a220aa1ff576d774ad56e7fd9f4811 Mon Sep 17 00:00:00 2001 From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Date: Mon, 3 Jun 2024 15:55:53 +0200 Subject: [PATCH 07/15] deps fix --- Cargo.lock | 1 + docs/sdk/src/reference_docs/chain_spec_runtime/Cargo.toml | 3 ++- .../chain_spec_runtime/tests/chain_spec_builder_tests.rs | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 056fe36c9c7e..76639be1099d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2522,6 +2522,7 @@ dependencies = [ "parity-scale-codec", "polkadot-sdk-frame", "scale-info", + "serde", "serde_json", "sp-application-crypto", "sp-core", diff --git a/docs/sdk/src/reference_docs/chain_spec_runtime/Cargo.toml b/docs/sdk/src/reference_docs/chain_spec_runtime/Cargo.toml index 0d8e97f6ea52..3623a9cd0fb3 100644 --- a/docs/sdk/src/reference_docs/chain_spec_runtime/Cargo.toml +++ b/docs/sdk/src/reference_docs/chain_spec_runtime/Cargo.toml @@ -13,7 +13,8 @@ publish = false docify = "0.2.8" parity-scale-codec = { version = "3.6.12", default-features = false } scale-info = { version = "2.6.0", default-features = false } -serde_json.workspace = true +serde = { workspace = true, default-features = false } +serde_json = { workspace = true } # this is a frame-based runtime, thus importing `frame` with runtime feature enabled. frame = { package = "polkadot-sdk-frame", path = "../../../../../substrate/frame", default-features = false, features = [ diff --git a/docs/sdk/src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs b/docs/sdk/src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs index 06190c55de01..af133e018148 100644 --- a/docs/sdk/src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs +++ b/docs/sdk/src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs @@ -7,7 +7,7 @@ const WASM_FILE_PATH: &str = const CHAIN_SPEC_BUILDER_PATH: &str = "../../../../../target/release/chain-spec-builder"; fn get_chain_spec_builder_path() -> &'static str { - // dev-dependencies do not build binary. So let's do shitty work-around here: + // dev-dependencies do not build binary. So let's do the naive work-around here: let _ = std::process::Command::new("cargo") .arg("build") .arg("--release") From 681f33ee67129c36c97cea1ac9ad92ed2d5c9357 Mon Sep 17 00:00:00 2001 From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Date: Mon, 3 Jun 2024 16:14:30 +0200 Subject: [PATCH 08/15] taplo fmt again --- .../chain_spec_runtime/Cargo.toml | 40 ++++++++++--------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/docs/sdk/src/reference_docs/chain_spec_runtime/Cargo.toml b/docs/sdk/src/reference_docs/chain_spec_runtime/Cargo.toml index 3623a9cd0fb3..7aa675112b43 100644 --- a/docs/sdk/src/reference_docs/chain_spec_runtime/Cargo.toml +++ b/docs/sdk/src/reference_docs/chain_spec_runtime/Cargo.toml @@ -18,8 +18,8 @@ serde_json = { workspace = true } # this is a frame-based runtime, thus importing `frame` with runtime feature enabled. frame = { package = "polkadot-sdk-frame", path = "../../../../../substrate/frame", default-features = false, features = [ - "experimental", - "runtime", + "experimental", + "runtime", ] } # pallets that we want to use @@ -31,11 +31,11 @@ pallet-transaction-payment-rpc-runtime-api = { path = "../../../../../substrate/ # genesis builder that allows us to interact with runtime genesis config sp-genesis-builder = { path = "../../../../../substrate/primitives/genesis-builder", default-features = false } -sp-runtime = { path = "../../../../../substrate/primitives/runtime", default-features = false, features = [ "serde" ] } +sp-runtime = { path = "../../../../../substrate/primitives/runtime", default-features = false, features = ["serde"] } sp-core = { path = "../../../../../substrate/primitives/core", default-features = false } sp-std = { path = "../../../../../substrate/primitives/std", default-features = false } sp-keyring = { path = "../../../../../substrate/primitives/keyring", default-features = false } -sp-application-crypto = { path = "../../../../../substrate/primitives/application-crypto", default-features = false, features = [ "serde" ] } +sp-application-crypto = { path = "../../../../../substrate/primitives/application-crypto", default-features = false, features = ["serde"] } [build-dependencies] substrate-wasm-builder = { path = "../../../../../substrate/utils/wasm-builder", optional = true } @@ -46,23 +46,25 @@ chain-spec-builder = { package = "staging-chain-spec-builder", path = "../../../ [features] default = ["std"] std = [ - "parity-scale-codec/std", - "scale-info/std", + "parity-scale-codec/std", + "scale-info/std", - "frame/std", + "frame/std", - "pallet-balances/std", - "pallet-sudo/std", - "pallet-timestamp/std", - "pallet-transaction-payment-rpc-runtime-api/std", - "pallet-transaction-payment/std", + "pallet-balances/std", + "pallet-sudo/std", + "pallet-timestamp/std", + "pallet-transaction-payment-rpc-runtime-api/std", + "pallet-transaction-payment/std", - "sp-genesis-builder/std", - "sp-runtime/std", - "sp-core/std", - "sp-std/std", - "sp-keyring/std", - "sp-application-crypto/std", + "sp-application-crypto/std", + "sp-core/std", + "sp-genesis-builder/std", + "sp-keyring/std", + "sp-runtime/std", + "sp-std/std", - "substrate-wasm-builder", + "serde/std", + "serde_json/std", + "substrate-wasm-builder", ] From 2d8d623ea8451f944276c5d76451a630c9e70ae3 Mon Sep 17 00:00:00 2001 From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Date: Mon, 3 Jun 2024 17:12:25 +0200 Subject: [PATCH 09/15] doc test fix --- docs/sdk/src/reference_docs/chain_spec_genesis.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/sdk/src/reference_docs/chain_spec_genesis.rs b/docs/sdk/src/reference_docs/chain_spec_genesis.rs index 55ccc574dff6..a2a9ad95727e 100644 --- a/docs/sdk/src/reference_docs/chain_spec_genesis.rs +++ b/docs/sdk/src/reference_docs/chain_spec_genesis.rs @@ -77,8 +77,9 @@ #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", preset_3)] //! It is worth noting that preset does not have to be the full `RuntimeGenesisConfig`, in that //! sense that it does not have to contain all the keys of the struct. The preset is acutally a json -//! patch that will be merged with default value of `RuntimeGenesisConfig`. Following example -//! illustrates runtime genesis config patch: +//! patch that will be merged with default value of `RuntimeGenesisConfig`. This approach should +//! simplify maintanance of builtin presets. Following example illustrates runtime genesis config +//! patch: #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", preset_4)] //! # Interacting with the tool @@ -87,7 +88,7 @@ //! presets and build the chain specification file. It is possible to use the tool with the //! [_demonstration runtime_][`chain_spec_guide_runtime`]. To build required packages just run the //! following command: -//! ``` +//! ```no_run //! cargo build -p staging-chain-spec-builder -p chain-spec-guide-runtime --release //! ``` //! Here are some examples in the form of rust tests: From 51cf9fe58469b4e1307717bf79f34b5ff2fb6aeb Mon Sep 17 00:00:00 2001 From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Date: Mon, 3 Jun 2024 21:42:20 +0200 Subject: [PATCH 10/15] rust doc again --- docs/sdk/src/reference_docs/chain_spec_genesis.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sdk/src/reference_docs/chain_spec_genesis.rs b/docs/sdk/src/reference_docs/chain_spec_genesis.rs index a2a9ad95727e..4c00faa46baa 100644 --- a/docs/sdk/src/reference_docs/chain_spec_genesis.rs +++ b/docs/sdk/src/reference_docs/chain_spec_genesis.rs @@ -88,7 +88,7 @@ //! presets and build the chain specification file. It is possible to use the tool with the //! [_demonstration runtime_][`chain_spec_guide_runtime`]. To build required packages just run the //! following command: -//! ```no_run +//! ```ignore //! cargo build -p staging-chain-spec-builder -p chain-spec-guide-runtime --release //! ``` //! Here are some examples in the form of rust tests: From e1da43b07839e5d6095fbd799feba335c4dc16ef Mon Sep 17 00:00:00 2001 From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Date: Tue, 4 Jun 2024 12:55:17 +0200 Subject: [PATCH 11/15] Apply suggestions from code review Co-authored-by: Sebastian Kunert --- .../src/reference_docs/chain_spec_genesis.rs | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/docs/sdk/src/reference_docs/chain_spec_genesis.rs b/docs/sdk/src/reference_docs/chain_spec_genesis.rs index 4c00faa46baa..edb0a9a1a0a0 100644 --- a/docs/sdk/src/reference_docs/chain_spec_genesis.rs +++ b/docs/sdk/src/reference_docs/chain_spec_genesis.rs @@ -2,24 +2,24 @@ //! //! # What is chain-spec. //! -//! Chain specicfication file defines the set of properties that are required to run the node as +//! A chain specification file defines the set of properties that are required to run the node as //! part of the chain. The chain specification consists of two main parts: //! - initial state of the runtime, //! - network / logical properties of the chain, the most important property being the list of //! bootnodes. //! //! This document describes how initial state is handled in pallets and runtime, and how to interact -//! with runtime in order to build genesis sate. +//! with the runtime in order to build genesis state. //! -//! For more informations on chain specification and its properties refer to +//! For more information on chain specification and its properties, refer to //! [`sc_chain_spec#from-initial-state-to-raw-genesis`]. //! //! The initial genesis state can be provided in the following formats: -//! - full, -//! - patch, -//! - raw, +//! - full +//! - patch +//! - raw //! -//! Each of the format is explained in [_chain-spec-format_][`sc_chain_spec#chain-spec-formats`]. +//! Each of the formats is explained in [_chain-spec-format_][`sc_chain_spec#chain-spec-formats`]. //! //! //! ## `GenesisConfig` for `pallet` @@ -31,12 +31,12 @@ //! The struct shall be defined within the pallet `mod`, as in the following code: #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/pallets.rs", pallet_bar)] -//! The initial state conveyed in `GenesisConfig` struct is transformed into state storage items by -//! means of the [`BuildGenesisConfig`] trait wich shall be implemented for pallet's `GenesisConfig` +//! The initial state conveyed in the `GenesisConfig` struct is transformed into state storage items by +//! means of the [`BuildGenesisConfig`] trait which shall be implemented for the pallet's `GenesisConfig` //! struct. The [`pallet::genesis_build`] attribute shall be attached to the `impl` block: #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/pallets.rs", pallet_bar_build)] -//! GenesisConfig may contain also more complicated types including nested structs or enums, as in +//! GenesisConfig may also contain more complicated types, including nested structs or enums, as in //! the example for `pallet_foo`: #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/pallets.rs", pallet_foo_GenesisConfig)] @@ -48,25 +48,25 @@ //! The runtime genesis config struct consists of configs for every pallet. For [_demonstration //! runtime_][`chain_spec_guide_runtime`] used in this guide, it consists of `SystemConfig`, //! `BarConfig` and `FooConfig`. This structure was automatically generated by macro and it can be -//! sneaked peaked here: [`RuntimeGenesisConfig`]. +//! sneak peeked here: [`RuntimeGenesisConfig`]. //! //! Runtime exposes a dedicated runtime API for interacting with its genesis config: //! [`sp_genesis_builder::GenesisBuilder`]. Implementation shall be provided within //! [`sp_api::impl_runtime_apis`] macro, typically making use of some helpers provided: //! [`build_state`], [`get_preset`]. -//! Usual implementation of [`sp_genesis_builder::GenesisBuilder`] is as follows: +//! A typical implementation of [`sp_genesis_builder::GenesisBuilder`] looks as follows: #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/runtime.rs", runtime_impl)] -//! Please note that two functions are customized `preset_names` and `get_preset`. The first one -//! just provdes a vec of names of supported presets, while the latter one delegats the call to -//! function that maps the name to preset: [`chain_spec_guide_runtime::presets::get_builtin_preset`] +//! Please note that two functions are customized: `preset_names` and `get_preset`. The first one +//! just provides a `Vec` of names of supported presets, while the latter one delegates the call to a +//! function that maps the name to a preset: [`chain_spec_guide_runtime::presets::get_builtin_preset`] #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", get_builtin_preset)] //! ## Genesis state presets for `runtime` -//! Rutime may provide many flavors of initial genesis state. This may be useful for predefined +//! The runtime may provide many flavors of initial genesis state. This may be useful for predefined //! testing networks, local development, or CI integration tests. Predefined genesis state may -//! contain a list of pre-funded accounts, predefined authorithies for consensue, sudo key and many +//! contain a list of pre-funded accounts, predefined authorities for consensus, sudo key and many //! others useful for testing. //! Internally presets can be provided in a number of ways: @@ -76,7 +76,7 @@ #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", preset_2)] #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", preset_3)] //! It is worth noting that preset does not have to be the full `RuntimeGenesisConfig`, in that -//! sense that it does not have to contain all the keys of the struct. The preset is acutally a json +//! sense that it does not have to contain all the keys of the struct. The preset is actually a json //! patch that will be merged with default value of `RuntimeGenesisConfig`. This approach should //! simplify maintanance of builtin presets. Following example illustrates runtime genesis config //! patch: From c5d1f865eb479c16bcf88d905e2d18c9c8edf45b Mon Sep 17 00:00:00 2001 From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Date: Tue, 4 Jun 2024 12:56:38 +0200 Subject: [PATCH 12/15] review comments (default section added) --- .../src/reference_docs/chain_spec_genesis.rs | 46 +++++++++++++++---- .../chain_spec_runtime/src/pallets.rs | 9 ++-- .../chain_spec_runtime/src/presets.rs | 6 +-- .../tests/chain_spec_builder_tests.rs | 4 +- 4 files changed, 46 insertions(+), 19 deletions(-) diff --git a/docs/sdk/src/reference_docs/chain_spec_genesis.rs b/docs/sdk/src/reference_docs/chain_spec_genesis.rs index edb0a9a1a0a0..bfd40d2048e6 100644 --- a/docs/sdk/src/reference_docs/chain_spec_genesis.rs +++ b/docs/sdk/src/reference_docs/chain_spec_genesis.rs @@ -31,16 +31,23 @@ //! The struct shall be defined within the pallet `mod`, as in the following code: #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/pallets.rs", pallet_bar)] -//! The initial state conveyed in the `GenesisConfig` struct is transformed into state storage items by -//! means of the [`BuildGenesisConfig`] trait which shall be implemented for the pallet's `GenesisConfig` -//! struct. The [`pallet::genesis_build`] attribute shall be attached to the `impl` block: +//! The initial state conveyed in the `GenesisConfig` struct is transformed into state storage +//! items by means of the [`BuildGenesisConfig`] trait which shall be implemented for the pallet's +//! `GenesisConfig` struct. The [`pallet::genesis_build`] attribute shall be attached to the `impl` +//! block: #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/pallets.rs", pallet_bar_build)] //! GenesisConfig may also contain more complicated types, including nested structs or enums, as in //! the example for `pallet_foo`: #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/pallets.rs", pallet_foo_GenesisConfig)] -//! Please note that fields of `GenesisConfig` may not be directly mapped to storage items: +//! Note that [`serde`] attributes can be used to control how the data +//! structures are being stored into JSON: +#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/pallets.rs", SomeFooData2)] + +//! Please note that fields of `GenesisConfig` may not be directly mapped to storage items. In the +//! following example the initial struct fields are used to compute (sum) the value that will be +//! stored in state as `ProcessedEnumValue`: #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/pallets.rs", pallet_foo_build)] //! ## `GenesisConfig` for `runtimes` @@ -48,7 +55,8 @@ //! The runtime genesis config struct consists of configs for every pallet. For [_demonstration //! runtime_][`chain_spec_guide_runtime`] used in this guide, it consists of `SystemConfig`, //! `BarConfig` and `FooConfig`. This structure was automatically generated by macro and it can be -//! sneak peeked here: [`RuntimeGenesisConfig`]. +//! sneak peaked here: [`RuntimeGenesisConfig`]. For further reading on generated runtime +//! types refer to [`frame_runtime_types`]. //! //! Runtime exposes a dedicated runtime API for interacting with its genesis config: //! [`sp_genesis_builder::GenesisBuilder`]. Implementation shall be provided within @@ -58,10 +66,26 @@ #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/runtime.rs", runtime_impl)] //! Please note that two functions are customized: `preset_names` and `get_preset`. The first one -//! just provides a `Vec` of names of supported presets, while the latter one delegates the call to a -//! function that maps the name to a preset: [`chain_spec_guide_runtime::presets::get_builtin_preset`] +//! just provides a `Vec` of names of supported presets, while the latter one delegates the call to +//! a function that maps the name to a preset: +//! [`chain_spec_guide_runtime::presets::get_builtin_preset`] #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", get_builtin_preset)] +//! ## `Default` for `GenesisConfig` +//! `GenesisConfig` of all pallets must implement `Defualt` trait. Those are aggregated into +//! runtime's `RuntimeGenesisConfig`'s `Default`. +//! +//! Default value of `RuntimeGenesisConfig` can be queried by [`get_preset`] function of +//! `GenesisBuilder` with `id:None`. +//! +//! A default value for RuntimeGenesisConfig usually is not operable. This is because for some +//! pallets it is not possible to define good defaults (e.g. initial set of authorities). +//! +//! A default value is a base upon which a patch for `GenesisConfig` is applied. A good description +//! of how it exactly works is provided in +//! [`get_storage_for_patch`]. A patch can be +//! provided as a external file (manually created) or as builtin runtime preset. + //! ## Genesis state presets for `runtime` //! The runtime may provide many flavors of initial genesis state. This may be useful for predefined @@ -70,13 +94,13 @@ //! others useful for testing. //! Internally presets can be provided in a number of ways: -//! - json in string form: +//! - JSON in string form: #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", preset_1)] -//! - json using runtime types to serialize values: +//! - JSON using runtime types to serialize values: #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", preset_2)] #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", preset_3)] //! It is worth noting that preset does not have to be the full `RuntimeGenesisConfig`, in that -//! sense that it does not have to contain all the keys of the struct. The preset is actually a json +//! sense that it does not have to contain all the keys of the struct. The preset is actually a JSON //! patch that will be merged with default value of `RuntimeGenesisConfig`. This approach should //! simplify maintanance of builtin presets. Following example illustrates runtime genesis config //! patch: @@ -108,3 +132,5 @@ //! [`pallet::genesis_config`]: frame_support::pallet_macros::genesis_config //! [`BuildGenesisConfig`]: frame_support::traits::BuildGenesisConfig //! [`chain_spec_builder`]: ../../../staging_chain_spec_builder/index.html +//! [`serde`]: https://serde.rs/field-attrs.html +//! [`get_storage_for_patch`]: sc_chain_spec::GenesisConfigBuilderRuntimeCaller::get_storage_for_patch diff --git a/docs/sdk/src/reference_docs/chain_spec_runtime/src/pallets.rs b/docs/sdk/src/reference_docs/chain_spec_runtime/src/pallets.rs index ff931b60f5cc..a7b0615669ac 100644 --- a/docs/sdk/src/reference_docs/chain_spec_runtime/src/pallets.rs +++ b/docs/sdk/src/reference_docs/chain_spec_runtime/src/pallets.rs @@ -54,9 +54,10 @@ pub struct SomeFooData1 { } #[derive(Default, serde::Serialize, serde::Deserialize)] +#[docify::export] pub struct SomeFooData2 { #[serde(default, with = "sp_core::bytes")] - pub v: Vec, + pub values: Vec, } #[derive(Default, serde::Serialize, serde::Deserialize)] @@ -97,12 +98,12 @@ pub mod pallet_foo { #[docify::export(pallet_foo_build)] impl BuildGenesisConfig for GenesisConfig { fn build(&self) { - let v: u64 = match &self.some_enum { + let processed_value: u64 = match &self.some_enum { FooEnum::Data0 => 0, FooEnum::Data1(v) => (v.a + v.b).into(), - FooEnum::Data2(v) => v.v.iter().map(|v| *v as u64).sum(), + FooEnum::Data2(v) => v.values.iter().map(|v| *v as u64).sum(), }; - ProcessedEnumValue::::set(Some(v)); + ProcessedEnumValue::::set(Some(processed_value)); SomeInteger::::set(Some(self.some_integer)); } } diff --git a/docs/sdk/src/reference_docs/chain_spec_runtime/src/presets.rs b/docs/sdk/src/reference_docs/chain_spec_runtime/src/presets.rs index 18b06ddf1f1c..067d4cb48c9b 100644 --- a/docs/sdk/src/reference_docs/chain_spec_runtime/src/presets.rs +++ b/docs/sdk/src/reference_docs/chain_spec_runtime/src/presets.rs @@ -37,7 +37,7 @@ fn preset_1() -> Value { "foo": { "someEnum": { "Data2": { - "v": "0x0c0f" + "values": "0x0c0f" } }, "someInteger": 100 @@ -52,7 +52,7 @@ fn preset_2() -> Value { "initialAccount": AccountKeyring::Ferdie.public().to_ss58check(), }, "foo": { - "someEnum": FooEnum::Data2(SomeFooData2 { v: vec![12,16] }), + "someEnum": FooEnum::Data2(SomeFooData2 { values: vec![12,16] }), "someInteger": 200 }, }) @@ -82,7 +82,7 @@ fn preset_4() -> Value { "foo": { "someEnum": { "Data2": { - "v": "0x0c0f" + "values": "0x0c0f" } }, }, diff --git a/docs/sdk/src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs b/docs/sdk/src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs index af133e018148..1a2a5acf94ad 100644 --- a/docs/sdk/src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs +++ b/docs/sdk/src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs @@ -65,7 +65,7 @@ fn get_preset() { "foo": { "someEnum": { "Data2": { - "v": "0x0c0f" + "values": "0x0c0f" } }, "someInteger": 100 @@ -115,7 +115,7 @@ fn generate_chain_spec() { "foo": { "someEnum": { "Data2": { - "v": "0x0c0f" + "values": "0x0c0f" } }, "someInteger": 100 From bf15ed31a6f1555750a67b2170b5c4b5cafb0aa1 Mon Sep 17 00:00:00 2001 From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:11:39 +0200 Subject: [PATCH 13/15] cargo install note added --- docs/sdk/src/reference_docs/chain_spec_genesis.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/sdk/src/reference_docs/chain_spec_genesis.rs b/docs/sdk/src/reference_docs/chain_spec_genesis.rs index bfd40d2048e6..33524fc4c445 100644 --- a/docs/sdk/src/reference_docs/chain_spec_genesis.rs +++ b/docs/sdk/src/reference_docs/chain_spec_genesis.rs @@ -115,6 +115,11 @@ //! ```ignore //! cargo build -p staging-chain-spec-builder -p chain-spec-guide-runtime --release //! ``` +//! The `chain-spec-builder` util can also be installed with `cargo install`: +//! ```ignore +//! cargo install staging-chain-spec-builder +//! cargo build -p chain-spec-guide-runtime --release +//! ``` //! Here are some examples in the form of rust tests: //! ### Listing available presets names: #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs", list_presets)] From db27a4b0cd63dc90c4c1663c6ef5a397a455dc8e Mon Sep 17 00:00:00 2001 From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Date: Tue, 4 Jun 2024 16:18:57 +0200 Subject: [PATCH 14/15] final touches + added some notes on serde attributes --- Cargo.lock | 1 + .../src/reference_docs/chain_spec_genesis.rs | 117 ++++++++++++------ .../chain_spec_runtime/Cargo.toml | 1 + .../chain_spec_runtime/src/pallets.rs | 27 ++++ .../chain_spec_runtime/src/presets.rs | 60 ++++++++- .../chain_spec_runtime/src/runtime.rs | 3 +- .../tests/chain_spec_builder_tests.rs | 15 +-- 7 files changed, 175 insertions(+), 49 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d4f58e95dd9b..fe86fc82b9fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2523,6 +2523,7 @@ dependencies = [ "pallet-transaction-payment-rpc-runtime-api", "parity-scale-codec", "polkadot-sdk-frame", + "sc-chain-spec", "scale-info", "serde", "serde_json", diff --git a/docs/sdk/src/reference_docs/chain_spec_genesis.rs b/docs/sdk/src/reference_docs/chain_spec_genesis.rs index 33524fc4c445..50888e85f92c 100644 --- a/docs/sdk/src/reference_docs/chain_spec_genesis.rs +++ b/docs/sdk/src/reference_docs/chain_spec_genesis.rs @@ -1,5 +1,3 @@ -//! Chain spec and genesis build. -//! //! # What is chain-spec. //! //! A chain specification file defines the set of properties that are required to run the node as @@ -22,77 +20,84 @@ //! Each of the formats is explained in [_chain-spec-format_][`sc_chain_spec#chain-spec-formats`]. //! //! -//! ## `GenesisConfig` for `pallet` +//! # `GenesisConfig` for `pallet` //! //! Every frame pallet may have its initial state which is defined by the `GenesisConfig` internal //! struct. It is a regular rust struct, annotated with [`pallet::genesis_config`] attribute. #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/pallets.rs", pallet_bar_GenesisConfig)] - +//! //! The struct shall be defined within the pallet `mod`, as in the following code: #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/pallets.rs", pallet_bar)] - +//! //! The initial state conveyed in the `GenesisConfig` struct is transformed into state storage //! items by means of the [`BuildGenesisConfig`] trait which shall be implemented for the pallet's //! `GenesisConfig` struct. The [`pallet::genesis_build`] attribute shall be attached to the `impl` //! block: #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/pallets.rs", pallet_bar_build)] - -//! GenesisConfig may also contain more complicated types, including nested structs or enums, as in +//! +//! `GenesisConfig` may also contain more complicated types, including nested structs or enums, as in //! the example for `pallet_foo`: #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/pallets.rs", pallet_foo_GenesisConfig)] - +//! //! Note that [`serde`] attributes can be used to control how the data -//! structures are being stored into JSON: +//! structures are being stored into JSON. In the following example [`sp_core::bytes`] function is used to serialize the +//! `values` field. #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/pallets.rs", SomeFooData2)] - +//! //! Please note that fields of `GenesisConfig` may not be directly mapped to storage items. In the //! following example the initial struct fields are used to compute (sum) the value that will be //! stored in state as `ProcessedEnumValue`: #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/pallets.rs", pallet_foo_build)] - -//! ## `GenesisConfig` for `runtimes` - +//! +//! # `GenesisConfig` for `runtimes` +//! //! The runtime genesis config struct consists of configs for every pallet. For [_demonstration //! runtime_][`chain_spec_guide_runtime`] used in this guide, it consists of `SystemConfig`, //! `BarConfig` and `FooConfig`. This structure was automatically generated by macro and it can be //! sneak peaked here: [`RuntimeGenesisConfig`]. For further reading on generated runtime //! types refer to [`frame_runtime_types`]. //! -//! Runtime exposes a dedicated runtime API for interacting with its genesis config: -//! [`sp_genesis_builder::GenesisBuilder`]. Implementation shall be provided within +//! The macro automatically adds an attribute that renames all the fields to [`camelCase`]. It is a good practice to add +//! it to nested structures too, to have the naming of the JSON keys consistent across the chain-spec file. +//! +//! ## `Default` for `GenesisConfig` +//! +//! `GenesisConfig` of all pallets must implement `Defualt` trait. Those are aggregated into +//! runtime's `RuntimeGenesisConfig`'s `Default`. +//! +//! Default value of `RuntimeGenesisConfig` can be queried by [`GenesisBuilder::get_preset`] function provided by +//! runtime with `id:None`. +//! +//! A default value for RuntimeGenesisConfig usually is not operational. This is because for some +//! pallets it is not possible to define good defaults (e.g. an initial set of authorities). +//! +//! A default value is a base upon which a patch for `GenesisConfig` is applied. A good description of how it exactly +//! works is provided in [`get_storage_for_patch`] (and also in [`GenesisBuilder::get_preset`]). A patch can be provided +//! as a external file (manually created) or as builtin runtime preset. More info on presets are in the material to +//! follow. +//! +//! ## Implementing `GenesisBuilder` for runtime +//! +//! The runtime exposes a dedicated runtime API for interacting with its genesis config: +//! [`sp_genesis_builder::GenesisBuilder`]. The implementation shall be provided within //! [`sp_api::impl_runtime_apis`] macro, typically making use of some helpers provided: //! [`build_state`], [`get_preset`]. //! A typical implementation of [`sp_genesis_builder::GenesisBuilder`] looks as follows: #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/runtime.rs", runtime_impl)] - +//! //! Please note that two functions are customized: `preset_names` and `get_preset`. The first one -//! just provides a `Vec` of names of supported presets, while the latter one delegates the call to -//! a function that maps the name to a preset: +//! just provides a `Vec` of the names of supported presets, while the latter one delegates the call to +//! a function that maps the name to an actual preset: //! [`chain_spec_guide_runtime::presets::get_builtin_preset`] #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", get_builtin_preset)] - -//! ## `Default` for `GenesisConfig` -//! `GenesisConfig` of all pallets must implement `Defualt` trait. Those are aggregated into -//! runtime's `RuntimeGenesisConfig`'s `Default`. //! -//! Default value of `RuntimeGenesisConfig` can be queried by [`get_preset`] function of -//! `GenesisBuilder` with `id:None`. +//! ## Genesis state presets for runtime //! -//! A default value for RuntimeGenesisConfig usually is not operable. This is because for some -//! pallets it is not possible to define good defaults (e.g. initial set of authorities). -//! -//! A default value is a base upon which a patch for `GenesisConfig` is applied. A good description -//! of how it exactly works is provided in -//! [`get_storage_for_patch`]. A patch can be -//! provided as a external file (manually created) or as builtin runtime preset. - -//! ## Genesis state presets for `runtime` - //! The runtime may provide many flavors of initial genesis state. This may be useful for predefined //! testing networks, local development, or CI integration tests. Predefined genesis state may //! contain a list of pre-funded accounts, predefined authorities for consensus, sudo key and many //! others useful for testing. - +//! //! Internally presets can be provided in a number of ways: //! - JSON in string form: #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", preset_1)] @@ -105,7 +110,36 @@ //! simplify maintanance of builtin presets. Following example illustrates runtime genesis config //! patch: #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", preset_4)] - +//! +//! ## Note on the importance of testing presets +//! +//! It is recommended to always test presets by adding the tests that convert the preset into the raw storage. +//! Converting to raw storage involves the deserialization of the provided JSON blob, what enforces the verification of +//! the preset. The following code shows one of the approaches that can be taken for testing: +#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", check_presets)] +//! +//! ## Note on tne importance of using `deny_unknown_fields` attribute +//! +//! It is worth noting that it is easy to make a hard to spot mistake as in the following example ([`FooStruct`] does +//! not contain `fieldC`): +#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", preset_invalid)] +//! Even though `preset_invalid` contains a key that does not exist, the deserialization of the JSON blob does not fail. +//! The mispelling is silently ignored due to lack of [`deny_unknown_fields`] attribute on the [`FooStruct`] struct, which is +//! internally used in `GenesisConfig`. +#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", invalid_preset_works)] +//! +//! ## Runtime `GenesisConfig` raw format +//! +//! A raw format of genesis config cotains just state's keys and values as they are stored in the storage. This format +//! is used to directly initialize the genesis storage. This format is useful for long-term running chains, where the +//! `GenesisConfig` stucture for pallets may be evolving over the time. The JSON representation created at some point in +//! time may no longer be deserializable in future, making a chain specification useless. The raw format is recommended +//! for the production chains. +//! +//! For detailed description on how raw format is built please refer to [_chain-spec-raw-genesis_][`sc_chain_spec#from-initial-state-to-raw-genesis`]. +//! A plain and corresponding raw examples of chain-spec are given in [_chain-spec-examples_][`sc_chain_spec#json-chain-specification-example`]. +//! The [`chain_spec_builder`] util supports building the raw storage. +//! //! # Interacting with the tool //! //! The [`chain_spec_builder`] util allows to interact with runtime in order to list or display @@ -121,15 +155,17 @@ //! cargo build -p chain-spec-guide-runtime --release //! ``` //! Here are some examples in the form of rust tests: -//! ### Listing available presets names: +//! ## Listing available presets names: #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs", list_presets)] -//! ### Displaying preset with given name +//! ## Displaying preset with given name #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs", get_preset)] -//! ### Building chain-spec using given preset +//! ## Building chain-spec using given preset #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs", generate_chain_spec)] //! //! [`RuntimeGenesisConfig`]: //! chain_spec_guide_runtime::runtime::RuntimeGenesisConfig +//! [`FooStruct`]: +//! chain_spec_guide_runtime::pallets::FooStruct //! [`impl_runtime_apis`]: frame::runtime::prelude::impl_runtime_apis //! [`build_state`]: frame_support::genesis_builder_helper::build_state //! [`get_preset`]: frame_support::genesis_builder_helper::get_preset @@ -139,3 +175,6 @@ //! [`chain_spec_builder`]: ../../../staging_chain_spec_builder/index.html //! [`serde`]: https://serde.rs/field-attrs.html //! [`get_storage_for_patch`]: sc_chain_spec::GenesisConfigBuilderRuntimeCaller::get_storage_for_patch +//! [`GenesisBuilder::get_preset`]: sp_genesis_builder::GenesisBuilder::get_preset +//! [`deny_unknown_fields`]: https://serde.rs/container-attrs.html#deny_unknown_fields +//! [`camelCase`]: https://serde.rs/container-attrs.html#rename_all diff --git a/docs/sdk/src/reference_docs/chain_spec_runtime/Cargo.toml b/docs/sdk/src/reference_docs/chain_spec_runtime/Cargo.toml index 7aa675112b43..c6dd3af9d90b 100644 --- a/docs/sdk/src/reference_docs/chain_spec_runtime/Cargo.toml +++ b/docs/sdk/src/reference_docs/chain_spec_runtime/Cargo.toml @@ -42,6 +42,7 @@ substrate-wasm-builder = { path = "../../../../../substrate/utils/wasm-builder", [dev-dependencies] chain-spec-builder = { package = "staging-chain-spec-builder", path = "../../../../../substrate/bin/utils/chain-spec-builder" } +sc-chain-spec = { path = "../../../../../substrate/client/chain-spec" } [features] default = ["std"] diff --git a/docs/sdk/src/reference_docs/chain_spec_runtime/src/pallets.rs b/docs/sdk/src/reference_docs/chain_spec_runtime/src/pallets.rs index a7b0615669ac..be4455aa2197 100644 --- a/docs/sdk/src/reference_docs/chain_spec_runtime/src/pallets.rs +++ b/docs/sdk/src/reference_docs/chain_spec_runtime/src/pallets.rs @@ -15,6 +15,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! Pallets for the chain-spec demo runtime. + use frame::prelude::*; #[docify::export] @@ -31,6 +33,7 @@ pub mod pallet_bar { #[pallet::storage] pub(super) type InitialAccount = StorageValue; + /// Simple `GenesisConfig`. #[pallet::genesis_config] #[derive(DefaultNoBound)] #[docify::export(pallet_bar_GenesisConfig)] @@ -41,25 +44,46 @@ pub mod pallet_bar { #[pallet::genesis_build] #[docify::export(pallet_bar_build)] impl BuildGenesisConfig for GenesisConfig { + /// The storage building function that presents a direct mapping of the initial config + /// values to the storage items. fn build(&self) { InitialAccount::::set(self.initial_account.clone()); } } } +/// The sample structure used in `GenesisConfig`. +/// +/// This structure does not deny unknown fields. This may lead to some problems. +#[derive(Default, serde::Serialize, serde::Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct FooStruct { + pub field_a: u8, + pub field_b: u8, +} + +/// The sample structure used in `GenesisConfig`. +/// +/// This structure does not deny unknown fields. This may lead to some problems. #[derive(Default, serde::Serialize, serde::Deserialize)] +#[serde(deny_unknown_fields, rename_all = "camelCase")] pub struct SomeFooData1 { pub a: u8, pub b: u8, } +/// Another sample structure used in `GenesisConfig`. +/// +/// The user defined serialization is used. #[derive(Default, serde::Serialize, serde::Deserialize)] #[docify::export] +#[serde(deny_unknown_fields, rename_all = "camelCase")] pub struct SomeFooData2 { #[serde(default, with = "sp_core::bytes")] pub values: Vec, } +/// Sample enum used in `GenesisConfig`. #[derive(Default, serde::Serialize, serde::Deserialize)] pub enum FooEnum { #[default] @@ -84,12 +108,14 @@ pub mod pallet_foo { #[pallet::storage] pub type SomeInteger = StorageValue; + /// The more sophisticated structure for conveying initial state. #[docify::export(pallet_foo_GenesisConfig)] #[pallet::genesis_config] #[derive(DefaultNoBound)] pub struct GenesisConfig { pub some_integer: u32, pub some_enum: FooEnum, + pub some_struct: FooStruct, #[serde(skip)] _phantom: PhantomData, } @@ -97,6 +123,7 @@ pub mod pallet_foo { #[pallet::genesis_build] #[docify::export(pallet_foo_build)] impl BuildGenesisConfig for GenesisConfig { + /// The build method that indirectly maps an initial config values into the storage items. fn build(&self) { let processed_value: u64 = match &self.some_enum { FooEnum::Data0 => 0, diff --git a/docs/sdk/src/reference_docs/chain_spec_runtime/src/presets.rs b/docs/sdk/src/reference_docs/chain_spec_runtime/src/presets.rs index 067d4cb48c9b..c51947f6cc7c 100644 --- a/docs/sdk/src/reference_docs/chain_spec_runtime/src/presets.rs +++ b/docs/sdk/src/reference_docs/chain_spec_runtime/src/presets.rs @@ -15,7 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Presets for demo runtime. +//! Presets for the chain-spec demo runtime. use crate::pallets::{FooEnum, SomeFooData1, SomeFooData2}; use serde_json::{json, to_string, Value}; @@ -23,12 +23,20 @@ use sp_application_crypto::Ss58Codec; use sp_keyring::AccountKeyring; use sp_std::vec; +/// A demo preset with strings only. pub const PRESET_1: &str = "preset_1"; +/// A demo preset with real types. pub const PRESET_2: &str = "preset_2"; +/// Another demo preset with real types. pub const PRESET_3: &str = "preset_3"; +/// A single value patch preset. pub const PRESET_4: &str = "preset_4"; +/// A single value patch preset. +pub const PRESET_INVALID: &str = "preset_invalid"; #[docify::export] +/// Function provides a preset demonstrating how use string representation of preset's internal +/// values. fn preset_1() -> Value { json!({ "bar": { @@ -40,12 +48,17 @@ fn preset_1() -> Value { "values": "0x0c0f" } }, + "someStruct" : { + "fieldA": 10, + "fieldB": 20 + }, "someInteger": 100 }, }) } #[docify::export] +/// Function provides a preset demonstrating how use the actual types to create a preset. fn preset_2() -> Value { json!({ "bar": { @@ -59,6 +72,7 @@ fn preset_2() -> Value { } #[docify::export] +/// Function provides a preset demonstrating how use the actual types to create a preset. fn preset_3() -> Value { json!({ "bar": { @@ -77,6 +91,8 @@ fn preset_3() -> Value { } #[docify::export] +/// Function provides a minimal preset demonstrating how to patch single key in +/// `RuntimeGenesisConfig`. fn preset_4() -> Value { json!({ "foo": { @@ -89,7 +105,20 @@ fn preset_4() -> Value { }) } -/// Provides a json representation of preset identified by given `id`. +#[docify::export] +/// Function provides an invalid preset demonstrating how important is use of +/// [`deny_unknown_fields`] in data structures used in `GenesisConfig`. +fn preset_invalid() -> Value { + json!({ + "foo": { + "someStruct": { + "fieldC": 5 + }, + }, + }) +} + +/// Provides a JSON representation of preset identified by given `id`. /// /// If no preset with given `id` exits `None` is returned. #[docify::export] @@ -99,6 +128,7 @@ pub fn get_builtin_preset(id: &sp_genesis_builder::PresetId) -> Option preset_2(), Ok(PRESET_3) => preset_3(), Ok(PRESET_4) => preset_4(), + Ok(PRESET_INVALID) => preset_invalid(), _ => return None, }; @@ -108,3 +138,29 @@ pub fn get_builtin_preset(id: &sp_genesis_builder::PresetId) -> Option::new( + crate::WASM_BINARY.expect("wasm binary shall exists"), + ); + assert!(builder.get_storage_for_named_preset(Some(&PRESET_1.to_string())).is_ok()); + assert!(builder.get_storage_for_named_preset(Some(&PRESET_2.to_string())).is_ok()); + assert!(builder.get_storage_for_named_preset(Some(&PRESET_3.to_string())).is_ok()); + assert!(builder.get_storage_for_named_preset(Some(&PRESET_4.to_string())).is_ok()); +} + +#[test] +#[docify::export] +fn invalid_preset_works() { + let builder = sc_chain_spec::GenesisConfigBuilderRuntimeCaller::<()>::new( + crate::WASM_BINARY.expect("wasm binary shall exists"), + ); + // Even though a preset contains invalid_key, conversion to raw storage does not fail. This is + // because the [`FooStruct`] structure is not annotated with `deny_unknown_fields` [`serde`] + // attribute. + // This may lead to hard to debug problems, that's why using ['deny_unknown_fields'] is + // recommended. + assert!(builder.get_storage_for_named_preset(Some(&PRESET_INVALID.to_string())).is_ok()); +} diff --git a/docs/sdk/src/reference_docs/chain_spec_runtime/src/runtime.rs b/docs/sdk/src/reference_docs/chain_spec_runtime/src/runtime.rs index 2c11a25e8c2f..6d9bc1260b11 100644 --- a/docs/sdk/src/reference_docs/chain_spec_runtime/src/runtime.rs +++ b/docs/sdk/src/reference_docs/chain_spec_runtime/src/runtime.rs @@ -108,7 +108,8 @@ impl_runtime_apis! { PresetId::from(PRESET_1), PresetId::from(PRESET_2), PresetId::from(PRESET_3), - PresetId::from(PRESET_4) + PresetId::from(PRESET_4), + PresetId::from(PRESET_INVALID) ] } } diff --git a/docs/sdk/src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs b/docs/sdk/src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs index 1a2a5acf94ad..d08f547bba61 100644 --- a/docs/sdk/src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs +++ b/docs/sdk/src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs @@ -37,7 +37,8 @@ fn list_presets() { "preset_1", "preset_2", "preset_3", - "preset_4" + "preset_4", + "preset_invalid" ] }); assert_eq!(output, expected_output, "Output did not match expected"); @@ -51,7 +52,7 @@ fn get_preset() { .arg("-r") .arg(WASM_FILE_PATH) .arg("-p") - .arg("preset_1") + .arg("preset_2") .output() .expect("Failed to execute command"); @@ -65,10 +66,10 @@ fn get_preset() { "foo": { "someEnum": { "Data2": { - "values": "0x0c0f" + "values": "0x0c10" } }, - "someInteger": 100 + "someInteger": 200 }, }); assert_eq!(output, expected_output, "Output did not match expected"); @@ -84,7 +85,7 @@ fn generate_chain_spec() { .arg("-r") .arg(WASM_FILE_PATH) .arg("named-preset") - .arg("preset_1") + .arg("preset_2") .output() .expect("Failed to execute command"); @@ -115,10 +116,10 @@ fn generate_chain_spec() { "foo": { "someEnum": { "Data2": { - "values": "0x0c0f" + "values": "0x0c10" } }, - "someInteger": 100 + "someInteger": 200 } } } From 50898ab9918acf65b9fc3836b1026ec0e1a030b5 Mon Sep 17 00:00:00 2001 From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Date: Tue, 4 Jun 2024 16:23:52 +0200 Subject: [PATCH 15/15] fmt --- .../src/reference_docs/chain_spec_genesis.rs | 61 ++++++++++--------- 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/docs/sdk/src/reference_docs/chain_spec_genesis.rs b/docs/sdk/src/reference_docs/chain_spec_genesis.rs index 50888e85f92c..b3377a330b3e 100644 --- a/docs/sdk/src/reference_docs/chain_spec_genesis.rs +++ b/docs/sdk/src/reference_docs/chain_spec_genesis.rs @@ -35,13 +35,13 @@ //! block: #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/pallets.rs", pallet_bar_build)] //! -//! `GenesisConfig` may also contain more complicated types, including nested structs or enums, as in -//! the example for `pallet_foo`: +//! `GenesisConfig` may also contain more complicated types, including nested structs or enums, as +//! in the example for `pallet_foo`: #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/pallets.rs", pallet_foo_GenesisConfig)] //! //! Note that [`serde`] attributes can be used to control how the data -//! structures are being stored into JSON. In the following example [`sp_core::bytes`] function is used to serialize the -//! `values` field. +//! structures are being stored into JSON. In the following example [`sp_core::bytes`] function is +//! used to serialize the `values` field. #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/pallets.rs", SomeFooData2)] //! //! Please note that fields of `GenesisConfig` may not be directly mapped to storage items. In the @@ -57,24 +57,25 @@ //! sneak peaked here: [`RuntimeGenesisConfig`]. For further reading on generated runtime //! types refer to [`frame_runtime_types`]. //! -//! The macro automatically adds an attribute that renames all the fields to [`camelCase`]. It is a good practice to add -//! it to nested structures too, to have the naming of the JSON keys consistent across the chain-spec file. +//! The macro automatically adds an attribute that renames all the fields to [`camelCase`]. It is a +//! good practice to add it to nested structures too, to have the naming of the JSON keys consistent +//! across the chain-spec file. //! //! ## `Default` for `GenesisConfig` //! //! `GenesisConfig` of all pallets must implement `Defualt` trait. Those are aggregated into //! runtime's `RuntimeGenesisConfig`'s `Default`. //! -//! Default value of `RuntimeGenesisConfig` can be queried by [`GenesisBuilder::get_preset`] function provided by -//! runtime with `id:None`. +//! Default value of `RuntimeGenesisConfig` can be queried by [`GenesisBuilder::get_preset`] +//! function provided by runtime with `id:None`. //! //! A default value for RuntimeGenesisConfig usually is not operational. This is because for some //! pallets it is not possible to define good defaults (e.g. an initial set of authorities). //! -//! A default value is a base upon which a patch for `GenesisConfig` is applied. A good description of how it exactly -//! works is provided in [`get_storage_for_patch`] (and also in [`GenesisBuilder::get_preset`]). A patch can be provided -//! as a external file (manually created) or as builtin runtime preset. More info on presets are in the material to -//! follow. +//! A default value is a base upon which a patch for `GenesisConfig` is applied. A good description +//! of how it exactly works is provided in [`get_storage_for_patch`] (and also in +//! [`GenesisBuilder::get_preset`]). A patch can be provided as a external file (manually created) +//! or as builtin runtime preset. More info on presets are in the material to follow. //! //! ## Implementing `GenesisBuilder` for runtime //! @@ -86,8 +87,8 @@ #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/runtime.rs", runtime_impl)] //! //! Please note that two functions are customized: `preset_names` and `get_preset`. The first one -//! just provides a `Vec` of the names of supported presets, while the latter one delegates the call to -//! a function that maps the name to an actual preset: +//! just provides a `Vec` of the names of supported presets, while the latter one delegates the call +//! to a function that maps the name to an actual preset: //! [`chain_spec_guide_runtime::presets::get_builtin_preset`] #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", get_builtin_preset)] //! @@ -113,31 +114,35 @@ //! //! ## Note on the importance of testing presets //! -//! It is recommended to always test presets by adding the tests that convert the preset into the raw storage. -//! Converting to raw storage involves the deserialization of the provided JSON blob, what enforces the verification of -//! the preset. The following code shows one of the approaches that can be taken for testing: +//! It is recommended to always test presets by adding the tests that convert the preset into the +//! raw storage. Converting to raw storage involves the deserialization of the provided JSON blob, +//! what enforces the verification of the preset. The following code shows one of the approaches +//! that can be taken for testing: #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", check_presets)] //! //! ## Note on tne importance of using `deny_unknown_fields` attribute //! -//! It is worth noting that it is easy to make a hard to spot mistake as in the following example ([`FooStruct`] does -//! not contain `fieldC`): +//! It is worth noting that it is easy to make a hard to spot mistake as in the following example +//! ([`FooStruct`] does not contain `fieldC`): #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", preset_invalid)] -//! Even though `preset_invalid` contains a key that does not exist, the deserialization of the JSON blob does not fail. -//! The mispelling is silently ignored due to lack of [`deny_unknown_fields`] attribute on the [`FooStruct`] struct, which is -//! internally used in `GenesisConfig`. +//! Even though `preset_invalid` contains a key that does not exist, the deserialization of the JSON +//! blob does not fail. The mispelling is silently ignored due to lack of [`deny_unknown_fields`] +//! attribute on the [`FooStruct`] struct, which is internally used in `GenesisConfig`. #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", invalid_preset_works)] //! //! ## Runtime `GenesisConfig` raw format //! -//! A raw format of genesis config cotains just state's keys and values as they are stored in the storage. This format -//! is used to directly initialize the genesis storage. This format is useful for long-term running chains, where the -//! `GenesisConfig` stucture for pallets may be evolving over the time. The JSON representation created at some point in -//! time may no longer be deserializable in future, making a chain specification useless. The raw format is recommended +//! A raw format of genesis config cotains just state's keys and values as they are stored in the +//! storage. This format is used to directly initialize the genesis storage. This format is useful +//! for long-term running chains, where the `GenesisConfig` stucture for pallets may be evolving +//! over the time. The JSON representation created at some point in time may no longer be +//! deserializable in future, making a chain specification useless. The raw format is recommended //! for the production chains. //! -//! For detailed description on how raw format is built please refer to [_chain-spec-raw-genesis_][`sc_chain_spec#from-initial-state-to-raw-genesis`]. -//! A plain and corresponding raw examples of chain-spec are given in [_chain-spec-examples_][`sc_chain_spec#json-chain-specification-example`]. +//! For detailed description on how raw format is built please refer to +//! [_chain-spec-raw-genesis_][`sc_chain_spec#from-initial-state-to-raw-genesis`]. A plain and +//! corresponding raw examples of chain-spec are given in +//! [_chain-spec-examples_][`sc_chain_spec#json-chain-specification-example`]. //! The [`chain_spec_builder`] util supports building the raw storage. //! //! # Interacting with the tool
-//! runtime / full config +//! full config //! A JSON object that provides an explicit and comprehensive representation of the //! RuntimeGenesisConfig struct, which is generated by polkadot_sdk_frame::runtime::prelude::construct_runtime macro (example of generated struct). Must contain all the keys of +//! >example of generated struct). Must contain *all* the keys of //! the genesis config, no defaults will be used. //! //! This format explicitly provides the code of the runtime. @@ -154,7 +157,8 @@ //! A JSON object that offers a partial representation of the //! RuntimeGenesisConfig provided by the runtime. It contains a patch, which is //! essentially a list of key-value pairs to customize in the default runtime's -//! RuntimeGenesisConfig. +//! RuntimeGenesisConfig: `full = default + patch`. Please note that `default` +//! `RuntimeGenesisConfig` may not be functional. //! This format explicitly provides the code of the runtime. //!