From 42ddb5b06578f6c2b2fc469de5161035b04fc79a Mon Sep 17 00:00:00 2001
From: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com>
Date: Tue, 4 Jun 2024 20:04:51 +0200
Subject: [PATCH] `chain-spec`/presets reference docs added (#4678)

Added reference doc about:
- the pallet genesis config and genesis build,
- runtime `genesis-builder` API,
- presets,
- interacting with the `chain-spec-builder` tool

I've added [minimal
runtime](https://github.com/paritytech/polkadot-sdk/tree/mku-chain-spec-guide/docs/sdk/src/reference_docs/chain_spec_runtime)
to demonstrate above topics.

I also sneaked in some little improvement to `chain-spec-builder` which
allows to parse output of the `list-presets` command.

---------

Co-authored-by: Alexandru Vasile <60601340+lexnv@users.noreply.github.com>
Co-authored-by: Sebastian Kunert <skunert49@gmail.com>
---
 Cargo.lock                                    |  31 ++-
 Cargo.toml                                    |   1 +
 docs/sdk/Cargo.toml                           |   5 +
 .../src/reference_docs/chain_spec_genesis.rs  | 187 +++++++++++++++++-
 .../chain_spec_runtime/Cargo.toml             |  71 +++++++
 .../chain_spec_runtime/build.rs               |  23 +++
 .../chain_spec_runtime/src/lib.rs             |  27 +++
 .../chain_spec_runtime/src/pallets.rs         | 137 +++++++++++++
 .../chain_spec_runtime/src/presets.rs         | 166 ++++++++++++++++
 .../chain_spec_runtime/src/runtime.rs         | 122 ++++++++++++
 .../tests/chain_spec_builder_tests.rs         | 129 ++++++++++++
 docs/sdk/src/reference_docs/mod.rs            |   1 -
 .../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 +-
 15 files changed, 905 insertions(+), 11 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
 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 00eea545c71..fe0de516f10 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2511,6 +2511,32 @@ 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",
+ "sc-chain-spec",
+ "scale-info",
+ "serde",
+ "serde_json",
+ "sp-application-crypto",
+ "sp-core",
+ "sp-genesis-builder",
+ "sp-keyring",
+ "sp-runtime",
+ "sp-std 14.0.0",
+ "staging-chain-spec-builder",
+ "substrate-wasm-builder",
+]
+
 [[package]]
 name = "chrono"
 version = "0.4.31"
@@ -14245,6 +14271,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",
@@ -14281,6 +14308,7 @@ dependencies = [
  "parity-scale-codec",
  "polkadot-sdk",
  "polkadot-sdk-frame",
+ "sc-chain-spec",
  "sc-cli",
  "sc-client-db",
  "sc-consensus-aura",
@@ -14299,6 +14327,7 @@ dependencies = [
  "sp-api",
  "sp-arithmetic",
  "sp-core",
+ "sp-genesis-builder",
  "sp-io",
  "sp-keyring",
  "sp-offchain",
@@ -20546,7 +20575,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/Cargo.toml b/Cargo.toml
index d6099e420f9..2b2a1cdc17d 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 a8c873be556..a0953896356 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" }
@@ -102,3 +104,6 @@ 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 2ac51a91f2d..b3377a330b3 100644
--- a/docs/sdk/src/reference_docs/chain_spec_genesis.rs
+++ b/docs/sdk/src/reference_docs/chain_spec_genesis.rs
@@ -1,4 +1,185 @@
-//! Chain spec and genesis build.
+//! # What is chain-spec.
 //!
-//! What is chain-spec.
-//! What is genesis state and how to build it.
+//! 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 the runtime in order to build genesis state.
+//!
+//! 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
+//!
+//! Each of the formats is explained in [_chain-spec-format_][`sc_chain_spec#chain-spec-formats`].
+//!
+//!
+//! # `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 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.
+#![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`
+//!
+//! 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`].
+//!
+//! 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 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)]
+//!
+//! ## 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)]
+//! - 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
+//! 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)]
+//!
+//! ## 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
+//! 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:
+//! ```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)]
+//! ## 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
+//! [`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
+//! [`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
+//! [`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
new file mode 100644
index 00000000000..c6dd3af9d90
--- /dev/null
+++ b/docs/sdk/src/reference_docs/chain_spec_runtime/Cargo.toml
@@ -0,0 +1,71 @@
+[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 }
+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 }
+
+[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"]
+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-application-crypto/std",
+	"sp-core/std",
+	"sp-genesis-builder/std",
+	"sp-keyring/std",
+	"sp-runtime/std",
+	"sp-std/std",
+
+	"serde/std",
+	"serde_json/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 00000000000..e6f92757e22
--- /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 00000000000..4606104fb96
--- /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 00000000000..be4455aa219
--- /dev/null
+++ b/docs/sdk/src/reference_docs/chain_spec_runtime/src/pallets.rs
@@ -0,0 +1,137 @@
+// 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.
+
+//! Pallets for the chain-spec demo runtime.
+
+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<T>(_);
+
+	#[pallet::storage]
+	pub(super) type InitialAccount<T: Config> = StorageValue<Value = T::AccountId>;
+
+	/// Simple `GenesisConfig`.
+	#[pallet::genesis_config]
+	#[derive(DefaultNoBound)]
+	#[docify::export(pallet_bar_GenesisConfig)]
+	pub struct GenesisConfig<T: Config> {
+		pub initial_account: Option<T::AccountId>,
+	}
+
+	#[pallet::genesis_build]
+	#[docify::export(pallet_bar_build)]
+	impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
+		/// The storage building function that presents a direct mapping of the initial config
+		/// values to the storage items.
+		fn build(&self) {
+			InitialAccount::<T>::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<u8>,
+}
+
+/// Sample enum used in `GenesisConfig`.
+#[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<T>(_);
+
+	#[pallet::storage]
+	pub type ProcessedEnumValue<T> = StorageValue<Value = u64>;
+	#[pallet::storage]
+	pub type SomeInteger<T> = StorageValue<Value = u32>;
+
+	/// The more sophisticated structure for conveying initial state.
+	#[docify::export(pallet_foo_GenesisConfig)]
+	#[pallet::genesis_config]
+	#[derive(DefaultNoBound)]
+	pub struct GenesisConfig<T: Config> {
+		pub some_integer: u32,
+		pub some_enum: FooEnum,
+		pub some_struct: FooStruct,
+		#[serde(skip)]
+		_phantom: PhantomData<T>,
+	}
+
+	#[pallet::genesis_build]
+	#[docify::export(pallet_foo_build)]
+	impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
+		/// 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,
+				FooEnum::Data1(v) => (v.a + v.b).into(),
+				FooEnum::Data2(v) => v.values.iter().map(|v| *v as u64).sum(),
+			};
+			ProcessedEnumValue::<T>::set(Some(processed_value));
+			SomeInteger::<T>::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 00000000000..c51947f6cc7
--- /dev/null
+++ b/docs/sdk/src/reference_docs/chain_spec_runtime/src/presets.rs
@@ -0,0 +1,166 @@
+// 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 the chain-spec 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;
+
+/// 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": {
+			"initialAccount": "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL",
+		},
+		"foo": {
+			"someEnum": {
+				"Data2": {
+					"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": {
+			"initialAccount": AccountKeyring::Ferdie.public().to_ss58check(),
+		},
+		"foo": {
+			"someEnum": FooEnum::Data2(SomeFooData2 { values: vec![12,16] }),
+			"someInteger": 200
+		},
+	})
+}
+
+#[docify::export]
+/// Function provides a preset demonstrating how use the actual types to create a preset.
+fn preset_3() -> Value {
+	json!({
+		"bar": {
+			"initialAccount": AccountKeyring::Alice.public().to_ss58check(),
+		},
+		"foo": {
+			"someEnum": FooEnum::Data1(
+				SomeFooData1 {
+					a: 12,
+					b: 16
+				}
+			),
+			"someInteger": 300
+		},
+	})
+}
+
+#[docify::export]
+/// Function provides a minimal preset demonstrating how to patch single key in
+/// `RuntimeGenesisConfig`.
+fn preset_4() -> Value {
+	json!({
+		"foo": {
+			"someEnum": {
+				"Data2": {
+					"values": "0x0c0f"
+				}
+			},
+		},
+	})
+}
+
+#[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]
+pub fn get_builtin_preset(id: &sp_genesis_builder::PresetId) -> Option<sp_std::vec::Vec<u8>> {
+	let preset = match id.try_into() {
+		Ok(PRESET_1) => preset_1(),
+		Ok(PRESET_2) => preset_2(),
+		Ok(PRESET_3) => preset_3(),
+		Ok(PRESET_4) => preset_4(),
+		Ok(PRESET_INVALID) => preset_invalid(),
+		_ => return None,
+	};
+
+	Some(
+		to_string(&preset)
+			.expect("serialization to json is expected to work. qed.")
+			.into_bytes(),
+	)
+}
+
+#[test]
+#[docify::export]
+fn check_presets() {
+	let builder = sc_chain_spec::GenesisConfigBuilderRuntimeCaller::<()>::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
new file mode 100644
index 00000000000..6d9bc1260b1
--- /dev/null
+++ b/docs/sdk/src/reference_docs/chain_spec_runtime/src/runtime.rs
@@ -0,0 +1,122 @@
+// 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<Runtime, SignedExtra>;
+type Header = HeaderFor<Runtime>;
+
+#[docify::export(runtime_impl)]
+impl_runtime_apis! {
+	impl sp_genesis_builder::GenesisBuilder<Block> for Runtime {
+		fn build_state(config: Vec<u8>) -> sp_genesis_builder::Result {
+			build_state::<RuntimeGenesisConfig>(config)
+		}
+
+		fn get_preset(id: &Option<sp_genesis_builder::PresetId>) -> Option<Vec<u8>> {
+			get_preset::<RuntimeGenesisConfig>(id, get_builtin_preset)
+		}
+
+		fn preset_names() -> Vec<sp_genesis_builder::PresetId> {
+			vec![
+				PresetId::from(PRESET_1),
+				PresetId::from(PRESET_2),
+				PresetId::from(PRESET_3),
+				PresetId::from(PRESET_4),
+				PresetId::from(PRESET_INVALID)
+			]
+		}
+	}
+
+	impl apis::Core<Block> for Runtime {
+		fn version() -> RuntimeVersion { VERSION }
+		fn execute_block(_: Block) { }
+		fn initialize_block(_: &Header) -> ExtrinsicInclusionMode { ExtrinsicInclusionMode::default() }
+	}
+}
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 00000000000..d08f547bba6
--- /dev/null
+++ b/docs/sdk/src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs
@@ -0,0 +1,129 @@
+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";
+
+fn get_chain_spec_builder_path() -> &'static str {
+	// 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")
+		.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(get_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",
+			"preset_invalid"
+		]
+	});
+	assert_eq!(output, expected_output, "Output did not match expected");
+}
+
+#[test]
+#[docify::export]
+fn get_preset() {
+	let output = Command::new(get_chain_spec_builder_path())
+		.arg("display-preset")
+		.arg("-r")
+		.arg(WASM_FILE_PATH)
+		.arg("-p")
+		.arg("preset_2")
+		.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": {
+					"values": "0x0c10"
+				}
+			},
+			"someInteger": 200
+		},
+	});
+	assert_eq!(output, expected_output, "Output did not match expected");
+}
+
+#[test]
+#[docify::export]
+fn generate_chain_spec() {
+	let output = Command::new(get_chain_spec_builder_path())
+		.arg("-c")
+		.arg("/dev/stdout")
+		.arg("create")
+		.arg("-r")
+		.arg(WASM_FILE_PATH)
+		.arg("named-preset")
+		.arg("preset_2")
+		.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": {
+				  "values": "0x0c10"
+				}
+			  },
+			  "someInteger": 200
+			}
+		  }
+		}
+	  }
+	});
+	assert_eq!(output, expected_output, "Output did not match expected");
+}
diff --git a/docs/sdk/src/reference_docs/mod.rs b/docs/sdk/src/reference_docs/mod.rs
index 6fa25bf36e1..e50690b5021 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.
diff --git a/substrate/bin/utils/chain-spec-builder/Cargo.toml b/substrate/bin/utils/chain-spec-builder/Cargo.toml
index de06bbb3fff..88585649acf 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 8d6425a46c7..18da3c30691 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 653c3c618b7..b59ad68610e 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:
 //!
 //! <table>
 //!   <thead>
@@ -135,14 +138,14 @@
 //!   <tbody>
 //!     <tr>
 //!       <td>
-//! 		<code>runtime / full config</code>
+//! 		<code>full config</code>
 //!       </td>
 //!       <td>A JSON object that provides an explicit and comprehensive representation of the
 //! <code>RuntimeGenesisConfig</code> struct, which is generated by <a
 //! href="../frame_support_procedural/macro.construct_runtime.html"
 //! ><code>polkadot_sdk_frame::runtime::prelude::construct_runtime</code></a> macro (<a
 //! href="../substrate_test_runtime/struct.RuntimeGenesisConfig.html#"
-//! >example of generated struct</a>). Must contain all the keys of
+//! >example of generated struct</a>). 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 @@
 //!       <td>A JSON object that offers a partial representation of the
 //!       <code>RuntimeGenesisConfig</code> provided by the runtime. It contains a patch, which is
 //! essentially a list of key-value pairs to customize in the default runtime's
-//! <code>RuntimeGenesisConfig</code>.
+//! <code>RuntimeGenesisConfig</code>: `full = default + patch`. Please note that `default`
+//! `RuntimeGenesisConfig` may not be functional.
 //! This format explicitly provides the code of the runtime.
 //! </td></tr>
 //!     <tr>
-- 
GitLab