mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 22:51:13 +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:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user