49 releases (29 major breaking)

31.0.0 Apr 9, 2024
30.0.1 Apr 4, 2024
30.0.0 Mar 18, 2024
29.0.0 Feb 26, 2024
2.0.0-alpha.5 Mar 24, 2020

#1154 in Magic Beans

Download history 878/week @ 2023-12-22 1040/week @ 2023-12-29 1715/week @ 2024-01-05 1771/week @ 2024-01-12 1589/week @ 2024-01-19 1313/week @ 2024-01-26 1115/week @ 2024-02-02 2302/week @ 2024-02-09 2887/week @ 2024-02-16 2939/week @ 2024-02-23 2052/week @ 2024-03-01 2519/week @ 2024-03-08 2444/week @ 2024-03-15 2823/week @ 2024-03-22 3127/week @ 2024-03-29 2504/week @ 2024-04-05

11,206 downloads per month
Used in 100 crates (14 directly)

GPL-3.0-or-later…

3.5MB
44K SLoC

Substrate chain configurations.

This crate contains structs and utilities to declare a runtime-specific configuration file (a.k.a chain spec). Refer to crate documentation for details.

License: GPL-3.0-or-later WITH Classpath-exception-2.0


lib.rs:

This crate includes structs and utilities for defining configuration files (known as chain specification) for both runtime and node.

Intro: Chain Specification

The chain specification comprises parameters and settings that define the properties and an initial state of a chain. Users typically interact with the JSON representation of the chain spec. Internally, the chain spec is embodied by the GenericChainSpec struct, and specific properties can be accessed using the ChainSpec trait.

In summary, although not restricted to, the primary role of the chain spec is to provide a list of well-known boot nodes for the blockchain network and the means for initializing the genesis storage. This initialization is necessary for creating a genesis block upon which subsequent blocks are built. When the node is launched for the first time, it reads the chain spec, initializes the genesis block, and establishes connections with the boot nodes.

The JSON chain spec is divided into two main logical sections:

  • one section details general chain properties,
  • second explicitly or indirectly defines the genesis storage, which, in turn, determines the genesis hash of the chain,

The chain specification consists of the following fields:

Chain spec key Description
name The human readable name of the chain.
id The id of the chain.
chainType The chain type of this chain (refer to ChainType ).
bootNodes A list of multi addresses that belong to boot nodes of the chain.
telemetryEndpoints Optional list of multi address, verbosity of telemetry endpoints. The verbosity goes from 0 to 9. With 0 being the mode with the lowest verbosity.
protocolId Optional networking protocol id that identifies the chain.
forkId Optional fork id. Should most likely be left empty. Can be used to signal a fork on the network level when two chains have the same genesis hash.
properties Custom properties. Shall be provided in the form of key-value json object.
consensusEngine Deprecated field. Should be ignored.
codeSubstitutes Optional map of block_number to wasm_code. More details in material to follow.
genesis Defines the initial state of the runtime. More details in material to follow.

genesis: Initial Runtime State

All nodes in the network must build subsequent blocks upon exactly the same genesis block.

The information configured in the genesis section of a chain specification is used to build the genesis storage, which is essential for creating the genesis block, since the block header includes the storage root hash.

The genesis key of the chain specification definition describes the initial state of the runtime. For example, it may contain:

  • an initial list of funded accounts,
  • the administrative account that controls the sudo key,
  • an initial authorities set for consensus, etc.

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:

Format Description
runtime / full config A JSON object that provides an explicit and comprehensive representation of the RuntimeGenesisConfig struct, which is generated by frame::runtime::prelude::construct_runtime macro (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.

patch 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. This format explicitly provides the code of the runtime.
raw A JSON object with two fields: top and children_default. Each field is a map of key => value pairs representing entries in a genesis storage trie. The runtime code is one of such entries.

For production or long-lasting blockchains, using the raw format in the chain specification is recommended. Only the raw format guarantees that storage root hash will remain unchanged when the RuntimeGenesisConfig format changes due to software upgrade.

JSON examples in the following section illustrate the raw patch and full genesis fields.

From Initial State to Raw Genesis.

To generate a raw genesis storage from the JSON representation of the runtime genesis config, the node needs to interact with the runtime.

This interaction involves passing the runtime genesis config JSON blob to the runtime using the sp_genesis_builder::GenesisBuilder::build_config function. During this operation, the runtime converts the JSON representation of the genesis config into sp_io::storage items. It is a crucial step for computing the storage root hash, which is a key component in determining the genesis hash.

Consequently, the runtime must support the sp_genesis_builder::GenesisBuilder API to utilize either patch or full formats.

This entire process is encapsulated within the implementation of the BuildStorage trait, which can be accessed through the ChainSpec::as_storage_builder method. There is an intermediate internal helper that facilitates this interaction, GenesisConfigBuilderRuntimeCaller, which serves as a straightforward wrapper for sc_executor::WasmExecutor.

In case of raw genesis state the node does not interact with the runtime regarding the computation of initial state.

The plain and raw chain specification JSON blobs can be found in JSON examples section.

Optional Code Mapping

Optional map of block_number to wasm_code.

The given wasm_code will be used to substitute the on-chain wasm code starting with the given block number until the spec_version on-chain changes. The given wasm_code should be as close as possible to the on-chain wasm code. A substitute should be used to fix a bug that cannot be fixed with a runtime upgrade, if for example the runtime is constantly panicking. Introducing new runtime APIs isn't supported, because the node will read the runtime version from the on-chain wasm code.

Use this functionality only when there is no other way around it, and only patch the problematic bug; the rest should be done with an on-chain runtime upgrade.

Building a Chain Specification

The ChainSpecBuilder should be used to create an instance of a chain specification. Its API allows configuration of all fields of the chain spec. To generate a JSON representation of the specification, use ChainSpec::as_json.

The sample code to generate a chain spec is as follows:

JSON chain specification example

The following are the plain and raw versions of the chain specification JSON files, resulting from executing of the above example:

The following example shows the plain full config version of chain spec:

The ChainSpec trait represents the API to access values defined in the JSON chain specification.

Custom Chain Spec Extensions

The basic chain spec type containing all required parameters is GenericChainSpec. It can be extended with additional options containing configuration specific to your chain. Usually, the extension will be a combination of types exposed by Substrate core modules.

To allow the core modules to retrieve their configuration from your extension, you should use ChainSpecExtension macro exposed by this crate.

use std::collections::HashMap;
use sc_chain_spec::{GenericChainSpec, ChainSpecExtension};

#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, ChainSpecExtension)]
pub struct MyExtension {
	pub known_blocks: HashMap<u64, String>,
}

pub type MyChainSpec<G> = GenericChainSpec<G, MyExtension>;

Some parameters may require different values depending on the current blockchain height (a.k.a. forks). You can use the ChainSpecGroup macro and the provided Forks structure to add such parameters to your chain spec. This will allow overriding a single parameter starting at a specific block number.

use sc_chain_spec::{Forks, ChainSpecGroup, ChainSpecExtension, GenericChainSpec};

#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, ChainSpecGroup)]
pub struct ClientParams {
	max_block_size: usize,
	max_extrinsic_size: usize,
}

#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, ChainSpecGroup)]
pub struct PoolParams {
	max_transaction_size: usize,
}

#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, ChainSpecGroup, ChainSpecExtension)]
pub struct Extension {
	pub client: ClientParams,
	pub pool: PoolParams,
}

pub type BlockNumber = u64;

/// A chain spec supporting forkable `ClientParams`.
pub type MyChainSpec1<G> = GenericChainSpec<G, Forks<BlockNumber, ClientParams>>;

/// A chain spec supporting forkable `Extension`.
pub type MyChainSpec2<G> = GenericChainSpec<G, Forks<BlockNumber, Extension>>;

It's also possible to have a set of parameters that are allowed to change with block numbers (i.e., they are forkable), and another set that is not subject to changes. This can also be achieved by declaring an extension that contains Forks within it.

use serde::{Serialize, Deserialize};
use sc_chain_spec::{Forks, GenericChainSpec, ChainSpecGroup, ChainSpecExtension};

#[derive(Clone, Debug, Serialize, Deserialize, ChainSpecGroup)]
pub struct ClientParams {
	max_block_size: usize,
	max_extrinsic_size: usize,
}

#[derive(Clone, Debug, Serialize, Deserialize, ChainSpecGroup)]
pub struct PoolParams {
	max_transaction_size: usize,
}

#[derive(Clone, Debug, Serialize, Deserialize, ChainSpecExtension)]
pub struct Extension {
	pub client: ClientParams,
	#[forks]
	pub pool: Forks<u64, PoolParams>,
}

pub type MyChainSpec<G> = GenericChainSpec<G, Extension>;

The chain spec can be extended with other fields that are opaque to the default chain spec. Specific node implementations will need to be able to deserialize these extensions.

Dependencies

~53–95MB
~1.5M SLoC