feat: Rebrand Polkadot/Substrate references to PezkuwiChain
This commit systematically rebrands various references from Parity Technologies' Polkadot/Substrate ecosystem to PezkuwiChain within the kurdistan-sdk. Key changes include: - Updated external repository URLs (zombienet-sdk, parity-db, parity-scale-codec, wasm-instrument) to point to pezkuwichain forks. - Modified internal documentation and code comments to reflect PezkuwiChain naming and structure. - Replaced direct references to with or specific paths within the for XCM, Pezkuwi, and other modules. - Cleaned up deprecated issue and PR references in various and files, particularly in and modules. - Adjusted image and logo URLs in documentation to point to PezkuwiChain assets. - Removed or rephrased comments related to external Polkadot/Substrate PRs and issues. This is a significant step towards fully customizing the SDK for the PezkuwiChain ecosystem.
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,433 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! Chain Spec extensions helpers.
|
||||
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
fmt::Debug,
|
||||
};
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
|
||||
/// A `ChainSpec` extension.
|
||||
///
|
||||
/// This trait is implemented automatically by `ChainSpecGroup` macro.
|
||||
pub trait Group: Clone + Sized {
|
||||
/// An associated type containing fork definition.
|
||||
type Fork: Fork<Base = Self>;
|
||||
|
||||
/// Convert to fork type.
|
||||
fn to_fork(self) -> Self::Fork;
|
||||
}
|
||||
|
||||
/// A `ChainSpec` extension fork definition.
|
||||
///
|
||||
/// Basically should look the same as `Group`, but
|
||||
/// all parameters are optional. This allows changing
|
||||
/// only one parameter as part of the fork.
|
||||
/// The forks can be combined (summed up) to specify
|
||||
/// a complete set of parameters
|
||||
pub trait Fork: Serialize + DeserializeOwned + Clone + Sized {
|
||||
/// A base `Group` type.
|
||||
type Base: Group<Fork = Self>;
|
||||
|
||||
/// Combine with another struct.
|
||||
///
|
||||
/// All parameters set in `other` should override the
|
||||
/// ones in the current struct.
|
||||
fn combine_with(&mut self, other: Self);
|
||||
|
||||
/// Attempt to convert to the base type if all parameters are set.
|
||||
fn to_base(self) -> Option<Self::Base>;
|
||||
}
|
||||
|
||||
macro_rules! impl_trivial {
|
||||
() => {};
|
||||
($A : ty) => {
|
||||
impl_trivial!($A ,);
|
||||
};
|
||||
($A : ty , $( $B : ty ),*) => {
|
||||
impl_trivial!($( $B ),*);
|
||||
|
||||
impl Group for $A {
|
||||
type Fork = $A;
|
||||
|
||||
fn to_fork(self) -> Self::Fork {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Fork for $A {
|
||||
type Base = $A;
|
||||
|
||||
fn combine_with(&mut self, other: Self) {
|
||||
*self = other;
|
||||
}
|
||||
|
||||
fn to_base(self) -> Option<Self::Base> {
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_trivial!((), u8, u16, u32, u64, usize, String, Vec<u8>);
|
||||
|
||||
impl<T: Group> Group for Option<T> {
|
||||
type Fork = Option<T::Fork>;
|
||||
|
||||
fn to_fork(self) -> Self::Fork {
|
||||
self.map(|a| a.to_fork())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Fork> Fork for Option<T> {
|
||||
type Base = Option<T::Base>;
|
||||
|
||||
fn combine_with(&mut self, other: Self) {
|
||||
*self = match (self.take(), other) {
|
||||
(Some(mut a), Some(b)) => {
|
||||
a.combine_with(b);
|
||||
Some(a)
|
||||
},
|
||||
(a, b) => a.or(b),
|
||||
};
|
||||
}
|
||||
|
||||
fn to_base(self) -> Option<Self::Base> {
|
||||
self.map(|x| x.to_base())
|
||||
}
|
||||
}
|
||||
|
||||
/// A collection of `ChainSpec` extensions.
|
||||
///
|
||||
/// This type can be passed around and allows the core
|
||||
/// modules to request a strongly-typed, but optional configuration.
|
||||
pub trait Extension: Serialize + DeserializeOwned + Clone {
|
||||
type Forks: IsForks;
|
||||
|
||||
/// Get an extension of specific type.
|
||||
fn get<T: 'static>(&self) -> Option<&T>;
|
||||
/// Get an extension of specific type as reference to `Any`.
|
||||
fn get_any(&self, t: TypeId) -> &dyn Any;
|
||||
/// Get an extension of specific type as mutable reference to `Any`.
|
||||
fn get_any_mut(&mut self, t: TypeId) -> &mut dyn Any;
|
||||
|
||||
/// Get forkable extensions of specific type.
|
||||
fn forks<BlockNumber, T>(&self) -> Option<Forks<BlockNumber, T>>
|
||||
where
|
||||
BlockNumber: Ord + Clone + 'static,
|
||||
T: Group + 'static,
|
||||
<Self::Forks as IsForks>::Extension: Extension,
|
||||
<<Self::Forks as IsForks>::Extension as Group>::Fork: Extension,
|
||||
{
|
||||
self.get::<Forks<BlockNumber, <Self::Forks as IsForks>::Extension>>()?
|
||||
.for_type()
|
||||
}
|
||||
}
|
||||
|
||||
impl Extension for crate::NoExtension {
|
||||
type Forks = Self;
|
||||
|
||||
fn get<T: 'static>(&self) -> Option<&T> {
|
||||
None
|
||||
}
|
||||
fn get_any(&self, _t: TypeId) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn get_any_mut(&mut self, _: TypeId) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub trait IsForks {
|
||||
type BlockNumber: Ord + 'static;
|
||||
type Extension: Group + 'static;
|
||||
}
|
||||
|
||||
impl IsForks for Option<()> {
|
||||
type BlockNumber = u64;
|
||||
type Extension = Self;
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Forks<BlockNumber: Ord, T: Group> {
|
||||
forks: BTreeMap<BlockNumber, T::Fork>,
|
||||
#[serde(flatten)]
|
||||
base: T,
|
||||
}
|
||||
|
||||
impl<B: Ord, T: Group + Default> Default for Forks<B, T> {
|
||||
fn default() -> Self {
|
||||
Self { base: Default::default(), forks: Default::default() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Ord, T: Group> Forks<B, T>
|
||||
where
|
||||
T::Fork: Debug,
|
||||
{
|
||||
/// Create new fork definition given the base and the forks.
|
||||
pub fn new(base: T, forks: BTreeMap<B, T::Fork>) -> Self {
|
||||
Self { base, forks }
|
||||
}
|
||||
|
||||
/// Return a set of parameters for `Group` including all forks up to `block` (inclusive).
|
||||
pub fn at_block(&self, block: B) -> T {
|
||||
let mut start = self.base.clone().to_fork();
|
||||
|
||||
for (_, fork) in self.forks.range(..=block) {
|
||||
start.combine_with(fork.clone());
|
||||
}
|
||||
|
||||
start
|
||||
.to_base()
|
||||
.expect("We start from the `base` object, so it's always fully initialized; qed")
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, T> IsForks for Forks<B, T>
|
||||
where
|
||||
B: Ord + 'static,
|
||||
T: Group + 'static,
|
||||
{
|
||||
type BlockNumber = B;
|
||||
type Extension = T;
|
||||
}
|
||||
|
||||
impl<B: Ord + Clone, T: Group + Extension> Forks<B, T>
|
||||
where
|
||||
T::Fork: Extension,
|
||||
{
|
||||
/// Get forks definition for a subset of this extension.
|
||||
///
|
||||
/// Returns the `Forks` struct, but limited to a particular type
|
||||
/// within the extension.
|
||||
pub fn for_type<X>(&self) -> Option<Forks<B, X>>
|
||||
where
|
||||
X: Group + 'static,
|
||||
{
|
||||
let base = self.base.get::<X>()?.clone();
|
||||
let forks = self
|
||||
.forks
|
||||
.iter()
|
||||
.filter_map(|(k, v)| Some((k.clone(), v.get::<Option<X::Fork>>()?.clone()?)))
|
||||
.collect();
|
||||
|
||||
Some(Forks { base, forks })
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, E> Extension for Forks<B, E>
|
||||
where
|
||||
B: Serialize + DeserializeOwned + Ord + Clone + 'static,
|
||||
E: Extension + Group + 'static,
|
||||
{
|
||||
type Forks = Self;
|
||||
|
||||
fn get<T: 'static>(&self) -> Option<&T> {
|
||||
if TypeId::of::<T>() == TypeId::of::<E>() {
|
||||
<dyn Any>::downcast_ref(&self.base)
|
||||
} else {
|
||||
self.base.get()
|
||||
}
|
||||
}
|
||||
|
||||
fn get_any(&self, t: TypeId) -> &dyn Any {
|
||||
if t == TypeId::of::<E>() {
|
||||
&self.base
|
||||
} else {
|
||||
self.base.get_any(t)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_any_mut(&mut self, t: TypeId) -> &mut dyn Any {
|
||||
if t == TypeId::of::<E>() {
|
||||
&mut self.base
|
||||
} else {
|
||||
self.base.get_any_mut(t)
|
||||
}
|
||||
}
|
||||
|
||||
fn forks<BlockNumber, T>(&self) -> Option<Forks<BlockNumber, T>>
|
||||
where
|
||||
BlockNumber: Ord + Clone + 'static,
|
||||
T: Group + 'static,
|
||||
<Self::Forks as IsForks>::Extension: Extension,
|
||||
<<Self::Forks as IsForks>::Extension as Group>::Fork: Extension,
|
||||
{
|
||||
if TypeId::of::<BlockNumber>() == TypeId::of::<B>() {
|
||||
<dyn Any>::downcast_ref(&self.for_type::<T>()?).cloned()
|
||||
} else {
|
||||
self.get::<Forks<BlockNumber, <Self::Forks as IsForks>::Extension>>()?
|
||||
.for_type()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A subset of the `Extension` trait that only allows for querying extensions.
|
||||
pub trait GetExtension {
|
||||
/// Get an extension of specific type.
|
||||
fn get_any(&self, t: TypeId) -> &dyn Any;
|
||||
|
||||
/// Get an extension of specific type with mutable access.
|
||||
fn get_any_mut(&mut self, t: TypeId) -> &mut dyn Any;
|
||||
}
|
||||
|
||||
impl<E: Extension> GetExtension for E {
|
||||
fn get_any(&self, t: TypeId) -> &dyn Any {
|
||||
Extension::get_any(self, t)
|
||||
}
|
||||
|
||||
fn get_any_mut(&mut self, t: TypeId) -> &mut dyn Any {
|
||||
Extension::get_any_mut(self, t)
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper function that queries an extension by type from `GetExtension` trait object.
|
||||
pub fn get_extension<T: 'static>(e: &dyn GetExtension) -> Option<&T> {
|
||||
<dyn Any>::downcast_ref(GetExtension::get_any(e, TypeId::of::<T>()))
|
||||
}
|
||||
|
||||
/// Helper function that queries an extension by type from `GetExtension` trait object.
|
||||
pub fn get_extension_mut<T: 'static>(e: &mut dyn GetExtension) -> Option<&mut T> {
|
||||
<dyn Any>::downcast_mut(GetExtension::get_any_mut(e, TypeId::of::<T>()))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use pezsc_chain_spec_derive::{ChainSpecExtension, ChainSpecGroup};
|
||||
// Make the proc macro work for tests and doc tests.
|
||||
use crate as pezsc_chain_spec;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ChainSpecGroup)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Extension1 {
|
||||
pub test: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ChainSpecGroup)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Extension2 {
|
||||
pub test: u8,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Debug, Clone, PartialEq, Serialize, Deserialize, ChainSpecGroup, ChainSpecExtension,
|
||||
)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Extensions {
|
||||
pub ext1: Extension1,
|
||||
pub ext2: Extension2,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ChainSpecExtension)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Ext2 {
|
||||
#[serde(flatten)]
|
||||
ext1: Extension1,
|
||||
#[forks]
|
||||
forkable: Forks<u64, Extensions>,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn forks_should_work_correctly() {
|
||||
use super::Extension as _;
|
||||
|
||||
// We first need to deserialize into a `Value` because of the following bug:
|
||||
// https://github.com/serde-rs/json/issues/505
|
||||
let ext_val: serde_json::Value = serde_json::from_str(
|
||||
r#"
|
||||
{
|
||||
"test": 11,
|
||||
"forkable": {
|
||||
"ext1": {
|
||||
"test": 15
|
||||
},
|
||||
"ext2": {
|
||||
"test": 123
|
||||
},
|
||||
"forks": {
|
||||
"1": {
|
||||
"ext1": { "test": 5 }
|
||||
},
|
||||
"2": {
|
||||
"ext2": { "test": 5 }
|
||||
},
|
||||
"5": {
|
||||
"ext2": { "test": 1 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let ext: Ext2 = serde_json::from_value(ext_val).unwrap();
|
||||
|
||||
assert_eq!(ext.get::<Extension1>(), Some(&Extension1 { test: 11 }));
|
||||
|
||||
// get forks definition
|
||||
let forks = ext.get::<Forks<u64, Extensions>>().unwrap();
|
||||
assert_eq!(
|
||||
forks.at_block(0),
|
||||
Extensions { ext1: Extension1 { test: 15 }, ext2: Extension2 { test: 123 } }
|
||||
);
|
||||
assert_eq!(
|
||||
forks.at_block(1),
|
||||
Extensions { ext1: Extension1 { test: 5 }, ext2: Extension2 { test: 123 } }
|
||||
);
|
||||
assert_eq!(
|
||||
forks.at_block(2),
|
||||
Extensions { ext1: Extension1 { test: 5 }, ext2: Extension2 { test: 5 } }
|
||||
);
|
||||
assert_eq!(
|
||||
forks.at_block(4),
|
||||
Extensions { ext1: Extension1 { test: 5 }, ext2: Extension2 { test: 5 } }
|
||||
);
|
||||
assert_eq!(
|
||||
forks.at_block(5),
|
||||
Extensions { ext1: Extension1 { test: 5 }, ext2: Extension2 { test: 1 } }
|
||||
);
|
||||
assert_eq!(
|
||||
forks.at_block(10),
|
||||
Extensions { ext1: Extension1 { test: 5 }, ext2: Extension2 { test: 1 } }
|
||||
);
|
||||
assert!(forks.at_block(10).get::<Extension2>().is_some());
|
||||
|
||||
// filter forks for `Extension2`
|
||||
let ext2 = forks.for_type::<Extension2>().unwrap();
|
||||
assert_eq!(ext2.at_block(0), Extension2 { test: 123 });
|
||||
assert_eq!(ext2.at_block(2), Extension2 { test: 5 });
|
||||
assert_eq!(ext2.at_block(10), Extension2 { test: 1 });
|
||||
|
||||
// make sure that it can return forks correctly
|
||||
let ext2_2 = forks.forks::<u64, Extension2>().unwrap();
|
||||
assert_eq!(ext2, ext2_2);
|
||||
|
||||
// also ext should be able to return forks correctly:
|
||||
let ext2_3 = ext.forks::<u64, Extension2>().unwrap();
|
||||
assert_eq!(ext2_2, ext2_3);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! Tool for creating the genesis block.
|
||||
|
||||
use std::{marker::PhantomData, sync::Arc};
|
||||
|
||||
use codec::Encode;
|
||||
use pezsc_client_api::{backend::Backend, BlockImportOperation};
|
||||
use pezsc_executor::RuntimeVersionOf;
|
||||
use pezsp_core::storage::{well_known_keys, StateVersion, Storage};
|
||||
use pezsp_runtime::{
|
||||
traits::{Block as BlockT, Hash as HashT, HashingFor, Header as HeaderT, Zero},
|
||||
BuildStorage,
|
||||
};
|
||||
|
||||
/// Return the state version given the genesis storage and executor.
|
||||
pub fn resolve_state_version_from_wasm<E, H>(
|
||||
storage: &Storage,
|
||||
executor: &E,
|
||||
) -> pezsp_blockchain::Result<StateVersion>
|
||||
where
|
||||
E: RuntimeVersionOf,
|
||||
H: HashT,
|
||||
{
|
||||
if let Some(wasm) = storage.top.get(well_known_keys::CODE) {
|
||||
let mut ext = pezsp_state_machine::BasicExternalities::new_empty(); // just to read runtime version.
|
||||
|
||||
let code_fetcher = pezsp_core::traits::WrappedRuntimeCode(wasm.as_slice().into());
|
||||
let runtime_code = pezsp_core::traits::RuntimeCode {
|
||||
code_fetcher: &code_fetcher,
|
||||
heap_pages: None,
|
||||
hash: <H as HashT>::hash(wasm).encode(),
|
||||
};
|
||||
let runtime_version = RuntimeVersionOf::runtime_version(executor, &mut ext, &runtime_code)
|
||||
.map_err(|e| pezsp_blockchain::Error::VersionInvalid(e.to_string()))?;
|
||||
Ok(runtime_version.state_version())
|
||||
} else {
|
||||
Err(pezsp_blockchain::Error::VersionInvalid(
|
||||
"Runtime missing from initial storage, could not read state version.".to_string(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a genesis block, given the initial storage.
|
||||
pub fn construct_genesis_block<Block: BlockT>(
|
||||
state_root: Block::Hash,
|
||||
state_version: StateVersion,
|
||||
) -> Block {
|
||||
let extrinsics_root = <<<Block as BlockT>::Header as HeaderT>::Hashing as HashT>::trie_root(
|
||||
Vec::new(),
|
||||
state_version,
|
||||
);
|
||||
|
||||
Block::new(
|
||||
<<Block as BlockT>::Header as HeaderT>::new(
|
||||
Zero::zero(),
|
||||
extrinsics_root,
|
||||
state_root,
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
),
|
||||
Default::default(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Trait for building the genesis block.
|
||||
pub trait BuildGenesisBlock<Block: BlockT> {
|
||||
/// The import operation used to import the genesis block into the backend.
|
||||
type BlockImportOperation;
|
||||
|
||||
/// Returns the built genesis block along with the block import operation
|
||||
/// after setting the genesis storage.
|
||||
fn build_genesis_block(self) -> pezsp_blockchain::Result<(Block, Self::BlockImportOperation)>;
|
||||
}
|
||||
|
||||
/// Default genesis block builder in Bizinikiwi.
|
||||
pub struct GenesisBlockBuilder<Block: BlockT, B, E> {
|
||||
genesis_storage: Storage,
|
||||
commit_genesis_state: bool,
|
||||
backend: Arc<B>,
|
||||
executor: E,
|
||||
_phantom: PhantomData<Block>,
|
||||
}
|
||||
|
||||
impl<Block: BlockT, B: Backend<Block>, E: RuntimeVersionOf> GenesisBlockBuilder<Block, B, E> {
|
||||
/// Constructs a new instance of [`GenesisBlockBuilder`].
|
||||
pub fn new(
|
||||
build_genesis_storage: &dyn BuildStorage,
|
||||
commit_genesis_state: bool,
|
||||
backend: Arc<B>,
|
||||
executor: E,
|
||||
) -> pezsp_blockchain::Result<Self> {
|
||||
let genesis_storage =
|
||||
build_genesis_storage.build_storage().map_err(pezsp_blockchain::Error::Storage)?;
|
||||
Self::new_with_storage(genesis_storage, commit_genesis_state, backend, executor)
|
||||
}
|
||||
|
||||
/// Constructs a new instance of [`GenesisBlockBuilder`] using provided storage.
|
||||
pub fn new_with_storage(
|
||||
genesis_storage: Storage,
|
||||
commit_genesis_state: bool,
|
||||
backend: Arc<B>,
|
||||
executor: E,
|
||||
) -> pezsp_blockchain::Result<Self> {
|
||||
Ok(Self {
|
||||
genesis_storage,
|
||||
commit_genesis_state,
|
||||
backend,
|
||||
executor,
|
||||
_phantom: PhantomData::<Block>,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<Block: BlockT, B: Backend<Block>, E: RuntimeVersionOf> BuildGenesisBlock<Block>
|
||||
for GenesisBlockBuilder<Block, B, E>
|
||||
{
|
||||
type BlockImportOperation = <B as Backend<Block>>::BlockImportOperation;
|
||||
|
||||
fn build_genesis_block(self) -> pezsp_blockchain::Result<(Block, Self::BlockImportOperation)> {
|
||||
let Self { genesis_storage, commit_genesis_state, backend, executor, _phantom } = self;
|
||||
|
||||
let genesis_state_version =
|
||||
resolve_state_version_from_wasm::<_, HashingFor<Block>>(&genesis_storage, &executor)?;
|
||||
let mut op = backend.begin_operation()?;
|
||||
let state_root =
|
||||
op.set_genesis_state(genesis_storage, commit_genesis_state, genesis_state_version)?;
|
||||
let genesis_block = construct_genesis_block::<Block>(state_root, genesis_state_version);
|
||||
|
||||
Ok((genesis_block, op))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! A helper module for calling the GenesisBuilder API from arbitrary runtime wasm blobs.
|
||||
|
||||
use codec::{Decode, Encode};
|
||||
pub use pezsc_executor::pezsp_wasm_interface::HostFunctions;
|
||||
use pezsc_executor::{error::Result, WasmExecutor};
|
||||
use serde_json::{from_slice, Value};
|
||||
use pezsp_core::{
|
||||
storage::Storage,
|
||||
traits::{CallContext, CodeExecutor, Externalities, FetchRuntimeCode, RuntimeCode},
|
||||
};
|
||||
use pezsp_genesis_builder::{PresetId, Result as BuildResult};
|
||||
pub use pezsp_genesis_builder::{DEV_RUNTIME_PRESET, LOCAL_TESTNET_RUNTIME_PRESET};
|
||||
use pezsp_state_machine::BasicExternalities;
|
||||
use std::borrow::Cow;
|
||||
|
||||
/// A utility that facilitates calling the GenesisBuilder API from the runtime wasm code blob.
|
||||
///
|
||||
/// `EHF` type allows to specify the extended host function required for building runtime's genesis
|
||||
/// config. The type will be combined with default `pezsp_io::BizinikiwiHostFunctions`.
|
||||
pub struct GenesisConfigBuilderRuntimeCaller<'a, EHF = ()>
|
||||
where
|
||||
EHF: HostFunctions,
|
||||
{
|
||||
code: Cow<'a, [u8]>,
|
||||
code_hash: Vec<u8>,
|
||||
executor: WasmExecutor<(pezsp_io::BizinikiwiHostFunctions, EHF)>,
|
||||
}
|
||||
|
||||
impl<'a, EHF> FetchRuntimeCode for GenesisConfigBuilderRuntimeCaller<'a, EHF>
|
||||
where
|
||||
EHF: HostFunctions,
|
||||
{
|
||||
fn fetch_runtime_code(&self) -> Option<Cow<'_, [u8]>> {
|
||||
Some(self.code.as_ref().into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, EHF> GenesisConfigBuilderRuntimeCaller<'a, EHF>
|
||||
where
|
||||
EHF: HostFunctions,
|
||||
{
|
||||
/// Creates new instance using the provided code blob.
|
||||
///
|
||||
/// This code is later referred to as `runtime`.
|
||||
pub fn new(code: &'a [u8]) -> Self {
|
||||
GenesisConfigBuilderRuntimeCaller {
|
||||
code: code.into(),
|
||||
code_hash: pezsp_crypto_hashing::blake2_256(code).to_vec(),
|
||||
executor: WasmExecutor::<(pezsp_io::BizinikiwiHostFunctions, EHF)>::builder()
|
||||
.with_allow_missing_host_functions(true)
|
||||
.build(),
|
||||
}
|
||||
}
|
||||
|
||||
fn call(&self, ext: &mut dyn Externalities, method: &str, data: &[u8]) -> Result<Vec<u8>> {
|
||||
self.executor
|
||||
.call(
|
||||
ext,
|
||||
&RuntimeCode { heap_pages: None, code_fetcher: self, hash: self.code_hash.clone() },
|
||||
method,
|
||||
data,
|
||||
CallContext::Offchain,
|
||||
)
|
||||
.0
|
||||
}
|
||||
|
||||
/// Returns a json representation of the default `RuntimeGenesisConfig` provided by the
|
||||
/// `runtime`.
|
||||
///
|
||||
/// Calls [`GenesisBuilder::get_preset`](pezsp_genesis_builder::GenesisBuilder::get_preset) in the
|
||||
/// `runtime` with `None` argument.
|
||||
pub fn get_default_config(&self) -> core::result::Result<Value, String> {
|
||||
self.get_named_preset(None)
|
||||
}
|
||||
|
||||
/// Returns a JSON blob representation of the builtin `GenesisConfig` identified by `id`.
|
||||
///
|
||||
/// Calls [`GenesisBuilder::get_preset`](pezsp_genesis_builder::GenesisBuilder::get_preset)
|
||||
/// provided by the `runtime`.
|
||||
pub fn get_named_preset(&self, id: Option<&String>) -> core::result::Result<Value, String> {
|
||||
let mut t = BasicExternalities::new_empty();
|
||||
let call_result = self
|
||||
.call(&mut t, "GenesisBuilder_get_preset", &id.encode())
|
||||
.map_err(|e| format!("wasm call error {e}"))?;
|
||||
|
||||
let named_preset = Option::<Vec<u8>>::decode(&mut &call_result[..])
|
||||
.map_err(|e| format!("scale codec error: {e}"))?;
|
||||
|
||||
if let Some(named_preset) = named_preset {
|
||||
Ok(from_slice(&named_preset[..]).expect("returned value is json. qed."))
|
||||
} else {
|
||||
Err(format!("The preset with name {id:?} is not available."))
|
||||
}
|
||||
}
|
||||
|
||||
/// Calls [`pezsp_genesis_builder::GenesisBuilder::build_state`] provided by runtime.
|
||||
pub fn get_storage_for_config(&self, config: Value) -> core::result::Result<Storage, String> {
|
||||
let mut ext = BasicExternalities::new_empty();
|
||||
|
||||
let json_pretty_str = serde_json::to_string_pretty(&config)
|
||||
.map_err(|e| format!("json to string failed: {e}"))?;
|
||||
|
||||
let call_result = self
|
||||
.call(&mut ext, "GenesisBuilder_build_state", &json_pretty_str.encode())
|
||||
.map_err(|e| format!("wasm call error {e}"))?;
|
||||
|
||||
BuildResult::decode(&mut &call_result[..])
|
||||
.map_err(|e| format!("scale codec error: {e}"))?
|
||||
.map_err(|e| format!("{e} for blob:\n{}", json_pretty_str))?;
|
||||
|
||||
Ok(ext.into_storages())
|
||||
}
|
||||
|
||||
/// Creates the genesis state by patching the default `RuntimeGenesisConfig`.
|
||||
///
|
||||
/// This function generates the `RuntimeGenesisConfig` for the runtime by applying a provided
|
||||
/// JSON patch. The patch modifies the default `RuntimeGenesisConfig` allowing customization of
|
||||
/// the specific keys. The resulting `RuntimeGenesisConfig` is then deserialized from the
|
||||
/// patched JSON representation and stored in the storage.
|
||||
///
|
||||
/// If the provided JSON patch is incorrect or the deserialization fails the error will be
|
||||
/// returned.
|
||||
///
|
||||
/// The patching process modifies the default `RuntimeGenesisConfig` according to the following
|
||||
/// rules:
|
||||
/// 1. Existing keys in the default configuration will be overridden by the corresponding values
|
||||
/// in the patch (also applies to `null` values).
|
||||
/// 2. If a key exists in the patch but not in the default configuration, it will be added to
|
||||
/// the resulting `RuntimeGenesisConfig`.
|
||||
///
|
||||
/// Please note that the patch may contain full `RuntimeGenesisConfig`.
|
||||
pub fn get_storage_for_patch(&self, patch: Value) -> core::result::Result<Storage, String> {
|
||||
let mut config = self.get_default_config()?;
|
||||
crate::json_patch::merge(&mut config, patch);
|
||||
self.get_storage_for_config(config)
|
||||
}
|
||||
|
||||
pub fn get_storage_for_named_preset(
|
||||
&self,
|
||||
name: Option<&String>,
|
||||
) -> core::result::Result<Storage, String> {
|
||||
self.get_storage_for_patch(self.get_named_preset(name)?)
|
||||
}
|
||||
|
||||
pub fn preset_names(&self) -> core::result::Result<Vec<PresetId>, String> {
|
||||
let mut t = BasicExternalities::new_empty();
|
||||
let call_result = self
|
||||
.call(&mut t, "GenesisBuilder_preset_names", &vec![])
|
||||
.map_err(|e| format!("wasm call error {e}"))?;
|
||||
|
||||
let preset_names = Vec::<PresetId>::decode(&mut &call_result[..])
|
||||
.map_err(|e| format!("scale codec error: {e}"))?;
|
||||
|
||||
Ok(preset_names)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use serde_json::{from_str, json};
|
||||
pub use pezsp_consensus_babe::{AllowedSlots, BabeEpochConfiguration};
|
||||
pub use pezsp_genesis_builder::PresetId;
|
||||
|
||||
#[test]
|
||||
fn list_presets_works() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
let presets =
|
||||
<GenesisConfigBuilderRuntimeCaller>::new(bizinikiwi_test_runtime::wasm_binary_unwrap())
|
||||
.preset_names()
|
||||
.unwrap();
|
||||
assert_eq!(presets, vec![PresetId::from("foobar"), PresetId::from("staging"),]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_default_config_works() {
|
||||
let config =
|
||||
<GenesisConfigBuilderRuntimeCaller>::new(bizinikiwi_test_runtime::wasm_binary_unwrap())
|
||||
.get_default_config()
|
||||
.unwrap();
|
||||
let expected = r#"{"babe": {"authorities": [], "epochConfig": {"allowed_slots": "PrimaryAndSecondaryVRFSlots", "c": [1, 4]}}, "balances": {"balances": [], "devAccounts": null}, "bizinikiwiTest": {"authorities": []}, "system": {}}"#;
|
||||
assert_eq!(from_str::<Value>(expected).unwrap(), config);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_named_preset_works() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
let config =
|
||||
<GenesisConfigBuilderRuntimeCaller>::new(bizinikiwi_test_runtime::wasm_binary_unwrap())
|
||||
.get_named_preset(Some(&"foobar".to_string()))
|
||||
.unwrap();
|
||||
let expected = r#"{"foo":"bar"}"#;
|
||||
assert_eq!(from_str::<Value>(expected).unwrap(), config);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_storage_for_patch_works() {
|
||||
let patch = json!({
|
||||
"babe": {
|
||||
"epochConfig": {
|
||||
"c": [
|
||||
69,
|
||||
696
|
||||
],
|
||||
"allowed_slots": "PrimaryAndSecondaryPlainSlots"
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
let storage =
|
||||
<GenesisConfigBuilderRuntimeCaller>::new(bizinikiwi_test_runtime::wasm_binary_unwrap())
|
||||
.get_storage_for_patch(patch)
|
||||
.unwrap();
|
||||
|
||||
//Babe|Authorities
|
||||
let value: Vec<u8> = storage
|
||||
.top
|
||||
.get(
|
||||
&array_bytes::hex2bytes(
|
||||
"1cb6f36e027abb2091cfb5110ab5087fdc6b171b77304263c292cc3ea5ed31ef",
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap()
|
||||
.clone();
|
||||
|
||||
assert_eq!(
|
||||
BabeEpochConfiguration::decode(&mut &value[..]).unwrap(),
|
||||
BabeEpochConfiguration {
|
||||
c: (69, 696),
|
||||
allowed_slots: AllowedSlots::PrimaryAndSecondaryPlainSlots
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! A helper module providing json patching functions.
|
||||
|
||||
use serde_json::Value;
|
||||
|
||||
/// Recursively merges two JSON objects, `a` and `b`, into a single object.
|
||||
///
|
||||
/// If a key exists in both objects, the value from `b` will override the value from `a` (also if
|
||||
/// value in `b` is `null`).
|
||||
/// If a key exists only in `b` and not in `a`, it will be added to `a`.
|
||||
/// No keys will be removed from `a`.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `a` - A mutable reference to the target JSON object to merge into.
|
||||
/// * `b` - The JSON object to merge with `a`.
|
||||
pub fn merge(a: &mut Value, b: Value) {
|
||||
match (a, b) {
|
||||
(Value::Object(a), Value::Object(b)) =>
|
||||
for (k, v) in b {
|
||||
merge(a.entry(k).or_insert(Value::Null), v);
|
||||
},
|
||||
(a, b) => *a = b,
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use serde_json::json;
|
||||
|
||||
#[test]
|
||||
fn test1_simple_merge() {
|
||||
let mut j1 = json!({ "a":123 });
|
||||
merge(&mut j1, json!({ "b":256 }));
|
||||
assert_eq!(j1, json!({ "a":123, "b":256 }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test2_patch_simple_merge_nested() {
|
||||
let mut j1 = json!({
|
||||
"a": {
|
||||
"name": "xxx",
|
||||
"value": 123
|
||||
},
|
||||
"b": { "c" : { "inner_name": "yyy" } }
|
||||
});
|
||||
|
||||
let j2 = json!({
|
||||
"a": {
|
||||
"keys": ["a", "b", "c" ]
|
||||
}
|
||||
});
|
||||
|
||||
merge(&mut j1, j2);
|
||||
assert_eq!(
|
||||
j1,
|
||||
json!({"a":{"keys":["a","b","c"],"name":"xxx","value":123}, "b": { "c" : { "inner_name": "yyy" } }})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test3_patch_overrides_existing_keys() {
|
||||
let mut j1 = json!({
|
||||
"a": {
|
||||
"name": "xxx",
|
||||
"value": 123,
|
||||
"keys": ["d"]
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
let j2 = json!({
|
||||
"a": {
|
||||
"keys": ["a", "b", "c" ]
|
||||
}
|
||||
});
|
||||
|
||||
merge(&mut j1, j2);
|
||||
assert_eq!(j1, json!({"a":{"keys":["a","b","c"],"name":"xxx","value":123}}));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test4_patch_overrides_existing_keys() {
|
||||
let mut j1 = json!({
|
||||
"a": {
|
||||
"name": "xxx",
|
||||
"value": 123,
|
||||
"b" : {
|
||||
"inner_name": "yyy"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let j2 = json!({
|
||||
"a": {
|
||||
"name": "new_name",
|
||||
"b" : {
|
||||
"inner_name": "inner_new_name"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
merge(&mut j1, j2);
|
||||
assert_eq!(
|
||||
j1,
|
||||
json!({ "a": {"name":"new_name", "value":123, "b" : { "inner_name": "inner_new_name" }} })
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test5_patch_overrides_existing_nested_keys() {
|
||||
let mut j1 = json!({
|
||||
"a": {
|
||||
"name": "xxx",
|
||||
"value": 123,
|
||||
"b": {
|
||||
"c": {
|
||||
"d": {
|
||||
"name": "yyy",
|
||||
"value": 256
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
let j2 = json!({
|
||||
"a": {
|
||||
"value": 456,
|
||||
"b": {
|
||||
"c": {
|
||||
"d": {
|
||||
"name": "new_name"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
merge(&mut j1, j2);
|
||||
assert_eq!(
|
||||
j1,
|
||||
json!({ "a": {"name":"xxx", "value":456, "b": { "c": { "d": { "name": "new_name", "value": 256 }}}}})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test6_patch_does_not_remove_keys_if_null() {
|
||||
let mut j1 = json!({
|
||||
"a": {
|
||||
"name": "xxx",
|
||||
"value": 123,
|
||||
"enum_variant_1": {
|
||||
"name": "yyy",
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
let j2 = json!({
|
||||
"a": {
|
||||
"value": 456,
|
||||
"enum_variant_1": null,
|
||||
"enum_variant_2": 32,
|
||||
}
|
||||
});
|
||||
|
||||
merge(&mut j1, j2);
|
||||
assert_eq!(
|
||||
j1,
|
||||
json!({
|
||||
"a": {
|
||||
"name":"xxx",
|
||||
"value":456,
|
||||
"enum_variant_1": null,
|
||||
"enum_variant_2": 32
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,433 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! 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:
|
||||
//!
|
||||
//! <table>
|
||||
//! <thead>
|
||||
//! <tr>
|
||||
//! <th>Chain spec key</th>
|
||||
//! <th>Description</th>
|
||||
//! </tr>
|
||||
//! </thead>
|
||||
//! <tbody>
|
||||
//! <tr>
|
||||
//! <td>name</td>
|
||||
//! <td>The human readable name of the chain.</td>
|
||||
//! </tr>
|
||||
//! <tr>
|
||||
//! <td>id</td>
|
||||
//! <td>The id of the chain.</td>
|
||||
//! </tr>
|
||||
//! <tr>
|
||||
//! <td>chainType</td>
|
||||
//! <td>The chain type of this chain
|
||||
//! (refer to
|
||||
//! <a href="enum.ChainType.html" title="enum pezsc_chain_spec::ChainType">
|
||||
//! <code>ChainType</code>
|
||||
//! </a>).
|
||||
//! </td>
|
||||
//! </tr>
|
||||
//! <tr>
|
||||
//! <td>bootNodes</td>
|
||||
//! <td>A list of
|
||||
//! <a href="https://github.com/multiformats/multiaddr">multi addresses</a>
|
||||
//! that belong to boot nodes of the chain.</td>
|
||||
//! </tr>
|
||||
//! <tr>
|
||||
//! <td>telemetryEndpoints</td>
|
||||
//! <td>Optional list of <code>multi address, verbosity</code> of telemetry endpoints. The
|
||||
//! verbosity goes from 0 to 9. With 0 being the mode with the lowest verbosity.</td>
|
||||
//! </tr>
|
||||
//! <tr>
|
||||
//! <td>protocolId</td>
|
||||
//! <td>Optional networking protocol id that identifies the chain.</td>
|
||||
//! </tr>
|
||||
//! <tr>
|
||||
//! <td>forkId</td>
|
||||
//! <td>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.</td>
|
||||
//! </tr>
|
||||
//! <tr>
|
||||
//! <td>properties</td>
|
||||
//! <td>Custom properties. Shall be provided in the form of
|
||||
//! <code>key</code>-<code>value</code> json object.
|
||||
//! </td>
|
||||
//! </tr>
|
||||
//! <tr>
|
||||
//! <td>consensusEngine</td>
|
||||
//! <td>Deprecated field. Should be ignored.</td>
|
||||
//! </tr>
|
||||
//! <tr>
|
||||
//! <td>codeSubstitutes</td>
|
||||
//! <td>Optional map of <code>block_number</code> to <code>wasm_code</code>. More details in
|
||||
//! material to follow.</td>
|
||||
//! </tr>
|
||||
//! <tr>
|
||||
//! <td>genesis</td>
|
||||
//! <td>Defines the initial state of the runtime. More details in material to follow.</td>
|
||||
//! </tr>
|
||||
//! </tbody>
|
||||
//! </table>
|
||||
//!
|
||||
//! # `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:
|
||||
//!
|
||||
//! <table>
|
||||
//! <thead>
|
||||
//! <tr>
|
||||
//! <th>Format</th>
|
||||
//! <th>Description</th>
|
||||
//! </tr>
|
||||
//! </thead>
|
||||
//! <tbody>
|
||||
//! <tr>
|
||||
//! <td>
|
||||
//! <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="../pezframe_support_procedural/macro.construct_runtime.html"
|
||||
//! ><code>pezkuwi_sdk_frame::runtime::prelude::construct_runtime</code></a> macro (<a
|
||||
//! href="../bizinikiwi_test_runtime/struct.RuntimeGenesisConfig.html#"
|
||||
//! >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.
|
||||
//! </td></tr>
|
||||
//! <tr>
|
||||
//! <td>
|
||||
//! <code>patch</code>
|
||||
//! </td>
|
||||
//! <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>: `full = default + patch`. Please note that `default`
|
||||
//! `RuntimeGenesisConfig` may not be functional.
|
||||
//! This format explicitly provides the code of the runtime.
|
||||
//! </td></tr>
|
||||
//! <tr>
|
||||
//! <td>
|
||||
//! <code>raw</code>
|
||||
//! </td>
|
||||
//! <td>A JSON object with two fields: <code>top</code> and <code>children_default</code>.
|
||||
//! Each field is a map of <code>key => value</code> pairs representing entries in a genesis storage
|
||||
//! trie. The runtime code is one of such entries.</td>
|
||||
//! </tr>
|
||||
//! </tbody>
|
||||
//! </table>
|
||||
//!
|
||||
//! 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](#json-chain-specification-example) 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
|
||||
//! [`pezsp_genesis_builder::GenesisBuilder::build_state`] function. During this operation, the
|
||||
//! runtime converts the JSON representation of the genesis config into [`pezsp_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 [`pezsp_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
|
||||
//! [`pezsc_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](#json-chain-specification-example) 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:
|
||||
#![doc = docify::embed!("src/chain_spec.rs", build_chain_spec_with_patch_works)]
|
||||
//! # 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](#building-a-chain-specification):
|
||||
//! ```ignore
|
||||
#![doc = include_str!("../res/bizinikiwi_test_runtime_from_patch.json")]
|
||||
//! ```
|
||||
//! ```ignore
|
||||
#![doc = include_str!("../res/bizinikiwi_test_runtime_from_patch_raw.json")]
|
||||
//! ```
|
||||
//! The following example shows the plain full config version of chain spec:
|
||||
//! ```ignore
|
||||
#![doc = include_str!("../res/bizinikiwi_test_runtime_from_config.json")]
|
||||
//! ```
|
||||
//! 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 Bizinikiwi core modules.
|
||||
//!
|
||||
//! To allow the core modules to retrieve their configuration from your extension, you should use
|
||||
//! `ChainSpecExtension` macro exposed by this crate.
|
||||
//! ```rust
|
||||
//! use std::collections::HashMap;
|
||||
//! use pezsc_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@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.
|
||||
//! ```rust
|
||||
//! use pezsc_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.
|
||||
//! ```rust
|
||||
//! use serde::{Serialize, Deserialize};
|
||||
//! use pezsc_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.
|
||||
|
||||
mod chain_spec;
|
||||
mod extension;
|
||||
mod genesis_block;
|
||||
mod genesis_config_builder;
|
||||
pub mod json_patch;
|
||||
|
||||
pub use self::{
|
||||
chain_spec::{
|
||||
set_code_substitute_in_json_chain_spec, update_code_in_json_chain_spec,
|
||||
ChainSpec as GenericChainSpec, ChainSpecBuilder, NoExtension,
|
||||
},
|
||||
extension::{get_extension, get_extension_mut, Extension, Fork, Forks, GetExtension, Group},
|
||||
genesis_block::{
|
||||
construct_genesis_block, resolve_state_version_from_wasm, BuildGenesisBlock,
|
||||
GenesisBlockBuilder,
|
||||
},
|
||||
genesis_config_builder::{
|
||||
GenesisConfigBuilderRuntimeCaller, DEV_RUNTIME_PRESET, LOCAL_TESTNET_RUNTIME_PRESET,
|
||||
},
|
||||
json_patch::merge as json_merge,
|
||||
};
|
||||
pub use pezsc_chain_spec_derive::{ChainSpecExtension, ChainSpecGroup};
|
||||
|
||||
use pezsc_network::config::MultiaddrWithPeerId;
|
||||
use pezsc_telemetry::TelemetryEndpoints;
|
||||
use pezsp_core::storage::Storage;
|
||||
use pezsp_runtime::BuildStorage;
|
||||
|
||||
/// The type of chain.
|
||||
///
|
||||
/// This can be used by tools to determine the type of chain for displaying
|
||||
/// additional information or enabling additional features.
|
||||
#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)]
|
||||
#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
|
||||
pub enum ChainType {
|
||||
/// A development chain that runs mainly on one node.
|
||||
Development,
|
||||
/// A local chain that runs locally on multiple nodes for testing purposes.
|
||||
Local,
|
||||
/// A live chain.
|
||||
Live,
|
||||
/// Some custom chain type.
|
||||
#[cfg_attr(feature = "clap", clap(skip))]
|
||||
Custom(String),
|
||||
}
|
||||
|
||||
impl Default for ChainType {
|
||||
fn default() -> Self {
|
||||
Self::Live
|
||||
}
|
||||
}
|
||||
|
||||
/// Arbitrary properties defined in chain spec as a JSON object
|
||||
pub type Properties = serde_json::map::Map<String, serde_json::Value>;
|
||||
|
||||
/// Common interface of a chain specification.
|
||||
pub trait ChainSpec: BuildStorage + Send + Sync {
|
||||
/// Spec name.
|
||||
fn name(&self) -> &str;
|
||||
/// Spec id.
|
||||
fn id(&self) -> &str;
|
||||
/// Type of the chain.
|
||||
fn chain_type(&self) -> ChainType;
|
||||
/// A list of bootnode addresses.
|
||||
fn boot_nodes(&self) -> &[MultiaddrWithPeerId];
|
||||
/// Telemetry endpoints (if any)
|
||||
fn telemetry_endpoints(&self) -> &Option<TelemetryEndpoints>;
|
||||
/// Network protocol id.
|
||||
fn protocol_id(&self) -> Option<&str>;
|
||||
/// Optional network fork identifier. `None` by default.
|
||||
fn fork_id(&self) -> Option<&str>;
|
||||
/// Additional loosely-typed properties of the chain.
|
||||
///
|
||||
/// Returns an empty JSON object if 'properties' not defined in config
|
||||
fn properties(&self) -> Properties;
|
||||
/// Returns a reference to the defined chain spec extensions.
|
||||
fn extensions(&self) -> &dyn GetExtension;
|
||||
/// Returns a mutable reference to the defined chain spec extensions.
|
||||
fn extensions_mut(&mut self) -> &mut dyn GetExtension;
|
||||
/// Add a bootnode to the list.
|
||||
fn add_boot_node(&mut self, addr: MultiaddrWithPeerId);
|
||||
/// Return spec as JSON.
|
||||
fn as_json(&self, raw: bool) -> Result<String, String>;
|
||||
/// Return StorageBuilder for this spec.
|
||||
fn as_storage_builder(&self) -> &dyn BuildStorage;
|
||||
/// Returns a cloned `Box<dyn ChainSpec>`.
|
||||
fn cloned_box(&self) -> Box<dyn ChainSpec>;
|
||||
/// Set the storage that should be used by this chain spec.
|
||||
///
|
||||
/// This will be used as storage at genesis.
|
||||
fn set_storage(&mut self, storage: Storage);
|
||||
/// Returns code substitutes that should be used for the on chain wasm.
|
||||
fn code_substitutes(&self) -> std::collections::BTreeMap<String, Vec<u8>>;
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for dyn ChainSpec {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "ChainSpec(name = {:?}, id = {:?})", self.name(), self.id())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user