mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 20:31:13 +00:00
chain-spec: getting ready for native-runtime-free world (#1256)
This PR prepares chains specs for _native-runtime-free_ world. This PR has following changes: - `substrate`: - adds support for: - JSON based `GenesisConfig` to `ChainSpec` allowing interaction with runtime `GenesisBuilder` API. - interacting with arbitrary runtime wasm blob to[ `chain-spec-builder`](https://github.com/paritytech/substrate/blob/3ef576eaeb3f42610e85daecc464961cf1295570/bin/utils/chain-spec-builder/src/lib.rs#L46) command line util, - removes [`code`](https://github.com/paritytech/substrate/blob/3ef576eaeb3f42610e85daecc464961cf1295570/frame/system/src/lib.rs#L660) from `system_pallet` - adds `code` to the `ChainSpec` - deprecates [`ChainSpec::from_genesis`](https://github.com/paritytech/substrate/blob/3ef576eaeb3f42610e85daecc464961cf1295570/client/chain-spec/src/chain_spec.rs#L263), but also changes the signature of this method extending it with `code` argument. [`ChainSpec::builder()`](https://github.com/paritytech/substrate/blob/20bee680ed098be7239cf7a6b804cd4de267983e/client/chain-spec/src/chain_spec.rs#L507) should be used instead. - `polkadot`: - all references to `RuntimeGenesisConfig` in `node/service` are removed, - all `(kusama|polkadot|versi|rococo|wococo)_(staging|dev)_genesis_config` functions now return the JSON patch for default runtime `GenesisConfig`, - `ChainSpecBuilder` is used, `ChainSpec::from_genesis` is removed, - `cumulus`: - `ChainSpecBuilder` is used, `ChainSpec::from_genesis` is removed, - _JSON_ patch configuration used instead of `RuntimeGenesisConfig struct` in all chain specs. --------- Co-authored-by: command-bot <> Co-authored-by: Javier Viola <javier@parity.io> Co-authored-by: Davide Galassi <davxy@datawok.net> Co-authored-by: Francisco Aguirre <franciscoaguirreperez@gmail.com> Co-authored-by: Kevin Krone <kevin@parity.io> Co-authored-by: Bastian Köcher <git@kchr.de>
This commit is contained in:
committed by
GitHub
parent
c46a7dbb61
commit
8ba7a6aba8
@@ -17,6 +17,7 @@ subkey = { path = "bin/utils/subkey" }
|
||||
chain-spec-builder = { package = "staging-chain-spec-builder", path = "bin/utils/chain-spec-builder" }
|
||||
|
||||
sc-service = { path = "client/service" }
|
||||
sc-chain-spec = { path = "client/chain-spec" }
|
||||
sc-cli = { path = "client/cli" }
|
||||
sc-consensus-aura = { path = "client/consensus/aura" }
|
||||
sc-consensus-babe = { path = "client/consensus/babe" }
|
||||
|
||||
@@ -21,6 +21,7 @@ clap = { version = "4.0.9", features = ["derive"] }
|
||||
futures = { version = "0.3.21", features = ["thread-pool"] }
|
||||
futures-timer = "3.0.1"
|
||||
jsonrpsee = { version = "0.16.2", features = ["server"] }
|
||||
serde_json = "1.0.107"
|
||||
|
||||
sc-cli = { path = "../../../client/cli" }
|
||||
sc-executor = { path = "../../../client/executor" }
|
||||
|
||||
@@ -15,12 +15,13 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use runtime::{BalancesConfig, RuntimeGenesisConfig, SudoConfig, SystemConfig, WASM_BINARY};
|
||||
use runtime::{BalancesConfig, SudoConfig, WASM_BINARY};
|
||||
use sc_service::{ChainType, Properties};
|
||||
use serde_json::{json, Value};
|
||||
use sp_keyring::AccountKeyring;
|
||||
|
||||
/// This is a specialization of the general Substrate ChainSpec type.
|
||||
pub type ChainSpec = sc_service::GenericChainSpec<RuntimeGenesisConfig>;
|
||||
pub type ChainSpec = sc_service::GenericChainSpec<()>;
|
||||
|
||||
fn props() -> Properties {
|
||||
let mut properties = Properties::new();
|
||||
@@ -30,37 +31,25 @@ fn props() -> Properties {
|
||||
}
|
||||
|
||||
pub fn development_config() -> Result<ChainSpec, String> {
|
||||
let wasm_binary = WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?;
|
||||
Ok(ChainSpec::from_genesis(
|
||||
"Development",
|
||||
"dev",
|
||||
ChainType::Development,
|
||||
move || testnet_genesis(wasm_binary),
|
||||
vec![],
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Some(props()),
|
||||
None,
|
||||
))
|
||||
Ok(ChainSpec::builder(WASM_BINARY.expect("Development wasm not available"), Default::default())
|
||||
.with_name("Development")
|
||||
.with_id("dev")
|
||||
.with_chain_type(ChainType::Development)
|
||||
.with_genesis_config_patch(testnet_genesis())
|
||||
.with_properties(props())
|
||||
.build())
|
||||
}
|
||||
|
||||
/// Configure initial storage state for FRAME pallets.
|
||||
fn testnet_genesis(wasm_binary: &[u8]) -> RuntimeGenesisConfig {
|
||||
fn testnet_genesis() -> Value {
|
||||
use frame::traits::Get;
|
||||
use runtime::interface::{Balance, MinimumBalance};
|
||||
let endowment = <MinimumBalance as Get<Balance>>::get().max(1) * 1000;
|
||||
let balances = AccountKeyring::iter()
|
||||
.map(|a| (a.to_account_id(), endowment))
|
||||
.collect::<Vec<_>>();
|
||||
RuntimeGenesisConfig {
|
||||
system: SystemConfig {
|
||||
// Add Wasm runtime to storage.
|
||||
code: wasm_binary.to_vec(),
|
||||
_config: Default::default(),
|
||||
},
|
||||
balances: BalancesConfig { balances },
|
||||
sudo: SudoConfig { key: Some(AccountKeyring::Alice.to_account_id()) },
|
||||
..Default::default()
|
||||
}
|
||||
json!({
|
||||
"balances": BalancesConfig { balances },
|
||||
"sudo": SudoConfig { key: Some(AccountKeyring::Alice.to_account_id()) },
|
||||
})
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ scale-info = { version = "2.6.0", default-features = false }
|
||||
|
||||
# this is a frame-based runtime, thus importing `frame` with runtime feature enabled.
|
||||
frame = { path = "../../../frame", default-features = false, features = ["runtime", "experimental"] }
|
||||
frame-support = { path = "../../../frame/support", default-features = false}
|
||||
|
||||
# pallets that we want to use
|
||||
pallet-balances = { path = "../../../frame/balances", default-features = false }
|
||||
@@ -17,6 +18,9 @@ pallet-timestamp = { path = "../../../frame/timestamp", default-features = false
|
||||
pallet-transaction-payment = { path = "../../../frame/transaction-payment", default-features = false }
|
||||
pallet-transaction-payment-rpc-runtime-api = { path = "../../../frame/transaction-payment/rpc/runtime-api", default-features = false }
|
||||
|
||||
# genesis builder that allows us to interacto with runtime genesis config
|
||||
sp-genesis-builder = { path = "../../../primitives/genesis-builder", default-features = false}
|
||||
|
||||
|
||||
[build-dependencies]
|
||||
substrate-wasm-builder = { path = "../../../utils/wasm-builder", optional = true }
|
||||
|
||||
@@ -31,6 +31,7 @@ use frame::{
|
||||
prelude::*,
|
||||
},
|
||||
};
|
||||
use frame_support::genesis_builder_helper::{build_config, create_default_config};
|
||||
|
||||
#[runtime_version]
|
||||
pub const VERSION: RuntimeVersion = RuntimeVersion {
|
||||
@@ -210,6 +211,16 @@ impl_runtime_apis! {
|
||||
TransactionPayment::length_to_fee(length)
|
||||
}
|
||||
}
|
||||
|
||||
impl sp_genesis_builder::GenesisBuilder<Block> for Runtime {
|
||||
fn create_default_config() -> Vec<u8> {
|
||||
create_default_config::<RuntimeGenesisConfig>()
|
||||
}
|
||||
|
||||
fn build_config(config: Vec<u8>) -> sp_genesis_builder::Result {
|
||||
build_config::<RuntimeGenesisConfig>(config)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Some re-exports that the node side code needs to know. Some are useful in this context as well.
|
||||
|
||||
@@ -19,6 +19,7 @@ name = "node-template"
|
||||
[dependencies]
|
||||
clap = { version = "4.4.6", features = ["derive"] }
|
||||
futures = { version = "0.3.21", features = ["thread-pool"]}
|
||||
serde_json = "1.0.85"
|
||||
|
||||
sc-cli = { path = "../../../client/cli" }
|
||||
sp-core = { path = "../../../primitives/core" }
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
use node_template_runtime::{
|
||||
AccountId, AuraConfig, BalancesConfig, GrandpaConfig, RuntimeGenesisConfig, Signature,
|
||||
SudoConfig, SystemConfig, WASM_BINARY,
|
||||
};
|
||||
use node_template_runtime::{AccountId, RuntimeGenesisConfig, Signature, WASM_BINARY};
|
||||
use sc_service::ChainType;
|
||||
use sp_consensus_aura::sr25519::AuthorityId as AuraId;
|
||||
use sp_consensus_grandpa::AuthorityId as GrandpaId;
|
||||
@@ -37,122 +34,84 @@ pub fn authority_keys_from_seed(s: &str) -> (AuraId, GrandpaId) {
|
||||
}
|
||||
|
||||
pub fn development_config() -> Result<ChainSpec, String> {
|
||||
let wasm_binary = WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?;
|
||||
|
||||
Ok(ChainSpec::from_genesis(
|
||||
// Name
|
||||
"Development",
|
||||
// ID
|
||||
"dev",
|
||||
ChainType::Development,
|
||||
move || {
|
||||
testnet_genesis(
|
||||
wasm_binary,
|
||||
// Initial PoA authorities
|
||||
vec![authority_keys_from_seed("Alice")],
|
||||
// Sudo account
|
||||
get_account_id_from_seed::<sr25519::Public>("Alice"),
|
||||
// Pre-funded accounts
|
||||
vec![
|
||||
get_account_id_from_seed::<sr25519::Public>("Alice"),
|
||||
get_account_id_from_seed::<sr25519::Public>("Bob"),
|
||||
get_account_id_from_seed::<sr25519::Public>("Alice//stash"),
|
||||
get_account_id_from_seed::<sr25519::Public>("Bob//stash"),
|
||||
],
|
||||
true,
|
||||
)
|
||||
},
|
||||
// Bootnodes
|
||||
vec![],
|
||||
// Telemetry
|
||||
None,
|
||||
// Protocol ID
|
||||
None,
|
||||
None,
|
||||
// Properties
|
||||
None,
|
||||
// Extensions
|
||||
Ok(ChainSpec::builder(
|
||||
WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?,
|
||||
None,
|
||||
)
|
||||
.with_name("Development")
|
||||
.with_id("dev")
|
||||
.with_chain_type(ChainType::Development)
|
||||
.with_genesis_config_patch(testnet_genesis(
|
||||
// Initial PoA authorities
|
||||
vec![authority_keys_from_seed("Alice")],
|
||||
// Sudo account
|
||||
get_account_id_from_seed::<sr25519::Public>("Alice"),
|
||||
// Pre-funded accounts
|
||||
vec![
|
||||
get_account_id_from_seed::<sr25519::Public>("Alice"),
|
||||
get_account_id_from_seed::<sr25519::Public>("Bob"),
|
||||
get_account_id_from_seed::<sr25519::Public>("Alice//stash"),
|
||||
get_account_id_from_seed::<sr25519::Public>("Bob//stash"),
|
||||
],
|
||||
true,
|
||||
))
|
||||
.build())
|
||||
}
|
||||
|
||||
pub fn local_testnet_config() -> Result<ChainSpec, String> {
|
||||
let wasm_binary = WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?;
|
||||
|
||||
Ok(ChainSpec::from_genesis(
|
||||
// Name
|
||||
"Local Testnet",
|
||||
// ID
|
||||
"local_testnet",
|
||||
ChainType::Local,
|
||||
move || {
|
||||
testnet_genesis(
|
||||
wasm_binary,
|
||||
// Initial PoA authorities
|
||||
vec![authority_keys_from_seed("Alice"), authority_keys_from_seed("Bob")],
|
||||
// Sudo account
|
||||
get_account_id_from_seed::<sr25519::Public>("Alice"),
|
||||
// Pre-funded accounts
|
||||
vec![
|
||||
get_account_id_from_seed::<sr25519::Public>("Alice"),
|
||||
get_account_id_from_seed::<sr25519::Public>("Bob"),
|
||||
get_account_id_from_seed::<sr25519::Public>("Charlie"),
|
||||
get_account_id_from_seed::<sr25519::Public>("Dave"),
|
||||
get_account_id_from_seed::<sr25519::Public>("Eve"),
|
||||
get_account_id_from_seed::<sr25519::Public>("Ferdie"),
|
||||
get_account_id_from_seed::<sr25519::Public>("Alice//stash"),
|
||||
get_account_id_from_seed::<sr25519::Public>("Bob//stash"),
|
||||
get_account_id_from_seed::<sr25519::Public>("Charlie//stash"),
|
||||
get_account_id_from_seed::<sr25519::Public>("Dave//stash"),
|
||||
get_account_id_from_seed::<sr25519::Public>("Eve//stash"),
|
||||
get_account_id_from_seed::<sr25519::Public>("Ferdie//stash"),
|
||||
],
|
||||
true,
|
||||
)
|
||||
},
|
||||
// Bootnodes
|
||||
vec![],
|
||||
// Telemetry
|
||||
None,
|
||||
// Protocol ID
|
||||
None,
|
||||
// Properties
|
||||
None,
|
||||
None,
|
||||
// Extensions
|
||||
Ok(ChainSpec::builder(
|
||||
WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?,
|
||||
None,
|
||||
)
|
||||
.with_name("Local Testnet")
|
||||
.with_id("local_testnet")
|
||||
.with_chain_type(ChainType::Local)
|
||||
.with_genesis_config_patch(testnet_genesis(
|
||||
// Initial PoA authorities
|
||||
vec![authority_keys_from_seed("Alice"), authority_keys_from_seed("Bob")],
|
||||
// Sudo account
|
||||
get_account_id_from_seed::<sr25519::Public>("Alice"),
|
||||
// Pre-funded accounts
|
||||
vec![
|
||||
get_account_id_from_seed::<sr25519::Public>("Alice"),
|
||||
get_account_id_from_seed::<sr25519::Public>("Bob"),
|
||||
get_account_id_from_seed::<sr25519::Public>("Charlie"),
|
||||
get_account_id_from_seed::<sr25519::Public>("Dave"),
|
||||
get_account_id_from_seed::<sr25519::Public>("Eve"),
|
||||
get_account_id_from_seed::<sr25519::Public>("Ferdie"),
|
||||
get_account_id_from_seed::<sr25519::Public>("Alice//stash"),
|
||||
get_account_id_from_seed::<sr25519::Public>("Bob//stash"),
|
||||
get_account_id_from_seed::<sr25519::Public>("Charlie//stash"),
|
||||
get_account_id_from_seed::<sr25519::Public>("Dave//stash"),
|
||||
get_account_id_from_seed::<sr25519::Public>("Eve//stash"),
|
||||
get_account_id_from_seed::<sr25519::Public>("Ferdie//stash"),
|
||||
],
|
||||
true,
|
||||
))
|
||||
.build())
|
||||
}
|
||||
|
||||
/// Configure initial storage state for FRAME modules.
|
||||
fn testnet_genesis(
|
||||
wasm_binary: &[u8],
|
||||
initial_authorities: Vec<(AuraId, GrandpaId)>,
|
||||
root_key: AccountId,
|
||||
endowed_accounts: Vec<AccountId>,
|
||||
_enable_println: bool,
|
||||
) -> RuntimeGenesisConfig {
|
||||
RuntimeGenesisConfig {
|
||||
system: SystemConfig {
|
||||
// Add Wasm runtime to storage.
|
||||
code: wasm_binary.to_vec(),
|
||||
..Default::default()
|
||||
},
|
||||
balances: BalancesConfig {
|
||||
) -> serde_json::Value {
|
||||
serde_json::json!({
|
||||
"balances": {
|
||||
// Configure endowed accounts with initial balance of 1 << 60.
|
||||
balances: endowed_accounts.iter().cloned().map(|k| (k, 1 << 60)).collect(),
|
||||
"balances": endowed_accounts.iter().cloned().map(|k| (k, 1u64 << 60)).collect::<Vec<_>>(),
|
||||
},
|
||||
aura: AuraConfig {
|
||||
authorities: initial_authorities.iter().map(|x| (x.0.clone())).collect(),
|
||||
"aura": {
|
||||
"authorities": initial_authorities.iter().map(|x| (x.0.clone())).collect::<Vec<_>>(),
|
||||
},
|
||||
grandpa: GrandpaConfig {
|
||||
authorities: initial_authorities.iter().map(|x| (x.1.clone(), 1)).collect(),
|
||||
..Default::default()
|
||||
"grandpa": {
|
||||
"authorities": initial_authorities.iter().map(|x| (x.1.clone(), 1)).collect::<Vec<_>>(),
|
||||
},
|
||||
sudo: SudoConfig {
|
||||
"sudo": {
|
||||
// Assign network admin rights.
|
||||
key: Some(root_key),
|
||||
"key": Some(root_key),
|
||||
},
|
||||
transaction_payment: Default::default(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
||||
[dependencies]
|
||||
codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] }
|
||||
scale-info = { version = "2.10.0", default-features = false, features = ["derive"] }
|
||||
scale-info = { version = "2.10.0", default-features = false, features = ["derive", "serde"] }
|
||||
|
||||
pallet-aura = { path = "../../../frame/aura", default-features = false}
|
||||
pallet-balances = { path = "../../../frame/balances", default-features = false}
|
||||
@@ -28,17 +28,18 @@ pallet-transaction-payment = { path = "../../../frame/transaction-payment", defa
|
||||
frame-executive = { path = "../../../frame/executive", default-features = false}
|
||||
sp-api = { path = "../../../primitives/api", default-features = false}
|
||||
sp-block-builder = { path = "../../../primitives/block-builder", default-features = false}
|
||||
sp-consensus-aura = { path = "../../../primitives/consensus/aura", default-features = false}
|
||||
sp-consensus-grandpa = { path = "../../../primitives/consensus/grandpa", default-features = false}
|
||||
sp-core = { path = "../../../primitives/core", default-features = false}
|
||||
sp-consensus-aura = { path = "../../../primitives/consensus/aura", default-features = false, features = ["serde"] }
|
||||
sp-consensus-grandpa = { path = "../../../primitives/consensus/grandpa", default-features = false, features = ["serde"] }
|
||||
sp-core = { path = "../../../primitives/core", default-features = false, features = ["serde"]}
|
||||
sp-inherents = { path = "../../../primitives/inherents", default-features = false}
|
||||
sp-offchain = { path = "../../../primitives/offchain", default-features = false}
|
||||
sp-runtime = { path = "../../../primitives/runtime", default-features = false}
|
||||
sp-runtime = { path = "../../../primitives/runtime", default-features = false, features = ["serde"] }
|
||||
sp-session = { path = "../../../primitives/session", default-features = false}
|
||||
sp-std = { path = "../../../primitives/std", default-features = false}
|
||||
sp-storage = { path = "../../../primitives/storage", default-features = false}
|
||||
sp-transaction-pool = { path = "../../../primitives/transaction-pool", default-features = false}
|
||||
sp-version = { path = "../../../primitives/version", default-features = false}
|
||||
sp-version = { path = "../../../primitives/version", default-features = false, features = ["serde"] }
|
||||
serde_json = { version = "1.0.85", default-features = false, features = ["alloc"] }
|
||||
sp-genesis-builder = { default-features = false, path = "../../../primitives/genesis-builder" }
|
||||
|
||||
# Used for the node template's RPCs
|
||||
@@ -75,6 +76,7 @@ std = [
|
||||
"pallet-transaction-payment-rpc-runtime-api/std",
|
||||
"pallet-transaction-payment/std",
|
||||
"scale-info/std",
|
||||
"serde_json/std",
|
||||
"sp-api/std",
|
||||
"sp-block-builder/std",
|
||||
"sp-consensus-aura/std",
|
||||
|
||||
@@ -124,7 +124,6 @@ futures = "0.3.21"
|
||||
tempfile = "3.1.0"
|
||||
assert_cmd = "2.0.2"
|
||||
nix = { version = "0.26.1", features = ["signal"] }
|
||||
serde_json = "1.0"
|
||||
regex = "1.6.0"
|
||||
platforms = "3.0"
|
||||
soketto = "0.7.1"
|
||||
|
||||
@@ -20,10 +20,7 @@
|
||||
|
||||
use grandpa_primitives::AuthorityId as GrandpaId;
|
||||
use kitchensink_runtime::{
|
||||
constants::currency::*, wasm_binary_unwrap, BabeConfig, BalancesConfig, Block, CouncilConfig,
|
||||
DemocracyConfig, ElectionsConfig, ImOnlineConfig, IndicesConfig, MaxNominations,
|
||||
NominationPoolsConfig, SessionConfig, SessionKeys, SocietyConfig, StakerStatus, StakingConfig,
|
||||
SudoConfig, SystemConfig, TechnicalCommitteeConfig,
|
||||
constants::currency::*, wasm_binary_unwrap, Block, MaxNominations, SessionKeys, StakerStatus,
|
||||
};
|
||||
use pallet_im_online::sr25519::AuthorityId as ImOnlineId;
|
||||
use sc_chain_spec::ChainSpecExtension;
|
||||
@@ -45,6 +42,8 @@ pub use node_primitives::{AccountId, Balance, Signature};
|
||||
type AccountPublic = <Signature as Verify>::Signer;
|
||||
|
||||
const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/";
|
||||
const ENDOWMENT: Balance = 10_000_000 * DOLLARS;
|
||||
const STASH: Balance = ENDOWMENT / 1000;
|
||||
|
||||
/// Node `ChainSpec` extensions.
|
||||
///
|
||||
@@ -78,7 +77,11 @@ fn session_keys(
|
||||
SessionKeys { grandpa, babe, im_online, authority_discovery, mixnet }
|
||||
}
|
||||
|
||||
fn staging_testnet_config_genesis() -> RuntimeGenesisConfig {
|
||||
fn configure_accounts_for_staging_testnet() -> (
|
||||
Vec<(AccountId, AccountId, GrandpaId, BabeId, ImOnlineId, AuthorityDiscoveryId, MixnetId)>,
|
||||
AccountId,
|
||||
Vec<AccountId>,
|
||||
) {
|
||||
#[rustfmt::skip]
|
||||
// stash, controller, session-key
|
||||
// generated with secret:
|
||||
@@ -190,28 +193,27 @@ fn staging_testnet_config_genesis() -> RuntimeGenesisConfig {
|
||||
);
|
||||
|
||||
let endowed_accounts: Vec<AccountId> = vec![root_key.clone()];
|
||||
(initial_authorities, root_key, endowed_accounts)
|
||||
}
|
||||
|
||||
fn staging_testnet_config_genesis() -> serde_json::Value {
|
||||
let (initial_authorities, root_key, endowed_accounts) =
|
||||
configure_accounts_for_staging_testnet();
|
||||
testnet_genesis(initial_authorities, vec![], root_key, Some(endowed_accounts))
|
||||
}
|
||||
|
||||
/// Staging testnet config.
|
||||
pub fn staging_testnet_config() -> ChainSpec {
|
||||
let boot_nodes = vec![];
|
||||
ChainSpec::from_genesis(
|
||||
"Staging Testnet",
|
||||
"staging_testnet",
|
||||
ChainType::Live,
|
||||
staging_testnet_config_genesis,
|
||||
boot_nodes,
|
||||
Some(
|
||||
ChainSpec::builder(wasm_binary_unwrap(), Default::default())
|
||||
.with_name("Staging Testnet")
|
||||
.with_id("staging_testnet")
|
||||
.with_chain_type(ChainType::Live)
|
||||
.with_genesis_config_patch(staging_testnet_config_genesis())
|
||||
.with_telemetry_endpoints(
|
||||
TelemetryEndpoints::new(vec![(STAGING_TELEMETRY_URL.to_string(), 0)])
|
||||
.expect("Staging telemetry url is valid; qed"),
|
||||
),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Default::default(),
|
||||
)
|
||||
)
|
||||
.build()
|
||||
}
|
||||
|
||||
/// Helper function to generate a crypto pair from seed.
|
||||
@@ -244,8 +246,7 @@ pub fn authority_keys_from_seed(
|
||||
)
|
||||
}
|
||||
|
||||
/// Helper function to create RuntimeGenesisConfig for testing.
|
||||
pub fn testnet_genesis(
|
||||
fn configure_accounts(
|
||||
initial_authorities: Vec<(
|
||||
AccountId,
|
||||
AccountId,
|
||||
@@ -256,9 +257,14 @@ pub fn testnet_genesis(
|
||||
MixnetId,
|
||||
)>,
|
||||
initial_nominators: Vec<AccountId>,
|
||||
root_key: AccountId,
|
||||
endowed_accounts: Option<Vec<AccountId>>,
|
||||
) -> RuntimeGenesisConfig {
|
||||
stash: Balance,
|
||||
) -> (
|
||||
Vec<(AccountId, AccountId, GrandpaId, BabeId, ImOnlineId, AuthorityDiscoveryId, MixnetId)>,
|
||||
Vec<AccountId>,
|
||||
usize,
|
||||
Vec<(AccountId, AccountId, Balance, StakerStatus<AccountId>)>,
|
||||
) {
|
||||
let mut endowed_accounts: Vec<AccountId> = endowed_accounts.unwrap_or_else(|| {
|
||||
vec![
|
||||
get_account_id_from_seed::<sr25519::Public>("Alice"),
|
||||
@@ -290,7 +296,7 @@ pub fn testnet_genesis(
|
||||
let mut rng = rand::thread_rng();
|
||||
let stakers = initial_authorities
|
||||
.iter()
|
||||
.map(|x| (x.0.clone(), x.0.clone(), STASH, StakerStatus::Validator))
|
||||
.map(|x| (x.0.clone(), x.0.clone(), stash, StakerStatus::Validator))
|
||||
.chain(initial_nominators.iter().map(|x| {
|
||||
use rand::{seq::SliceRandom, Rng};
|
||||
let limit = (MaxNominations::get() as usize).min(initial_authorities.len());
|
||||
@@ -301,23 +307,39 @@ pub fn testnet_genesis(
|
||||
.into_iter()
|
||||
.map(|choice| choice.0.clone())
|
||||
.collect::<Vec<_>>();
|
||||
(x.clone(), x.clone(), STASH, StakerStatus::Nominator(nominations))
|
||||
(x.clone(), x.clone(), stash, StakerStatus::Nominator(nominations))
|
||||
}))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let num_endowed_accounts = endowed_accounts.len();
|
||||
|
||||
const ENDOWMENT: Balance = 10_000_000 * DOLLARS;
|
||||
const STASH: Balance = ENDOWMENT / 1000;
|
||||
(initial_authorities, endowed_accounts, num_endowed_accounts, stakers)
|
||||
}
|
||||
|
||||
RuntimeGenesisConfig {
|
||||
system: SystemConfig { code: wasm_binary_unwrap().to_vec(), ..Default::default() },
|
||||
balances: BalancesConfig {
|
||||
balances: endowed_accounts.iter().cloned().map(|x| (x, ENDOWMENT)).collect(),
|
||||
/// Helper function to create RuntimeGenesisConfig json patch for testing.
|
||||
pub fn testnet_genesis(
|
||||
initial_authorities: Vec<(
|
||||
AccountId,
|
||||
AccountId,
|
||||
GrandpaId,
|
||||
BabeId,
|
||||
ImOnlineId,
|
||||
AuthorityDiscoveryId,
|
||||
MixnetId,
|
||||
)>,
|
||||
initial_nominators: Vec<AccountId>,
|
||||
root_key: AccountId,
|
||||
endowed_accounts: Option<Vec<AccountId>>,
|
||||
) -> serde_json::Value {
|
||||
let (initial_authorities, endowed_accounts, num_endowed_accounts, stakers) =
|
||||
configure_accounts(initial_authorities, initial_nominators, endowed_accounts, STASH);
|
||||
|
||||
serde_json::json!({
|
||||
"balances": {
|
||||
"balances": endowed_accounts.iter().cloned().map(|x| (x, ENDOWMENT)).collect::<Vec<_>>(),
|
||||
},
|
||||
indices: IndicesConfig { indices: vec![] },
|
||||
session: SessionConfig {
|
||||
keys: initial_authorities
|
||||
"session": {
|
||||
"keys": initial_authorities
|
||||
.iter()
|
||||
.map(|x| {
|
||||
(
|
||||
@@ -334,67 +356,45 @@ pub fn testnet_genesis(
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
},
|
||||
staking: StakingConfig {
|
||||
validator_count: initial_authorities.len() as u32,
|
||||
minimum_validator_count: initial_authorities.len() as u32,
|
||||
invulnerables: initial_authorities.iter().map(|x| x.0.clone()).collect(),
|
||||
slash_reward_fraction: Perbill::from_percent(10),
|
||||
stakers,
|
||||
..Default::default()
|
||||
"staking": {
|
||||
"validatorCount": initial_authorities.len() as u32,
|
||||
"minimumValidatorCount": initial_authorities.len() as u32,
|
||||
"invulnerables": initial_authorities.iter().map(|x| x.0.clone()).collect::<Vec<_>>(),
|
||||
"slashRewardFraction": Perbill::from_percent(10),
|
||||
"stakers": stakers.clone(),
|
||||
},
|
||||
democracy: DemocracyConfig::default(),
|
||||
elections: ElectionsConfig {
|
||||
members: endowed_accounts
|
||||
"elections": {
|
||||
"members": endowed_accounts
|
||||
.iter()
|
||||
.take((num_endowed_accounts + 1) / 2)
|
||||
.cloned()
|
||||
.map(|member| (member, STASH))
|
||||
.collect(),
|
||||
.collect::<Vec<_>>(),
|
||||
},
|
||||
council: CouncilConfig::default(),
|
||||
technical_committee: TechnicalCommitteeConfig {
|
||||
members: endowed_accounts
|
||||
"technicalCommittee": {
|
||||
"members": endowed_accounts
|
||||
.iter()
|
||||
.take((num_endowed_accounts + 1) / 2)
|
||||
.cloned()
|
||||
.collect(),
|
||||
phantom: Default::default(),
|
||||
.collect::<Vec<_>>(),
|
||||
},
|
||||
sudo: SudoConfig { key: Some(root_key) },
|
||||
babe: BabeConfig {
|
||||
epoch_config: Some(kitchensink_runtime::BABE_GENESIS_EPOCH_CONFIG),
|
||||
..Default::default()
|
||||
"sudo": { "key": Some(root_key.clone()) },
|
||||
"babe": {
|
||||
"epochConfig": Some(kitchensink_runtime::BABE_GENESIS_EPOCH_CONFIG),
|
||||
},
|
||||
im_online: ImOnlineConfig { keys: vec![] },
|
||||
authority_discovery: Default::default(),
|
||||
grandpa: Default::default(),
|
||||
technical_membership: Default::default(),
|
||||
treasury: Default::default(),
|
||||
society: SocietyConfig { pot: 0 },
|
||||
vesting: Default::default(),
|
||||
assets: pallet_assets::GenesisConfig {
|
||||
"society": { "pot": 0 },
|
||||
"assets": {
|
||||
// This asset is used by the NIS pallet as counterpart currency.
|
||||
assets: vec![(9, get_account_id_from_seed::<sr25519::Public>("Alice"), true, 1)],
|
||||
..Default::default()
|
||||
"assets": vec![(9, get_account_id_from_seed::<sr25519::Public>("Alice"), true, 1)],
|
||||
},
|
||||
pool_assets: Default::default(),
|
||||
transaction_storage: Default::default(),
|
||||
transaction_payment: Default::default(),
|
||||
alliance: Default::default(),
|
||||
safe_mode: Default::default(),
|
||||
tx_pause: Default::default(),
|
||||
alliance_motion: Default::default(),
|
||||
nomination_pools: NominationPoolsConfig {
|
||||
min_create_bond: 10 * DOLLARS,
|
||||
min_join_bond: 1 * DOLLARS,
|
||||
..Default::default()
|
||||
"nominationPools": {
|
||||
"minCreateBond": 10 * DOLLARS,
|
||||
"minJoinBond": 1 * DOLLARS,
|
||||
},
|
||||
glutton: Default::default(),
|
||||
mixnet: Default::default(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn development_config_genesis() -> RuntimeGenesisConfig {
|
||||
fn development_config_genesis_json() -> serde_json::Value {
|
||||
testnet_genesis(
|
||||
vec![authority_keys_from_seed("Alice")],
|
||||
vec![],
|
||||
@@ -405,21 +405,15 @@ fn development_config_genesis() -> RuntimeGenesisConfig {
|
||||
|
||||
/// Development config (single validator Alice).
|
||||
pub fn development_config() -> ChainSpec {
|
||||
ChainSpec::from_genesis(
|
||||
"Development",
|
||||
"dev",
|
||||
ChainType::Development,
|
||||
development_config_genesis,
|
||||
vec![],
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Default::default(),
|
||||
)
|
||||
ChainSpec::builder(wasm_binary_unwrap(), Default::default())
|
||||
.with_name("Development")
|
||||
.with_id("dev")
|
||||
.with_chain_type(ChainType::Development)
|
||||
.with_genesis_config_patch(development_config_genesis_json())
|
||||
.build()
|
||||
}
|
||||
|
||||
fn local_testnet_genesis() -> RuntimeGenesisConfig {
|
||||
fn local_testnet_genesis() -> serde_json::Value {
|
||||
testnet_genesis(
|
||||
vec![authority_keys_from_seed("Alice"), authority_keys_from_seed("Bob")],
|
||||
vec![],
|
||||
@@ -430,18 +424,12 @@ fn local_testnet_genesis() -> RuntimeGenesisConfig {
|
||||
|
||||
/// Local testnet config (multivalidator Alice + Bob).
|
||||
pub fn local_testnet_config() -> ChainSpec {
|
||||
ChainSpec::from_genesis(
|
||||
"Local Testnet",
|
||||
"local_testnet",
|
||||
ChainType::Local,
|
||||
local_testnet_genesis,
|
||||
vec![],
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Default::default(),
|
||||
)
|
||||
ChainSpec::builder(wasm_binary_unwrap(), Default::default())
|
||||
.with_name("Local Testnet")
|
||||
.with_id("local_testnet")
|
||||
.with_chain_type(ChainType::Local)
|
||||
.with_genesis_config_patch(local_testnet_genesis())
|
||||
.build()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -451,45 +439,29 @@ pub(crate) mod tests {
|
||||
use sc_service_test;
|
||||
use sp_runtime::BuildStorage;
|
||||
|
||||
fn local_testnet_genesis_instant_single() -> RuntimeGenesisConfig {
|
||||
testnet_genesis(
|
||||
vec![authority_keys_from_seed("Alice")],
|
||||
vec![],
|
||||
get_account_id_from_seed::<sr25519::Public>("Alice"),
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
/// Local testnet config (single validator - Alice).
|
||||
pub fn integration_test_config_with_single_authority() -> ChainSpec {
|
||||
ChainSpec::from_genesis(
|
||||
"Integration Test",
|
||||
"test",
|
||||
ChainType::Development,
|
||||
local_testnet_genesis_instant_single,
|
||||
vec![],
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Default::default(),
|
||||
)
|
||||
ChainSpec::builder(wasm_binary_unwrap(), Default::default())
|
||||
.with_name("Integration Test")
|
||||
.with_id("test")
|
||||
.with_chain_type(ChainType::Development)
|
||||
.with_genesis_config_patch(testnet_genesis(
|
||||
vec![authority_keys_from_seed("Alice")],
|
||||
vec![],
|
||||
get_account_id_from_seed::<sr25519::Public>("Alice"),
|
||||
None,
|
||||
))
|
||||
.build()
|
||||
}
|
||||
|
||||
/// Local testnet config (multivalidator Alice + Bob).
|
||||
pub fn integration_test_config_with_two_authorities() -> ChainSpec {
|
||||
ChainSpec::from_genesis(
|
||||
"Integration Test",
|
||||
"test",
|
||||
ChainType::Development,
|
||||
local_testnet_genesis,
|
||||
vec![],
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Default::default(),
|
||||
)
|
||||
ChainSpec::builder(wasm_binary_unwrap(), Default::default())
|
||||
.with_name("Integration Test")
|
||||
.with_id("test")
|
||||
.with_chain_type(ChainType::Development)
|
||||
.with_genesis_config_patch(local_testnet_genesis())
|
||||
.build()
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -14,17 +14,17 @@ targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
||||
[dependencies]
|
||||
codec = { package = "parity-scale-codec", version = "3.6.1" }
|
||||
scale-info = { version = "2.10.0", features = ["derive"] }
|
||||
scale-info = { version = "2.10.0", features = ["derive", "serde"] }
|
||||
frame-benchmarking = { path = "../../../frame/benchmarking" }
|
||||
node-primitives = { path = "../primitives" }
|
||||
kitchensink-runtime = { path = "../runtime" }
|
||||
sc-executor = { path = "../../../client/executor" }
|
||||
sp-core = { path = "../../../primitives/core" }
|
||||
sp-core = { path = "../../../primitives/core", features=["serde"] }
|
||||
sp-keystore = { path = "../../../primitives/keystore" }
|
||||
sp-state-machine = { path = "../../../primitives/state-machine" }
|
||||
sp-tracing = { path = "../../../primitives/tracing" }
|
||||
sp-trie = { path = "../../../primitives/trie" }
|
||||
sp-statement-store = { path = "../../../primitives/statement-store" }
|
||||
sp-statement-store = { path = "../../../primitives/statement-store", features=["serde"] }
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = "0.4.0"
|
||||
@@ -47,6 +47,7 @@ sp-consensus-babe = { path = "../../../primitives/consensus/babe" }
|
||||
sp-externalities = { path = "../../../primitives/externalities" }
|
||||
sp-keyring = { path = "../../../primitives/keyring" }
|
||||
sp-runtime = { path = "../../../primitives/runtime" }
|
||||
serde_json = "1.0.85"
|
||||
|
||||
[features]
|
||||
stress-test = []
|
||||
|
||||
@@ -190,7 +190,7 @@ fn bench_execute_block(c: &mut Criterion) {
|
||||
|
||||
for strategy in execution_methods {
|
||||
group.bench_function(format!("{:?}", strategy), |b| {
|
||||
let genesis_config = node_testing::genesis::config(Some(compact_code_unwrap()));
|
||||
let genesis_config = node_testing::genesis::config();
|
||||
let use_native = match strategy {
|
||||
ExecutionMethod::Native => true,
|
||||
ExecutionMethod::Wasm(..) => false,
|
||||
|
||||
@@ -857,3 +857,19 @@ fn should_import_block_with_test_client() {
|
||||
|
||||
futures::executor::block_on(client.import(BlockOrigin::Own, block)).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_config_as_json_works() {
|
||||
let mut t = new_test_ext(compact_code_unwrap());
|
||||
let r = executor_call(&mut t, "GenesisBuilder_create_default_config", &vec![], false)
|
||||
.0
|
||||
.unwrap();
|
||||
let r = Vec::<u8>::decode(&mut &r[..]).unwrap();
|
||||
let json = String::from_utf8(r.into()).expect("returned value is json. qed.");
|
||||
let expected = include_str!("res/default_genesis_config.json").to_string();
|
||||
|
||||
assert_eq!(
|
||||
serde_json::from_str::<serde_json::Value>(&expected).unwrap(),
|
||||
serde_json::from_str::<serde_json::Value>(&json).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ pub fn executor_call(
|
||||
pub fn new_test_ext(code: &[u8]) -> TestExternalities<BlakeTwo256> {
|
||||
let ext = TestExternalities::new_with_code(
|
||||
code,
|
||||
node_testing::genesis::config(Some(code)).build_storage().unwrap(),
|
||||
node_testing::genesis::config().build_storage().unwrap(),
|
||||
);
|
||||
ext
|
||||
}
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
{
|
||||
"system": {},
|
||||
"babe": {
|
||||
"authorities": [],
|
||||
"epochConfig": null
|
||||
},
|
||||
"indices": {
|
||||
"indices": []
|
||||
},
|
||||
"balances": {
|
||||
"balances": []
|
||||
},
|
||||
"transactionPayment": {
|
||||
"multiplier": "1000000000000000000"
|
||||
},
|
||||
"staking": {
|
||||
"validatorCount": 0,
|
||||
"minimumValidatorCount": 0,
|
||||
"invulnerables": [],
|
||||
"forceEra": "NotForcing",
|
||||
"slashRewardFraction": 0,
|
||||
"canceledPayout": 0,
|
||||
"stakers": [],
|
||||
"minNominatorBond": 0,
|
||||
"minValidatorBond": 0,
|
||||
"maxValidatorCount": null,
|
||||
"maxNominatorCount": null
|
||||
},
|
||||
"session": {
|
||||
"keys": []
|
||||
},
|
||||
"democracy": {},
|
||||
"council": {
|
||||
"members": []
|
||||
},
|
||||
"technicalCommittee": {
|
||||
"members": []
|
||||
},
|
||||
"elections": {
|
||||
"members": []
|
||||
},
|
||||
"technicalMembership": {
|
||||
"members": []
|
||||
},
|
||||
"grandpa": {
|
||||
"authorities": []
|
||||
},
|
||||
"treasury": {},
|
||||
"sudo": {
|
||||
"key": null
|
||||
},
|
||||
"imOnline": {
|
||||
"keys": []
|
||||
},
|
||||
"authorityDiscovery": {
|
||||
"keys": []
|
||||
},
|
||||
"society": {
|
||||
"pot": 0
|
||||
},
|
||||
"vesting": {
|
||||
"vesting": []
|
||||
},
|
||||
"glutton": {
|
||||
"compute": "0",
|
||||
"storage": "0",
|
||||
"trashDataCount": 0
|
||||
},
|
||||
"assets": {
|
||||
"assets": [],
|
||||
"metadata": [],
|
||||
"accounts": []
|
||||
},
|
||||
"poolAssets": {
|
||||
"assets": [],
|
||||
"metadata": [],
|
||||
"accounts": []
|
||||
},
|
||||
"transactionStorage": {
|
||||
"byteFee": 10,
|
||||
"entryFee": 1000,
|
||||
"storagePeriod": 100800
|
||||
},
|
||||
"allianceMotion": {
|
||||
"members": []
|
||||
},
|
||||
"alliance": {
|
||||
"fellows": [],
|
||||
"allies": []
|
||||
},
|
||||
"mixnet": {
|
||||
"mixnodes": []
|
||||
},
|
||||
"nominationPools": {
|
||||
"minJoinBond": 0,
|
||||
"minCreateBond": 0,
|
||||
"maxPools": 16,
|
||||
"maxMembersPerPool": 32,
|
||||
"maxMembers": 512,
|
||||
"globalMaxCommission": null
|
||||
},
|
||||
"txPause": {
|
||||
"paused": []
|
||||
},
|
||||
"safeMode": {
|
||||
"enteredUntil": null
|
||||
}
|
||||
}
|
||||
@@ -20,33 +20,34 @@ codec = { package = "parity-scale-codec", version = "3.6.1", default-features =
|
||||
"derive",
|
||||
"max-encoded-len",
|
||||
] }
|
||||
scale-info = { version = "2.10.0", default-features = false, features = ["derive"] }
|
||||
scale-info = { version = "2.10.0", default-features = false, features = ["derive", "serde"] }
|
||||
static_assertions = "1.1.0"
|
||||
log = { version = "0.4.17", default-features = false }
|
||||
serde_json = { version = "1.0.85", default-features = false, features = ["alloc", "arbitrary_precision"] }
|
||||
|
||||
# pallet-asset-conversion: turn on "num-traits" feature
|
||||
primitive-types = { version = "0.12.0", default-features = false, features = ["codec", "scale-info", "num-traits"] }
|
||||
|
||||
# primitives
|
||||
sp-authority-discovery = { path = "../../../primitives/authority-discovery", default-features = false}
|
||||
sp-consensus-babe = { path = "../../../primitives/consensus/babe", default-features = false}
|
||||
sp-consensus-grandpa = { path = "../../../primitives/consensus/grandpa", default-features = false}
|
||||
sp-authority-discovery = { path = "../../../primitives/authority-discovery", default-features = false, features=["serde"] }
|
||||
sp-consensus-babe = { path = "../../../primitives/consensus/babe", default-features = false, features=["serde"] }
|
||||
sp-consensus-grandpa = { path = "../../../primitives/consensus/grandpa", default-features = false, features=["serde"] }
|
||||
sp-block-builder = { path = "../../../primitives/block-builder", default-features = false}
|
||||
sp-genesis-builder = { default-features = false, path = "../../../primitives/genesis-builder" }
|
||||
sp-inherents = { path = "../../../primitives/inherents", default-features = false}
|
||||
node-primitives = { path = "../primitives", default-features = false}
|
||||
sp-mixnet = { path = "../../../primitives/mixnet", default-features = false }
|
||||
sp-offchain = { path = "../../../primitives/offchain", default-features = false}
|
||||
sp-core = { path = "../../../primitives/core", default-features = false}
|
||||
sp-core = { path = "../../../primitives/core", default-features = false, features=["serde"] }
|
||||
sp-std = { path = "../../../primitives/std", default-features = false}
|
||||
sp-api = { path = "../../../primitives/api", default-features = false}
|
||||
sp-runtime = { path = "../../../primitives/runtime", default-features = false}
|
||||
sp-staking = { path = "../../../primitives/staking", default-features = false}
|
||||
sp-runtime = { path = "../../../primitives/runtime", default-features = false, features=["serde"] }
|
||||
sp-staking = { path = "../../../primitives/staking", default-features = false, features=["serde"] }
|
||||
sp-storage = { path = "../../../primitives/storage", default-features = false}
|
||||
sp-session = { path = "../../../primitives/session", default-features = false}
|
||||
sp-transaction-pool = { path = "../../../primitives/transaction-pool", default-features = false}
|
||||
sp-statement-store = { path = "../../../primitives/statement-store", default-features = false}
|
||||
sp-version = { path = "../../../primitives/version", default-features = false}
|
||||
sp-statement-store = { path = "../../../primitives/statement-store", default-features = false, features=["serde"] }
|
||||
sp-version = { path = "../../../primitives/version", default-features = false, features=["serde"] }
|
||||
sp-io = { path = "../../../primitives/io", default-features = false}
|
||||
|
||||
# frame dependencies
|
||||
@@ -230,6 +231,7 @@ std = [
|
||||
"pallet-whitelist/std",
|
||||
"primitive-types/std",
|
||||
"scale-info/std",
|
||||
"serde_json/std",
|
||||
"sp-api/std",
|
||||
"sp-authority-discovery/std",
|
||||
"sp-block-builder/std",
|
||||
|
||||
@@ -398,7 +398,7 @@ impl BenchDb {
|
||||
|
||||
let client_config = sc_service::ClientConfig::default();
|
||||
let genesis_block_builder = sc_service::GenesisBlockBuilder::new(
|
||||
&keyring.generate_genesis(),
|
||||
keyring.as_storage_builder(),
|
||||
!client_config.no_genesis,
|
||||
backend.clone(),
|
||||
executor.clone(),
|
||||
@@ -590,12 +590,20 @@ impl BenchKeyring {
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate genesis with accounts from this keyring endowed with some balance.
|
||||
pub fn generate_genesis(&self) -> kitchensink_runtime::RuntimeGenesisConfig {
|
||||
crate::genesis::config_endowed(
|
||||
Some(kitchensink_runtime::wasm_binary_unwrap()),
|
||||
self.collect_account_ids(),
|
||||
)
|
||||
/// Generate genesis with accounts from this keyring endowed with some balance and
|
||||
/// kitchensink_runtime code blob.
|
||||
pub fn as_storage_builder(&self) -> &dyn sp_runtime::BuildStorage {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl sp_runtime::BuildStorage for BenchKeyring {
|
||||
fn assimilate_storage(&self, storage: &mut sp_core::storage::Storage) -> Result<(), String> {
|
||||
storage.top.insert(
|
||||
sp_core::storage::well_known_keys::CODE.to_vec(),
|
||||
kitchensink_runtime::wasm_binary_unwrap().into(),
|
||||
);
|
||||
crate::genesis::config_endowed(self.collect_account_ids()).assimilate_storage(storage)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,12 @@ pub struct GenesisParameters;
|
||||
|
||||
impl substrate_test_client::GenesisInit for GenesisParameters {
|
||||
fn genesis_storage(&self) -> Storage {
|
||||
crate::genesis::config(None).build_storage().unwrap()
|
||||
let mut storage = crate::genesis::config().build_storage().unwrap();
|
||||
storage.top.insert(
|
||||
sp_core::storage::well_known_keys::CODE.to_vec(),
|
||||
kitchensink_runtime::wasm_binary_unwrap().into(),
|
||||
);
|
||||
storage
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,22 +20,21 @@
|
||||
|
||||
use crate::keyring::*;
|
||||
use kitchensink_runtime::{
|
||||
constants::currency::*, wasm_binary_unwrap, AccountId, AssetsConfig, BabeConfig,
|
||||
BalancesConfig, GluttonConfig, GrandpaConfig, IndicesConfig, RuntimeGenesisConfig,
|
||||
SessionConfig, SocietyConfig, StakerStatus, StakingConfig, SystemConfig,
|
||||
BABE_GENESIS_EPOCH_CONFIG,
|
||||
constants::currency::*, AccountId, AssetsConfig, BabeConfig, BalancesConfig, GluttonConfig,
|
||||
GrandpaConfig, IndicesConfig, RuntimeGenesisConfig, SessionConfig, SocietyConfig, StakerStatus,
|
||||
StakingConfig, BABE_GENESIS_EPOCH_CONFIG,
|
||||
};
|
||||
use sp_keyring::{Ed25519Keyring, Sr25519Keyring};
|
||||
use sp_runtime::Perbill;
|
||||
|
||||
/// Create genesis runtime configuration for tests.
|
||||
pub fn config(code: Option<&[u8]>) -> RuntimeGenesisConfig {
|
||||
config_endowed(code, Default::default())
|
||||
pub fn config() -> RuntimeGenesisConfig {
|
||||
config_endowed(Default::default())
|
||||
}
|
||||
|
||||
/// Create genesis runtime configuration for tests with some extra
|
||||
/// endowed accounts.
|
||||
pub fn config_endowed(code: Option<&[u8]>, extra_endowed: Vec<AccountId>) -> RuntimeGenesisConfig {
|
||||
pub fn config_endowed(extra_endowed: Vec<AccountId>) -> RuntimeGenesisConfig {
|
||||
let mut endowed = vec![
|
||||
(alice(), 111 * DOLLARS),
|
||||
(bob(), 100 * DOLLARS),
|
||||
@@ -48,10 +47,7 @@ pub fn config_endowed(code: Option<&[u8]>, extra_endowed: Vec<AccountId>) -> Run
|
||||
endowed.extend(extra_endowed.into_iter().map(|endowed| (endowed, 100 * DOLLARS)));
|
||||
|
||||
RuntimeGenesisConfig {
|
||||
system: SystemConfig {
|
||||
code: code.map(|x| x.to_vec()).unwrap_or_else(|| wasm_binary_unwrap().to_vec()),
|
||||
..Default::default()
|
||||
},
|
||||
system: Default::default(),
|
||||
indices: IndicesConfig { indices: vec![] },
|
||||
balances: BalancesConfig { balances: endowed },
|
||||
session: SessionConfig {
|
||||
|
||||
@@ -23,8 +23,12 @@ crate-type = ["rlib"]
|
||||
ansi_term = "0.12.1"
|
||||
clap = { version = "4.4.6", features = ["derive"] }
|
||||
rand = "0.8"
|
||||
kitchensink-runtime = { version = "3.0.0-dev", path = "../../node/runtime" }
|
||||
log = "0.4.17"
|
||||
node-cli = { package = "staging-node-cli", path = "../../node/cli" }
|
||||
sc-chain-spec = { path = "../../../client/chain-spec" }
|
||||
sc-keystore = { path = "../../../client/keystore" }
|
||||
serde_json = "1.0.100"
|
||||
sp-core = { path = "../../../primitives/core" }
|
||||
sp-keystore = { path = "../../../primitives/keystore" }
|
||||
sp-tracing = { version = "10.0.0", path = "../../../primitives/tracing" }
|
||||
|
||||
@@ -17,28 +17,40 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use chain_spec_builder::{
|
||||
generate_authority_keys_and_store, generate_chain_spec, print_seeds, ChainSpecBuilder,
|
||||
generate_authority_keys_and_store, generate_chain_spec, generate_chain_spec_for_runtime,
|
||||
print_seeds, ChainSpecBuilder, ChainSpecBuilderCmd, EditCmd, GenerateCmd, NewCmd, VerifyCmd,
|
||||
};
|
||||
use clap::Parser;
|
||||
use node_cli::chain_spec;
|
||||
use rand::{distributions::Alphanumeric, rngs::OsRng, Rng};
|
||||
use sc_chain_spec::{update_code_in_json_chain_spec, GenericChainSpec};
|
||||
use sp_core::{crypto::Ss58Codec, sr25519};
|
||||
use staging_chain_spec_builder as chain_spec_builder;
|
||||
use std::fs;
|
||||
|
||||
fn main() -> Result<(), String> {
|
||||
#[cfg(build_type = "debug")]
|
||||
println!(
|
||||
"The chain spec builder builds a chain specification that includes a Substrate runtime \
|
||||
compiled as WASM. To ensure proper functioning of the included runtime compile (or run) \
|
||||
the chain spec builder binary in `--release` mode.\n",
|
||||
);
|
||||
sp_tracing::try_init_simple();
|
||||
|
||||
let builder = ChainSpecBuilder::parse();
|
||||
let chain_spec_path = builder.chain_spec_path().to_path_buf();
|
||||
#[cfg(build_type = "debug")]
|
||||
if matches!(builder.command, ChainSpecBuilderCmd::Generate(_) | ChainSpecBuilderCmd::New(_)) {
|
||||
println!(
|
||||
"The chain spec builder builds a chain specification that includes a Substrate runtime \
|
||||
compiled as WASM. To ensure proper functioning of the included runtime compile (or run) \
|
||||
the chain spec builder binary in `--release` mode.\n",
|
||||
);
|
||||
}
|
||||
|
||||
let (authority_seeds, nominator_accounts, endowed_accounts, sudo_account) = match builder {
|
||||
ChainSpecBuilder::Generate { authorities, nominators, endowed, keystore_path, .. } => {
|
||||
let chain_spec_path = builder.chain_spec_path.to_path_buf();
|
||||
let mut write_chain_spec = true;
|
||||
|
||||
let chain_spec_json = match builder.command {
|
||||
ChainSpecBuilderCmd::Generate(GenerateCmd {
|
||||
authorities,
|
||||
nominators,
|
||||
endowed,
|
||||
keystore_path,
|
||||
}) => {
|
||||
let authorities = authorities.max(1);
|
||||
let rand_str = || -> String {
|
||||
OsRng.sample_iter(&Alphanumeric).take(32).map(char::from).collect()
|
||||
@@ -72,19 +84,58 @@ fn main() -> Result<(), String> {
|
||||
let sudo_account =
|
||||
chain_spec::get_account_id_from_seed::<sr25519::Public>(&sudo_seed).to_ss58check();
|
||||
|
||||
(authority_seeds, nominator_accounts, endowed_accounts, sudo_account)
|
||||
generate_chain_spec(authority_seeds, nominator_accounts, endowed_accounts, sudo_account)
|
||||
},
|
||||
ChainSpecBuilder::New {
|
||||
ChainSpecBuilderCmd::New(NewCmd {
|
||||
authority_seeds,
|
||||
nominator_accounts,
|
||||
endowed_accounts,
|
||||
sudo_account,
|
||||
..
|
||||
} => (authority_seeds, nominator_accounts, endowed_accounts, sudo_account),
|
||||
};
|
||||
}) =>
|
||||
generate_chain_spec(authority_seeds, nominator_accounts, endowed_accounts, sudo_account),
|
||||
ChainSpecBuilderCmd::Runtime(cmd) => generate_chain_spec_for_runtime(&cmd),
|
||||
ChainSpecBuilderCmd::Edit(EditCmd {
|
||||
ref input_chain_spec,
|
||||
ref runtime_wasm_path,
|
||||
convert_to_raw,
|
||||
}) => {
|
||||
let chain_spec = GenericChainSpec::<()>::from_json_file(input_chain_spec.clone())?;
|
||||
|
||||
let json =
|
||||
generate_chain_spec(authority_seeds, nominator_accounts, endowed_accounts, sudo_account)?;
|
||||
let mut chain_spec_json =
|
||||
serde_json::from_str::<serde_json::Value>(&chain_spec.as_json(convert_to_raw)?)
|
||||
.map_err(|e| format!("Conversion to json failed: {e}"))?;
|
||||
if let Some(path) = runtime_wasm_path {
|
||||
update_code_in_json_chain_spec(
|
||||
&mut chain_spec_json,
|
||||
&fs::read(path.as_path())
|
||||
.map_err(|e| format!("Wasm blob file could not be read: {e}"))?[..],
|
||||
);
|
||||
}
|
||||
|
||||
fs::write(chain_spec_path, json).map_err(|err| err.to_string())
|
||||
serde_json::to_string_pretty(&chain_spec_json)
|
||||
.map_err(|e| format!("to pretty failed: {e}"))
|
||||
},
|
||||
ChainSpecBuilderCmd::Verify(VerifyCmd { ref input_chain_spec, ref runtime_wasm_path }) => {
|
||||
write_chain_spec = false;
|
||||
let chain_spec = GenericChainSpec::<()>::from_json_file(input_chain_spec.clone())?;
|
||||
let mut chain_spec_json =
|
||||
serde_json::from_str::<serde_json::Value>(&chain_spec.as_json(true)?)
|
||||
.map_err(|e| format!("Conversion to json failed: {e}"))?;
|
||||
if let Some(path) = runtime_wasm_path {
|
||||
update_code_in_json_chain_spec(
|
||||
&mut chain_spec_json,
|
||||
&fs::read(path.as_path())
|
||||
.map_err(|e| format!("Wasm blob file could not be read: {e}"))?[..],
|
||||
);
|
||||
};
|
||||
serde_json::to_string_pretty(&chain_spec_json)
|
||||
.map_err(|e| format!("to pretty failed: {e}"))
|
||||
},
|
||||
}?;
|
||||
|
||||
if write_chain_spec {
|
||||
fs::write(chain_spec_path, chain_spec_json).map_err(|err| err.to_string())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,96 +29,169 @@
|
||||
//! [`sc-chain-spec`]: ../sc_chain_spec/index.html
|
||||
//! [`node-cli`]: ../node_cli/index.html
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::{
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use ansi_term::Style;
|
||||
use clap::Parser;
|
||||
use clap::{Parser, Subcommand};
|
||||
use sc_chain_spec::GenesisConfigBuilderRuntimeCaller;
|
||||
|
||||
use node_cli::chain_spec::{self, AccountId};
|
||||
use sc_keystore::LocalKeystore;
|
||||
use serde_json::Value;
|
||||
use sp_core::crypto::{ByteArray, Ss58Codec};
|
||||
use sp_keystore::KeystorePtr;
|
||||
|
||||
/// A utility to easily create a testnet chain spec definition with a given set
|
||||
/// of authorities and endowed accounts and/or generate random accounts.
|
||||
#[derive(Parser)]
|
||||
#[derive(Debug, Parser)]
|
||||
#[command(rename_all = "kebab-case")]
|
||||
pub enum ChainSpecBuilder {
|
||||
/// Create a new chain spec with the given authorities, endowed and sudo
|
||||
/// accounts.
|
||||
New {
|
||||
/// Authority key seed.
|
||||
#[arg(long, short, required = true)]
|
||||
authority_seeds: Vec<String>,
|
||||
/// Active nominators (SS58 format), each backing a random subset of the aforementioned
|
||||
/// authorities.
|
||||
#[arg(long, short, default_value = "0")]
|
||||
nominator_accounts: Vec<String>,
|
||||
/// Endowed account address (SS58 format).
|
||||
#[arg(long, short)]
|
||||
endowed_accounts: Vec<String>,
|
||||
/// Sudo account address (SS58 format).
|
||||
#[arg(long, short)]
|
||||
sudo_account: String,
|
||||
/// The path where the chain spec should be saved.
|
||||
#[arg(long, short, default_value = "./chain_spec.json")]
|
||||
chain_spec_path: PathBuf,
|
||||
},
|
||||
/// Create a new chain spec with the given number of authorities and endowed
|
||||
/// accounts. Random keys will be generated as required.
|
||||
Generate {
|
||||
/// The number of authorities.
|
||||
#[arg(long, short)]
|
||||
authorities: usize,
|
||||
/// The number of nominators backing the aforementioned authorities.
|
||||
///
|
||||
/// Will nominate a random subset of `authorities`.
|
||||
#[arg(long, short, default_value_t = 0)]
|
||||
nominators: usize,
|
||||
/// The number of endowed accounts.
|
||||
#[arg(long, short, default_value_t = 0)]
|
||||
endowed: usize,
|
||||
/// The path where the chain spec should be saved.
|
||||
#[arg(long, short, default_value = "./chain_spec.json")]
|
||||
chain_spec_path: PathBuf,
|
||||
/// Path to use when saving generated keystores for each authority.
|
||||
///
|
||||
/// At this path, a new folder will be created for each authority's
|
||||
/// keystore named `auth-$i` where `i` is the authority index, i.e.
|
||||
/// `auth-0`, `auth-1`, etc.
|
||||
#[arg(long, short)]
|
||||
keystore_path: Option<PathBuf>,
|
||||
},
|
||||
pub struct ChainSpecBuilder {
|
||||
#[command(subcommand)]
|
||||
pub command: ChainSpecBuilderCmd,
|
||||
/// The path where the chain spec should be saved.
|
||||
#[arg(long, short, default_value = "./chain_spec.json")]
|
||||
pub chain_spec_path: PathBuf,
|
||||
}
|
||||
|
||||
impl ChainSpecBuilder {
|
||||
/// Returns the path where the chain spec should be saved.
|
||||
pub fn chain_spec_path(&self) -> &Path {
|
||||
match self {
|
||||
ChainSpecBuilder::New { chain_spec_path, .. } => chain_spec_path.as_path(),
|
||||
ChainSpecBuilder::Generate { chain_spec_path, .. } => chain_spec_path.as_path(),
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Subcommand)]
|
||||
#[command(rename_all = "kebab-case")]
|
||||
pub enum ChainSpecBuilderCmd {
|
||||
New(NewCmd),
|
||||
Generate(GenerateCmd),
|
||||
Runtime(RuntimeCmd),
|
||||
Edit(EditCmd),
|
||||
Verify(VerifyCmd),
|
||||
}
|
||||
|
||||
fn genesis_constructor(
|
||||
authority_seeds: &[String],
|
||||
nominator_accounts: &[AccountId],
|
||||
endowed_accounts: &[AccountId],
|
||||
sudo_account: &AccountId,
|
||||
) -> chain_spec::RuntimeGenesisConfig {
|
||||
let authorities = authority_seeds
|
||||
.iter()
|
||||
.map(AsRef::as_ref)
|
||||
.map(chain_spec::authority_keys_from_seed)
|
||||
.collect::<Vec<_>>();
|
||||
/// Create a new chain spec with the given authorities, endowed and sudo
|
||||
/// accounts. Only works for kitchen-sink runtime
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(rename_all = "kebab-case")]
|
||||
pub struct NewCmd {
|
||||
/// Authority key seed.
|
||||
#[arg(long, short, required = true)]
|
||||
pub authority_seeds: Vec<String>,
|
||||
/// Active nominators (SS58 format), each backing a random subset of the aforementioned
|
||||
/// authorities.
|
||||
#[arg(long, short, default_value = "0")]
|
||||
pub nominator_accounts: Vec<String>,
|
||||
/// Endowed account address (SS58 format).
|
||||
#[arg(long, short)]
|
||||
pub endowed_accounts: Vec<String>,
|
||||
/// Sudo account address (SS58 format).
|
||||
#[arg(long, short)]
|
||||
pub sudo_account: String,
|
||||
}
|
||||
|
||||
chain_spec::testnet_genesis(
|
||||
authorities,
|
||||
nominator_accounts.to_vec(),
|
||||
sudo_account.clone(),
|
||||
Some(endowed_accounts.to_vec()),
|
||||
)
|
||||
/// Create a new chain spec with the given number of authorities and endowed
|
||||
/// accounts. Random keys will be generated as required.
|
||||
#[derive(Parser, Debug)]
|
||||
pub struct GenerateCmd {
|
||||
/// The number of authorities.
|
||||
#[arg(long, short)]
|
||||
pub authorities: usize,
|
||||
/// The number of nominators backing the aforementioned authorities.
|
||||
///
|
||||
/// Will nominate a random subset of `authorities`.
|
||||
#[arg(long, short, default_value_t = 0)]
|
||||
pub nominators: usize,
|
||||
/// The number of endowed accounts.
|
||||
#[arg(long, short, default_value_t = 0)]
|
||||
pub endowed: usize,
|
||||
/// Path to use when saving generated keystores for each authority.
|
||||
///
|
||||
/// At this path, a new folder will be created for each authority's
|
||||
/// keystore named `auth-$i` where `i` is the authority index, i.e.
|
||||
/// `auth-0`, `auth-1`, etc.
|
||||
#[arg(long, short)]
|
||||
pub keystore_path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
/// Create a new chain spec by interacting with the provided runtime wasm blob.
|
||||
#[derive(Parser, Debug)]
|
||||
pub struct RuntimeCmd {
|
||||
/// The name of chain
|
||||
#[arg(long, short = 'n', default_value = "Custom")]
|
||||
chain_name: String,
|
||||
/// The chain id
|
||||
#[arg(long, short = 'i', default_value = "custom")]
|
||||
chain_id: String,
|
||||
/// The path to runtime wasm blob
|
||||
#[arg(long, short)]
|
||||
runtime_wasm_path: PathBuf,
|
||||
/// Export chainspec as raw storage
|
||||
#[arg(long, short = 's')]
|
||||
raw_storage: bool,
|
||||
/// Verify the genesis config. This silently generates the raw storage from genesis config. Any
|
||||
/// errors will be reported.
|
||||
#[arg(long, short = 'v')]
|
||||
verify: bool,
|
||||
#[command(subcommand)]
|
||||
action: GenesisBuildAction,
|
||||
}
|
||||
|
||||
#[derive(Subcommand, Debug, Clone)]
|
||||
enum GenesisBuildAction {
|
||||
Patch(PatchCmd),
|
||||
Full(FullCmd),
|
||||
Default(DefaultCmd),
|
||||
}
|
||||
|
||||
/// Patches the runtime's default genesis config with provided patch.
|
||||
#[derive(Parser, Debug, Clone)]
|
||||
struct PatchCmd {
|
||||
/// The path to the runtime genesis config patch.
|
||||
#[arg(long, short)]
|
||||
patch_path: PathBuf,
|
||||
}
|
||||
|
||||
/// Build the genesis config for runtime using provided json file. No defaults will be used.
|
||||
#[derive(Parser, Debug, Clone)]
|
||||
struct FullCmd {
|
||||
/// The path to the full runtime genesis config json file.
|
||||
#[arg(long, short)]
|
||||
config_path: PathBuf,
|
||||
}
|
||||
|
||||
/// Gets the default genesis config for the runtime and uses it in ChainSpec. Please note that
|
||||
/// default genesis config may not be valid. For some runtimes initial values should be added there
|
||||
/// (e.g. session keys, babe epoch).
|
||||
#[derive(Parser, Debug, Clone)]
|
||||
struct DefaultCmd {
|
||||
/// If provided stores the default genesis config json file at given path (in addition to
|
||||
/// chain-spec).
|
||||
#[arg(long, short)]
|
||||
default_config_path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
/// Edits provided input chain spec. Input can be converted into raw storage chain-spec. The code
|
||||
/// can be updated with the runtime provided in the command line.
|
||||
#[derive(Parser, Debug, Clone)]
|
||||
pub struct EditCmd {
|
||||
/// Chain spec to be edited
|
||||
#[arg(long, short)]
|
||||
pub input_chain_spec: PathBuf,
|
||||
/// The path to new runtime wasm blob to be stored into chain-spec
|
||||
#[arg(long, short = 'r')]
|
||||
pub runtime_wasm_path: Option<PathBuf>,
|
||||
/// Convert genesis spec to raw format
|
||||
#[arg(long, short = 's')]
|
||||
pub convert_to_raw: bool,
|
||||
}
|
||||
|
||||
/// Verifies provided input chain spec. If the runtime is provided verification is performed against
|
||||
/// new runtime.
|
||||
#[derive(Parser, Debug, Clone)]
|
||||
pub struct VerifyCmd {
|
||||
/// Chain spec to be edited
|
||||
#[arg(long, short)]
|
||||
pub input_chain_spec: PathBuf,
|
||||
/// The path to new runtime wasm blob to be stored into chain-spec
|
||||
#[arg(long, short = 'r')]
|
||||
pub runtime_wasm_path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
/// Generate the chain spec using the given seeds and accounts.
|
||||
@@ -145,27 +218,24 @@ pub fn generate_chain_spec(
|
||||
|
||||
let sudo_account = parse_account(sudo_account)?;
|
||||
|
||||
let chain_spec = chain_spec::ChainSpec::from_genesis(
|
||||
"Custom",
|
||||
"custom",
|
||||
sc_chain_spec::ChainType::Live,
|
||||
move || {
|
||||
genesis_constructor(
|
||||
&authority_seeds,
|
||||
&nominator_accounts,
|
||||
&endowed_accounts,
|
||||
&sudo_account,
|
||||
)
|
||||
},
|
||||
vec![],
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Default::default(),
|
||||
);
|
||||
let authorities = authority_seeds
|
||||
.iter()
|
||||
.map(AsRef::as_ref)
|
||||
.map(chain_spec::authority_keys_from_seed)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
chain_spec.as_json(false)
|
||||
chain_spec::ChainSpec::builder(kitchensink_runtime::wasm_binary_unwrap(), Default::default())
|
||||
.with_name("Custom")
|
||||
.with_id("custom")
|
||||
.with_chain_type(sc_chain_spec::ChainType::Live)
|
||||
.with_genesis_config_patch(chain_spec::testnet_genesis(
|
||||
authorities,
|
||||
nominator_accounts,
|
||||
sudo_account,
|
||||
Some(endowed_accounts),
|
||||
))
|
||||
.build()
|
||||
.as_json(false)
|
||||
}
|
||||
|
||||
/// Generate the authority keys and store them in the given `keystore_path`.
|
||||
@@ -241,3 +311,54 @@ pub fn print_seeds(
|
||||
println!("{}", header.paint("Sudo seed"));
|
||||
println!("//{}", sudo_seed);
|
||||
}
|
||||
|
||||
/// Processes `RuntimeCmd` and returns JSON version of `ChainSpec`
|
||||
pub fn generate_chain_spec_for_runtime(cmd: &RuntimeCmd) -> Result<String, String> {
|
||||
let code = fs::read(cmd.runtime_wasm_path.as_path())
|
||||
.map_err(|e| format!("wasm blob shall be readable {e}"))?;
|
||||
|
||||
let builder = chain_spec::ChainSpec::builder(&code[..], Default::default())
|
||||
.with_name(&cmd.chain_name[..])
|
||||
.with_id(&cmd.chain_id[..])
|
||||
.with_chain_type(sc_chain_spec::ChainType::Live);
|
||||
|
||||
let builder = match cmd.action {
|
||||
GenesisBuildAction::Patch(PatchCmd { ref patch_path }) => {
|
||||
let patch = fs::read(patch_path.as_path())
|
||||
.map_err(|e| format!("patch file {patch_path:?} shall be readable: {e}"))?;
|
||||
builder.with_genesis_config_patch(serde_json::from_slice::<Value>(&patch[..]).map_err(
|
||||
|e| format!("patch file {patch_path:?} shall contain a valid json: {e}"),
|
||||
)?)
|
||||
},
|
||||
GenesisBuildAction::Full(FullCmd { ref config_path }) => {
|
||||
let config = fs::read(config_path.as_path())
|
||||
.map_err(|e| format!("config file {config_path:?} shall be readable: {e}"))?;
|
||||
builder.with_genesis_config(serde_json::from_slice::<Value>(&config[..]).map_err(
|
||||
|e| format!("config file {config_path:?} shall contain a valid json: {e}"),
|
||||
)?)
|
||||
},
|
||||
GenesisBuildAction::Default(DefaultCmd { ref default_config_path }) => {
|
||||
let caller = GenesisConfigBuilderRuntimeCaller::new(&code[..]);
|
||||
let default_config = caller
|
||||
.get_default_config()
|
||||
.map_err(|e| format!("getting default config from runtime should work: {e}"))?;
|
||||
default_config_path.clone().map(|path| {
|
||||
fs::write(path.as_path(), serde_json::to_string_pretty(&default_config).unwrap())
|
||||
.map_err(|err| err.to_string())
|
||||
});
|
||||
builder.with_genesis_config(default_config)
|
||||
},
|
||||
};
|
||||
|
||||
let chain_spec = builder.build();
|
||||
|
||||
match (cmd.verify, cmd.raw_storage) {
|
||||
(_, true) => chain_spec.as_json(true),
|
||||
(true, false) => {
|
||||
chain_spec.as_json(true)?;
|
||||
println!("Genesis config verification: OK");
|
||||
chain_spec.as_json(false)
|
||||
},
|
||||
(false, false) => chain_spec.as_json(false),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,15 +13,27 @@ readme = "README.md"
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
||||
[dependencies]
|
||||
codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] }
|
||||
memmap2 = "0.5.0"
|
||||
serde = { version = "1.0.188", features = ["derive"] }
|
||||
serde_json = "1.0.107"
|
||||
sc-client-api = { path = "../api" }
|
||||
sc-chain-spec-derive = { path = "derive" }
|
||||
sc-executor = { path = "../executor" }
|
||||
sp-io = { default-features = false, path = "../../primitives/io" }
|
||||
sc-network = { path = "../network" }
|
||||
sc-telemetry = { path = "../telemetry" }
|
||||
sp-blockchain = { path = "../../primitives/blockchain" }
|
||||
sp-core = { path = "../../primitives/core" }
|
||||
sp-genesis-builder = { path = "../../primitives/genesis-builder" }
|
||||
sp-runtime = { path = "../../primitives/runtime" }
|
||||
sp-state-machine = { path = "../../primitives/state-machine" }
|
||||
log = { version = "0.4.17", default-features = false }
|
||||
array-bytes = { version = "6.1" }
|
||||
docify = "0.2.0"
|
||||
|
||||
[dev-dependencies]
|
||||
substrate-test-runtime = { path = "../../test-utils/runtime" }
|
||||
sp-keyring = { path = "../../primitives/keyring" }
|
||||
sp-application-crypto = { default-features = false, path = "../../primitives/application-crypto", features = ["serde"] }
|
||||
sp-consensus-babe = { default-features = false, path = "../../primitives/consensus/babe", features = ["serde"] }
|
||||
|
||||
@@ -1,92 +1,6 @@
|
||||
Substrate chain configurations.
|
||||
|
||||
This crate contains structs and utilities to declare
|
||||
a runtime-specific configuration file (a.k.a chain spec).
|
||||
|
||||
Basic chain spec type containing all required parameters is
|
||||
[`ChainSpec`](https://docs.rs/sc-chain-spec/latest/sc_chain_spec/struct.GenericChainSpec.html). It can be extended with
|
||||
additional options that contain configuration specific to your chain.
|
||||
Usually the extension is going to be an amalgamate 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.
|
||||
|
||||
```rust
|
||||
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 `ChainSpecGroup`
|
||||
macro and provided [`Forks`](https://docs.rs/sc-chain-spec/latest/sc_chain_spec/struct.Forks.html) structure to put
|
||||
such parameters to your chain spec.
|
||||
This will allow to override a single parameter starting at specific
|
||||
block number.
|
||||
|
||||
```rust
|
||||
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 is allowed to change
|
||||
with block numbers (i.e. is forkable), and another set that is not subject to changes.
|
||||
This is also possible by declaring an extension that contains `Forks` within it.
|
||||
|
||||
|
||||
```rust
|
||||
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>;
|
||||
```
|
||||
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
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "TestName",
|
||||
"id": "test_id",
|
||||
"chainType": "Local",
|
||||
"bootNodes": [],
|
||||
"telemetryEndpoints": null,
|
||||
"protocolId": null,
|
||||
"properties": null,
|
||||
"codeSubstitutes": {},
|
||||
"genesis": {
|
||||
"raw": {
|
||||
"top": {
|
||||
"0x3a636f6465": "0x010101"
|
||||
},
|
||||
"childrenDefault": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "TestName",
|
||||
"id": "test_id",
|
||||
"chainType": "Local",
|
||||
"bootNodes": [],
|
||||
"telemetryEndpoints": null,
|
||||
"protocolId": null,
|
||||
"properties": null,
|
||||
"codeSubstitutes": {},
|
||||
"genesis": {
|
||||
"raw": {
|
||||
"top": {
|
||||
"0x3a636f6465": "0x010101"
|
||||
},
|
||||
"childrenDefault": {}
|
||||
}
|
||||
},
|
||||
"code": "0x060708"
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"name": "TestName",
|
||||
"id": "test_id",
|
||||
"chainType": "Local",
|
||||
"bootNodes": [],
|
||||
"telemetryEndpoints": null,
|
||||
"protocolId": null,
|
||||
"properties": null,
|
||||
"codeSubstitutes": {},
|
||||
"genesis": {
|
||||
"raw": {
|
||||
"top": {
|
||||
"0x00771836bebdd29870ff246d305c578c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000",
|
||||
"0x00771836bebdd29870ff246d305c578c5e0621c4869aa60c02be9adcc98a0d1d": "0x0cd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a4890b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe22",
|
||||
"0x1cb6f36e027abb2091cfb5110ab5087f4e7b9012096b41c4eb3aaf947f6ea429": "0x0000",
|
||||
"0x1cb6f36e027abb2091cfb5110ab5087f5e0621c4869aa60c02be9adcc98a0d1d": "0x0cd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01000000000000008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48010000000000000090b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe220100000000000000",
|
||||
"0x1cb6f36e027abb2091cfb5110ab5087f66e8f035c8adbe7f1547b43c51e6f8a4": "0x00000000",
|
||||
"0x1cb6f36e027abb2091cfb5110ab5087faacf00b9b41fda7a9268821c2a2b3e4c": "0x0cd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01000000000000008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48010000000000000090b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe220100000000000000",
|
||||
"0x1cb6f36e027abb2091cfb5110ab5087fdc6b171b77304263c292cc3ea5ed31ef": "0x03000000000000000a0000000000000001",
|
||||
"0x26aa394eea5630e07c48ae0c9558cef74e7b9012096b41c4eb3aaf947f6ea429": "0x0000",
|
||||
"0x26aa394eea5630e07c48ae0c9558cef75684a022a34dd8bfa2baaf44f172b710": "0x01",
|
||||
"0x26aa394eea5630e07c48ae0c9558cef78a42f33323cb5ced3b44dd825fda9fcc": "0x4545454545454545454545454545454545454545454545454545454545454545",
|
||||
"0x26aa394eea5630e07c48ae0c9558cef7a44704b568d21667356a5a050c118746bb1bdbcacd6ac9340000000000000000": "0x4545454545454545454545454545454545454545454545454545454545454545",
|
||||
"0x26aa394eea5630e07c48ae0c9558cef7a7fd6c28836b9a28522dc924110cf439": "0x01",
|
||||
"0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da901cae4e3edfbb32c91ed3f01ab964f4eeeab50338d8e5176d3141802d7b010a55dadcd5f23cf8aaafa724627e967e90e": "0x000000000000000000000000010000000000000000008a5d784563010000000000000000000000000000000000000000000000000000000000000080",
|
||||
"0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da91b614bd4a126f2d5d294e9a8af9da25248d7e931307afb4b68d8d565d4c66e00d856c6d65f5fed6bb82dcfb60e936c67": "0x000000000000000000000000010000000000000000008a5d784563010000000000000000000000000000000000000000000000000000000000000080",
|
||||
"0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da94b21aff9fe1e8b2fc4b0775b8cbeff28ba8e2c7594dd74730f3ca835e95455d199261897edc9735d602ea29615e2b10b": "0x000000000000000000000000010000000000000000008a5d784563010000000000000000000000000000000000000000000000000000000000000080",
|
||||
"0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da94f9aea1afa791265fae359272badc1cf8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48": "0x000000000000000000000000010000000000000000008a5d784563010000000000000000000000000000000000000000000000000000000000000080",
|
||||
"0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da95786a2916fcb81e1bd5dcd81e0d2452884617f575372edb5a36d85c04cdf2e4699f96fe33eb5f94a28c041b88e398d0c": "0x000000000000000000000000010000000000000000008a5d784563010000000000000000000000000000000000000000000000000000000000000080",
|
||||
"0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da95b8542d9672c7b7e779cc7c1e6b605691c2115d06120ea2bee32dd601d02f36367564e7ddf84ae2717ca3f097459652e": "0x000000000000000000000000010000000000000000008a5d784563010000000000000000000000000000000000000000000000000000000000000080",
|
||||
"0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da996c30bdbfab640838e6b6d3c33ab4adb4211b79e34ee8072eab506edd4b93a7b85a14c9a05e5cdd056d98e7dbca87730": "0x000000000000000000000000010000000000000000008a5d784563010000000000000000000000000000000000000000000000000000000000000080",
|
||||
"0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da99dc65b1339ec388fbf2ca0cdef51253512c6cfd663203ea16968594f24690338befd906856c4d2f4ef32dad578dba20c": "0x000000000000000000000000010000000000000000008a5d784563010000000000000000000000000000000000000000000000000000000000000080",
|
||||
"0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da99e6eb5abd62f5fd54793da91a47e6af6125d57171ff9241f07acaa1bb6a6103517965cf2cd00e643b27e7599ebccba70": "0x000000000000000000000000010000000000000000008a5d784563010000000000000000000000000000000000000000000000000000000000000080",
|
||||
"0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9b0edae20838083f2cde1c4080db8cf8090b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe22": "0x000000000000000000000000010000000000000000008a5d784563010000000000000000000000000000000000000000000000000000000000000080",
|
||||
"0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9d0052993b6f3bd0544fd1f5e4125b9fbde3e789ecd53431fe5c06c12b72137153496dace35c695b5f4d7b41f7ed5763b": "0x000000000000000000000000010000000000000000008a5d784563010000000000000000000000000000000000000000000000000000000000000080",
|
||||
"0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9d6b7e9a5f12bc571053265dade10d3b4b606fc73f57f03cdb4c932d475ab426043e429cecc2ffff0d2672b0df8398c48": "0x000000000000000000000000010000000000000000008a5d784563010000000000000000000000000000000000000000000000000000000000000080",
|
||||
"0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d": "0x000000000000000000000000010000000000000000008a5d784563010000000000000000000000000000000000000000000000000000000000000080",
|
||||
"0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9e1a35f56ee295d39287cbffcfc60c4b346f136b564e1fad55031404dd84e5cd3fa76bfe7cc7599b39d38fd06663bbc0a": "0x000000000000000000000000010000000000000000008a5d784563010000000000000000000000000000000000000000000000000000000000000080",
|
||||
"0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9e2c1dc507e2035edbbd8776c440d870460c57f0008067cc01c5ff9eb2e2f9b3a94299a915a91198bd1021a6c55596f57": "0x000000000000000000000000010000000000000000008a5d784563010000000000000000000000000000000000000000000000000000000000000080",
|
||||
"0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9eca0e653a94f4080f6311b4e7b6934eb2afba9278e30ccf6a6ceb3a8b6e336b70068f045c666f2e7f4f9cc5f47db8972": "0x000000000000000000000000010000000000000000008a5d784563010000000000000000000000000000000000000000000000000000000000000080",
|
||||
"0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9ee8bf7ef90fc56a8aa3b90b344c599550c29b161e27ff8ba45bf6bad4711f326fc506a8803453a4d7e3158e993495f10": "0x000000000000000000000000010000000000000000008a5d784563010000000000000000000000000000000000000000000000000000000000000080",
|
||||
"0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9f5d6f1c082fe63eec7a71fcad00f4a892e3d43b7b0d04e776e69e7be35247cecdac65504c579195731eaf64b7940966e": "0x000000000000000000000000010000000000000000008a5d784563010000000000000000000000000000000000000000000000000000000000000080",
|
||||
"0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9fbf0818841edf110e05228a6379763c4fc3c37459d9bdc61f58a5ebc01e9e2305a19d390c0543dc733861ec3cf1de01f": "0x000000000000000000000000010000000000000000008a5d784563010000000000000000000000000000000000000000000000000000000000000080",
|
||||
"0x26aa394eea5630e07c48ae0c9558cef7f9cce9c888469bb1a0dceaa129672ef8": "0x0000",
|
||||
"0x3a636f6465": "0x0",
|
||||
"0x3a65787472696e7369635f696e646578": "0x00000000",
|
||||
"0xc2261276cc9d1f8598ea4b6a74b15c2f4e7b9012096b41c4eb3aaf947f6ea429": "0x0100",
|
||||
"0xc2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80": "0x00003ef1ee275e1a"
|
||||
},
|
||||
"childrenDefault": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"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": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,8 +18,10 @@
|
||||
|
||||
//! Substrate chain configurations.
|
||||
#![warn(missing_docs)]
|
||||
|
||||
use crate::{extension::GetExtension, ChainType, Properties, RuntimeGenesis};
|
||||
use crate::{
|
||||
extension::GetExtension, ChainType, GenesisConfigBuilderRuntimeCaller as RuntimeCaller,
|
||||
Properties, RuntimeGenesis,
|
||||
};
|
||||
use sc_network::config::MultiaddrWithPeerId;
|
||||
use sc_telemetry::TelemetryEndpoints;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -29,13 +31,32 @@ use sp_core::{
|
||||
Bytes,
|
||||
};
|
||||
use sp_runtime::BuildStorage;
|
||||
use std::{borrow::Cow, collections::BTreeMap, fs::File, path::PathBuf, sync::Arc};
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
collections::{BTreeMap, VecDeque},
|
||||
fs::File,
|
||||
marker::PhantomData,
|
||||
path::PathBuf,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
enum GenesisBuildAction {
|
||||
Patch(json::Value),
|
||||
Full(json::Value),
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
enum GenesisSource<G> {
|
||||
File(PathBuf),
|
||||
Binary(Cow<'static, [u8]>),
|
||||
Factory(Arc<dyn Fn() -> G + Send + Sync>),
|
||||
/// factory function + code
|
||||
//Factory and G type parameter shall be removed togheter with `ChainSpec::from_genesis`
|
||||
Factory(Arc<dyn Fn() -> G + Send + Sync>, Vec<u8>),
|
||||
Storage(Storage),
|
||||
/// build action + code
|
||||
GenesisBuilderApi(GenesisBuildAction, Vec<u8>),
|
||||
}
|
||||
|
||||
impl<G> Clone for GenesisSource<G> {
|
||||
@@ -43,14 +64,17 @@ impl<G> Clone for GenesisSource<G> {
|
||||
match *self {
|
||||
Self::File(ref path) => Self::File(path.clone()),
|
||||
Self::Binary(ref d) => Self::Binary(d.clone()),
|
||||
Self::Factory(ref f) => Self::Factory(f.clone()),
|
||||
Self::Factory(ref f, ref c) => Self::Factory(f.clone(), c.clone()),
|
||||
Self::Storage(ref s) => Self::Storage(s.clone()),
|
||||
Self::GenesisBuilderApi(ref s, ref c) => Self::GenesisBuilderApi(s.clone(), c.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<G: RuntimeGenesis> GenesisSource<G> {
|
||||
fn resolve(&self) -> Result<Genesis<G>, String> {
|
||||
/// helper container for deserializing genesis from the JSON file (ChainSpec JSON file is
|
||||
/// also supported here)
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct GenesisContainer<G> {
|
||||
genesis: Genesis<G>,
|
||||
@@ -79,31 +103,21 @@ impl<G: RuntimeGenesis> GenesisSource<G> {
|
||||
.map_err(|e| format!("Error parsing embedded file: {}", e))?;
|
||||
Ok(genesis.genesis)
|
||||
},
|
||||
Self::Factory(f) => Ok(Genesis::Runtime(f())),
|
||||
Self::Storage(storage) => {
|
||||
let top = storage
|
||||
.top
|
||||
.iter()
|
||||
.map(|(k, v)| (StorageKey(k.clone()), StorageData(v.clone())))
|
||||
.collect();
|
||||
|
||||
let children_default = storage
|
||||
.children_default
|
||||
.iter()
|
||||
.map(|(k, child)| {
|
||||
(
|
||||
StorageKey(k.clone()),
|
||||
child
|
||||
.data
|
||||
.iter()
|
||||
.map(|(k, v)| (StorageKey(k.clone()), StorageData(v.clone())))
|
||||
.collect(),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(Genesis::Raw(RawGenesis { top, children_default }))
|
||||
},
|
||||
Self::Factory(f, code) => Ok(Genesis::RuntimeAndCode(RuntimeInnerWrapper {
|
||||
runtime: f(),
|
||||
code: code.clone(),
|
||||
})),
|
||||
Self::Storage(storage) => Ok(Genesis::Raw(RawGenesis::from(storage.clone()))),
|
||||
Self::GenesisBuilderApi(GenesisBuildAction::Full(config), code) =>
|
||||
Ok(Genesis::RuntimeGenesis(RuntimeGenesisInner {
|
||||
json_blob: RuntimeGenesisConfigJson::Config(config.clone()),
|
||||
code: code.clone(),
|
||||
})),
|
||||
Self::GenesisBuilderApi(GenesisBuildAction::Patch(patch), code) =>
|
||||
Ok(Genesis::RuntimeGenesis(RuntimeGenesisInner {
|
||||
json_blob: RuntimeGenesisConfigJson::Patch(patch.clone()),
|
||||
code: code.clone(),
|
||||
})),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -111,7 +125,18 @@ impl<G: RuntimeGenesis> GenesisSource<G> {
|
||||
impl<G: RuntimeGenesis, E> BuildStorage for ChainSpec<G, E> {
|
||||
fn assimilate_storage(&self, storage: &mut Storage) -> Result<(), String> {
|
||||
match self.genesis.resolve()? {
|
||||
Genesis::Runtime(gc) => gc.assimilate_storage(storage),
|
||||
#[allow(deprecated)]
|
||||
Genesis::Runtime(runtime_genesis_config) => {
|
||||
runtime_genesis_config.assimilate_storage(storage)?;
|
||||
},
|
||||
#[allow(deprecated)]
|
||||
Genesis::RuntimeAndCode(RuntimeInnerWrapper {
|
||||
runtime: runtime_genesis_config,
|
||||
code,
|
||||
}) => {
|
||||
runtime_genesis_config.assimilate_storage(storage)?;
|
||||
storage.top.insert(sp_core::storage::well_known_keys::CODE.to_vec(), code);
|
||||
},
|
||||
Genesis::Raw(RawGenesis { top: map, children_default: children_map }) => {
|
||||
storage.top.extend(map.into_iter().map(|(k, v)| (k.0, v.0)));
|
||||
children_map.into_iter().for_each(|(k, v)| {
|
||||
@@ -123,13 +148,37 @@ impl<G: RuntimeGenesis, E> BuildStorage for ChainSpec<G, E> {
|
||||
.data
|
||||
.extend(v.into_iter().map(|(k, v)| (k.0, v.0)));
|
||||
});
|
||||
Ok(())
|
||||
},
|
||||
// The `StateRootHash` variant exists as a way to keep note that other clients support
|
||||
// it, but Substrate itself isn't capable of loading chain specs with just a hash at the
|
||||
// moment.
|
||||
Genesis::StateRootHash(_) => Err("Genesis storage in hash format not supported".into()),
|
||||
}
|
||||
Genesis::StateRootHash(_) =>
|
||||
return Err("Genesis storage in hash format not supported".into()),
|
||||
Genesis::RuntimeGenesis(RuntimeGenesisInner {
|
||||
json_blob: RuntimeGenesisConfigJson::Config(config),
|
||||
code,
|
||||
}) => {
|
||||
RuntimeCaller::new(&code[..])
|
||||
.get_storage_for_config(config)?
|
||||
.assimilate_storage(storage)?;
|
||||
storage
|
||||
.top
|
||||
.insert(sp_core::storage::well_known_keys::CODE.to_vec(), code.clone());
|
||||
},
|
||||
Genesis::RuntimeGenesis(RuntimeGenesisInner {
|
||||
json_blob: RuntimeGenesisConfigJson::Patch(patch),
|
||||
code,
|
||||
}) => {
|
||||
RuntimeCaller::new(&code[..])
|
||||
.get_storage_for_patch(patch)?
|
||||
.assimilate_storage(storage)?;
|
||||
storage
|
||||
.top
|
||||
.insert(sp_core::storage::well_known_keys::CODE.to_vec(), code.clone());
|
||||
},
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,20 +193,98 @@ pub struct RawGenesis {
|
||||
pub children_default: BTreeMap<StorageKey, GenesisStorage>,
|
||||
}
|
||||
|
||||
impl From<sp_core::storage::Storage> for RawGenesis {
|
||||
fn from(value: sp_core::storage::Storage) -> Self {
|
||||
Self {
|
||||
top: value.top.into_iter().map(|(k, v)| (StorageKey(k), StorageData(v))).collect(),
|
||||
children_default: value
|
||||
.children_default
|
||||
.into_iter()
|
||||
.map(|(sk, child)| {
|
||||
(
|
||||
StorageKey(sk),
|
||||
child
|
||||
.data
|
||||
.into_iter()
|
||||
.map(|(k, v)| (StorageKey(k), StorageData(v)))
|
||||
.collect(),
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Inner representation of [`Genesis<G>::RuntimeGenesis`] format
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
struct RuntimeGenesisInner {
|
||||
/// Runtime wasm code, expected to be hex-encoded in JSON.
|
||||
/// The code shall be capable of parsing `json_blob`.
|
||||
#[serde(default, with = "sp_core::bytes")]
|
||||
code: Vec<u8>,
|
||||
/// The patch or full representation of runtime's `RuntimeGenesisConfig` struct.
|
||||
#[serde(flatten)]
|
||||
json_blob: RuntimeGenesisConfigJson,
|
||||
}
|
||||
|
||||
/// Represents two possible variants of the contained JSON blob for the
|
||||
/// [`Genesis<G>::RuntimeGenesis`] format.
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
enum RuntimeGenesisConfigJson {
|
||||
/// Represents the explicit and comprehensive runtime genesis config in JSON format.
|
||||
/// The contained object is a JSON blob that can be parsed by a compatible runtime.
|
||||
///
|
||||
/// Using a full config is useful for when someone wants to ensure that a change in the runtime
|
||||
/// makes the deserialization fail and not silently add some default values.
|
||||
Config(json::Value),
|
||||
/// Represents a patch for the default runtime genesis config in JSON format which is
|
||||
/// essentially a list of keys that are to be customized in runtime genesis config.
|
||||
/// The contained value is a JSON blob that can be parsed by a compatible runtime.
|
||||
Patch(json::Value),
|
||||
}
|
||||
|
||||
/// Inner variant wrapper for deprecated runtime.
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
struct RuntimeInnerWrapper<G> {
|
||||
/// The native `RuntimeGenesisConfig` struct.
|
||||
runtime: G,
|
||||
/// Runtime code.
|
||||
#[serde(with = "sp_core::bytes")]
|
||||
code: Vec<u8>,
|
||||
}
|
||||
|
||||
/// Represents the different formats of the genesis state within chain spec JSON blob.
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[serde(deny_unknown_fields)]
|
||||
enum Genesis<G> {
|
||||
/// (Deprecated) Contains the JSON representation of G (the native type representing the
|
||||
/// runtime's `RuntimeGenesisConfig` struct) (will be removed with `ChainSpec::from_genesis`)
|
||||
/// without the runtime code. It is required to deserialize the legacy chainspecs genereted
|
||||
/// with `ChainsSpec::from_genesis` method.
|
||||
Runtime(G),
|
||||
/// (Deprecated) Contains the JSON representation of G (the native type representing the
|
||||
/// runtime's `RuntimeGenesisConfig` struct) (will be removed with `ChainSpec::from_genesis`)
|
||||
/// and the runtime code. It is required to create and deserialize JSON chainspecs created with
|
||||
/// deprecated `ChainSpec::from_genesis` method.
|
||||
RuntimeAndCode(RuntimeInnerWrapper<G>),
|
||||
/// The genesis storage as raw data. Typically raw key-value entries in state.
|
||||
Raw(RawGenesis),
|
||||
/// State root hash of the genesis storage.
|
||||
StateRootHash(StorageData),
|
||||
/// Represents the runtime genesis config in JSON format toghether with runtime code.
|
||||
RuntimeGenesis(RuntimeGenesisInner),
|
||||
}
|
||||
|
||||
/// A configuration of a client. Does not include runtime storage initialization.
|
||||
/// Note: `genesis` field is ignored due to way how the chain specification is serialized into
|
||||
/// JSON file. Refer to [`ChainSpecJsonContainer`], which flattens [`ClientSpec`] and denies uknown
|
||||
/// fields.
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[serde(deny_unknown_fields)]
|
||||
// we cannot #[serde(deny_unknown_fields)]. Otherwise chain-spec-builder will fail on any
|
||||
// non-standard spec
|
||||
struct ClientSpec<E> {
|
||||
name: String,
|
||||
id: String,
|
||||
@@ -194,6 +321,137 @@ struct ClientSpec<E> {
|
||||
/// We use `Option` here since `()` is not flattenable by serde.
|
||||
pub type NoExtension = Option<()>;
|
||||
|
||||
/// Builder for creating [`ChainSpec`] instances.
|
||||
pub struct ChainSpecBuilder<G, E = NoExtension> {
|
||||
code: Vec<u8>,
|
||||
extensions: E,
|
||||
name: String,
|
||||
id: String,
|
||||
chain_type: ChainType,
|
||||
genesis_build_action: GenesisBuildAction,
|
||||
boot_nodes: Option<Vec<MultiaddrWithPeerId>>,
|
||||
telemetry_endpoints: Option<TelemetryEndpoints>,
|
||||
protocol_id: Option<String>,
|
||||
fork_id: Option<String>,
|
||||
properties: Option<Properties>,
|
||||
_genesis: PhantomData<G>,
|
||||
}
|
||||
|
||||
impl<G, E> ChainSpecBuilder<G, E> {
|
||||
/// Creates a new builder instance with no defaults.
|
||||
pub fn new(code: &[u8], extensions: E) -> Self {
|
||||
Self {
|
||||
code: code.into(),
|
||||
extensions,
|
||||
name: "Development".to_string(),
|
||||
id: "dev".to_string(),
|
||||
chain_type: ChainType::Local,
|
||||
genesis_build_action: GenesisBuildAction::Patch(Default::default()),
|
||||
boot_nodes: None,
|
||||
telemetry_endpoints: None,
|
||||
protocol_id: None,
|
||||
fork_id: None,
|
||||
properties: None,
|
||||
_genesis: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the spec name.
|
||||
pub fn with_name(mut self, name: &str) -> Self {
|
||||
self.name = name.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the spec ID.
|
||||
pub fn with_id(mut self, id: &str) -> Self {
|
||||
self.id = id.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the type of the chain.
|
||||
pub fn with_chain_type(mut self, chain_type: ChainType) -> Self {
|
||||
self.chain_type = chain_type;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets a list of bootnode addresses.
|
||||
pub fn with_boot_nodes(mut self, boot_nodes: Vec<MultiaddrWithPeerId>) -> Self {
|
||||
self.boot_nodes = Some(boot_nodes);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets telemetry endpoints.
|
||||
pub fn with_telemetry_endpoints(mut self, telemetry_endpoints: TelemetryEndpoints) -> Self {
|
||||
self.telemetry_endpoints = Some(telemetry_endpoints);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the network protocol ID.
|
||||
pub fn with_protocol_id(mut self, protocol_id: &str) -> Self {
|
||||
self.protocol_id = Some(protocol_id.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets an optional network fork identifier.
|
||||
pub fn with_fork_id(mut self, fork_id: &str) -> Self {
|
||||
self.fork_id = Some(fork_id.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets additional loosely-typed properties of the chain.
|
||||
pub fn with_properties(mut self, properties: Properties) -> Self {
|
||||
self.properties = Some(properties);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets chain spec extensions.
|
||||
pub fn with_extensions(mut self, extensions: E) -> Self {
|
||||
self.extensions = extensions;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the code.
|
||||
pub fn with_code(mut self, code: &[u8]) -> Self {
|
||||
self.code = code.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the JSON patch for runtime's GenesisConfig.
|
||||
pub fn with_genesis_config_patch(mut self, patch: json::Value) -> Self {
|
||||
self.genesis_build_action = GenesisBuildAction::Patch(patch);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the full runtime's GenesisConfig JSON.
|
||||
pub fn with_genesis_config(mut self, config: json::Value) -> Self {
|
||||
self.genesis_build_action = GenesisBuildAction::Full(config);
|
||||
self
|
||||
}
|
||||
|
||||
/// Builds a [`ChainSpec`] instance using the provided settings.
|
||||
pub fn build(self) -> ChainSpec<G, E> {
|
||||
let client_spec = ClientSpec {
|
||||
name: self.name,
|
||||
id: self.id,
|
||||
chain_type: self.chain_type,
|
||||
boot_nodes: self.boot_nodes.unwrap_or_default(),
|
||||
telemetry_endpoints: self.telemetry_endpoints,
|
||||
protocol_id: self.protocol_id,
|
||||
fork_id: self.fork_id,
|
||||
properties: self.properties,
|
||||
extensions: self.extensions,
|
||||
consensus_engine: (),
|
||||
genesis: Default::default(),
|
||||
code_substitutes: BTreeMap::new(),
|
||||
};
|
||||
|
||||
ChainSpec {
|
||||
client_spec,
|
||||
genesis: GenesisSource::GenesisBuilderApi(self.genesis_build_action, self.code.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A configuration of a chain. Can be used to build a genesis block.
|
||||
pub struct ChainSpec<G, E = NoExtension> {
|
||||
client_spec: ClientSpec<E>,
|
||||
@@ -260,6 +518,10 @@ impl<G, E> ChainSpec<G, E> {
|
||||
}
|
||||
|
||||
/// Create hardcoded spec.
|
||||
#[deprecated(
|
||||
note = "`from_genesis` is planned to be removed in May 2024. Use `builder()` instead."
|
||||
)]
|
||||
// deprecated note: Genesis<G>::Runtime + GenesisSource::Factory shall also be removed
|
||||
pub fn from_genesis<F: Fn() -> G + 'static + Send + Sync>(
|
||||
name: &str,
|
||||
id: &str,
|
||||
@@ -271,6 +533,7 @@ impl<G, E> ChainSpec<G, E> {
|
||||
fork_id: Option<&str>,
|
||||
properties: Option<Properties>,
|
||||
extensions: E,
|
||||
code: &[u8],
|
||||
) -> Self {
|
||||
let client_spec = ClientSpec {
|
||||
name: name.to_owned(),
|
||||
@@ -287,21 +550,30 @@ impl<G, E> ChainSpec<G, E> {
|
||||
code_substitutes: BTreeMap::new(),
|
||||
};
|
||||
|
||||
ChainSpec { client_spec, genesis: GenesisSource::Factory(Arc::new(constructor)) }
|
||||
ChainSpec {
|
||||
client_spec,
|
||||
genesis: GenesisSource::Factory(Arc::new(constructor), code.into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Type of the chain.
|
||||
fn chain_type(&self) -> ChainType {
|
||||
self.client_spec.chain_type.clone()
|
||||
}
|
||||
|
||||
/// Provides a `ChainSpec` builder.
|
||||
pub fn builder(code: &[u8], extensions: E) -> ChainSpecBuilder<G, E> {
|
||||
ChainSpecBuilder::new(code, extensions)
|
||||
}
|
||||
}
|
||||
|
||||
impl<G, E: serde::de::DeserializeOwned> ChainSpec<G, E> {
|
||||
impl<G: serde::de::DeserializeOwned, E: serde::de::DeserializeOwned> ChainSpec<G, E> {
|
||||
/// Parse json content into a `ChainSpec`
|
||||
pub fn from_json_bytes(json: impl Into<Cow<'static, [u8]>>) -> Result<Self, String> {
|
||||
let json = json.into();
|
||||
let client_spec = json::from_slice(json.as_ref())
|
||||
.map_err(|e| format!("Error parsing spec file: {}", e))?;
|
||||
|
||||
Ok(ChainSpec { client_spec, genesis: GenesisSource::Binary(json) })
|
||||
}
|
||||
|
||||
@@ -318,50 +590,74 @@ impl<G, E: serde::de::DeserializeOwned> ChainSpec<G, E> {
|
||||
memmap2::Mmap::map(&file)
|
||||
.map_err(|e| format!("Error mmaping spec file `{}`: {}", path.display(), e))?
|
||||
};
|
||||
|
||||
let client_spec =
|
||||
json::from_slice(&bytes).map_err(|e| format!("Error parsing spec file: {}", e))?;
|
||||
|
||||
Ok(ChainSpec { client_spec, genesis: GenesisSource::File(path) })
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper structure for serializing (and only serializing) the ChainSpec into JSON file. It
|
||||
/// represents the layout of `ChainSpec` JSON file.
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct JsonContainer<G, E> {
|
||||
// we cannot #[serde(deny_unknown_fields)]. Otherwise chain-spec-builder will fail on any
|
||||
// non-standard spec.
|
||||
struct ChainSpecJsonContainer<G, E> {
|
||||
#[serde(flatten)]
|
||||
client_spec: ClientSpec<E>,
|
||||
genesis: Genesis<G>,
|
||||
}
|
||||
|
||||
impl<G: RuntimeGenesis, E: serde::Serialize + Clone + 'static> ChainSpec<G, E> {
|
||||
fn json_container(&self, raw: bool) -> Result<JsonContainer<G, E>, String> {
|
||||
let genesis = match (raw, self.genesis.resolve()?) {
|
||||
fn json_container(&self, raw: bool) -> Result<ChainSpecJsonContainer<G, E>, String> {
|
||||
let raw_genesis = match (raw, self.genesis.resolve()?) {
|
||||
(
|
||||
true,
|
||||
Genesis::RuntimeGenesis(RuntimeGenesisInner {
|
||||
json_blob: RuntimeGenesisConfigJson::Config(config),
|
||||
code,
|
||||
}),
|
||||
) => {
|
||||
let mut storage = RuntimeCaller::new(&code[..]).get_storage_for_config(config)?;
|
||||
storage.top.insert(sp_core::storage::well_known_keys::CODE.to_vec(), code);
|
||||
RawGenesis::from(storage)
|
||||
},
|
||||
(
|
||||
true,
|
||||
Genesis::RuntimeGenesis(RuntimeGenesisInner {
|
||||
json_blob: RuntimeGenesisConfigJson::Patch(patch),
|
||||
code,
|
||||
}),
|
||||
) => {
|
||||
let mut storage = RuntimeCaller::new(&code[..]).get_storage_for_patch(patch)?;
|
||||
storage.top.insert(sp_core::storage::well_known_keys::CODE.to_vec(), code);
|
||||
RawGenesis::from(storage)
|
||||
},
|
||||
|
||||
#[allow(deprecated)]
|
||||
(true, Genesis::RuntimeAndCode(RuntimeInnerWrapper { runtime: g, code })) => {
|
||||
let mut storage = g.build_storage()?;
|
||||
storage.top.insert(sp_core::storage::well_known_keys::CODE.to_vec(), code);
|
||||
RawGenesis::from(storage)
|
||||
},
|
||||
#[allow(deprecated)]
|
||||
(true, Genesis::Runtime(g)) => {
|
||||
let storage = g.build_storage()?;
|
||||
let top =
|
||||
storage.top.into_iter().map(|(k, v)| (StorageKey(k), StorageData(v))).collect();
|
||||
let children_default = storage
|
||||
.children_default
|
||||
.into_iter()
|
||||
.map(|(sk, child)| {
|
||||
(
|
||||
StorageKey(sk),
|
||||
child
|
||||
.data
|
||||
.into_iter()
|
||||
.map(|(k, v)| (StorageKey(k), StorageData(v)))
|
||||
.collect(),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
Genesis::Raw(RawGenesis { top, children_default })
|
||||
RawGenesis::from(storage)
|
||||
},
|
||||
(_, genesis) => genesis,
|
||||
(true, Genesis::Raw(raw)) => raw,
|
||||
|
||||
(_, genesis) =>
|
||||
return Ok(ChainSpecJsonContainer { client_spec: self.client_spec.clone(), genesis }),
|
||||
};
|
||||
Ok(JsonContainer { client_spec: self.client_spec.clone(), genesis })
|
||||
|
||||
Ok(ChainSpecJsonContainer {
|
||||
client_spec: self.client_spec.clone(),
|
||||
genesis: Genesis::Raw(raw_genesis),
|
||||
})
|
||||
}
|
||||
|
||||
/// Dump to json string.
|
||||
/// Dump the chain specification to JSON string.
|
||||
pub fn as_json(&self, raw: bool) -> Result<String, String> {
|
||||
let container = self.json_container(raw)?;
|
||||
json::to_string_pretty(&container).map_err(|e| format!("Error generating spec json: {}", e))
|
||||
@@ -442,9 +738,87 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// The `fun` will be called with the value at `path`.
|
||||
///
|
||||
/// If exists, the value at given `path` will be passed to the `fun` and the result of `fun`
|
||||
/// call will be returned. Otherwise false is returned.
|
||||
/// `path` will be modified.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```ignore
|
||||
/// use serde_json::{from_str, json, Value};
|
||||
/// let doc = json!({"a":{"b":{"c":"5"}}});
|
||||
/// let mut path = ["a", "b", "c"].into();
|
||||
/// assert!(json_eval_value_at_key(&doc, &mut path, &|v| { assert_eq!(v,"5"); true }));
|
||||
/// ```
|
||||
fn json_eval_value_at_key(
|
||||
doc: &json::Value,
|
||||
path: &mut VecDeque<&str>,
|
||||
fun: &dyn Fn(&json::Value) -> bool,
|
||||
) -> bool {
|
||||
let Some(key) = path.pop_front() else {
|
||||
return false;
|
||||
};
|
||||
|
||||
if path.is_empty() {
|
||||
doc.as_object().map_or(false, |o| o.get(key).map_or(false, |v| fun(v)))
|
||||
} else {
|
||||
doc.as_object()
|
||||
.map_or(false, |o| o.get(key).map_or(false, |v| json_eval_value_at_key(v, path, fun)))
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! json_path {
|
||||
[ $($x:expr),+ ] => {
|
||||
VecDeque::<&str>::from([$($x),+])
|
||||
};
|
||||
}
|
||||
|
||||
fn json_contains_path(doc: &json::Value, path: &mut VecDeque<&str>) -> bool {
|
||||
json_eval_value_at_key(doc, path, &|_| true)
|
||||
}
|
||||
|
||||
/// This function updates the code in given chain spec.
|
||||
///
|
||||
/// Function support updating the runtime code in provided JSON chain spec blob. `Genesis<G>::Raw`
|
||||
/// and `Genesis<G>::RuntimeGenesis` formats are supported.
|
||||
///
|
||||
/// If update was successful `true` is returned, otherwise `false`. Chain spec JSON is modified in
|
||||
/// place.
|
||||
pub fn update_code_in_json_chain_spec(chain_spec: &mut json::Value, code: &[u8]) -> bool {
|
||||
let mut path = json_path!["genesis", "runtimeGenesis", "code"];
|
||||
let mut raw_path = json_path!["genesis", "raw", "top"];
|
||||
|
||||
if json_contains_path(&chain_spec, &mut path) {
|
||||
#[derive(Serialize)]
|
||||
struct Container<'a> {
|
||||
#[serde(with = "sp_core::bytes")]
|
||||
code: &'a [u8],
|
||||
}
|
||||
let code_patch = json::json!({"genesis":{"runtimeGenesis": Container { code }}});
|
||||
crate::json_patch::merge(chain_spec, code_patch);
|
||||
true
|
||||
} else if json_contains_path(&chain_spec, &mut raw_path) {
|
||||
#[derive(Serialize)]
|
||||
struct Container<'a> {
|
||||
#[serde(with = "sp_core::bytes", rename = "0x3a636f6465")]
|
||||
code: &'a [u8],
|
||||
}
|
||||
let code_patch = json::json!({"genesis":{"raw":{"top": Container { code }}}});
|
||||
crate::json_patch::merge(chain_spec, code_patch);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use serde_json::{from_str, json, Value};
|
||||
use sp_application_crypto::Ss58Codec;
|
||||
use sp_core::storage::well_known_keys;
|
||||
use sp_keyring::AccountKeyring;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct Genesis(BTreeMap<String, String>);
|
||||
@@ -536,4 +910,336 @@ mod tests {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
// some tests for json path utils
|
||||
fn test_json_eval_value_at_key() {
|
||||
let doc = json!({"a":{"b1":"20","b":{"c":{"d":"10"}}}});
|
||||
|
||||
assert!(json_eval_value_at_key(&doc, &mut json_path!["a", "b1"], &|v| { *v == "20" }));
|
||||
assert!(json_eval_value_at_key(&doc, &mut json_path!["a", "b", "c", "d"], &|v| {
|
||||
*v == "10"
|
||||
}));
|
||||
assert!(!json_eval_value_at_key(&doc, &mut json_path!["a", "c", "d"], &|_| { true }));
|
||||
assert!(!json_eval_value_at_key(&doc, &mut json_path!["d"], &|_| { true }));
|
||||
|
||||
assert!(json_contains_path(&doc, &mut json_path!["a", "b1"]));
|
||||
assert!(json_contains_path(&doc, &mut json_path!["a", "b"]));
|
||||
assert!(json_contains_path(&doc, &mut json_path!["a", "b", "c"]));
|
||||
assert!(json_contains_path(&doc, &mut json_path!["a", "b", "c", "d"]));
|
||||
assert!(!json_contains_path(&doc, &mut json_path!["a", "b", "c", "d", "e"]));
|
||||
assert!(!json_contains_path(&doc, &mut json_path!["a", "b", "b1"]));
|
||||
assert!(!json_contains_path(&doc, &mut json_path!["d"]));
|
||||
}
|
||||
|
||||
fn zeroize_code_key_in_json(encoded: bool, json: &str) -> Value {
|
||||
let mut json = from_str::<Value>(json).unwrap();
|
||||
let (zeroing_patch, mut path) = if encoded {
|
||||
(
|
||||
json!({"genesis":{"raw":{"top":{"0x3a636f6465":"0x0"}}}}),
|
||||
json_path!["genesis", "raw", "top", "0x3a636f6465"],
|
||||
)
|
||||
} else {
|
||||
(
|
||||
json!({"genesis":{"runtimeGenesis":{"code":"0x0"}}}),
|
||||
json_path!["genesis", "runtimeGenesis", "code"],
|
||||
)
|
||||
};
|
||||
assert!(json_contains_path(&json, &mut path));
|
||||
crate::json_patch::merge(&mut json, zeroing_patch);
|
||||
json
|
||||
}
|
||||
|
||||
#[docify::export]
|
||||
#[test]
|
||||
fn build_chain_spec_with_patch_works() {
|
||||
let output: ChainSpec<()> = 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());
|
||||
}
|
||||
|
||||
#[docify::export]
|
||||
#[test]
|
||||
fn generate_chain_spec_with_patch_works() {
|
||||
let output: ChainSpec<()> = 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 actual = output.as_json(false).unwrap();
|
||||
let actual_raw = output.as_json(true).unwrap();
|
||||
|
||||
let expected =
|
||||
from_str::<Value>(include_str!("../res/substrate_test_runtime_from_patch.json"))
|
||||
.unwrap();
|
||||
let expected_raw =
|
||||
from_str::<Value>(include_str!("../res/substrate_test_runtime_from_patch_raw.json"))
|
||||
.unwrap();
|
||||
|
||||
//wasm blob may change overtime so let's zero it. Also ensure it is there:
|
||||
let actual = zeroize_code_key_in_json(false, actual.as_str());
|
||||
let actual_raw = zeroize_code_key_in_json(true, actual_raw.as_str());
|
||||
|
||||
assert_eq!(actual, expected);
|
||||
assert_eq!(actual_raw, expected_raw);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generate_chain_spec_with_full_config_works() {
|
||||
let j = include_str!("../../../test-utils/runtime/res/default_genesis_config.json");
|
||||
let output: ChainSpec<()> = 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(from_str(j).unwrap())
|
||||
.build();
|
||||
|
||||
let actual = output.as_json(false).unwrap();
|
||||
let actual_raw = output.as_json(true).unwrap();
|
||||
|
||||
let expected =
|
||||
from_str::<Value>(include_str!("../res/substrate_test_runtime_from_config.json"))
|
||||
.unwrap();
|
||||
let expected_raw =
|
||||
from_str::<Value>(include_str!("../res/substrate_test_runtime_from_config_raw.json"))
|
||||
.unwrap();
|
||||
|
||||
//wasm blob may change overtime so let's zero it. Also ensure it is there:
|
||||
let actual = zeroize_code_key_in_json(false, actual.as_str());
|
||||
let actual_raw = zeroize_code_key_in_json(true, actual_raw.as_str());
|
||||
|
||||
assert_eq!(actual, expected);
|
||||
assert_eq!(actual_raw, expected_raw);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chain_spec_as_json_fails_with_invalid_config() {
|
||||
let j =
|
||||
include_str!("../../../test-utils/runtime/res/default_genesis_config_invalid_2.json");
|
||||
let output: ChainSpec<()> = 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(from_str(j).unwrap())
|
||||
.build();
|
||||
|
||||
assert_eq!(
|
||||
output.as_json(true),
|
||||
Err("Invalid JSON blob: unknown field `babex`, expected one of `system`, `babe`, `substrateTest`, `balances` at line 1 column 8".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chain_spec_as_json_fails_with_invalid_patch() {
|
||||
let output: ChainSpec<()> = 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!({
|
||||
"invalid_pallet": {},
|
||||
"substrateTest": {
|
||||
"authorities": [
|
||||
AccountKeyring::Ferdie.public().to_ss58check(),
|
||||
AccountKeyring::Alice.public().to_ss58check()
|
||||
],
|
||||
}
|
||||
}))
|
||||
.build();
|
||||
|
||||
assert!(output.as_json(true).unwrap_err().contains("Invalid JSON blob: unknown field `invalid_pallet`, expected one of `system`, `babe`, `substrateTest`, `balances`"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_if_code_is_valid_for_raw_without_code() {
|
||||
let spec = ChainSpec::<()>::from_json_bytes(Cow::Owned(
|
||||
include_bytes!("../res/raw_no_code.json").to_vec(),
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
let j = from_str::<Value>(&spec.as_json(true).unwrap()).unwrap();
|
||||
|
||||
assert!(json_eval_value_at_key(
|
||||
&j,
|
||||
&mut json_path!["genesis", "raw", "top", "0x3a636f6465"],
|
||||
&|v| { *v == "0x010101" }
|
||||
));
|
||||
assert!(!json_contains_path(&j, &mut json_path!["code"]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_code_in_assimilated_storage_for_raw_without_code() {
|
||||
let spec = ChainSpec::<()>::from_json_bytes(Cow::Owned(
|
||||
include_bytes!("../res/raw_no_code.json").to_vec(),
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
let storage = spec.build_storage().unwrap();
|
||||
assert!(storage
|
||||
.top
|
||||
.get(&well_known_keys::CODE.to_vec())
|
||||
.map(|v| *v == vec![1, 1, 1])
|
||||
.unwrap())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn update_code_works_with_runtime_genesis_config() {
|
||||
let j = include_str!("../../../test-utils/runtime/res/default_genesis_config.json");
|
||||
let chain_spec: ChainSpec<()> = 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(from_str(j).unwrap())
|
||||
.build();
|
||||
|
||||
let mut chain_spec_json = from_str::<Value>(&chain_spec.as_json(false).unwrap()).unwrap();
|
||||
assert!(update_code_in_json_chain_spec(&mut chain_spec_json, &[0, 1, 2, 4, 5, 6]));
|
||||
|
||||
assert!(json_eval_value_at_key(
|
||||
&chain_spec_json,
|
||||
&mut json_path!["genesis", "runtimeGenesis", "code"],
|
||||
&|v| { *v == "0x000102040506" }
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn update_code_works_for_raw() {
|
||||
let j = include_str!("../../../test-utils/runtime/res/default_genesis_config.json");
|
||||
let chain_spec: ChainSpec<()> = 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(from_str(j).unwrap())
|
||||
.build();
|
||||
|
||||
let mut chain_spec_json = from_str::<Value>(&chain_spec.as_json(true).unwrap()).unwrap();
|
||||
assert!(update_code_in_json_chain_spec(&mut chain_spec_json, &[0, 1, 2, 4, 5, 6]));
|
||||
|
||||
assert!(json_eval_value_at_key(
|
||||
&chain_spec_json,
|
||||
&mut json_path!["genesis", "raw", "top", "0x3a636f6465"],
|
||||
&|v| { *v == "0x000102040506" }
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn update_code_works_with_runtime_genesis_patch() {
|
||||
let chain_spec: ChainSpec<()> = 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!({}))
|
||||
.build();
|
||||
|
||||
let mut chain_spec_json = from_str::<Value>(&chain_spec.as_json(false).unwrap()).unwrap();
|
||||
assert!(update_code_in_json_chain_spec(&mut chain_spec_json, &[0, 1, 2, 4, 5, 6]));
|
||||
|
||||
assert!(json_eval_value_at_key(
|
||||
&chain_spec_json,
|
||||
&mut json_path!["genesis", "runtimeGenesis", "code"],
|
||||
&|v| { *v == "0x000102040506" }
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generate_from_genesis_is_still_supported() {
|
||||
#[allow(deprecated)]
|
||||
let chain_spec: ChainSpec<substrate_test_runtime::RuntimeGenesisConfig> = ChainSpec::from_genesis(
|
||||
"TestName",
|
||||
"test",
|
||||
ChainType::Local,
|
||||
move || substrate_test_runtime::RuntimeGenesisConfig {
|
||||
babe: substrate_test_runtime::BabeConfig {
|
||||
epoch_config: Some(
|
||||
substrate_test_runtime::TEST_RUNTIME_BABE_EPOCH_CONFIGURATION,
|
||||
),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
Vec::new(),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Default::default(),
|
||||
&vec![0, 1, 2, 4, 5, 6],
|
||||
);
|
||||
|
||||
let chain_spec_json = from_str::<Value>(&chain_spec.as_json(false).unwrap()).unwrap();
|
||||
assert!(json_eval_value_at_key(
|
||||
&chain_spec_json,
|
||||
&mut json_path!["genesis", "runtimeAndCode", "code"],
|
||||
&|v| { *v == "0x000102040506" }
|
||||
));
|
||||
let chain_spec_json = from_str::<Value>(&chain_spec.as_json(true).unwrap()).unwrap();
|
||||
assert!(json_eval_value_at_key(
|
||||
&chain_spec_json,
|
||||
&mut json_path!["genesis", "raw", "top", "0x3a636f6465"],
|
||||
&|v| { *v == "0x000102040506" }
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// 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};
|
||||
use sc_executor::{error::Result, WasmExecutor};
|
||||
use serde_json::{from_slice, Value};
|
||||
use sp_core::{
|
||||
storage::Storage,
|
||||
traits::{CallContext, CodeExecutor, Externalities, FetchRuntimeCode, RuntimeCode},
|
||||
};
|
||||
use sp_genesis_builder::Result as BuildResult;
|
||||
use sp_state_machine::BasicExternalities;
|
||||
use std::borrow::Cow;
|
||||
|
||||
/// A utility that facilitates calling the GenesisBuilder API from the runtime wasm code blob.
|
||||
pub struct GenesisConfigBuilderRuntimeCaller<'a> {
|
||||
code: Cow<'a, [u8]>,
|
||||
code_hash: Vec<u8>,
|
||||
executor: WasmExecutor<sp_io::SubstrateHostFunctions>,
|
||||
}
|
||||
|
||||
impl<'a> FetchRuntimeCode for GenesisConfigBuilderRuntimeCaller<'a> {
|
||||
fn fetch_runtime_code(&self) -> Option<Cow<[u8]>> {
|
||||
Some(self.code.as_ref().into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> GenesisConfigBuilderRuntimeCaller<'a> {
|
||||
/// 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: sp_core::blake2_256(code).to_vec(),
|
||||
executor: WasmExecutor::<sp_io::SubstrateHostFunctions>::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,
|
||||
false,
|
||||
CallContext::Offchain,
|
||||
)
|
||||
.0
|
||||
}
|
||||
|
||||
/// Returns the default `GenesisConfig` provided by the `runtime`.
|
||||
///
|
||||
/// Calls [`GenesisBuilder::create_default_config`](sp_genesis_builder::GenesisBuilder::create_default_config) in the `runtime`.
|
||||
pub fn get_default_config(&self) -> core::result::Result<Value, String> {
|
||||
let mut t = BasicExternalities::new_empty();
|
||||
let call_result = self
|
||||
.call(&mut t, "GenesisBuilder_create_default_config", &[])
|
||||
.map_err(|e| format!("wasm call error {e}"))?;
|
||||
let default_config = Vec::<u8>::decode(&mut &call_result[..])
|
||||
.map_err(|e| format!("scale codec error: {e}"))?;
|
||||
Ok(from_slice(&default_config[..]).expect("returned value is json. qed."))
|
||||
}
|
||||
|
||||
/// Build the given `GenesisConfig` and returns the genesis state.
|
||||
///
|
||||
/// Calls [`GenesisBuilder::build_config`](sp_genesis_builder::GenesisBuilder::build_config)
|
||||
/// provided by the `runtime`.
|
||||
pub fn get_storage_for_config(&self, config: Value) -> core::result::Result<Storage, String> {
|
||||
let mut ext = BasicExternalities::new_empty();
|
||||
|
||||
let call_result = self
|
||||
.call(&mut ext, "GenesisBuilder_build_config", &config.to_string().encode())
|
||||
.map_err(|e| format!("wasm call error {e}"))?;
|
||||
|
||||
BuildResult::decode(&mut &call_result[..])
|
||||
.map_err(|e| format!("scale codec error: {e}"))??;
|
||||
|
||||
Ok(ext.into_storages())
|
||||
}
|
||||
|
||||
/// Creates the genesis state by patching the default `GenesisConfig` and applying it.
|
||||
///
|
||||
/// This function generates the `GenesisConfig` for the runtime by applying a provided JSON
|
||||
/// patch. The patch modifies the default `GenesisConfig` allowing customization of the specific
|
||||
/// keys. The resulting `GenesisConfig` 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 `GenesisConfig` according to the following rules:
|
||||
/// 1. Existing keys in the default configuration will be overridden by the corresponding values
|
||||
/// in the patch.
|
||||
/// 2. If a key exists in the patch but not in the default configuration, it will be added to
|
||||
/// the resulting `GenesisConfig`.
|
||||
/// 3. Keys in the default configuration that have null values in the patch will be removed from
|
||||
/// the resulting `GenesisConfig`. This is helpful for changing enum variant value.
|
||||
///
|
||||
/// Please note that the patch may contain full `GenesisConfig`.
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use serde_json::{from_str, json};
|
||||
pub use sp_consensus_babe::{AllowedSlots, BabeEpochConfiguration, Slot};
|
||||
|
||||
#[test]
|
||||
fn get_default_config_works() {
|
||||
let config =
|
||||
GenesisConfigBuilderRuntimeCaller::new(substrate_test_runtime::wasm_binary_unwrap())
|
||||
.get_default_config()
|
||||
.unwrap();
|
||||
let expected = r#"{"system":{},"babe":{"authorities":[],"epochConfig":null},"substrateTest":{"authorities":[]},"balances":{"balances":[]}}"#;
|
||||
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(substrate_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,191 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// 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`.
|
||||
/// If a key exists in `b` with a `null` value, it will be removed from `a`.
|
||||
/// If a key exists only in `b` and not in `a`, it will be added to `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 {
|
||||
if v.is_null() {
|
||||
a.remove(&k);
|
||||
} else {
|
||||
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_removes_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_2": 32 }}));
|
||||
}
|
||||
}
|
||||
@@ -16,38 +16,253 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! Substrate chain configurations.
|
||||
//! This crate includes structs and utilities for defining configuration files (known as chain
|
||||
//! specification) for both runtime and node.
|
||||
//!
|
||||
//! This crate contains structs and utilities to declare
|
||||
//! a runtime-specific configuration file (a.k.a chain spec).
|
||||
//! # Intro: Chain Specification
|
||||
//!
|
||||
//! Basic chain spec type containing all required parameters is
|
||||
//! [`GenericChainSpec`]. It can be extended with
|
||||
//! additional options that contain configuration specific to your chain.
|
||||
//! Usually the extension is going to be an amalgamate 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.
|
||||
//! 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 sc_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.
|
||||
//!
|
||||
//! In essence, the most important formats of genesis initial state are:
|
||||
//!
|
||||
//! <table>
|
||||
//! <thead>
|
||||
//! <tr>
|
||||
//! <th>Format</th>
|
||||
//! <th>Description</th>
|
||||
//! </tr>
|
||||
//! </thead>
|
||||
//! <tbody>
|
||||
//! <tr>
|
||||
//! <td>
|
||||
//! <code>runtime / 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="../frame_support_procedural/macro.construct_runtime.html"
|
||||
//! ><code>frame::runtime::prelude::construct_runtime</code></a> macro (<a
|
||||
//! href="../substrate_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>.
|
||||
//! 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>
|
||||
//!
|
||||
//! 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
|
||||
//! [`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](#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/substrate_test_runtime_from_patch.json")]
|
||||
//! ```
|
||||
//! ```ignore
|
||||
#![doc = include_str!("../res/substrate_test_runtime_from_patch_raw.json")]
|
||||
//! ```
|
||||
//! The following example shows the plain full config version of chain spec:
|
||||
//! ```ignore
|
||||
#![doc = include_str!("../res/substrate_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 Substrate 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 sc_chain_spec::{GenericChainSpec, ChainSpecExtension};
|
||||
//!
|
||||
//! #[derive(Clone, Debug, serde::Serialize, serde::Deserialize, ChainSpecExtension)]
|
||||
//! pub struct MyExtension {
|
||||
//! pub known_blocks: HashMap<u64, String>,
|
||||
//! 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 `ChainSpecGroup`
|
||||
//! macro and provided [`Forks`](./struct.Forks.html) structure to put
|
||||
//! such parameters to your chain spec.
|
||||
//! This will allow to override a single parameter starting at specific
|
||||
//! block number.
|
||||
//!
|
||||
//! 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 sc_chain_spec::{Forks, ChainSpecGroup, ChainSpecExtension, GenericChainSpec};
|
||||
//!
|
||||
@@ -76,12 +291,9 @@
|
||||
//! /// 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 is allowed to change
|
||||
//! with block numbers (i.e. is forkable), and another set that is not subject to changes.
|
||||
//! This is also possible by declaring an extension that contains `Forks` within it.
|
||||
//!
|
||||
//!
|
||||
//! 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 sc_chain_spec::{Forks, GenericChainSpec, ChainSpecGroup, ChainSpecExtension};
|
||||
@@ -106,98 +318,26 @@
|
||||
//!
|
||||
//! pub type MyChainSpec<G> = GenericChainSpec<G, Extension>;
|
||||
//! ```
|
||||
//!
|
||||
//! # Substrate chain specification format
|
||||
//!
|
||||
//! The Substrate chain specification is a `json` file that describes the basics of a chain. Most
|
||||
//! importantly it lays out the genesis storage which leads to the genesis hash. The default
|
||||
//! Substrate chain specification format is the following:
|
||||
//!
|
||||
//! ```json
|
||||
//! // The human readable name of the chain.
|
||||
//! "name": "Flaming Fir",
|
||||
//!
|
||||
//! // The id of the chain.
|
||||
//! "id": "flamingfir9",
|
||||
//!
|
||||
//! // The chain type of this chain.
|
||||
//! // Possible values are `Live`, `Development`, `Local`.
|
||||
//! "chainType": "Live",
|
||||
//!
|
||||
//! // A list of multi addresses that belong to boot nodes of the chain.
|
||||
//! "bootNodes": [
|
||||
//! "/dns/0.flamingfir.paritytech.net/tcp/30333/p2p/12D3KooWLK2gMLhWsYJzjW3q35zAs9FDDVqfqVfVuskiGZGRSMvR",
|
||||
//! ],
|
||||
//!
|
||||
//! // 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.
|
||||
//! "telemetryEndpoints": [
|
||||
//! [
|
||||
//! "/dns/telemetry.polkadot.io/tcp/443/x-parity-wss/%2Fsubmit%2F",
|
||||
//! 0
|
||||
//! ]
|
||||
//! ],
|
||||
//!
|
||||
//! // Optional networking protocol id that identifies the chain.
|
||||
//! "protocolId": "fir9",
|
||||
//!
|
||||
//! // 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.
|
||||
//! "forkId": "random_fork",
|
||||
//!
|
||||
//! // Custom properties.
|
||||
//! "properties": {
|
||||
//! "tokenDecimals": 15,
|
||||
//! "tokenSymbol": "FIR"
|
||||
//! },
|
||||
//!
|
||||
//! // Deprecated field. Should be ignored.
|
||||
//! "consensusEngine": null,
|
||||
//!
|
||||
//! // The genesis declaration of the chain.
|
||||
//! //
|
||||
//! // `runtime`, `raw`, `stateRootHash` denote the type of the genesis declaration.
|
||||
//! //
|
||||
//! // These declarations are in the following formats:
|
||||
//! // - `runtime` is a `json` object that can be parsed by a compatible `GenesisConfig`. This
|
||||
//! // `GenesisConfig` is declared by a runtime and opaque to the node.
|
||||
//! // - `raw` is a `json` object with two fields `top` and `children_default`. Each of these
|
||||
//! // fields is a map of `key => value`. These key/value pairs represent the genesis storage.
|
||||
//! // - `stateRootHash` is a single hex encoded hash that represents the genesis hash. The hash
|
||||
//! // type depends on the hash used by the chain.
|
||||
//! //
|
||||
//! "genesis": { "runtime": {} },
|
||||
//!
|
||||
//! /// 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 can not 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 a on-chain runtime upgrade.
|
||||
//! "codeSubstitutes": [],
|
||||
//! ```
|
||||
//!
|
||||
//! See [`ChainSpec`] for a trait representation of the above.
|
||||
//!
|
||||
//! 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;
|
||||
mod genesis_block;
|
||||
mod genesis_config_builder;
|
||||
mod json_patch;
|
||||
|
||||
pub use self::{
|
||||
chain_spec::{ChainSpec as GenericChainSpec, NoExtension},
|
||||
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::{
|
||||
genesis_block::{
|
||||
construct_genesis_block, resolve_state_version_from_wasm, BuildGenesisBlock,
|
||||
GenesisBlockBuilder,
|
||||
},
|
||||
genesis_config_builder::GenesisConfigBuilderRuntimeCaller,
|
||||
};
|
||||
pub use sc_chain_spec_derive::{ChainSpecExtension, ChainSpecGroup};
|
||||
|
||||
|
||||
@@ -126,18 +126,14 @@ mod tests {
|
||||
}
|
||||
|
||||
fn load_spec(&self, _: &str) -> std::result::Result<Box<dyn ChainSpec>, String> {
|
||||
Ok(Box::new(GenericChainSpec::from_genesis(
|
||||
"test",
|
||||
"test_id",
|
||||
ChainType::Development,
|
||||
|| unimplemented!("Not required in tests"),
|
||||
Vec::new(),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
NoExtension::None,
|
||||
)))
|
||||
Ok(Box::new(
|
||||
GenericChainSpec::<()>::builder(Default::default(), NoExtension::None)
|
||||
.with_name("test")
|
||||
.with_id("test_id")
|
||||
.with_chain_type(ChainType::Development)
|
||||
.with_genesis_config_patch(Default::default())
|
||||
.build(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -251,18 +251,14 @@ mod tests {
|
||||
trie_cache_maximum_size: None,
|
||||
state_pruning: None,
|
||||
blocks_pruning: sc_client_db::BlocksPruning::KeepAll,
|
||||
chain_spec: Box::new(GenericChainSpec::from_genesis(
|
||||
"test",
|
||||
"test_id",
|
||||
ChainType::Development,
|
||||
|| unimplemented!("Not required in tests"),
|
||||
Vec::new(),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
NoExtension::None,
|
||||
)),
|
||||
chain_spec: Box::new(
|
||||
GenericChainSpec::<()>::builder(Default::default(), NoExtension::None)
|
||||
.with_name("test")
|
||||
.with_id("test_id")
|
||||
.with_chain_type(ChainType::Development)
|
||||
.with_genesis_config_patch(Default::default())
|
||||
.build(),
|
||||
),
|
||||
wasm_method: Default::default(),
|
||||
wasm_runtime_overrides: None,
|
||||
rpc_addr: None,
|
||||
|
||||
@@ -724,8 +724,6 @@ pub mod pallet {
|
||||
#[derive(frame_support::DefaultNoBound)]
|
||||
#[pallet::genesis_config]
|
||||
pub struct GenesisConfig<T: Config> {
|
||||
#[serde(with = "sp_core::bytes")]
|
||||
pub code: Vec<u8>,
|
||||
#[serde(skip)]
|
||||
pub _config: sp_std::marker::PhantomData<T>,
|
||||
}
|
||||
@@ -739,7 +737,6 @@ pub mod pallet {
|
||||
<UpgradedToU32RefCount<T>>::put(true);
|
||||
<UpgradedToTripleRefCount<T>>::put(true);
|
||||
|
||||
sp_io::storage::set(well_known_keys::CODE, &self.code);
|
||||
sp_io::storage::set(well_known_keys::EXTRINSIC_INDEX, &0u32.encode());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,8 +141,9 @@
|
||||
//! side features. The corresponding runtime, called [`kitchensink_runtime`] contains all of the
|
||||
//! modules that are provided with `FRAME`. This node and runtime is only used for testing and
|
||||
//! demonstration.
|
||||
//! * [`chain-spec-builder`]: Utility to build more detailed chain-specs for the aforementioned
|
||||
//! node. Other projects typically contain a `build-spec` subcommand that does the same.
|
||||
//! * [`chain-spec-builder`]: Utility to build more detailed [chain-spec][`sc-chain-spec`] for the
|
||||
//! aforementioned node. Other projects typically contain a `build-spec` subcommand that does the
|
||||
//! same.
|
||||
//! * [`node-template`]: a template node that contains a minimal set of features and can act as a
|
||||
//! starting point of a project.
|
||||
//! * [`subkey`]: Substrate's key management utility.
|
||||
@@ -177,6 +178,8 @@
|
||||
//!
|
||||
//! Additional noteworthy crates within substrate:
|
||||
//!
|
||||
//! - Chain specification of a Substrate node:
|
||||
//! - [`sc-chain-spec`]
|
||||
//! - RPC APIs of a Substrate node: [`sc-rpc-api`]/[`sc-rpc`]
|
||||
//! - CLI Options of a Substrate node: [`sc-cli`]
|
||||
//! - All of the consensus related crates provided by Substrate:
|
||||
@@ -217,6 +220,7 @@
|
||||
//! [`sp-api`]: ../sp_api/index.html
|
||||
//! [`sp-api`]: ../sp_api/index.html
|
||||
//! [`sc-client-db`]: ../sc_client_db/index.html
|
||||
//! [`sc-chain-spec`]: ../sc_chain_spec/index.html
|
||||
//! [`sc-network`]: ../sc_network/index.html
|
||||
//! [`sc-rpc-api`]: ../sc_rpc_api/index.html
|
||||
//! [`sc-rpc`]: ../sc_rpc/index.html
|
||||
|
||||
@@ -48,8 +48,6 @@ sp-externalities = { path = "../../primitives/externalities", default-features =
|
||||
# 3rd party
|
||||
array-bytes = { version = "6.1", optional = true }
|
||||
log = { version = "0.4.17", default-features = false }
|
||||
serde = { version = "1.0.188", features = ["alloc", "derive"], default-features = false }
|
||||
serde_json = { version = "1.0.107", default-features = false, features = ["alloc"] }
|
||||
|
||||
[dev-dependencies]
|
||||
futures = "0.3.21"
|
||||
@@ -60,6 +58,8 @@ sp-consensus = { path = "../../primitives/consensus/common" }
|
||||
substrate-test-runtime-client = { path = "client" }
|
||||
sp-tracing = { path = "../../primitives/tracing" }
|
||||
json-patch = { version = "1.0.0", default-features = false }
|
||||
serde = { version = "1.0.188", features = ["alloc", "derive"], default-features = false }
|
||||
serde_json = { version = "1.0.107", default-features = false, features = ["alloc"] }
|
||||
|
||||
[build-dependencies]
|
||||
substrate-wasm-builder = { path = "../../utils/wasm-builder", optional = true }
|
||||
|
||||
+1
-3
@@ -1,7 +1,5 @@
|
||||
{
|
||||
"system": {
|
||||
"code": "0x52"
|
||||
},
|
||||
"system": {},
|
||||
"babe": {
|
||||
"authorities": [
|
||||
[
|
||||
+1
-3
@@ -1,7 +1,5 @@
|
||||
{
|
||||
"system": {
|
||||
"code": "0x52"
|
||||
},
|
||||
"system": {},
|
||||
"babe": {
|
||||
"epochConfig": {
|
||||
"c": [
|
||||
+1
-3
@@ -1,7 +1,5 @@
|
||||
{
|
||||
"system": {
|
||||
"code": "0x52"
|
||||
},
|
||||
"system": {},
|
||||
"babe": {
|
||||
"renamed_authorities": [
|
||||
[
|
||||
@@ -0,0 +1,113 @@
|
||||
{
|
||||
"system": {},
|
||||
"babex": {
|
||||
"authorities": [
|
||||
[
|
||||
"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
|
||||
1
|
||||
],
|
||||
[
|
||||
"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty",
|
||||
1
|
||||
],
|
||||
[
|
||||
"5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y",
|
||||
1
|
||||
]
|
||||
],
|
||||
"epochConfig": {
|
||||
"c": [
|
||||
3,
|
||||
10
|
||||
],
|
||||
"allowed_slots": "PrimaryAndSecondaryPlainSlots"
|
||||
}
|
||||
},
|
||||
"substrateTest": {
|
||||
"authorities": [
|
||||
"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
|
||||
"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty",
|
||||
"5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y"
|
||||
]
|
||||
},
|
||||
"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
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -117,10 +117,7 @@ impl GenesisStorageBuilder {
|
||||
.collect();
|
||||
|
||||
RuntimeGenesisConfig {
|
||||
system: frame_system::GenesisConfig {
|
||||
code: self.wasm_code.clone().unwrap_or(wasm_binary_unwrap().to_vec()),
|
||||
..Default::default()
|
||||
},
|
||||
system: Default::default(),
|
||||
babe: pallet_babe::GenesisConfig {
|
||||
authorities: authorities_sr25519
|
||||
.clone()
|
||||
@@ -149,6 +146,11 @@ impl GenesisStorageBuilder {
|
||||
storage.top.insert(well_known_keys::HEAP_PAGES.into(), heap_pages.encode());
|
||||
}
|
||||
|
||||
storage.top.insert(
|
||||
well_known_keys::CODE.into(),
|
||||
self.wasm_code.clone().unwrap_or(wasm_binary_unwrap().to_vec()),
|
||||
);
|
||||
|
||||
storage.top.extend(self.extra_storage.top.clone());
|
||||
storage.children_default.extend(self.extra_storage.children_default.clone());
|
||||
|
||||
|
||||
@@ -464,11 +464,10 @@ impl_opaque_keys! {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) const TEST_RUNTIME_BABE_EPOCH_CONFIGURATION: BabeEpochConfiguration =
|
||||
BabeEpochConfiguration {
|
||||
c: (3, 10),
|
||||
allowed_slots: AllowedSlots::PrimaryAndSecondaryPlainSlots,
|
||||
};
|
||||
pub const TEST_RUNTIME_BABE_EPOCH_CONFIGURATION: BabeEpochConfiguration = BabeEpochConfiguration {
|
||||
c: (3, 10),
|
||||
allowed_slots: AllowedSlots::PrimaryAndSecondaryPlainSlots,
|
||||
};
|
||||
|
||||
impl_runtime_apis! {
|
||||
impl sp_api::Core<Block> for Runtime {
|
||||
@@ -1237,7 +1236,7 @@ mod tests {
|
||||
#[test]
|
||||
fn build_minimal_genesis_config_works() {
|
||||
sp_tracing::try_init_simple();
|
||||
let default_minimal_json = r#"{"system":{"code":"0x"},"babe":{"authorities":[],"epochConfig":{"c": [ 3, 10 ],"allowed_slots":"PrimaryAndSecondaryPlainSlots"}},"substrateTest":{"authorities":[]},"balances":{"balances":[]}}"#;
|
||||
let default_minimal_json = r#"{"system":{},"babe":{"authorities":[],"epochConfig":{"c": [ 3, 10 ],"allowed_slots":"PrimaryAndSecondaryPlainSlots"}},"substrateTest":{"authorities":[]},"balances":{"balances":[]}}"#;
|
||||
let mut t = BasicExternalities::new_empty();
|
||||
|
||||
executor_call(&mut t, "GenesisBuilder_build_config", &default_minimal_json.encode())
|
||||
@@ -1264,8 +1263,6 @@ mod tests {
|
||||
|
||||
// System|LastRuntimeUpgrade
|
||||
"26aa394eea5630e07c48ae0c9558cef7f9cce9c888469bb1a0dceaa129672ef8",
|
||||
// :code
|
||||
"3a636f6465",
|
||||
// :extrinsic_index
|
||||
"3a65787472696e7369635f696e646578",
|
||||
// Balances|TotalIssuance
|
||||
@@ -1294,35 +1291,55 @@ mod tests {
|
||||
let r = Vec::<u8>::decode(&mut &r[..]).unwrap();
|
||||
let json = String::from_utf8(r.into()).expect("returned value is json. qed.");
|
||||
|
||||
let expected = r#"{"system":{"code":"0x"},"babe":{"authorities":[],"epochConfig":null},"substrateTest":{"authorities":[]},"balances":{"balances":[]}}"#;
|
||||
let expected = r#"{"system":{},"babe":{"authorities":[],"epochConfig":null},"substrateTest":{"authorities":[]},"balances":{"balances":[]}}"#;
|
||||
assert_eq!(expected.to_string(), json);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_config_from_json_works() {
|
||||
sp_tracing::try_init_simple();
|
||||
let j = include_str!("test_json/default_genesis_config.json");
|
||||
let j = include_str!("../res/default_genesis_config.json");
|
||||
|
||||
let mut t = BasicExternalities::new_empty();
|
||||
let r = executor_call(&mut t, "GenesisBuilder_build_config", &j.encode()).unwrap();
|
||||
let r = BuildResult::decode(&mut &r[..]);
|
||||
assert!(r.is_ok());
|
||||
|
||||
let keys = t.into_storages().top.keys().cloned().map(hex).collect::<Vec<String>>();
|
||||
let mut keys = t.into_storages().top.keys().cloned().map(hex).collect::<Vec<String>>();
|
||||
|
||||
// following keys are not placed during `<RuntimeGenesisConfig as GenesisBuild>::build`
|
||||
// process, add them `keys` to assert against known keys.
|
||||
keys.push(hex(b":code"));
|
||||
keys.sort();
|
||||
|
||||
assert_eq!(keys, storage_key_generator::get_expected_storage_hashed_keys(false));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_config_from_invalid_json_fails() {
|
||||
sp_tracing::try_init_simple();
|
||||
let j = include_str!("test_json/default_genesis_config_invalid.json");
|
||||
let j = include_str!("../res/default_genesis_config_invalid.json");
|
||||
let mut t = BasicExternalities::new_empty();
|
||||
let r = executor_call(&mut t, "GenesisBuilder_build_config", &j.encode()).unwrap();
|
||||
let r = BuildResult::decode(&mut &r[..]).unwrap();
|
||||
log::info!("result: {:#?}", r);
|
||||
assert_eq!(r, Err(
|
||||
sp_runtime::RuntimeString::Owned(
|
||||
"Invalid JSON blob: unknown field `renamed_authorities`, expected `authorities` or `epochConfig` at line 6 column 25".to_string(),
|
||||
"Invalid JSON blob: unknown field `renamed_authorities`, expected `authorities` or `epochConfig` at line 4 column 25".to_string(),
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_config_from_invalid_json_fails_2() {
|
||||
sp_tracing::try_init_simple();
|
||||
let j = include_str!("../res/default_genesis_config_invalid_2.json");
|
||||
let mut t = BasicExternalities::new_empty();
|
||||
let r = executor_call(&mut t, "GenesisBuilder_build_config", &j.encode()).unwrap();
|
||||
let r = BuildResult::decode(&mut &r[..]).unwrap();
|
||||
assert_eq!(r, Err(
|
||||
sp_runtime::RuntimeString::Owned(
|
||||
"Invalid JSON blob: unknown field `babex`, expected one of `system`, `babe`, `substrateTest`, `balances` at line 3 column 9".to_string(),
|
||||
))
|
||||
);
|
||||
}
|
||||
@@ -1330,7 +1347,7 @@ mod tests {
|
||||
#[test]
|
||||
fn build_config_from_incomplete_json_fails() {
|
||||
sp_tracing::try_init_simple();
|
||||
let j = include_str!("test_json/default_genesis_config_incomplete.json");
|
||||
let j = include_str!("../res/default_genesis_config_incomplete.json");
|
||||
|
||||
let mut t = BasicExternalities::new_empty();
|
||||
let r = executor_call(&mut t, "GenesisBuilder_build_config", &j.encode()).unwrap();
|
||||
@@ -1339,7 +1356,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
r,
|
||||
Err(sp_runtime::RuntimeString::Owned(
|
||||
"Invalid JSON blob: missing field `authorities` at line 13 column 3"
|
||||
"Invalid JSON blob: missing field `authorities` at line 11 column 3"
|
||||
.to_string()
|
||||
))
|
||||
);
|
||||
@@ -1438,10 +1455,6 @@ mod tests {
|
||||
);
|
||||
assert_eq!(u64::decode(&mut &value[..]).unwrap(), 0);
|
||||
|
||||
// :code
|
||||
let value: Vec<u8> = get_from_storage("3a636f6465");
|
||||
assert!(Vec::<u8>::decode(&mut &value[..]).is_err());
|
||||
|
||||
//System|ParentHash
|
||||
let value: Vec<u8> = get_from_storage(
|
||||
"26aa394eea5630e07c48ae0c9558cef78a42f33323cb5ced3b44dd825fda9fcc",
|
||||
|
||||
Reference in New Issue
Block a user