mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-11 07:11:06 +00:00
add doc-only substrate entry point crate (#14581)
* add doc-only substrate entry point crate * document a few more things * add more * fix width * Update primitives/io/src/lib.rs Co-authored-by: Gonçalo Pestana <g6pestana@gmail.com> * add link * update cargo toml file * fix sp-io docs * improve * small update * add license * satisfy license job * add a line about FRAME * CI happy now * make CI more happy * Let the check run for the whole workspace * Forward the substrate node again as default run * update binary names * upate verison test * Fix fix fix * Fix * rename to substrate-node in more places * Revert "rename to substrate-node in more places" This reverts commit 66960f84a1b6f1f7c638b4040e28e9fbabb8adf5. * fix * Fix build pipeline * Fix properly plus add some docs --------- Co-authored-by: Gonçalo Pestana <g6pestana@gmail.com> Co-authored-by: Bastian Köcher <info@kchr.de>
This commit is contained in:
Generated
+20
@@ -11920,6 +11920,26 @@ dependencies = [
|
||||
"sc-cli",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "substrate"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"aquamarine",
|
||||
"chain-spec-builder",
|
||||
"frame-support",
|
||||
"node-cli",
|
||||
"sc-cli",
|
||||
"sc-consensus-aura",
|
||||
"sc-consensus-babe",
|
||||
"sc-consensus-beefy",
|
||||
"sc-consensus-grandpa",
|
||||
"sc-consensus-manual-seal",
|
||||
"sc-consensus-pow",
|
||||
"sc-service",
|
||||
"sp-runtime",
|
||||
"subkey",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "substrate-bip39"
|
||||
version = "0.4.4"
|
||||
|
||||
@@ -1,3 +1,42 @@
|
||||
[package]
|
||||
name = "substrate"
|
||||
description = "Next-generation framework for blockchain innovation"
|
||||
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
|
||||
homepage = "https://substrate.io"
|
||||
repository = "https://github.com/paritytech/substrate/"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
edition = "2021"
|
||||
version = "0.0.0"
|
||||
|
||||
# This list of dependencies is for documentation purposes only.
|
||||
[dependencies]
|
||||
aquamarine = "0.3.2"
|
||||
|
||||
subkey = { path = "bin/utils/subkey" }
|
||||
chain-spec-builder = { path = "bin/utils/chain-spec-builder" }
|
||||
|
||||
sc-service = { path = "client/service" }
|
||||
sc-cli = { path = "client/cli" }
|
||||
sc-consensus-aura = { path = "client/consensus/aura" }
|
||||
sc-consensus-babe = { path = "client/consensus/babe" }
|
||||
sc-consensus-grandpa = { path = "client/consensus/grandpa" }
|
||||
sc-consensus-beefy = { path = "client/consensus/beefy" }
|
||||
sc-consensus-manual-seal = { path = "client/consensus/manual-seal" }
|
||||
sc-consensus-pow = { path = "client/consensus/pow" }
|
||||
|
||||
sp-runtime = { path = "primitives/runtime" }
|
||||
frame-support = { path = "frame/support" }
|
||||
|
||||
node-cli = { path = "bin/node/cli" }
|
||||
|
||||
# Exists here to be backwards compatible and to support `cargo run` in the workspace.
|
||||
#
|
||||
# Just uses the `node-cli` main binary. `node-cli` itself also again exposes the node as
|
||||
# `substrate-node`. Using `node-cli` directly you can also enable features.
|
||||
[[bin]]
|
||||
name = "substrate"
|
||||
path = "bin/node/cli/bin/main.rs"
|
||||
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ description = "Generic Substrate node implementation in Rust."
|
||||
build = "build.rs"
|
||||
edition = "2021"
|
||||
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
|
||||
default-run = "substrate"
|
||||
default-run = "substrate-node"
|
||||
homepage = "https://substrate.io"
|
||||
repository = "https://github.com/paritytech/substrate/"
|
||||
publish = false
|
||||
@@ -25,8 +25,10 @@ maintenance = { status = "actively-developed" }
|
||||
is-it-maintained-issue-resolution = { repository = "paritytech/substrate" }
|
||||
is-it-maintained-open-issues = { repository = "paritytech/substrate" }
|
||||
|
||||
# The same node binary as the `substrate` (defined in the workspace `Cargo.toml`) binary,
|
||||
# but just exposed by this crate here.
|
||||
[[bin]]
|
||||
name = "substrate"
|
||||
name = "substrate-node"
|
||||
path = "bin/main.rs"
|
||||
required-features = ["cli"]
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ async fn benchmark_block_works() {
|
||||
common::run_node_for_a_while(base_dir.path(), &["--dev", "--no-hardware-benchmarks"]).await;
|
||||
|
||||
// Invoke `benchmark block` with all options to make sure that they are valid.
|
||||
let status = Command::new(cargo_bin("substrate"))
|
||||
let status = Command::new(cargo_bin("substrate-node"))
|
||||
.args(["benchmark", "block", "--dev"])
|
||||
.arg("-d")
|
||||
.arg(base_dir.path())
|
||||
|
||||
@@ -32,7 +32,7 @@ fn benchmark_extrinsic_works() {
|
||||
fn benchmark_extrinsic(pallet: &str, extrinsic: &str) {
|
||||
let base_dir = tempdir().expect("could not create a temp dir");
|
||||
|
||||
let status = Command::new(cargo_bin("substrate"))
|
||||
let status = Command::new(cargo_bin("substrate-node"))
|
||||
.args(&["benchmark", "extrinsic", "--dev"])
|
||||
.arg("-d")
|
||||
.arg(base_dir.path())
|
||||
|
||||
@@ -22,7 +22,7 @@ use std::process::Command;
|
||||
/// Tests that the `benchmark machine` command works for the substrate dev runtime.
|
||||
#[test]
|
||||
fn benchmark_machine_works() {
|
||||
let status = Command::new(cargo_bin("substrate"))
|
||||
let status = Command::new(cargo_bin("substrate-node"))
|
||||
.args(["benchmark", "machine", "--dev"])
|
||||
.args([
|
||||
"--verify-duration",
|
||||
@@ -48,7 +48,7 @@ fn benchmark_machine_works() {
|
||||
#[test]
|
||||
#[cfg(debug_assertions)]
|
||||
fn benchmark_machine_fails_with_slow_hardware() {
|
||||
let output = Command::new(cargo_bin("substrate"))
|
||||
let output = Command::new(cargo_bin("substrate-node"))
|
||||
.args(["benchmark", "machine", "--dev"])
|
||||
.args([
|
||||
"--verify-duration",
|
||||
|
||||
@@ -28,7 +28,7 @@ fn benchmark_overhead_works() {
|
||||
|
||||
// Only put 10 extrinsics into the block otherwise it takes forever to build it
|
||||
// especially for a non-release build.
|
||||
let status = Command::new(cargo_bin("substrate"))
|
||||
let status = Command::new(cargo_bin("substrate-node"))
|
||||
.args(&["benchmark", "overhead", "--dev", "-d"])
|
||||
.arg(base_path)
|
||||
.arg("--weight-path")
|
||||
|
||||
@@ -34,7 +34,7 @@ fn benchmark_pallet_works() {
|
||||
}
|
||||
|
||||
fn benchmark_pallet(steps: u32, repeat: u32, should_work: bool) {
|
||||
let status = Command::new(cargo_bin("substrate"))
|
||||
let status = Command::new(cargo_bin("substrate-node"))
|
||||
.args(["benchmark", "pallet", "--dev"])
|
||||
// Use the `addition` benchmark since is the fastest.
|
||||
.args(["--pallet", "frame-benchmarking", "--extrinsic", "addition"])
|
||||
|
||||
@@ -40,7 +40,7 @@ fn benchmark_storage_works() {
|
||||
}
|
||||
|
||||
fn benchmark_storage(db: &str, base_path: &Path) -> ExitStatus {
|
||||
Command::new(cargo_bin("substrate"))
|
||||
Command::new(cargo_bin("substrate-node"))
|
||||
.args(&["benchmark", "storage", "--dev"])
|
||||
.arg("--db")
|
||||
.arg(db)
|
||||
|
||||
@@ -24,7 +24,7 @@ use tempfile::tempdir;
|
||||
fn build_spec_works() {
|
||||
let base_path = tempdir().expect("could not create a temp dir");
|
||||
|
||||
let output = Command::new(cargo_bin("substrate"))
|
||||
let output = Command::new(cargo_bin("substrate-node"))
|
||||
.args(&["build-spec", "--dev", "-d"])
|
||||
.arg(base_path.path())
|
||||
.output()
|
||||
|
||||
@@ -30,7 +30,7 @@ async fn check_block_works() {
|
||||
|
||||
common::run_node_for_a_while(base_path.path(), &["--dev", "--no-hardware-benchmarks"]).await;
|
||||
|
||||
let status = Command::new(cargo_bin("substrate"))
|
||||
let status = Command::new(cargo_bin("substrate-node"))
|
||||
.args(&["check-block", "--dev", "-d"])
|
||||
.arg(base_path.path())
|
||||
.arg("1")
|
||||
|
||||
@@ -96,7 +96,7 @@ impl<'a> ExportImportRevertExecutor<'a> {
|
||||
};
|
||||
|
||||
// Running the command and capturing the output.
|
||||
let output = Command::new(cargo_bin("substrate"))
|
||||
let output = Command::new(cargo_bin("substrate-node"))
|
||||
.args(&arguments)
|
||||
.arg(&base_path)
|
||||
.arg(&self.exported_blocks_file)
|
||||
@@ -160,7 +160,7 @@ impl<'a> ExportImportRevertExecutor<'a> {
|
||||
|
||||
/// Runs the `revert` command.
|
||||
fn run_revert(&self) {
|
||||
let output = Command::new(cargo_bin("substrate"))
|
||||
let output = Command::new(cargo_bin("substrate-node"))
|
||||
.args(&["revert", "--dev", "-d"])
|
||||
.arg(&self.base_path.path())
|
||||
.output()
|
||||
|
||||
@@ -30,7 +30,7 @@ async fn inspect_works() {
|
||||
|
||||
common::run_node_for_a_while(base_path.path(), &["--dev", "--no-hardware-benchmarks"]).await;
|
||||
|
||||
let status = Command::new(cargo_bin("substrate"))
|
||||
let status = Command::new(cargo_bin("substrate-node"))
|
||||
.args(&["inspect", "--dev", "-d"])
|
||||
.arg(base_path.path())
|
||||
.args(&["block", "1"])
|
||||
|
||||
@@ -29,7 +29,7 @@ async fn purge_chain_works() {
|
||||
|
||||
common::run_node_for_a_while(base_path.path(), &["--dev", "--no-hardware-benchmarks"]).await;
|
||||
|
||||
let status = Command::new(cargo_bin("substrate"))
|
||||
let status = Command::new(cargo_bin("substrate-node"))
|
||||
.args(&["purge-chain", "--dev", "-d"])
|
||||
.arg(base_path.path())
|
||||
.arg("-y")
|
||||
|
||||
@@ -33,7 +33,7 @@ async fn running_the_node_works_and_can_be_interrupted() {
|
||||
async fn run_command_and_kill(signal: Signal) {
|
||||
let base_path = tempdir().expect("could not create a temp dir");
|
||||
let mut cmd = common::KillChildOnDrop(
|
||||
Command::new(cargo_bin("substrate"))
|
||||
Command::new(cargo_bin("substrate-node"))
|
||||
.stdout(process::Stdio::piped())
|
||||
.stderr(process::Stdio::piped())
|
||||
.args(&["--dev", "-d"])
|
||||
|
||||
@@ -67,7 +67,7 @@ async fn telemetry_works() {
|
||||
}
|
||||
});
|
||||
|
||||
let mut substrate = process::Command::new(cargo_bin("substrate"));
|
||||
let mut substrate = process::Command::new(cargo_bin("substrate-node"));
|
||||
|
||||
let mut substrate = KillChildOnDrop(
|
||||
substrate
|
||||
|
||||
@@ -31,7 +31,7 @@ use substrate_cli_test_utils as common;
|
||||
//#[tokio::test]
|
||||
async fn temp_base_path_works() {
|
||||
common::run_with_timeout(Duration::from_secs(60 * 10), async move {
|
||||
let mut cmd = Command::new(cargo_bin("substrate"));
|
||||
let mut cmd = Command::new(cargo_bin("substrate-node"));
|
||||
let mut child = common::KillChildOnDrop(
|
||||
cmd.args(&["--dev", "--tmp", "--no-hardware-benchmarks"])
|
||||
.stdout(Stdio::piped())
|
||||
|
||||
@@ -21,17 +21,17 @@ use regex::Regex;
|
||||
use std::process::Command;
|
||||
|
||||
fn expected_regex() -> Regex {
|
||||
Regex::new(r"^substrate (.+)-([a-f\d]+)$").unwrap()
|
||||
Regex::new(r"^substrate-node (.+)-([a-f\d]+)$").unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn version_is_full() {
|
||||
let expected = expected_regex();
|
||||
let output = Command::new(cargo_bin("substrate")).args(&["--version"]).output().unwrap();
|
||||
let output = Command::new(cargo_bin("substrate-node")).args(&["--version"]).output().unwrap();
|
||||
|
||||
assert!(output.status.success(), "command returned with non-success exit code");
|
||||
|
||||
let output = String::from_utf8_lossy(&output.stdout).trim().to_owned();
|
||||
let output = dbg!(String::from_utf8_lossy(&output.stdout).trim().to_owned());
|
||||
let captures = expected.captures(output.as_str()).expect("could not parse version in output");
|
||||
|
||||
assert_eq!(&captures[1], env!("CARGO_PKG_VERSION"));
|
||||
@@ -41,11 +41,11 @@ fn version_is_full() {
|
||||
fn test_regex_matches_properly() {
|
||||
let expected = expected_regex();
|
||||
|
||||
let captures = expected.captures("substrate 2.0.0-da487d19d").unwrap();
|
||||
let captures = expected.captures("substrate-node 2.0.0-da487d19d").unwrap();
|
||||
assert_eq!(&captures[1], "2.0.0");
|
||||
assert_eq!(&captures[2], "da487d19d");
|
||||
|
||||
let captures = expected.captures("substrate 2.0.0-alpha.5-da487d19d").unwrap();
|
||||
let captures = expected.captures("substrate-node 2.0.0-alpha.5-da487d19d").unwrap();
|
||||
assert_eq!(&captures[1], "2.0.0-alpha.5");
|
||||
assert_eq!(&captures[2], "da487d19d");
|
||||
}
|
||||
|
||||
@@ -13,6 +13,13 @@ publish = false
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
||||
[[bin]]
|
||||
path = "bin/main.rs"
|
||||
name = "chain-spec-builder"
|
||||
|
||||
[lib]
|
||||
crate-type = ["rlib"]
|
||||
|
||||
[dependencies]
|
||||
ansi_term = "0.12.1"
|
||||
clap = { version = "4.2.5", features = ["derive"] }
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
License: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
@@ -0,0 +1,89 @@
|
||||
// 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/>.
|
||||
|
||||
use chain_spec_builder::{
|
||||
generate_authority_keys_and_store, generate_chain_spec, print_seeds, ChainSpecBuilder,
|
||||
};
|
||||
use clap::Parser;
|
||||
use node_cli::chain_spec;
|
||||
use rand::{distributions::Alphanumeric, rngs::OsRng, Rng};
|
||||
use sp_core::{crypto::Ss58Codec, sr25519};
|
||||
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",
|
||||
);
|
||||
|
||||
let builder = ChainSpecBuilder::parse();
|
||||
let chain_spec_path = builder.chain_spec_path().to_path_buf();
|
||||
|
||||
let (authority_seeds, nominator_accounts, endowed_accounts, sudo_account) = match builder {
|
||||
ChainSpecBuilder::Generate { authorities, nominators, endowed, keystore_path, .. } => {
|
||||
let authorities = authorities.max(1);
|
||||
let rand_str = || -> String {
|
||||
OsRng.sample_iter(&Alphanumeric).take(32).map(char::from).collect()
|
||||
};
|
||||
|
||||
let authority_seeds = (0..authorities).map(|_| rand_str()).collect::<Vec<_>>();
|
||||
let nominator_seeds = (0..nominators).map(|_| rand_str()).collect::<Vec<_>>();
|
||||
let endowed_seeds = (0..endowed).map(|_| rand_str()).collect::<Vec<_>>();
|
||||
let sudo_seed = rand_str();
|
||||
|
||||
print_seeds(&authority_seeds, &nominator_seeds, &endowed_seeds, &sudo_seed);
|
||||
|
||||
if let Some(keystore_path) = keystore_path {
|
||||
generate_authority_keys_and_store(&authority_seeds, &keystore_path)?;
|
||||
}
|
||||
|
||||
let nominator_accounts = nominator_seeds
|
||||
.into_iter()
|
||||
.map(|seed| {
|
||||
chain_spec::get_account_id_from_seed::<sr25519::Public>(&seed).to_ss58check()
|
||||
})
|
||||
.collect();
|
||||
|
||||
let endowed_accounts = endowed_seeds
|
||||
.into_iter()
|
||||
.map(|seed| {
|
||||
chain_spec::get_account_id_from_seed::<sr25519::Public>(&seed).to_ss58check()
|
||||
})
|
||||
.collect();
|
||||
|
||||
let sudo_account =
|
||||
chain_spec::get_account_id_from_seed::<sr25519::Public>(&sudo_seed).to_ss58check();
|
||||
|
||||
(authority_seeds, nominator_accounts, endowed_accounts, sudo_account)
|
||||
},
|
||||
ChainSpecBuilder::New {
|
||||
authority_seeds,
|
||||
nominator_accounts,
|
||||
endowed_accounts,
|
||||
sudo_account,
|
||||
..
|
||||
} => (authority_seeds, nominator_accounts, endowed_accounts, sudo_account),
|
||||
};
|
||||
|
||||
let json =
|
||||
generate_chain_spec(authority_seeds, nominator_accounts, endowed_accounts, sudo_account)?;
|
||||
|
||||
fs::write(chain_spec_path, json).map_err(|err| err.to_string())
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
// 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/>.
|
||||
|
||||
//! Substrate's chain spec builder utility.
|
||||
//!
|
||||
//! A chain-spec is short for `chain-configuration`. See the [`sc-chain-spec`] for more information.
|
||||
//!
|
||||
//! See [`ChainSpecBuilder`] for a list of available commands.
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use ansi_term::Style;
|
||||
use clap::Parser;
|
||||
|
||||
use node_cli::chain_spec::{self, AccountId};
|
||||
use sc_keystore::LocalKeystore;
|
||||
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)]
|
||||
#[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>,
|
||||
},
|
||||
}
|
||||
|
||||
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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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<_>>();
|
||||
|
||||
chain_spec::testnet_genesis(
|
||||
authorities,
|
||||
nominator_accounts.to_vec(),
|
||||
sudo_account.clone(),
|
||||
Some(endowed_accounts.to_vec()),
|
||||
)
|
||||
}
|
||||
|
||||
/// Generate the chain spec using the given seeds and accounts.
|
||||
pub fn generate_chain_spec(
|
||||
authority_seeds: Vec<String>,
|
||||
nominator_accounts: Vec<String>,
|
||||
endowed_accounts: Vec<String>,
|
||||
sudo_account: String,
|
||||
) -> Result<String, String> {
|
||||
let parse_account = |address: String| {
|
||||
AccountId::from_string(&address)
|
||||
.map_err(|err| format!("Failed to parse account address: {:?}", err))
|
||||
};
|
||||
|
||||
let nominator_accounts = nominator_accounts
|
||||
.into_iter()
|
||||
.map(parse_account)
|
||||
.collect::<Result<Vec<_>, String>>()?;
|
||||
|
||||
let endowed_accounts = endowed_accounts
|
||||
.into_iter()
|
||||
.map(parse_account)
|
||||
.collect::<Result<Vec<_>, String>>()?;
|
||||
|
||||
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(),
|
||||
);
|
||||
|
||||
chain_spec.as_json(false)
|
||||
}
|
||||
|
||||
/// Generate the authority keys and store them in the given `keystore_path`.
|
||||
pub fn generate_authority_keys_and_store(
|
||||
seeds: &[String],
|
||||
keystore_path: &Path,
|
||||
) -> Result<(), String> {
|
||||
for (n, seed) in seeds.iter().enumerate() {
|
||||
let keystore: KeystorePtr =
|
||||
LocalKeystore::open(keystore_path.join(format!("auth-{}", n)), None)
|
||||
.map_err(|err| err.to_string())?
|
||||
.into();
|
||||
|
||||
let (_, _, grandpa, babe, im_online, authority_discovery) =
|
||||
chain_spec::authority_keys_from_seed(seed);
|
||||
|
||||
let insert_key = |key_type, public| {
|
||||
keystore
|
||||
.insert(key_type, &format!("//{}", seed), public)
|
||||
.map_err(|_| format!("Failed to insert key: {}", grandpa))
|
||||
};
|
||||
|
||||
insert_key(sp_core::crypto::key_types::BABE, babe.as_slice())?;
|
||||
|
||||
insert_key(sp_core::crypto::key_types::GRANDPA, grandpa.as_slice())?;
|
||||
|
||||
insert_key(sp_core::crypto::key_types::IM_ONLINE, im_online.as_slice())?;
|
||||
|
||||
insert_key(
|
||||
sp_core::crypto::key_types::AUTHORITY_DISCOVERY,
|
||||
authority_discovery.as_slice(),
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Print the given seeds
|
||||
pub fn print_seeds(
|
||||
authority_seeds: &[String],
|
||||
nominator_seeds: &[String],
|
||||
endowed_seeds: &[String],
|
||||
sudo_seed: &str,
|
||||
) {
|
||||
let header = Style::new().bold().underline();
|
||||
let entry = Style::new().bold();
|
||||
|
||||
println!("{}", header.paint("Authority seeds"));
|
||||
|
||||
for (n, seed) in authority_seeds.iter().enumerate() {
|
||||
println!("{} //{}", entry.paint(format!("auth-{}:", n)), seed);
|
||||
}
|
||||
|
||||
println!("{}", header.paint("Nominator seeds"));
|
||||
|
||||
for (n, seed) in nominator_seeds.iter().enumerate() {
|
||||
println!("{} //{}", entry.paint(format!("nom-{}:", n)), seed);
|
||||
}
|
||||
|
||||
println!();
|
||||
|
||||
if !endowed_seeds.is_empty() {
|
||||
println!("{}", header.paint("Endowed seeds"));
|
||||
for (n, seed) in endowed_seeds.iter().enumerate() {
|
||||
println!("{} //{}", entry.paint(format!("endowed-{}:", n)), seed);
|
||||
}
|
||||
|
||||
println!();
|
||||
}
|
||||
|
||||
println!("{}", header.paint("Sudo seed"));
|
||||
println!("//{}", sudo_seed);
|
||||
}
|
||||
@@ -16,6 +16,12 @@
|
||||
// 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's chain spec builder utility.
|
||||
//!
|
||||
//! A chain-spec is short for `chain-configuration`. See the [`sc-chain-spec`] for more information.
|
||||
//!
|
||||
//! See [`ChainSpecBuilder`] for a list of available commands.
|
||||
|
||||
use std::{
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
@@ -37,7 +43,7 @@ use sp_keystore::KeystorePtr;
|
||||
/// of authorities and endowed accounts and/or generate random accounts.
|
||||
#[derive(Parser)]
|
||||
#[command(rename_all = "kebab-case")]
|
||||
enum ChainSpecBuilder {
|
||||
pub enum ChainSpecBuilder {
|
||||
/// Create a new chain spec with the given authorities, endowed and sudo
|
||||
/// accounts.
|
||||
New {
|
||||
|
||||
@@ -16,6 +16,298 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! # Subkey
|
||||
//!
|
||||
//! Subkey is a commandline utility included with Substrate. It allows generating and restoring keys
|
||||
//! for Substrate based chains such as Polkadot, Kusama and a growing number of parachains and
|
||||
//! Substrate based projects.
|
||||
|
||||
//! `subkey` provides a few sub-commands to generate keys, check keys, sign messages, verify
|
||||
//! messages, etc...
|
||||
//!
|
||||
//! You can see the full list of commands with `subkey --help`. Most commands have additional help
|
||||
//! available with for instance `subkey generate --help` for the `generate` command.
|
||||
//!
|
||||
//! ## Safety first
|
||||
//!
|
||||
//! `subkey` does not need an internet connection to work. Indeed, for the best security, you should
|
||||
//! be using `subkey` on a machine that is **not connected** to the internet.
|
||||
//!
|
||||
//! `subkey` deals with **seeds** and **private keys**. Make sure to use `subkey` in a safe
|
||||
//! environment (ie. no one looking over your shoulder) and on a safe computer (ie. no one able to
|
||||
//! check your command history).
|
||||
//!
|
||||
//! If you save any output of `subkey` into a file, make sure to apply proper permissions and/or
|
||||
//! delete the file as soon as possible.
|
||||
//!
|
||||
//! ## Usage
|
||||
//!
|
||||
//! The following guide explains *some* of the `subkey` commands. For the full list and the most up
|
||||
//! to date documentation, make sure to check the integrated help with `subkey --help`.
|
||||
//!
|
||||
//! ### Install with Cargo
|
||||
//!
|
||||
//! You will need to have the Substrate build dependencies to install Subkey. Use the following two
|
||||
//! commands to install the dependencies and Subkey, respectively:
|
||||
//!
|
||||
//! Command:
|
||||
//!
|
||||
//! ```bash
|
||||
//! # Install only `subkey`, at a specific version of the subkey crate
|
||||
//! cargo install --force subkey --git https://github.com/paritytech/substrate --version <SET VERSION> --locked
|
||||
//! # If you run into issues building, you likely are missing deps defined in https://docs.substrate.io/install/
|
||||
//! ```
|
||||
//!
|
||||
//! ### Run in a container
|
||||
//!
|
||||
//! ```bash
|
||||
//! # Use `--pull=always` with the `latest` tag, or specify a version in a tag
|
||||
//! docker run -it --pull=always docker.io/parity/subkey:latest <command to subkey>
|
||||
//! ```
|
||||
//!
|
||||
//! ### Generate a random account
|
||||
//!
|
||||
//! Generating a new key is as simple as running:
|
||||
//!
|
||||
//! ```bash
|
||||
//! subkey generate
|
||||
//! ```
|
||||
//!
|
||||
//! The output looks similar to:
|
||||
//!
|
||||
//! ```text
|
||||
//! Secret phrase `hotel forest jar hover kite book view eight stuff angle legend defense` is account:
|
||||
//! Secret seed: 0xa05c75731970cc7868a2fb7cb577353cd5b31f62dccced92c441acd8fee0c92d
|
||||
//! Public key (hex): 0xfec70cfbf1977c6965b5af10a4534a6a35d548eb14580594d0bc543286892515
|
||||
//! Account ID: 0xfec70cfbf1977c6965b5af10a4534a6a35d548eb14580594d0bc543286892515
|
||||
//! SS58 Address: 5Hpm9fq3W3dQgwWpAwDS2ZHKAdnk86QRCu7iX4GnmDxycrte
|
||||
//! ```
|
||||
//!
|
||||
//! ---
|
||||
//! ☠️ DO NT RE-USE ANY OF THE SEEDS AND SECRETS FROM THIS PAGE ☠️.
|
||||
//!
|
||||
//! You can read more about security and risks in [SECURITY.md](./SECURITY.md) and in the [Polkadot Wiki](https://wiki.polkadot.network/docs/learn-account-generation).
|
||||
//!
|
||||
//! ---
|
||||
//!
|
||||
//! The output above shows a **secret phrase** (also called **mnemonic phrase**) and the **secret
|
||||
//! seed** (also called **Private Key**). Those 2 secrets are the pieces of information you MUST
|
||||
//! keep safe and secret. All the other information below can be derived from those secrets.
|
||||
//!
|
||||
//! The output above also show the **public key** and the **Account ID**. Those are the independant
|
||||
//! from the network where you will use the key.
|
||||
//!
|
||||
//! The **SS58 address** (or **Public Address**) of a new account is a reprensentation of the public
|
||||
//! keys of an account for a given network (for instance Kusama or Polkadot).
|
||||
//!
|
||||
//! You can read more about the [SS58 format in the Substrate Docs](https://docs.substrate.io/reference/address-formats/) and see the list of reserved prefixes in the [SS58 Registry](https://github.com/paritytech/ss58-registry).
|
||||
//!
|
||||
//! For instance, considering the previous seed
|
||||
//! `0xa05c75731970cc7868a2fb7cb577353cd5b31f62dccced92c441acd8fee0c92d` the SS58 addresses are:
|
||||
//!
|
||||
//! - Polkadot: `16m4J167Mptt8UXL8aGSAi7U2FnPpPxZHPrCgMG9KJzVoFqM`
|
||||
//! - Kusama: `JLNozAv8QeLSbLFwe2UvWeKKE4yvmDbfGxTuiYkF2BUMx4M`
|
||||
//!
|
||||
//! ### Json output
|
||||
//!
|
||||
//! `subkey` can calso generate the output as *json*. This is useful for automation.
|
||||
//!
|
||||
//! command:
|
||||
//!
|
||||
//! ```bash
|
||||
//! subkey generate --output-type json
|
||||
//! ```
|
||||
//!
|
||||
//! output:
|
||||
//!
|
||||
//! ```json
|
||||
//! {
|
||||
//! "accountId": "0xfec70cfbf1977c6965b5af10a4534a6a35d548eb14580594d0bc543286892515",
|
||||
//! "publicKey": "0xfec70cfbf1977c6965b5af10a4534a6a35d548eb14580594d0bc543286892515",
|
||||
//! "secretPhrase": "hotel forest jar hover kite book view eight stuff angle legend defense",
|
||||
//! "secretSeed": "0xa05c75731970cc7868a2fb7cb577353cd5b31f62dccced92c441acd8fee0c92d",
|
||||
//! "ss58Address": "5Hpm9fq3W3dQgwWpAwDS2ZHKAdnk86QRCu7iX4GnmDxycrte"
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! So if you only want to get the `secretSeed` for instance, you can use:
|
||||
//!
|
||||
//! command:
|
||||
//!
|
||||
//! ```bash
|
||||
//! subkey generate --output-type json | jq -r .secretSeed
|
||||
//! ```
|
||||
//!
|
||||
//! output:
|
||||
//!
|
||||
//! ```text
|
||||
//! 0xa05c75731970cc7868a2fb7cb577353cd5b31f62dccced92c441acd8fee0c92d
|
||||
//! ```
|
||||
//!
|
||||
//! ### Additional user-defined password
|
||||
//!
|
||||
//! `subkey` supports an additional user-defined secret that will be appended to the seed. Let's see
|
||||
//! the following example:
|
||||
//!
|
||||
//! ```bash
|
||||
//! subkey generate --password extra_secret
|
||||
//! ```
|
||||
//!
|
||||
//! output:
|
||||
//!
|
||||
//! ```text
|
||||
//! Secret phrase `soup lyrics media market way crouch elevator put moon useful question wide` is account:
|
||||
//! Secret seed: 0xe7cfd179d6537a676cb94bac3b5c5c9cb1550e846ac4541040d077dfbac2e7fd
|
||||
//! Public key (hex): 0xf6a233c3e1de1a2ae0486100b460b3ce3d7231ddfe9dadabbd35ab968c70905d
|
||||
//! Account ID: 0xf6a233c3e1de1a2ae0486100b460b3ce3d7231ddfe9dadabbd35ab968c70905d
|
||||
//! SS58 Address: 5He5pZpc7AJ8evPuab37vJF6KkFDqq9uDq2WXh877Qw6iaVC
|
||||
//! ```
|
||||
//!
|
||||
//! Using the `inspect` command (see more details below), we see that knowning only the **secret
|
||||
//! seed** is no longer sufficient to recover the account:
|
||||
//!
|
||||
//! ```bash
|
||||
//! subkey inspect "soup lyrics media market way crouch elevator put moon useful question wide"
|
||||
//! ```
|
||||
//!
|
||||
//! which recovers the account `5Fe4sqj2K4fRuzEGvToi4KATqZfiDU7TqynjXG6PZE2dxwyh` and not
|
||||
//! `5He5pZpc7AJ8evPuab37vJF6KkFDqq9uDq2WXh877Qw6iaVC` as we expected. The additional user-defined
|
||||
//! **password** (`extra_secret` in our example) is now required to fully recover the account. Let's
|
||||
//! inspect the the previous mnemonic, this time passing also the required `password` as shown
|
||||
//! below:
|
||||
//!
|
||||
//! ```bash
|
||||
//! subkey inspect --password extra_secret "soup lyrics media market way crouch elevator put moon useful question wide"
|
||||
//! ```
|
||||
//!
|
||||
//! This time, we properly recovered `5He5pZpc7AJ8evPuab37vJF6KkFDqq9uDq2WXh877Qw6iaVC`.
|
||||
//!
|
||||
//! ### Inspecting a key
|
||||
//!
|
||||
//! If you have *some data* about a key, `subkey inpsect` will help you discover more information
|
||||
//! about it.
|
||||
//!
|
||||
//! If you have **secrets** that you would like to verify for instance, you can use:
|
||||
//!
|
||||
//! ```bash
|
||||
//! subkey inspect < mnemonic | seed >
|
||||
//! ```
|
||||
//!
|
||||
//! If you have only **public data**, you can see a subset of the information:
|
||||
//!
|
||||
//! ```bash
|
||||
//! subkey inspect --public < pubkey | address >
|
||||
//! ```
|
||||
//!
|
||||
//! **NOTE**: While you will be able to recover the secret seed from the mnemonic, the opposite is
|
||||
//! not possible.
|
||||
//!
|
||||
//! **NOTE**: For obvious reasons, the **secrets** cannot be recovered from passing **public data**
|
||||
//! such as `pubkey` or `address` as input.
|
||||
//!
|
||||
//! command:
|
||||
//!
|
||||
//! ```bash
|
||||
//! subkey inspect 0xa05c75731970cc7868a2fb7cb577353cd5b31f62dccced92c441acd8fee0c92d
|
||||
//! ```
|
||||
//!
|
||||
//! output:
|
||||
//!
|
||||
//! ```text
|
||||
//! Secret Key URI `0xa05c75731970cc7868a2fb7cb577353cd5b31f62dccced92c441acd8fee0c92d` is account:
|
||||
//! Secret seed: 0xa05c75731970cc7868a2fb7cb577353cd5b31f62dccced92c441acd8fee0c92d
|
||||
//! Public key (hex): 0xfec70cfbf1977c6965b5af10a4534a6a35d548eb14580594d0bc543286892515
|
||||
//! Account ID: 0xfec70cfbf1977c6965b5af10a4534a6a35d548eb14580594d0bc543286892515
|
||||
//! SS58 Address: 5Hpm9fq3W3dQgwWpAwDS2ZHKAdnk86QRCu7iX4GnmDxycrte
|
||||
//! ```
|
||||
//!
|
||||
//! ### Signing
|
||||
//!
|
||||
//! `subkey` allows using a **secret key** to sign a random message. The signature can then be
|
||||
//! verified by anyone using your **public key**:
|
||||
//!
|
||||
//! ```bash
|
||||
//! echo -n <msg> | subkey sign --suri <seed|mnemonic>
|
||||
//! ```
|
||||
//!
|
||||
//! example:
|
||||
//!
|
||||
//! ```text
|
||||
//! MESSAGE=hello
|
||||
//! SURI=0xa05c75731970cc7868a2fb7cb577353cd5b31f62dccced92c441acd8fee0c92d
|
||||
//! echo -n $MESSAGE | subkey sign --suri $SURI
|
||||
//! ```
|
||||
//!
|
||||
//! output:
|
||||
//!
|
||||
//! ```text
|
||||
//! 9201af3788ad4f986b800853c79da47155f2e08fde2070d866be4c27ab060466fea0623dc2b51f4392f4c61f25381a62848dd66c5d8217fae3858e469ebd668c
|
||||
//! ```
|
||||
//!
|
||||
//! **NOTE**: Each run of the `sign` command will yield a different output. While each signature is
|
||||
//! different, they are all valid.
|
||||
//!
|
||||
//! ### Verifying a signature
|
||||
//!
|
||||
//! Given a message, a signature and an address, `subkey` can verify whether the **message** has
|
||||
//! been digitally signed by the holder (or one of the holders) of the **private key** for the given
|
||||
//! **address**:
|
||||
//!
|
||||
//! ```bash
|
||||
//! echo -n <msg> | subkey verify <sig> <address>
|
||||
//! ```
|
||||
//!
|
||||
//! example:
|
||||
//!
|
||||
//! ```bash
|
||||
//! MESSAGE=hello
|
||||
//! URI=0xfec70cfbf1977c6965b5af10a4534a6a35d548eb14580594d0bc543286892515
|
||||
//! SIGNATURE=9201af3788ad4f986b800853c79da47155f2e08fde2070d866be4c27ab060466fea0623dc2b51f4392f4c61f25381a62848dd66c5d8217fae3858e469ebd668c
|
||||
//! echo -n $MESSAGE | subkey verify $SIGNATURE $URI
|
||||
//! ```
|
||||
//!
|
||||
//! output:
|
||||
//!
|
||||
//! ```text
|
||||
//! Signature verifies correctly.
|
||||
//! ```
|
||||
//!
|
||||
//! A failure looks like:
|
||||
//!
|
||||
//! ```text
|
||||
//! Error: SignatureInvalid
|
||||
//! ```
|
||||
//!
|
||||
//! ### Using the vanity generator
|
||||
//!
|
||||
//! You can use the included vanity generator to find a seed that provides an address which includes
|
||||
//! the desired pattern. Be warned, depending on your hardware this may take a while.
|
||||
//!
|
||||
//! command:
|
||||
//!
|
||||
//! ```bash
|
||||
//! subkey vanity --network polkadot --pattern bob
|
||||
//! ```
|
||||
//!
|
||||
//! output:
|
||||
//!
|
||||
//! ```text
|
||||
//! Generating key containing pattern 'bob'
|
||||
//! best: 190 == top: 189
|
||||
//! Secret Key URI `0x8c9a73097f235b84021a446bc2826a00c690ea0be3e0d81a84931cb4146d6691` is account:
|
||||
//! Secret seed: 0x8c9a73097f235b84021a446bc2826a00c690ea0be3e0d81a84931cb4146d6691
|
||||
//! Public key (hex): 0x1a8b32e95c1f571118ea0b84801264c3c70f823e320d099e5de31b9b1f18f843
|
||||
//! Account ID: 0x1a8b32e95c1f571118ea0b84801264c3c70f823e320d099e5de31b9b1f18f843
|
||||
//! SS58 Address: 1bobYxBPjZWRPbVo35aSwci1u5Zmq8P6J2jpa4kkudBZMqE
|
||||
//! ```
|
||||
//!
|
||||
//! `Bob` now got a nice address starting with their name:
|
||||
//! 1**bob**YxBPjZWRPbVo35aSwci1u5Zmq8P6J2jpa4kkudBZMqE.
|
||||
//!
|
||||
//! **Note**: While `Bob`, having a short name (3 chars), got a result rather quickly, it will take
|
||||
//! much longer for `Alice` who has a much longer name, thus the chances to generate a random
|
||||
//! address that contains the chain `alice` will be much smaller.
|
||||
|
||||
use clap::Parser;
|
||||
use sc_cli::{
|
||||
Error, GenerateCmd, GenerateNodeKeyCmd, InspectKeyCmd, InspectNodeKeyCmd, SignCmd, VanityCmd,
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! Various subcommands that can be included in a substrate-based chain's CLI.
|
||||
|
||||
mod build_spec_cmd;
|
||||
mod chain_info_cmd;
|
||||
mod check_block_cmd;
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! Substrate CLI library.
|
||||
//!
|
||||
//! To see a full list of commands available, see [`commands`].
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![warn(unused_extern_crates)]
|
||||
@@ -26,7 +28,7 @@ use clap::{CommandFactory, FromArgMatches, Parser};
|
||||
use sc_service::Configuration;
|
||||
|
||||
pub mod arg_enums;
|
||||
mod commands;
|
||||
pub mod commands;
|
||||
mod config;
|
||||
mod error;
|
||||
mod params;
|
||||
|
||||
@@ -15,19 +15,67 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! I/O host interface for substrate runtime.
|
||||
//! # Substrate Primitives: IO
|
||||
//!
|
||||
//! This crate contains interfaces for the runtime to communicate with the outside world, ergo `io`.
|
||||
//! In other context, such interfaces are referred to as "**host functions**".
|
||||
//!
|
||||
//! Each set of host functions are defined with an instance of the
|
||||
//! [`sp_runtime_interface::runtime_interface`] macro.
|
||||
//!
|
||||
//! Most notably, this crate contains host functions for:
|
||||
//!
|
||||
//! - [`hashing`]
|
||||
//! - [`crypto`]
|
||||
//! - [`trie`]
|
||||
//! - [`offchain`]
|
||||
//! - [`storage`]
|
||||
//! - [`allocator`]
|
||||
//! - [`logging`]
|
||||
//!
|
||||
//! All of the default host functions provided by this crate, and by default contained in all
|
||||
//! substrate-based clients are amalgamated in [`SubstrateHostFunctions`].
|
||||
//!
|
||||
//! ## Externalities
|
||||
//!
|
||||
//! Host functions go hand in hand with the concept of externalities. Externalities are an
|
||||
//! environment in which host functions are provided, and thus can be accessed. Some host functions
|
||||
//! are only accessible in an externality environment that provides it.
|
||||
//!
|
||||
//! A typical error for substrate developers is the following:
|
||||
//!
|
||||
//! ```should_panic
|
||||
//! use sp_io::storage::get;
|
||||
//! # fn main() {
|
||||
//! let data = get(b"hello world");
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! This code will panic with the following error:
|
||||
//!
|
||||
//! ```no_compile
|
||||
//! thread 'main' panicked at '`get_version_1` called outside of an Externalities-provided environment.'
|
||||
//! ```
|
||||
//!
|
||||
//! Such error messages should always be interpreted as "code accessing host functions accessed
|
||||
//! outside of externalities".
|
||||
//!
|
||||
//! An externality is any type that implements [`sp_externalities::Externalities`]. A simple example
|
||||
//! of which is [`TestExternalities`], which is commonly used in tests and is exported from this
|
||||
//! crate.
|
||||
//!
|
||||
//! ```
|
||||
//! use sp_io::{storage::get, TestExternalities};
|
||||
//! # fn main() {
|
||||
//! TestExternalities::default().execute_with(|| {
|
||||
//! let data = get(b"hello world");
|
||||
//! });
|
||||
//! # }
|
||||
//! ```
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
#![cfg_attr(enable_alloc_error_handler, feature(alloc_error_handler))]
|
||||
#![cfg_attr(
|
||||
feature = "std",
|
||||
doc = "Substrate runtime standard library as compiled when linked with Rust's standard library."
|
||||
)]
|
||||
#![cfg_attr(
|
||||
not(feature = "std"),
|
||||
doc = "Substrate's runtime standard library as compiled without Rust's standard library."
|
||||
)]
|
||||
|
||||
use sp_std::vec::Vec;
|
||||
|
||||
|
||||
@@ -96,6 +96,7 @@ exceptions = [
|
||||
{ allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-transaction-pool" },
|
||||
{ allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "sc-transaction-pool-api" },
|
||||
{ allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "subkey" },
|
||||
{ allow = ["GPL-3.0 WITH Classpath-exception-2.0"], name = "substrate" },
|
||||
]
|
||||
|
||||
# Some crates don't have (easily) machine readable licensing information,
|
||||
|
||||
@@ -77,8 +77,8 @@ build-linux-substrate:
|
||||
- git checkout -B "$CI_COMMIT_REF_NAME" "$CI_COMMIT_SHA"
|
||||
script:
|
||||
- rusty-cachier snapshot create
|
||||
- WASM_BUILD_NO_COLOR=1 time cargo build --locked --release --verbose
|
||||
- mv $CARGO_TARGET_DIR/release/substrate ./artifacts/substrate/.
|
||||
- WASM_BUILD_NO_COLOR=1 time cargo build --locked --release -p node-cli --verbose
|
||||
- mv $CARGO_TARGET_DIR/release/substrate-node ./artifacts/substrate/substrate
|
||||
- echo -n "Substrate version = "
|
||||
- if [ "${CI_COMMIT_TAG}" ]; then
|
||||
echo "${CI_COMMIT_TAG}" | tee ./artifacts/substrate/VERSION;
|
||||
|
||||
@@ -162,7 +162,7 @@ cargo-check-try-runtime-and-experimental:
|
||||
- .test-refs
|
||||
script:
|
||||
- rusty-cachier snapshot create
|
||||
- time cargo check --locked --features try-runtime,experimental
|
||||
- time cargo check --workspace --locked --features try-runtime,experimental
|
||||
- rusty-cachier cache upload
|
||||
|
||||
test-deterministic-wasm:
|
||||
@@ -305,7 +305,7 @@ quick-benchmarks:
|
||||
WASM_BUILD_RUSTFLAGS: "-C debug-assertions -D warnings"
|
||||
script:
|
||||
- rusty-cachier snapshot create
|
||||
- time cargo run --locked --release --features runtime-benchmarks -- benchmark pallet --wasm-execution compiled --chain dev --pallet "*" --extrinsic "*" --steps 2 --repeat 1
|
||||
- time cargo run --locked --release -p node-cli --features runtime-benchmarks -- benchmark pallet --wasm-execution compiled --chain dev --pallet "*" --extrinsic "*" --steps 2 --repeat 1
|
||||
- rusty-cachier cache upload
|
||||
|
||||
test-frame-examples-compile-to-wasm:
|
||||
|
||||
@@ -0,0 +1,262 @@
|
||||
// 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/>.
|
||||
|
||||
//! # Substrate
|
||||
//!
|
||||
//! Substrate is a Rust framework for building blockchains in a modular and extensible way. While in
|
||||
//! itself un-opinionated, it is the main engine behind the Polkadot ecosystem.
|
||||
//!
|
||||
//! [![github]](https://github.com/paritytech/substrate/) - [![polkadot]](https://polkadot.network)
|
||||
//!
|
||||
//! This crate in itself does not contain any code and is just meant ot be a documentation hub for
|
||||
//! substrate-based crates.
|
||||
//!
|
||||
//! ## Overview
|
||||
//!
|
||||
//! Substrate approaches blockchain development with an acknowledgement of a few self-evident
|
||||
//! truths:
|
||||
//!
|
||||
//! 1. Society and technology evolves.
|
||||
//! 2. Humans are fallible.
|
||||
//!
|
||||
//! This, specifically, makes the task of designing a correct, safe and long-lasting blockchain
|
||||
//! system hard.
|
||||
//!
|
||||
//! Nonetheless, in order to achieve this goal, substrate embraces the following:
|
||||
//!
|
||||
//! 1. Use of **Rust** as a modern, and safe programming language, which limits human error through
|
||||
//! various means, most notably memory safety.
|
||||
//! 2. Substrate is written from the ground-up with a generic, modular and extensible design. This
|
||||
//! ensures that software components can be easily swapped and upgraded. Examples of this is
|
||||
//! multiple consensus mechanisms provided by Substrate, as listed below.
|
||||
//! 3. Lastly, the final blockchain system created with the above properties needs to be
|
||||
//! upgradeable. In order to achieve this, Substrate is designed as a meta-protocol, whereby the
|
||||
//! application logic of the blockchain (called "Runtime") is encoded as a Wasm blob, and is
|
||||
//! stored onchain. The rest of the system (called "Client") acts as the executor of the Wasm
|
||||
//! blob.
|
||||
//!
|
||||
//! In essence, the meta-protocol of all Substrate based chains is the "Runtime as Wasm blob"
|
||||
//! accord. This enables the Runtime to become inherently upgradeable (without forks). The upgrade
|
||||
//! is merely a matter of the Wasm blob being changed in the chain state, which is, in principle,
|
||||
//! same as updating an account's balance.
|
||||
//!
|
||||
//! To learn more about the substrate architecture using some visuals, see [`substrate_diagram`].
|
||||
//!
|
||||
//! `FRAME`, Substrate's default runtime development library takes the above even further by
|
||||
//! embracing a declarative programming model whereby correctness is enhanced and the system is
|
||||
//! highly configurable through parameterization.
|
||||
//!
|
||||
//! All in all, this design enables all substrate-based chains to achieve forkless, self-enacting
|
||||
//! upgrades out of the box. Combined with governance abilities that are shipped with `FRAME`, this
|
||||
//! enables a chain to survive the test of time.
|
||||
//!
|
||||
//! ## How to Get Stared
|
||||
//!
|
||||
//! Most developers want to leave the client side code as-is, and focus on the runtime. To do so,
|
||||
//! look into the [`frame_support`] crate, which is the entry point crate into runtime development
|
||||
//! with FRAME.
|
||||
//!
|
||||
//! > Side note, it is entirely possible to craft a substrate-based runtime without FRAME, an
|
||||
//! > example of which can be found [here](https://github.com/JoshOrndorff/frameless-node-template).
|
||||
//!
|
||||
//! In more broad terms, the following avenues exist into developing with substrate:
|
||||
//!
|
||||
//! * **Templates**: A number of substrate-based templates exist and they can be used for various
|
||||
//! purposes, with zero to little additional code needed. All of these templates contain runtimes
|
||||
//! that are highly configurable and are likely suitable for basic needs.
|
||||
//! * `FRAME`: If need, one can customize that runtime even further, by using `FRAME` and developing
|
||||
//! custom modules.
|
||||
//! * **Core**: To the contrary, some developers may want to customize the client side software to
|
||||
//! achieve novel goals such as a new consensus engine, or a new database backend. While
|
||||
//! Substrate's main configurability is in the runtime, the client is also highly generic and can
|
||||
//! be customized to a great extent.
|
||||
//!
|
||||
//! ## Structure
|
||||
//!
|
||||
//! Substrate is a massive cargo workspace with hundreds of crates, therefore it is useful to know
|
||||
//! how to navigate its crates.
|
||||
//!
|
||||
//! In broad terms, it is divided into three categories:
|
||||
//!
|
||||
//! * `sc-*` (short for *substrate-client*) crates, located under `./client` folder. These are all
|
||||
//! the client crates. Notable examples are crates such as [`sc-network`], various consensus
|
||||
//! crates, [`sc-rpc-api`] and [`sc-client-db`], all of which are expected to reside in the client
|
||||
//! side.
|
||||
//! * `sp-*` (short for *substrate-primitives*) crates, located under `./primitives` folder. These
|
||||
//! are the traits that glue the client and runtime together, but are not opinionated about what
|
||||
//! framework is using for building the runtime. Notable examples are [`sp-api`] and [`sp-io`],
|
||||
//! which form the communication bridge between the client and runtime, as explained in
|
||||
//! [`substrate_diagram`].
|
||||
//! * `pallet-*` and `frame-*` crates, located under `./frame` folder. These are the crates related
|
||||
//! to FRAME. See [`frame_support`] for more information.
|
||||
//!
|
||||
//! ### Binaries
|
||||
//!
|
||||
//! Multiple binaries are shipped with substrate, the most important of which are located in the
|
||||
//! `./bin` folder.
|
||||
//!
|
||||
//! * [`node`] is an extensive substrate node that contains the superset of all runtime and client
|
||||
//! 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.
|
||||
//! * [`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.
|
||||
//! * [`chain-spec-builder`]: Substrate's utility to build *chain specifications*. Such
|
||||
//! specifications can then be used with `--chain` argument of a typical substrate node's CLI.
|
||||
//!
|
||||
//! ## Parachain?
|
||||
//!
|
||||
//! As noted above, Substrate is the main engine behind the Polkadot ecosystem. One of the ways
|
||||
//! through which Polkadot can be utilized is by building "parachains", blockchains that are
|
||||
//! connected to Polkadot's shared security.
|
||||
//!
|
||||
//! To build a parachain, one could use [`Cumulus`](https://github.com/paritytech/cumulus/), the
|
||||
//! library on top of Substrate, empowering any substrate-based chain to be a Polkadot parachain.
|
||||
//!
|
||||
//! ## Where To Go Next?
|
||||
//!
|
||||
//! Additional noteworthy crates within substrate:
|
||||
//!
|
||||
//! - RPC APIs of a Substrate node: [`sc-rpc-api`]
|
||||
//! - CLI Options of a Substrate node: [`sc-cli`]
|
||||
//! - All of the consensus related crates provided by Substrate:
|
||||
//! - [`sc-consensus-aura`]
|
||||
//! - [`sc-consensus-babe`]
|
||||
//! - [`sc-consensus-grandpa`]
|
||||
//! - [`sc-consensus-beefy`]
|
||||
//! - [`sc-consensus-manual-seal`]
|
||||
//! - [`sc-consensus-pow`]
|
||||
//!
|
||||
//! Additional noteworthy external resources:
|
||||
//!
|
||||
//! - [Substrate Developer Hub](https://substrate.dev)
|
||||
//! - [Parity Tech's Documentation Hub](https://paritytech.github.io/)
|
||||
//! - [Frontier: Substrate's Ethereum Compatibility Library](https://paritytech.github.io/frontier/)
|
||||
//! - [Polkadot Wiki](https://wiki.polkadot.network/en/)
|
||||
//!
|
||||
//! Notable upstream crates:
|
||||
//!
|
||||
//! - [`parity-db`](https://github.com/paritytech/parity-db)
|
||||
//! - [`trie`](https://github.com/paritytech/trie)
|
||||
//! - [`parity-common`](https://github.com/paritytech/parity-common)
|
||||
//!
|
||||
//! Templates:
|
||||
//!
|
||||
//! - classic [`substrate-node-template`](https://github.com/substrate-developer-hub/substrate-node-template)
|
||||
//! - classic [cumulus-parachain-template](https://github.com/substrate-developer-hub/substrate-parachain-template)
|
||||
//! - [`extended-parachain-template`](https://github.com/paritytech/extended-parachain-template)
|
||||
//! - [`frontier-parachain-template`](https://github.com/paritytech/frontier-parachain-template)
|
||||
//!
|
||||
//! [polkadot]:
|
||||
//! https://img.shields.io/badge/polkadot-E6007A?style=for-the-badge&logo=polkadot&logoColor=white
|
||||
//! [github]:
|
||||
//! https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
|
||||
//! [`sp-io`]: ../sp_io/index.html
|
||||
//! [`sp-api`]: ../sp_api/index.html
|
||||
//! [`sp-api`]: ../sp_api/index.html
|
||||
//! [`sc-client-db`]: ../sc_client_db/index.html
|
||||
//! [`sc-network`]: ../sc_network/index.html
|
||||
//! [`sc-rpc-api`]: ../sc_rpc_api/index.html
|
||||
//! [`sc-cli`]: ../sc_cli/index.html
|
||||
//! [`sc-consensus-aura`]: ../sc_consensus_aura/index.html
|
||||
//! [`sc-consensus-babe`]: ../sc_consensus_babe/index.html
|
||||
//! [`sc-consensus-grandpa`]: ../sc_consensus_grandpa/index.html
|
||||
//! [`sc-consensus-beefy`]: ../sc_consensus_beefy/index.html
|
||||
//! [`sc-consensus-manual-seal`]: ../sc_consensus_manual_seal/index.html
|
||||
//! [`sc-consensus-pow`]: ../sc_consensus_pow/index.html
|
||||
//! [`node`]: ../node_cli/index.html
|
||||
//! [`node-template`]: ../node_template/index.html
|
||||
//! [`kitchensink_runtime`]: ../kitchensink_runtime/index.html
|
||||
//! [`subkey`]: ..//subkey/index.html
|
||||
//! [`chian-spec-builder`]: ../chain_spec_builder/index.html
|
||||
|
||||
#![deny(rustdoc::broken_intra_doc_links)]
|
||||
#![deny(rustdoc::private_intra_doc_links)]
|
||||
|
||||
#[cfg_attr(doc, aquamarine::aquamarine)]
|
||||
/// In this module, we explore substrate at a more depth. First, let's establish substrate being
|
||||
/// divided into a client and runtime.
|
||||
///
|
||||
/// ```mermaid
|
||||
/// graph TB
|
||||
/// subgraph Substrate
|
||||
/// direction LR
|
||||
/// subgraph Client
|
||||
/// end
|
||||
/// subgraph Runtime
|
||||
/// end
|
||||
/// end
|
||||
/// ```
|
||||
///
|
||||
/// The client and the runtime of course need to communicate. This is done through two concepts:
|
||||
///
|
||||
/// 1. Host functions: a way for the (Wasm) runtime to talk to the client. All host functions are
|
||||
/// defined in [`sp-io`]. For example, [`sp-io::storage`] are the set of host functions that
|
||||
/// allow the runtime to read and write data to the on-chain state.
|
||||
/// 2. Runtime APIs: a way for the client to talk to the Wasm runtime. Runtime APIs are defined
|
||||
/// using macros and utilities in [`sp-api`]. For example, [`sp-api::Core`] is the most basic
|
||||
/// runtime API that any blockchain must implement in order to be able to (re) execute blocks.
|
||||
///
|
||||
/// ```mermaid
|
||||
/// graph TB
|
||||
/// subgraph Substrate
|
||||
/// direction LR
|
||||
/// subgraph Client
|
||||
/// end
|
||||
/// subgraph Runtime
|
||||
/// end
|
||||
/// Client --runtime-api--> Runtime
|
||||
/// Runtime --host-functions--> Client
|
||||
/// end
|
||||
/// ```
|
||||
///
|
||||
/// Finally, let's expand the diagram a bit further and look at the internals of each component:
|
||||
///
|
||||
/// ```mermaid
|
||||
/// graph TB
|
||||
/// subgraph Substrate
|
||||
/// direction LR
|
||||
/// subgraph Client
|
||||
/// Database
|
||||
/// Networking
|
||||
/// Consensus
|
||||
/// end
|
||||
/// subgraph Runtime
|
||||
/// subgraph FRAME
|
||||
/// direction LR
|
||||
/// Governance
|
||||
/// Currency
|
||||
/// Staking
|
||||
/// Identity
|
||||
/// end
|
||||
/// end
|
||||
/// Client --runtime-api--> Runtime
|
||||
/// Runtime --host-functions--> Client
|
||||
/// end
|
||||
/// ```
|
||||
///
|
||||
/// As noted the runtime contains all of the application specific logic of the blockchain. This is
|
||||
/// usually written with `FRAME`. The client, on the other hand, contains reusable and generic
|
||||
/// components that are not specific to one single blockchain, such as networking, database, and the
|
||||
/// consensus engine.
|
||||
///
|
||||
/// [`sp-io`]: ../../sp_io/index.html
|
||||
/// [`sp-api`]: ../../sp_api/index.html
|
||||
/// [`sp-io::storage`]: ../../sp_io/storage/index.html
|
||||
/// [`sp-api::Core`]: ../../sp_api/trait.Core.html
|
||||
pub mod substrate_diagram {}
|
||||
@@ -0,0 +1,25 @@
|
||||
// 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/>.
|
||||
|
||||
use std::env;
|
||||
|
||||
fn main() {
|
||||
if let Ok(profile) = env::var("PROFILE") {
|
||||
println!("cargo:rustc-cfg=build_type=\"{}\"", profile);
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,6 @@ use node_primitives::{Hash, Header};
|
||||
use regex::Regex;
|
||||
use sp_rpc::{list::ListOrValue, number::NumberOrHex};
|
||||
use std::{
|
||||
env,
|
||||
io::{BufRead, BufReader, Read},
|
||||
ops::{Deref, DerefMut},
|
||||
path::{Path, PathBuf},
|
||||
@@ -62,7 +61,7 @@ use tokio::io::{AsyncBufReadExt, AsyncRead};
|
||||
///
|
||||
/// [`Child`]: std::process::Child
|
||||
pub fn start_node() -> Child {
|
||||
Command::new(cargo_bin("substrate"))
|
||||
Command::new(cargo_bin("substrate-node"))
|
||||
.stdout(process::Stdio::piped())
|
||||
.stderr(process::Stdio::piped())
|
||||
.args(&["--dev", "--tmp", "--rpc-port=45789", "--no-hardware-benchmarks"])
|
||||
@@ -99,15 +98,19 @@ pub fn start_node() -> Child {
|
||||
/// build_substrate(&["--features=try-runtime"]);
|
||||
/// ```
|
||||
pub fn build_substrate(args: &[&str]) {
|
||||
let is_release_build = !cfg!(build_type = "debug");
|
||||
|
||||
// Get the root workspace directory from the CARGO_MANIFEST_DIR environment variable
|
||||
let manifest_dir = env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set");
|
||||
let root_dir = std::path::Path::new(&manifest_dir)
|
||||
.parent()
|
||||
.expect("Failed to find root workspace directory");
|
||||
let output = Command::new("cargo")
|
||||
.arg("build")
|
||||
let mut cmd = Command::new("cargo");
|
||||
|
||||
cmd.arg("build").arg("-p=node-cli");
|
||||
|
||||
if is_release_build {
|
||||
cmd.arg("--release");
|
||||
}
|
||||
|
||||
let output = cmd
|
||||
.args(args)
|
||||
.current_dir(root_dir)
|
||||
.output()
|
||||
.expect(format!("Failed to execute 'cargo b' with args {:?}'", args).as_str());
|
||||
|
||||
@@ -196,7 +199,7 @@ pub async fn wait_n_finalized_blocks(n: usize, url: &str) {
|
||||
/// Run the node for a while (3 blocks)
|
||||
pub async fn run_node_for_a_while(base_path: &Path, args: &[&str]) {
|
||||
run_with_timeout(Duration::from_secs(60 * 10), async move {
|
||||
let mut cmd = Command::new(cargo_bin("substrate"))
|
||||
let mut cmd = Command::new(cargo_bin("substrate-node"))
|
||||
.stdout(process::Stdio::piped())
|
||||
.stderr(process::Stdio::piped())
|
||||
.args(args)
|
||||
|
||||
@@ -47,7 +47,7 @@ async fn create_snapshot_works() {
|
||||
|
||||
common::run_with_timeout(Duration::from_secs(60), async move {
|
||||
fn create_snapshot(ws_url: &str, snap_file: &PathBuf, at: Hash) -> Child {
|
||||
Command::new(cargo_bin("substrate"))
|
||||
Command::new(cargo_bin("substrate-node"))
|
||||
.stdout(process::Stdio::piped())
|
||||
.stderr(process::Stdio::piped())
|
||||
.args(&["try-runtime", "--runtime=existing"])
|
||||
|
||||
@@ -33,7 +33,7 @@ async fn block_execution_works() {
|
||||
|
||||
common::run_with_timeout(Duration::from_secs(60), async move {
|
||||
fn execute_block(ws_url: &str, at: Hash) -> Child {
|
||||
Command::new(cargo_bin("substrate"))
|
||||
Command::new(cargo_bin("substrate-node"))
|
||||
.stdout(process::Stdio::piped())
|
||||
.stderr(process::Stdio::piped())
|
||||
.args(&["try-runtime", "--runtime=existing"])
|
||||
|
||||
@@ -35,7 +35,7 @@ async fn follow_chain_works() {
|
||||
|
||||
common::run_with_timeout(Duration::from_secs(60), async move {
|
||||
fn start_follow(ws_url: &str) -> Child {
|
||||
Command::new(cargo_bin("substrate"))
|
||||
Command::new(cargo_bin("substrate-node"))
|
||||
.stdout(process::Stdio::piped())
|
||||
.stderr(process::Stdio::piped())
|
||||
.args(&["try-runtime", "--runtime=existing"])
|
||||
|
||||
Reference in New Issue
Block a user