Expand description
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.
§chain-spec
formats
In essence, the most important formats of genesis initial state in chain specification files are:
Format | Description |
---|---|
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
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 : `full = default + patch`. Please note that `default`
`RuntimeGenesisConfig` may not be functional.
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. |
The main purpose of the RuntimeGenesisConfig
patch is to:
- minimize the maintenance effort when RuntimeGenesisConfig is changed in the future (e.g. new pallets added to the runtime or pallet’s genesis config changed),
- increase the readability - it only contains the relevant fields,
- allow to apply numerous changes in distinct domains (e.g. for zombienet).
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_state
function. During this operation, the
runtime converts the JSON representation of the genesis config into sp_io::storage
items. It
is a crucial step for computing the storage root hash, which is a key component in determining
the genesis hash.
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:
#[test]
fn build_chain_spec_with_patch_works() {
let output = ChainSpec::<()>::builder(
substrate_test_runtime::wasm_binary_unwrap().into(),
Default::default(),
)
.with_name("TestName")
.with_id("test_id")
.with_chain_type(ChainType::Local)
.with_genesis_config_patch(json!({
"babe": {
"epochConfig": {
"c": [
7,
10
],
"allowed_slots": "PrimaryAndSecondaryPlainSlots"
}
},
"substrateTest": {
"authorities": [
AccountKeyring::Ferdie.public().to_ss58check(),
AccountKeyring::Alice.public().to_ss58check()
],
}
}))
.build();
let raw_chain_spec = output.as_json(true);
assert!(raw_chain_spec.is_ok());
}
§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:
{
"name": "TestName",
"id": "test_id",
"chainType": "Local",
"bootNodes": [],
"telemetryEndpoints": null,
"protocolId": null,
"properties": null,
"codeSubstitutes": {},
"genesis": {
"runtimeGenesis": {
"patch": {
"babe": {
"epochConfig": {
"allowed_slots": "PrimaryAndSecondaryPlainSlots",
"c": [
7,
10
]
}
},
"substrateTest": {
"authorities": [
"5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL",
"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"
]
}
},
"code": "0x0"
}
}
}
{
"name": "TestName",
"id": "test_id",
"chainType": "Local",
"bootNodes": [],
"telemetryEndpoints": null,
"protocolId": null,
"properties": null,
"codeSubstitutes": {},
"genesis": {
"raw": {
"top": {
"0x00771836bebdd29870ff246d305c578c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000",
"0x00771836bebdd29870ff246d305c578c5e0621c4869aa60c02be9adcc98a0d1d": "0x081cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07cd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d",
"0x1cb6f36e027abb2091cfb5110ab5087f4e7b9012096b41c4eb3aaf947f6ea429": "0x0000",
"0x1cb6f36e027abb2091cfb5110ab5087f66e8f035c8adbe7f1547b43c51e6f8a4": "0x00000000",
"0x1cb6f36e027abb2091cfb5110ab5087fdc6b171b77304263c292cc3ea5ed31ef": "0x07000000000000000a0000000000000001",
"0x26aa394eea5630e07c48ae0c9558cef74e7b9012096b41c4eb3aaf947f6ea429": "0x0000",
"0x26aa394eea5630e07c48ae0c9558cef75684a022a34dd8bfa2baaf44f172b710": "0x01",
"0x26aa394eea5630e07c48ae0c9558cef78a42f33323cb5ced3b44dd825fda9fcc": "0x4545454545454545454545454545454545454545454545454545454545454545",
"0x26aa394eea5630e07c48ae0c9558cef7a44704b568d21667356a5a050c118746bb1bdbcacd6ac9340000000000000000": "0x4545454545454545454545454545454545454545454545454545454545454545",
"0x26aa394eea5630e07c48ae0c9558cef7a7fd6c28836b9a28522dc924110cf439": "0x01",
"0x26aa394eea5630e07c48ae0c9558cef7f9cce9c888469bb1a0dceaa129672ef8": "0x0000",
"0x3a636f6465": "0x0",
"0x3a65787472696e7369635f696e646578": "0x00000000",
"0xc2261276cc9d1f8598ea4b6a74b15c2f4e7b9012096b41c4eb3aaf947f6ea429": "0x0100",
"0xc2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80": "0x0000000000000000"
},
"childrenDefault": {}
}
}
}
The following example shows the plain full config version of chain spec:
{
"name": "TestName",
"id": "test_id",
"chainType": "Local",
"bootNodes": [],
"telemetryEndpoints": null,
"protocolId": null,
"properties": null,
"codeSubstitutes": {},
"genesis": {
"runtimeGenesis": {
"config": {
"babe": {
"authorities": [
[
"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
1
],
[
"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty",
1
],
[
"5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y",
1
]
],
"epochConfig": {
"allowed_slots": "PrimaryAndSecondaryPlainSlots",
"c": [
3,
10
]
}
},
"balances": {
"balances": [
[
"5D34dL5prEUaGNQtPPZ3yN5Y6BnkfXunKXXz6fo7ZJbLwRRH",
100000000000000000
],
[
"5GBNeWRhZc2jXu7D55rBimKYDk8PGk8itRYFTPfC8RJLKG5o",
100000000000000000
],
[
"5Dfis6XL8J2P6JHUnUtArnFWndn62SydeP8ee8sG2ky9nfm9",
100000000000000000
],
[
"5F4H97f7nQovyrbiq4ZetaaviNwThSVcFobcA5aGab6167dK",
100000000000000000
],
[
"5DiDShBWa1fQx6gLzpf3SFBhMinCoyvHM1BWjPNsmXS8hkrW",
100000000000000000
],
[
"5EFb84yH9tpcFuiKUcsmdoF7xeeY3ajG1ZLQimxQoFt9HMKR",
100000000000000000
],
[
"5DZLHESsfGrJ5YzT3HuRPXsSNb589xQ4Unubh1mYLodzKdVY",
100000000000000000
],
[
"5GHJzqvG6tXnngCpG7B12qjUvbo5e4e9z8Xjidk3CQZHxTPZ",
100000000000000000
],
[
"5CUnSsgAyLND3bxxnfNhgWXSe9Wn676JzLpGLgyJv858qhoX",
100000000000000000
],
[
"5CVKn7HAZW1Ky4r7Vkgsr7VEW88C2sHgUNDiwHY9Ct2hjU8q",
100000000000000000
],
[
"5H673aukQ4PeDe1U2nuv1bi32xDEziimh3PZz7hDdYUB7TNz",
100000000000000000
],
[
"5HTe9L15LJryjUAt1jZXZCBPnzbbGnpvFwbjE3NwCWaAqovf",
100000000000000000
],
[
"5D7LFzGpMwHPyDBavkRbWSKWTtJhCaPPZ379wWLT23bJwXJz",
100000000000000000
],
[
"5CLepMARnEgtVR1EkUuJVUvKh97gzergpSxUU3yKGx1v6EwC",
100000000000000000
],
[
"5Chb2UhfvZpmjjEziHbFbotM4quX32ZscRV6QJBt1rUKzz51",
100000000000000000
],
[
"5HmRp3i3ZZk7xsAvbi8hyXVP6whSMnBJGebVC4FsiZVhx52e",
100000000000000000
],
[
"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
100000000000000000
],
[
"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty",
100000000000000000
],
[
"5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y",
100000000000000000
]
]
},
"substrateTest": {
"authorities": [
"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty",
"5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y"
]
},
"system": {}
},
"code": "0x0"
}
}
}
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 = GenericChainSpec<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 = GenericChainSpec<Forks<BlockNumber, ClientParams>>;
/// A chain spec supporting forkable `Extension`.
pub type MyChainSpec2 = GenericChainSpec<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 = GenericChainSpec<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.
Re-exports§
pub use self::json_patch::merge as json_merge;
Modules§
- A helper module providing json patching functions.
Structs§
- Builder for creating
ChainSpec
instances. - A configuration of a chain. Can be used to build a genesis block.
- Default genesis block builder in Substrate.
- A utility that facilitates calling the GenesisBuilder API from the runtime wasm code blob.
Enums§
- The type of chain.
Constants§
- The default
development
preset used to communicate with the runtime viaGenesisBuilder
interface. - The default
local_testnet
preset used to communicate with the runtime viaGenesisBuilder
interface.
Traits§
- Trait for building the genesis block.
- Common interface of a chain specification.
- A collection of
ChainSpec
extensions. - A
ChainSpec
extension fork definition. - A subset of the
Extension
trait that only allows for querying extensions. - A
ChainSpec
extension.
Functions§
- Create a genesis block, given the initial storage.
- Helper function that queries an extension by type from
GetExtension
trait object. - Helper function that queries an extension by type from
GetExtension
trait object. - Return the state version given the genesis storage and executor.
- This function sets a codeSubstitute in the chain spec.
- This function updates the code in given chain spec.
Type Aliases§
- A type denoting empty extensions.
- Arbitrary properties defined in chain spec as a JSON object