54个版本 (34个主要版本)

37.0.0 2024年7月18日
35.0.0 2024年7月12日
34.0.0 2024年6月24日
33.0.0 2024年5月24日
2.0.0-alpha.52020年3月24日

#1173 in 魔法豆

Download history 2318/week @ 2024-04-22 2762/week @ 2024-04-29 2193/week @ 2024-05-06 2434/week @ 2024-05-13 2963/week @ 2024-05-20 3413/week @ 2024-05-27 3198/week @ 2024-06-03 2391/week @ 2024-06-10 2324/week @ 2024-06-17 3776/week @ 2024-06-24 1117/week @ 2024-07-01 2103/week @ 2024-07-08 3804/week @ 2024-07-15 2556/week @ 2024-07-22 2635/week @ 2024-07-29 2587/week @ 2024-08-05

11,797次每月下载
103 个crate中使用了(17个直接使用)

GPL-3.0-or-later…

4MB
52K SLoC

Substrate链配置。

此crate包含用于声明特定运行时配置文件(即链规范)的结构和实用工具。有关详细信息,请参阅crate文档。

许可:GPL-3.0-or-later WITH Classpath-exception-2.0


lib.rs:

此crate包括用于定义运行时和节点配置文件的结构的实用工具(也称为链规范)。

简介:链规范

链规范包含定义链属性和初始状态的参数和设置。用户通常与链规范的JSON表示进行交互。在内部,链规范通过GenericChainSpec结构体实现,并且可以通过ChainSpec特质访问特定的属性。

总之,尽管不限于,链规范的主要作用是提供区块链网络的一组已知启动节点以及初始化创世存储的方法。这种初始化对于创建后续块构建的基础创世块是必要的。当节点首次启动时,它会读取链规范,初始化创世块,并与启动节点建立连接。

JSON链规范分为两个主要逻辑部分

  • 一部分详细说明通用链属性,
  • 另一部分明确或间接定义创世存储,这反过来又决定了链的创世哈希,

链规范包含以下字段

链规范键 描述
name 链的易读名称。
id 链的id。
chainType 此链的链类型(请参阅 ChainType )。
bootNodes 属于链的启动节点的多地址列表。
telemetryEndpoints 可选的多地址和遥测端点的multi address列表。详细程度从0到9,其中0是最小详细程度的模式。
protocolId 可选的网络协议id,用于标识链。
forkId 可选的分叉id。通常应留空。可以用于在两个链具有相同的创世哈希时在网络级别发出分叉信号。
properties 自定义属性。应以key-value JSON对象的形式提供。
consensusEngine 已弃用字段。应忽略。
codeSubstitutes 可选的block_numberwasm_code的映射。更多详情将在后续材料中提供。
genesis 定义运行时的初始状态。更多详情将在后续材料中提供。

genesis:初始运行时状态

网络中的所有节点都必须在完全相同的创世块上构建后续块。

链规范中genesis部分配置的信息用于构建创世存储,这对于创建包含存储根哈希的创世块是必不可少的。

链规范定义中genesis键描述运行时的初始状态。例如,它可能包含

  • 一组初始资助的账户,
  • 控制sudo密钥的行政账户,
  • 用于共识的初始权威集等。

由于编译后的运行时代码WASM blob存储在链的状态中,初始运行时也必须在链规范中提供。

chain-spec格式

本质上,链规范文件中最重要的创世初始状态格式是

格式 描述
完整配置 一个JSON对象,提供了对由宏 polkadot_sdk_frame::runtime::prelude::construct_runtime 生成的 RuntimeGenesisConfig 结构体的明确和全面表示。该宏可以在 这里 找到示例。必须包含创世纪配置的所有键,不使用默认值。

此格式明确提供了运行时代码。

补丁 一个JSON对象,提供了运行时提供的 RuntimeGenesisConfig 的部分表示。它包含一个补丁,本质上是一个键值对的列表,用于自定义默认运行时的 RuntimeGenesisConfig:`full = default + patch`。请注意,默认的 `RuntimeGenesisConfig` 可能不可用。此格式明确提供了运行时代码。
原始 一个包含两个字段的JSON对象:topchildren_default。每个字段都是一个表示创世纪存储 trie 中条目的键值对映射 key => value。运行时代码是此类条目之一。

对于生产或长期运行的区块链,建议在链规范中使用 raw 格式。只有 raw 格式可以保证在软件升级导致 RuntimeGenesisConfig 格式更改时,存储根哈希保持不变。

以下部分的JSON示例说明了 rawpatch 和完整创世纪字段。

从初始状态到原始创世纪。

要从运行时创世纪配置的JSON表示生成原始创世纪存储,节点需要与运行时进行交互。

这种交互包括使用 sp_genesis_builder::GenesisBuilder::build_state 函数将运行时创世纪配置JSON blob传递给运行时。在此操作过程中,运行时将创世纪配置的JSON表示转换为 sp_io::storage 项目。这是计算存储根哈希的关键步骤,它是确定创世纪哈希的关键组件。

因此,运行时必须支持 sp_genesis_builder::GenesisBuilder API,以利用 patchfull 格式。

整个过程封装在 BuildStorage 特性的实现中,可以通过 ChainSpec::as_storage_builder 方法访问。有一个中间内部助手,GenesisConfigBuilderRuntimeCaller,它为 sc_executor::WasmExecutor 提供了一个简单的包装。

raw 创世状态的情况下,节点不与运行时交互来计算初始状态。

纯文本和 raw 链规范 JSON 文件可以在 JSON 示例 部分找到。

可选代码映射

可选将 block_number 映射到 wasm_code 的映射。

给定的 wasm_code 将用于替换从给定块号开始的链上 wasm 代码,直到 spec_version 在链上发生变化。给定的 wasm_code 应尽可能接近链上 wasm 代码。如果运行时常发生崩溃等问题,可以使用替换来修复无法通过运行时升级修复的 bug。不支持引入新的运行时 API,因为节点将读取链上 wasm 代码中的运行时版本。

仅在别无选择的情况下使用此功能,并且仅修补有问题的 bug;其余应通过链上运行时升级完成。

构建链规范

应使用 ChainSpecBuilder 来创建链规范的一个实例。它的 API 允许配置链规范的所有字段。要生成规范的 JSON 表示形式,请使用 ChainSpec::as_json

以下是一个生成链规范的示例代码

JSON 链规范示例

以下是从上述 示例 执行后得到的链规范 JSON 文件的纯文本和 raw 版本。

以下示例展示了链规范的纯文本完整配置版本

ChainSpec trait 表示访问 JSON 链规范中定义的值的 API。

自定义链规范扩展

包含所有必需参数的基本链规范类型是 GenericChainSpec。它可以扩展到包含特定于您链的配置的附加选项。通常,扩展将是 Substrate 核心模块公开的类型组合。

为了允许核心模块从您的扩展中检索其配置,您应使用此 crate 公开的 ChainSpecExtension 宏。

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>;

某些参数可能需要根据当前区块链高度(即分叉)具有不同的值。您可以使用 ChainSpecGroup 宏和提供的 Forks 结构来将此类参数添加到您的链规范中。这将允许从特定块号开始覆盖单个参数。

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>>;

还可能有一组参数可以随着块号的变化而变化(即可以分叉),而另一组则不受变化的影响。这也可以通过声明一个包含 Forks 的扩展来实现。

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>;

链规范可以扩展其他对默认链规范来说是不可见的字段。特定的节点实现需要能够反序列化这些扩展。

依赖关系

~73–115MB
~2M SLoC