Skip to main content

soil_chain_spec/
lib.rs

1// This file is part of Soil.
2
3// Copyright (C) Soil contributors.
4// Copyright (C) Parity Technologies (UK) Ltd.
5// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
6
7//! This crate includes structs and utilities for defining configuration files (known as chain
8//! specification) for both runtime and node.
9//!
10//! # Intro: Chain Specification
11//!
12//! The chain specification comprises parameters and settings that define the properties and an
13//! initial state of a chain. Users typically interact with the JSON representation of the chain
14//! spec. Internally, the chain spec is embodied by the [`GenericChainSpec`] struct, and specific
15//! properties can be accessed using the [`ChainSpec`] trait.
16//!
17//! In summary, although not restricted to, the primary role of the chain spec is to provide a list
18//! of well-known boot nodes for the blockchain network and the means for initializing the genesis
19//! storage. This initialization is necessary for creating a genesis block upon which subsequent
20//! blocks are built. When the node is launched for the first time, it reads the chain spec,
21//! initializes the genesis block, and establishes connections with the boot nodes.
22//!
23//! The JSON chain spec is divided into two main logical sections:
24//! - one section details general chain properties,
25//! - second explicitly or indirectly defines the genesis storage, which, in turn, determines the
26//!   genesis hash of the chain,
27//!
28//! The chain specification consists of the following fields:
29//!
30//! <table>
31//!   <thead>
32//!     <tr>
33//!       <th>Chain spec key</th>
34//!       <th>Description</th>
35//!     </tr>
36//!   </thead>
37//!   <tbody>
38//!     <tr>
39//!       <td>name</td>
40//!       <td>The human readable name of the chain.</td>
41//!     </tr>
42//!     <tr>
43//!       <td>id</td>
44//!       <td>The id of the chain.</td>
45//!     </tr>
46//!     <tr>
47//!       <td>chainType</td>
48//!       <td>The chain type of this chain
49//!           (refer to
50//!            <a href="enum.ChainType.html" title="enum soil_chain_spec::ChainType">
51//!              <code>ChainType</code>
52//!            </a>).
53//!       </td>
54//!     </tr>
55//!     <tr>
56//!       <td>bootNodes</td>
57//!       <td>A list of
58//!       <a href="https://github.com/multiformats/multiaddr">multi addresses</a>
59//!       that belong to boot nodes of the chain.</td>
60//!     </tr>
61//!     <tr>
62//!       <td>telemetryEndpoints</td>
63//!       <td>Optional list of <code>multi address, verbosity</code> of telemetry endpoints. The
64//! verbosity goes from 0 to 9. With 0 being the mode with the lowest verbosity.</td>
65//!     </tr>
66//!     <tr>
67//!       <td>protocolId</td>
68//!       <td>Optional networking protocol id that identifies the chain.</td>
69//!     </tr>
70//!     <tr>
71//!       <td>forkId</td>
72//!       <td>Optional fork id. Should most likely be left empty. Can be used to signal a fork on
73//! the network level when two chains have the same genesis hash.</td>
74//!     </tr>
75//!     <tr>
76//!       <td>properties</td>
77//!       <td>Custom properties. Shall be provided in the form of
78//! <code>key</code>-<code>value</code> json object.
79//!     </td>
80//!     </tr>
81//!     <tr>
82//!       <td>consensusEngine</td>
83//!       <td>Deprecated field. Should be ignored.</td>
84//!     </tr>
85//!     <tr>
86//!       <td>codeSubstitutes</td>
87//!       <td>Optional map of <code>block_number</code> to <code>wasm_code</code>. More details in
88//! material to follow.</td>
89//!     </tr>
90//!     <tr>
91//!       <td>genesis</td>
92//!       <td>Defines the initial state of the runtime. More details in material to follow.</td>
93//!     </tr>
94//!   </tbody>
95//! </table>
96//!
97//! # `genesis`: Initial Runtime State
98//!
99//! All nodes in the network must build subsequent blocks upon exactly the same genesis block.
100//!
101//! The information configured in the `genesis` section of a chain specification is used to build
102//! the genesis storage, which is essential for creating the genesis block, since the block header
103//! includes the storage root hash.
104//!
105//! The `genesis` key of the chain specification definition describes the
106//! initial state of the runtime. For example, it may contain:
107//! - an initial list of funded accounts,
108//! - the administrative account that controls the sudo key,
109//! - an initial authorities set for consensus, etc.
110//!
111//! As the compiled WASM blob of the runtime code is stored in the chain's state, the initial
112//! runtime must also be provided within the chain specification.
113//!
114//! # `chain-spec` formats
115//!
116//! In essence, the most important formats of genesis initial state in chain specification files
117//! are:
118//!
119//! <table>
120//!   <thead>
121//!     <tr>
122//!       <th>Format</th>
123//!       <th>Description</th>
124//!     </tr>
125//!   </thead>
126//!   <tbody>
127//!     <tr>
128//!       <td>
129//! 		<code>full config</code>
130//!       </td>
131//!       <td>A JSON object that provides an explicit and comprehensive representation of the
132//! <code>RuntimeGenesisConfig</code> struct, which is generated by <a
133//! href="../topsoil_core_procedural/macro.construct_runtime.html"
134//! ><code>topsoil::runtime::prelude::construct_runtime</code></a> macro (<a
135//! href="../soil_test_node_runtime/struct.RuntimeGenesisConfig.html#"
136//! >example of generated struct</a>). Must contain *all* the keys of
137//! the genesis config, no defaults will be used.
138//!
139//! This format explicitly provides the code of the runtime.
140//! </td></tr>
141//!     <tr>
142//!       <td>
143//! 		<code>patch</code>
144//!       </td>
145//!       <td>A JSON object that offers a partial representation of the
146//!       <code>RuntimeGenesisConfig</code> provided by the runtime. It contains a patch, which is
147//! essentially a list of key-value pairs to customize in the default runtime's
148//! <code>RuntimeGenesisConfig</code>: `full = default + patch`. Please note that `default`
149//! `RuntimeGenesisConfig` may not be functional.
150//! This format explicitly provides the code of the runtime.
151//! </td></tr>
152//!     <tr>
153//!       <td>
154//! 		<code>raw</code>
155//!       </td>
156//!       <td>A JSON object with two fields: <code>top</code> and <code>children_default</code>.
157//! Each field is a map of <code>key => value</code> pairs representing entries in a genesis storage
158//! trie. The runtime code is one of such entries.</td>
159//!     </tr>
160//!   </tbody>
161//! </table>
162//!
163//! The main purpose of the `RuntimeGenesisConfig` patch is to:
164//! - minimize the maintenance effort when RuntimeGenesisConfig is changed in the future (e.g. new
165//!   pallets added to the runtime or pallet's genesis config changed),
166//! - increase the readability - it only contains the relevant fields,
167//! - allow to apply numerous changes in distinct domains (e.g. for zombienet).
168//!
169//! For production or long-lasting blockchains, using the `raw` format in the chain specification is
170//! recommended. Only the `raw` format guarantees that storage root hash will remain unchanged when
171//! the `RuntimeGenesisConfig` format changes due to software upgrade.
172//!
173//! JSON examples in the [following section](#json-chain-specification-example) illustrate the `raw`
174//! `patch` and full genesis fields.
175//!
176//! # From Initial State to Raw Genesis.
177//!
178//! To generate a raw genesis storage from the JSON representation of the runtime genesis config,
179//! the node needs to interact with the runtime.
180//!
181//! This interaction involves passing the runtime genesis config JSON blob to the runtime using the
182//! [`subsoil::genesis_builder::GenesisBuilder::build_state`] function. During this operation, the
183//! runtime converts the JSON representation of the genesis config into [`subsoil::io::storage`] items. It
184//! is a crucial step for computing the storage root hash, which is a key component in determining
185//! the genesis hash.
186//!
187//! Consequently, the runtime must support the [`subsoil::genesis_builder::GenesisBuilder`] API to
188//! utilize either `patch` or `full` formats.
189//!
190//! This entire process is encapsulated within the implementation of the [`BuildStorage`] trait,
191//! which can be accessed through the [`ChainSpec::as_storage_builder`] method. There is an
192//! intermediate internal helper that facilitates this interaction,
193//! [`GenesisConfigBuilderRuntimeCaller`], which serves as a straightforward wrapper for
194//! [`soil_client::executor::WasmExecutor`].
195//!
196//! In case of `raw` genesis state the node does not interact with the runtime regarding the
197//! computation of initial state.
198//!
199//! The plain and `raw` chain specification JSON blobs can be found in
200//! [JSON examples](#json-chain-specification-example) section.
201//!
202//! # Optional Code Mapping
203//!
204//! Optional map of `block_number` to `wasm_code`.
205//!
206//! The given `wasm_code` will be used to substitute the on-chain wasm code starting with the
207//! given block number until the `spec_version` on-chain changes. The given `wasm_code` should
208//! be as close as possible to the on-chain wasm code. A substitute should be used to fix a bug
209//! that cannot be fixed with a runtime upgrade, if for example the runtime is constantly
210//! panicking. Introducing new runtime APIs isn't supported, because the node
211//! will read the runtime version from the on-chain wasm code.
212//!
213//! Use this functionality only when there is no other way around it, and only patch the problematic
214//! bug; the rest should be done with an on-chain runtime upgrade.
215//!
216//! # Building a Chain Specification
217//!
218//! The [`ChainSpecBuilder`] should be used to create an instance of a chain specification. Its API
219//! allows configuration of all fields of the chain spec. To generate a JSON representation of the
220//! specification, use [`ChainSpec::as_json`].
221//!
222//! The sample code to generate a chain spec is as follows:
223#![doc = docify::embed!("src/chain_spec.rs", build_chain_spec_with_patch_works)]
224//! # JSON chain specification example
225//!
226//! The following are the plain and `raw` versions of the chain specification JSON files, resulting
227//! from executing of the above [example](#building-a-chain-specification):
228//! ```ignore
229#![doc = include_str!("../res/substrate_test_runtime_from_patch.json")]
230//! ```
231//! ```ignore
232#![doc = include_str!("../res/substrate_test_runtime_from_patch_raw.json")]
233//! ```
234//! The following example shows the plain full config version of chain spec:
235//! ```ignore
236#![doc = include_str!("../res/substrate_test_runtime_from_config.json")]
237//! ```
238//! The [`ChainSpec`] trait represents the API to access values defined in the JSON chain specification.
239//!
240//!
241//! # Custom Chain Spec Extensions
242//!
243//! The basic chain spec type containing all required parameters is [`GenericChainSpec`]. It can be
244//! extended with additional options containing configuration specific to your chain. Usually, the
245//! extension will be a combination of types exposed by Substrate core modules.
246//!
247//! To allow the core modules to retrieve their configuration from your extension, you should use
248//! `ChainSpecExtension` macro exposed by this crate.
249//! ```rust
250//! use std::collections::HashMap;
251//! use soil_chain_spec::{GenericChainSpec, ChainSpecExtension};
252//!
253//! #[derive(Clone, Debug, serde::Serialize, serde::Deserialize, ChainSpecExtension)]
254//! pub struct MyExtension {
255//! 	pub known_blocks: HashMap<u64, String>,
256//! }
257//!
258//! pub type MyChainSpec = GenericChainSpec<MyExtension>;
259//! ```
260//! Some parameters may require different values depending on the current blockchain height (a.k.a.
261//! forks). You can use the [`ChainSpecGroup`](macro@ChainSpecGroup) macro and the provided [`Forks`]
262//! structure to add such parameters to your chain spec. This will allow overriding a single
263//! parameter starting at a specific block number.
264//! ```rust
265//! use soil_chain_spec::{Forks, ChainSpecGroup, ChainSpecExtension, GenericChainSpec};
266//!
267//! #[derive(Clone, Debug, serde::Serialize, serde::Deserialize, ChainSpecGroup)]
268//! pub struct ClientParams {
269//! 	max_block_size: usize,
270//! 	max_extrinsic_size: usize,
271//! }
272//!
273//! #[derive(Clone, Debug, serde::Serialize, serde::Deserialize, ChainSpecGroup)]
274//! pub struct PoolParams {
275//! 	max_transaction_size: usize,
276//! }
277//!
278//! #[derive(Clone, Debug, serde::Serialize, serde::Deserialize, ChainSpecGroup, ChainSpecExtension)]
279//! pub struct Extension {
280//! 	pub client: ClientParams,
281//! 	pub pool: PoolParams,
282//! }
283//!
284//! pub type BlockNumber = u64;
285//!
286//! /// A chain spec supporting forkable `ClientParams`.
287//! pub type MyChainSpec1 = GenericChainSpec<Forks<BlockNumber, ClientParams>>;
288//!
289//! /// A chain spec supporting forkable `Extension`.
290//! pub type MyChainSpec2 = GenericChainSpec<Forks<BlockNumber, Extension>>;
291//! ```
292//! It's also possible to have a set of parameters that are allowed to change with block numbers
293//! (i.e., they are forkable), and another set that is not subject to changes. This can also be
294//! achieved by declaring an extension that contains [`Forks`] within it.
295//! ```rust
296//! use serde::{Serialize, Deserialize};
297//! use soil_chain_spec::{Forks, GenericChainSpec, ChainSpecGroup, ChainSpecExtension};
298//!
299//! #[derive(Clone, Debug, Serialize, Deserialize, ChainSpecGroup)]
300//! pub struct ClientParams {
301//! 	max_block_size: usize,
302//! 	max_extrinsic_size: usize,
303//! }
304//!
305//! #[derive(Clone, Debug, Serialize, Deserialize, ChainSpecGroup)]
306//! pub struct PoolParams {
307//! 	max_transaction_size: usize,
308//! }
309//!
310//! #[derive(Clone, Debug, Serialize, Deserialize, ChainSpecExtension)]
311//! pub struct Extension {
312//! 	pub client: ClientParams,
313//! 	#[forks]
314//! 	pub pool: Forks<u64, PoolParams>,
315//! }
316//!
317//! pub type MyChainSpec = GenericChainSpec<Extension>;
318//! ```
319//! The chain spec can be extended with other fields that are opaque to the default chain spec.
320//! Specific node implementations will need to be able to deserialize these extensions.
321
322mod chain_spec;
323mod extension;
324mod genesis_block;
325mod genesis_config_builder;
326pub mod json_patch;
327
328pub use self::{
329	chain_spec::{
330		set_code_substitute_in_json_chain_spec, update_code_in_json_chain_spec,
331		ChainSpec as GenericChainSpec, ChainSpecBuilder, NoExtension,
332	},
333	extension::{get_extension, get_extension_mut, Extension, Fork, Forks, GetExtension, Group},
334	genesis_block::{
335		construct_genesis_block, resolve_state_version_from_wasm, BuildGenesisBlock,
336		GenesisBlockBuilder,
337	},
338	genesis_config_builder::{
339		GenesisConfigBuilderRuntimeCaller, DEV_RUNTIME_PRESET, LOCAL_TESTNET_RUNTIME_PRESET,
340	},
341	json_patch::merge as json_merge,
342};
343pub use soil_chain_spec_macros::{ChainSpecExtension, ChainSpecGroup};
344
345use soil_network::config::MultiaddrWithPeerId;
346use soil_telemetry::TelemetryEndpoints;
347use subsoil::core::storage::Storage;
348use subsoil::runtime::BuildStorage;
349
350/// The type of chain.
351///
352/// This can be used by tools to determine the type of chain for displaying
353/// additional information or enabling additional features.
354#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)]
355#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
356pub enum ChainType {
357	/// A development chain that runs mainly on one node.
358	Development,
359	/// A local chain that runs locally on multiple nodes for testing purposes.
360	Local,
361	/// A live chain.
362	Live,
363	/// Some custom chain type.
364	#[cfg_attr(feature = "clap", clap(skip))]
365	Custom(String),
366}
367
368impl Default for ChainType {
369	fn default() -> Self {
370		Self::Live
371	}
372}
373
374/// Arbitrary properties defined in chain spec as a JSON object
375pub type Properties = serde_json::map::Map<String, serde_json::Value>;
376
377/// Common interface of a chain specification.
378pub trait ChainSpec: BuildStorage + Send + Sync {
379	/// Spec name.
380	fn name(&self) -> &str;
381	/// Spec id.
382	fn id(&self) -> &str;
383	/// Type of the chain.
384	fn chain_type(&self) -> ChainType;
385	/// A list of bootnode addresses.
386	fn boot_nodes(&self) -> &[MultiaddrWithPeerId];
387	/// Telemetry endpoints (if any)
388	fn telemetry_endpoints(&self) -> &Option<TelemetryEndpoints>;
389	/// Network protocol id.
390	fn protocol_id(&self) -> Option<&str>;
391	/// Optional network fork identifier. `None` by default.
392	fn fork_id(&self) -> Option<&str>;
393	/// Additional loosely-typed properties of the chain.
394	///
395	/// Returns an empty JSON object if 'properties' not defined in config
396	fn properties(&self) -> Properties;
397	/// Returns a reference to the defined chain spec extensions.
398	fn extensions(&self) -> &dyn GetExtension;
399	/// Returns a mutable reference to the defined chain spec extensions.
400	fn extensions_mut(&mut self) -> &mut dyn GetExtension;
401	/// Add a bootnode to the list.
402	fn add_boot_node(&mut self, addr: MultiaddrWithPeerId);
403	/// Return spec as JSON.
404	fn as_json(&self, raw: bool) -> Result<String, String>;
405	/// Return StorageBuilder for this spec.
406	fn as_storage_builder(&self) -> &dyn BuildStorage;
407	/// Returns a cloned `Box<dyn ChainSpec>`.
408	fn cloned_box(&self) -> Box<dyn ChainSpec>;
409	/// Set the storage that should be used by this chain spec.
410	///
411	/// This will be used as storage at genesis.
412	fn set_storage(&mut self, storage: Storage);
413	/// Returns code substitutes that should be used for the on chain wasm.
414	fn code_substitutes(&self) -> std::collections::BTreeMap<String, Vec<u8>>;
415}
416
417impl std::fmt::Debug for dyn ChainSpec {
418	fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
419		write!(f, "ChainSpec(name = {:?}, id = {:?})", self.name(), self.id())
420	}
421}