mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 02:21:04 +00:00
Merge Subkey into sc-cli (#4954)
* draft * revert * WIP * all that remains is tests * update Cargo.lock * tests WIP * WIP refactor node-template-runtime and node-runtime * implments sc_cli::RuntimeAdapter for node_template_runtime::Runtime * final draft * fix update_config for subcommands * proper AccountId decoding * test-runtime tests * revert * move RuntimeAdapter to cli-utils * use &'static str for TryFrom::<&'a str>::Error for Ss58AddressFormat * tests * add frame-system to sc-cli dev-dependencies * add frame-system to sc-cli dev-dependencies * fix ui test * wip * fixed inspect test * bump impl version * bump impl version, fixx spaces remove todos * pallet-balances-cli, rustc for some reason cannot resolve pallet_balances_cli in node-cli 😩 * wip * Subcommand::run takes &self * can't believe i missed that 🤦🏾♂️ * bump wasm-bindgen for some reason * adds key subcommand, rename generate-node-key to generate-node-id * cargo update and crossed fingers 🤞🏽 * update ui test * update more ui tests * should be all good now * revert subkey change * revert subkey change * adds frame-utilities-cli * Apply suggestions from code review Co-authored-by: Benjamin Kampmann <ben@gnunicorn.org> * removes frame from sc-cli, fix license * my editor and ci disagrees on line width * bump spec version * turn off default features for parity-scale-codec * enable full_crypto feature for sp-core in cli-utils * merge frame-utilities-cli with pallet-balances-cli * remove full_crypto feature from sp_core in cli-utils * bump Cargo.lock * cli-utils -> frame-utils * rename BlockNumber to GenericNumber, fix spaces * fix spaces * construct additional_signed manually * sign test * remove unused vars * implement subkey with frame-utilities-cli and sc_cli * fix moduleid test * CI and clion disagree on line widths * adds associated Params type to SignedExtensionProvider * Apply suggestions from code review Co-authored-by: Benjamin Kampmann <ben@gnunicorn.org> * move some code around * removes unneccesary generic params * moves module_id back to frame_utilities_cli * Apply suggestions from code review Co-authored-by: Benjamin Kampmann <ben@gnunicorn.org> Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> * remove print_ext * remove MaybeDisplay from pallet_balances::Trait::Balance * a lot of stuff tbh * adds ExtrasParamsBuilder * remove tests for ModuleIdCmd * address comments from PR * bump Cargo.lock * manually insert key into keystore * remove unnecessary SharedParams * add validation to vanity pattern, remove unused arg * remove SharedParams from Sign, Vanity, Verify * remove SharedParams from ModuleIdCmd, remove expect from Verify, new line to Cargo.toml * remove SharedParams from InsertCmd * 🤦🏾♂️ * deleted prometheus.yml * move a few things around * fix vanity test Co-authored-by: Benjamin Kampmann <ben@gnunicorn.org> Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> Co-authored-by: Benjamin Kampmann <ben@parity.io>
This commit is contained in:
@@ -17,7 +17,7 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::error;
|
||||
use crate::params::{BlockNumber, DatabaseParams, PruningParams, SharedParams};
|
||||
use crate::params::{GenericNumber, DatabaseParams, PruningParams, SharedParams};
|
||||
use crate::CliConfiguration;
|
||||
use log::info;
|
||||
use sc_service::{
|
||||
@@ -44,13 +44,13 @@ pub struct ExportBlocksCmd {
|
||||
///
|
||||
/// Default is 1.
|
||||
#[structopt(long = "from", value_name = "BLOCK")]
|
||||
pub from: Option<BlockNumber>,
|
||||
pub from: Option<GenericNumber>,
|
||||
|
||||
/// Specify last block number.
|
||||
///
|
||||
/// Default is best block.
|
||||
#[structopt(long = "to", value_name = "BLOCK")]
|
||||
pub to: Option<BlockNumber>,
|
||||
pub to: Option<GenericNumber>,
|
||||
|
||||
/// Use binary output rather than JSON.
|
||||
#[structopt(long)]
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2020 Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Implementation of the `generate` subcommand
|
||||
use bip39::{MnemonicType, Mnemonic, Language};
|
||||
use structopt::StructOpt;
|
||||
use crate::{
|
||||
utils::print_from_uri, KeystoreParams, Error,
|
||||
with_crypto_scheme, NetworkSchemeFlag, OutputTypeFlag, CryptoSchemeFlag,
|
||||
};
|
||||
|
||||
/// The `generate` command
|
||||
#[derive(Debug, StructOpt)]
|
||||
#[structopt(name = "generate", about = "Generate a random account")]
|
||||
pub struct GenerateCmd {
|
||||
/// The number of words in the phrase to generate. One of 12 (default), 15, 18, 21 and 24.
|
||||
#[structopt(long, short = "w", value_name = "WORDS")]
|
||||
words: Option<usize>,
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[structopt(flatten)]
|
||||
pub keystore_params: KeystoreParams,
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[structopt(flatten)]
|
||||
pub network_scheme: NetworkSchemeFlag,
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[structopt(flatten)]
|
||||
pub output_scheme: OutputTypeFlag,
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[structopt(flatten)]
|
||||
pub crypto_scheme: CryptoSchemeFlag,
|
||||
}
|
||||
|
||||
impl GenerateCmd {
|
||||
/// Run the command
|
||||
pub fn run(&self) -> Result<(), Error> {
|
||||
let words = match self.words {
|
||||
Some(words) => {
|
||||
MnemonicType::for_word_count(words)
|
||||
.map_err(|_| {
|
||||
Error::Input("Invalid number of words given for phrase: must be 12/15/18/21/24".into())
|
||||
})?
|
||||
},
|
||||
None => MnemonicType::Words12,
|
||||
};
|
||||
let mnemonic = Mnemonic::new(words, Language::English);
|
||||
let password = self.keystore_params.read_password()?;
|
||||
let maybe_network = self.network_scheme.network.clone();
|
||||
let output = self.output_scheme.output_type.clone();
|
||||
|
||||
with_crypto_scheme!(
|
||||
self.crypto_scheme.scheme,
|
||||
print_from_uri(
|
||||
mnemonic.phrase(),
|
||||
password,
|
||||
maybe_network,
|
||||
output
|
||||
)
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::GenerateCmd;
|
||||
use structopt::StructOpt;
|
||||
|
||||
#[test]
|
||||
fn generate() {
|
||||
let generate = GenerateCmd::from_iter(&["generate", "--password", "12345"]);
|
||||
assert!(generate.run().is_ok())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2020 Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Implementation of the `generate-node-key` subcommand
|
||||
|
||||
use crate::Error;
|
||||
use structopt::StructOpt;
|
||||
use std::{path::PathBuf, fs};
|
||||
use libp2p::identity::{ed25519 as libp2p_ed25519, PublicKey};
|
||||
|
||||
/// The `generate-node-key` command
|
||||
#[derive(Debug, StructOpt)]
|
||||
#[structopt(
|
||||
name = "generate-node-key",
|
||||
about = "Generate a random node libp2p key, save it to file and print its peer ID"
|
||||
)]
|
||||
pub struct GenerateNodeKeyCmd {
|
||||
/// Name of file to save secret key to.
|
||||
#[structopt(long)]
|
||||
file: PathBuf,
|
||||
}
|
||||
|
||||
impl GenerateNodeKeyCmd {
|
||||
/// Run the command
|
||||
pub fn run(&self) -> Result<(), Error> {
|
||||
let file = &self.file;
|
||||
|
||||
let keypair = libp2p_ed25519::Keypair::generate();
|
||||
let secret = keypair.secret();
|
||||
let peer_id = PublicKey::Ed25519(keypair.public()).into_peer_id();
|
||||
|
||||
fs::write(file, hex::encode(secret.as_ref()))?;
|
||||
|
||||
println!("{}", peer_id);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use tempfile::Builder;
|
||||
use std::io::Read;
|
||||
|
||||
#[test]
|
||||
fn generate_node_key() {
|
||||
let mut file = Builder::new().prefix("keyfile").tempfile().unwrap();
|
||||
let generate =
|
||||
GenerateNodeKeyCmd::from_iter(&["generate-node-key", "--file", "/tmp/keyfile"]);
|
||||
assert!(generate.run().is_ok());
|
||||
let mut buf = String::new();
|
||||
assert!(file.read_to_string(&mut buf).is_ok());
|
||||
assert!(hex::decode(buf).is_ok());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2020 Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Implementation of the `insert` subcommand
|
||||
|
||||
use crate::{Error, KeystoreParams, CryptoSchemeFlag, SharedParams, utils, with_crypto_scheme};
|
||||
use structopt::StructOpt;
|
||||
use sp_core::{crypto::KeyTypeId, traits::BareCryptoStore};
|
||||
use std::convert::TryFrom;
|
||||
use sc_service::config::KeystoreConfig;
|
||||
use sc_keystore::Store as KeyStore;
|
||||
use sp_core::crypto::SecretString;
|
||||
|
||||
/// The `insert` command
|
||||
#[derive(Debug, StructOpt)]
|
||||
#[structopt(
|
||||
name = "insert",
|
||||
about = "Insert a key to the keystore of a node."
|
||||
)]
|
||||
pub struct InsertCmd {
|
||||
/// The secret key URI.
|
||||
/// If the value is a file, the file content is used as URI.
|
||||
/// If not given, you will be prompted for the URI.
|
||||
#[structopt(long)]
|
||||
suri: Option<String>,
|
||||
|
||||
/// Key type, examples: "gran", or "imon"
|
||||
#[structopt(long)]
|
||||
key_type: String,
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[structopt(flatten)]
|
||||
pub shared_params: SharedParams,
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[structopt(flatten)]
|
||||
pub keystore_params: KeystoreParams,
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[structopt(flatten)]
|
||||
pub crypto_scheme: CryptoSchemeFlag,
|
||||
}
|
||||
|
||||
impl InsertCmd {
|
||||
/// Run the command
|
||||
pub fn run(&self) -> Result<(), Error> {
|
||||
let suri = utils::read_uri(self.suri.as_ref())?;
|
||||
let base_path = self.shared_params.base_path.as_ref()
|
||||
.ok_or_else(|| Error::Other("please supply base path".into()))?;
|
||||
|
||||
let (keystore, public) = match self.keystore_params.keystore_config(base_path)? {
|
||||
KeystoreConfig::Path { path, password } => {
|
||||
let public = with_crypto_scheme!(
|
||||
self.crypto_scheme.scheme,
|
||||
to_vec(&suri, password.clone())
|
||||
)?;
|
||||
let keystore = KeyStore::open(path, password)
|
||||
.map_err(|e| format!("{}", e))?;
|
||||
(keystore, public)
|
||||
},
|
||||
_ => unreachable!("keystore_config always returns path and password; qed")
|
||||
};
|
||||
|
||||
let key_type = KeyTypeId::try_from(self.key_type.as_str())
|
||||
.map_err(|_| {
|
||||
Error::Other("Cannot convert argument to keytype: argument should be 4-character string".into())
|
||||
})?;
|
||||
|
||||
keystore.write()
|
||||
.insert_unknown(key_type, &suri, &public[..])
|
||||
.map_err(|e| Error::Other(format!("{:?}", e)))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn to_vec<P: sp_core::Pair>(uri: &str, pass: Option<SecretString>) -> Result<Vec<u8>, Error> {
|
||||
let p = utils::pair_from_suri::<P>(uri, pass)?;
|
||||
Ok(p.public().as_ref().to_vec())
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2020 Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Implementation of the `inspect` subcommand
|
||||
|
||||
use crate::{
|
||||
utils, KeystoreParams, with_crypto_scheme, NetworkSchemeFlag,
|
||||
OutputTypeFlag, CryptoSchemeFlag, Error,
|
||||
};
|
||||
use structopt::StructOpt;
|
||||
/// The `inspect` command
|
||||
#[derive(Debug, StructOpt)]
|
||||
#[structopt(
|
||||
name = "inspect-key",
|
||||
about = "Gets a public key and a SS58 address from the provided Secret URI"
|
||||
)]
|
||||
pub struct InspectCmd {
|
||||
/// A Key URI to be inspected. May be a secret seed, secret URI
|
||||
/// (with derivation paths and password), SS58 or public URI.
|
||||
/// If the value is a file, the file content is used as URI.
|
||||
/// If not given, you will be prompted for the URI.
|
||||
#[structopt(long)]
|
||||
uri: Option<String>,
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[structopt(flatten)]
|
||||
pub keystore_params: KeystoreParams,
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[structopt(flatten)]
|
||||
pub network_scheme: NetworkSchemeFlag,
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[structopt(flatten)]
|
||||
pub output_scheme: OutputTypeFlag,
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[structopt(flatten)]
|
||||
pub crypto_scheme: CryptoSchemeFlag,
|
||||
}
|
||||
|
||||
impl InspectCmd {
|
||||
/// Run the command
|
||||
pub fn run(&self) -> Result<(), Error> {
|
||||
let uri = utils::read_uri(self.uri.as_ref())?;
|
||||
let password = self.keystore_params.read_password()?;
|
||||
|
||||
use utils::print_from_uri;
|
||||
with_crypto_scheme!(
|
||||
self.crypto_scheme.scheme,
|
||||
print_from_uri(
|
||||
&uri,
|
||||
password,
|
||||
self.network_scheme.network.clone(),
|
||||
self.output_scheme.output_type.clone()
|
||||
)
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::InspectCmd;
|
||||
use structopt::StructOpt;
|
||||
|
||||
#[test]
|
||||
fn inspect() {
|
||||
let words =
|
||||
"remember fiber forum demise paper uniform squirrel feel access exclude casual effort";
|
||||
let seed = "0xad1fb77243b536b90cfe5f0d351ab1b1ac40e3890b41dc64f766ee56340cfca5";
|
||||
|
||||
let inspect =
|
||||
InspectCmd::from_iter(&["inspect-key", "--uri", words, "--password", "12345"]);
|
||||
assert!(inspect.run().is_ok());
|
||||
|
||||
let inspect = InspectCmd::from_iter(&["inspect-key", "--uri", seed]);
|
||||
assert!(inspect.run().is_ok());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2020 Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Implementation of the `inspect-node-key` subcommand
|
||||
|
||||
use crate::{Error, NetworkSchemeFlag};
|
||||
use std::fs;
|
||||
use libp2p::identity::{PublicKey, ed25519};
|
||||
use std::path::PathBuf;
|
||||
use structopt::StructOpt;
|
||||
|
||||
/// The `inspect-node-key` command
|
||||
#[derive(Debug, StructOpt)]
|
||||
#[structopt(
|
||||
name = "inspect-node-key",
|
||||
about = "Print the peer ID corresponding to the node key in the given file."
|
||||
)]
|
||||
pub struct InspectNodeKeyCmd {
|
||||
/// Name of file to read the secret key from.
|
||||
#[structopt(long)]
|
||||
file: PathBuf,
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[structopt(flatten)]
|
||||
pub network_scheme: NetworkSchemeFlag,
|
||||
}
|
||||
|
||||
impl InspectNodeKeyCmd {
|
||||
/// runs the command
|
||||
pub fn run(&self) -> Result<(), Error> {
|
||||
let mut file_content = hex::decode(fs::read(&self.file)?)
|
||||
.map_err(|_| "failed to decode secret as hex")?;
|
||||
let secret = ed25519::SecretKey::from_bytes(&mut file_content)
|
||||
.map_err(|_| "Bad node key file")?;
|
||||
|
||||
let keypair = ed25519::Keypair::from(secret);
|
||||
let peer_id = PublicKey::Ed25519(keypair.public()).into_peer_id();
|
||||
|
||||
println!("{}", peer_id);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use super::super::GenerateNodeKeyCmd;
|
||||
|
||||
#[test]
|
||||
fn inspect_node_key() {
|
||||
let path = tempfile::tempdir().unwrap().into_path().join("node-id").into_os_string();
|
||||
let path = path.to_str().unwrap();
|
||||
let cmd = GenerateNodeKeyCmd::from_iter(&["generate-node-key", "--file", path.clone()]);
|
||||
|
||||
assert!(cmd.run().is_ok());
|
||||
|
||||
let cmd = InspectNodeKeyCmd::from_iter(&["inspect-node-key", "--file", path]);
|
||||
assert!(cmd.run().is_ok());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2020 Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Key related CLI utilities
|
||||
|
||||
use crate::Error;
|
||||
use structopt::StructOpt;
|
||||
|
||||
use super::{
|
||||
insert::InsertCmd,
|
||||
inspect::InspectCmd,
|
||||
generate::GenerateCmd,
|
||||
inspect_node_key::InspectNodeKeyCmd,
|
||||
generate_node_key::GenerateNodeKeyCmd,
|
||||
};
|
||||
|
||||
/// key utilities for the cli.
|
||||
#[derive(Debug, StructOpt)]
|
||||
pub enum KeySubcommand {
|
||||
/// Generate a random node libp2p key, save it to file and print its peer ID
|
||||
GenerateNodeKey(GenerateNodeKeyCmd),
|
||||
|
||||
/// Generate a random account
|
||||
Generate(GenerateCmd),
|
||||
|
||||
/// Gets a public key and a SS58 address from the provided Secret URI
|
||||
InspectKey(InspectCmd),
|
||||
|
||||
/// Print the peer ID corresponding to the node key in the given file
|
||||
InspectNodeKey(InspectNodeKeyCmd),
|
||||
|
||||
/// Insert a key to the keystore of a node.
|
||||
Insert(InsertCmd),
|
||||
}
|
||||
|
||||
impl KeySubcommand {
|
||||
/// run the key subcommands
|
||||
pub fn run(&self) -> Result<(), Error> {
|
||||
match self {
|
||||
KeySubcommand::GenerateNodeKey(cmd) => cmd.run(),
|
||||
KeySubcommand::Generate(cmd) => cmd.run(),
|
||||
KeySubcommand::InspectKey(cmd) => cmd.run(),
|
||||
KeySubcommand::Insert(cmd) => cmd.run(),
|
||||
KeySubcommand::InspectNodeKey(cmd) => cmd.run(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,20 +21,42 @@ mod export_blocks_cmd;
|
||||
mod export_state_cmd;
|
||||
mod import_blocks_cmd;
|
||||
mod purge_chain_cmd;
|
||||
mod sign;
|
||||
mod verify;
|
||||
mod vanity;
|
||||
mod revert_cmd;
|
||||
mod run_cmd;
|
||||
mod generate_node_key;
|
||||
mod generate;
|
||||
mod insert;
|
||||
mod inspect_node_key;
|
||||
mod inspect;
|
||||
mod key;
|
||||
pub mod utils;
|
||||
|
||||
pub use self::build_spec_cmd::BuildSpecCmd;
|
||||
pub use self::check_block_cmd::CheckBlockCmd;
|
||||
pub use self::export_blocks_cmd::ExportBlocksCmd;
|
||||
pub use self::export_state_cmd::ExportStateCmd;
|
||||
pub use self::import_blocks_cmd::ImportBlocksCmd;
|
||||
pub use self::purge_chain_cmd::PurgeChainCmd;
|
||||
pub use self::revert_cmd::RevertCmd;
|
||||
pub use self::run_cmd::RunCmd;
|
||||
use std::fmt::Debug;
|
||||
use structopt::StructOpt;
|
||||
|
||||
pub use self::{
|
||||
build_spec_cmd::BuildSpecCmd,
|
||||
check_block_cmd::CheckBlockCmd,
|
||||
export_blocks_cmd::ExportBlocksCmd,
|
||||
export_state_cmd::ExportStateCmd,
|
||||
import_blocks_cmd::ImportBlocksCmd,
|
||||
purge_chain_cmd::PurgeChainCmd,
|
||||
sign::SignCmd,
|
||||
generate::GenerateCmd,
|
||||
insert::InsertCmd,
|
||||
inspect::InspectCmd,
|
||||
generate_node_key::GenerateNodeKeyCmd,
|
||||
inspect_node_key::InspectNodeKeyCmd,
|
||||
key::KeySubcommand,
|
||||
vanity::VanityCmd,
|
||||
verify::VerifyCmd,
|
||||
revert_cmd::RevertCmd,
|
||||
run_cmd::RunCmd,
|
||||
};
|
||||
|
||||
/// All core commands that are provided by default.
|
||||
///
|
||||
/// The core commands are split into multiple subcommands and `Run` is the default subcommand. From
|
||||
@@ -54,14 +76,14 @@ pub enum Subcommand {
|
||||
/// Validate a single block.
|
||||
CheckBlock(CheckBlockCmd),
|
||||
|
||||
/// Export state as raw chain spec.
|
||||
ExportState(ExportStateCmd),
|
||||
|
||||
/// Revert chain to the previous state.
|
||||
Revert(RevertCmd),
|
||||
|
||||
/// Remove the whole chain data.
|
||||
PurgeChain(PurgeChainCmd),
|
||||
|
||||
/// Export state as raw chain spec.
|
||||
ExportState(ExportStateCmd),
|
||||
}
|
||||
|
||||
/// Macro that helps implement CliConfiguration on an enum of subcommand automatically
|
||||
@@ -425,5 +447,12 @@ macro_rules! substrate_cli_subcommands {
|
||||
}
|
||||
|
||||
substrate_cli_subcommands!(
|
||||
Subcommand => BuildSpec, ExportBlocks, ImportBlocks, CheckBlock, Revert, PurgeChain, ExportState
|
||||
Subcommand =>
|
||||
BuildSpec,
|
||||
ExportBlocks,
|
||||
ExportState,
|
||||
ImportBlocks,
|
||||
CheckBlock,
|
||||
Revert,
|
||||
PurgeChain
|
||||
);
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::error;
|
||||
use crate::params::{BlockNumber, PruningParams, SharedParams};
|
||||
use crate::params::{GenericNumber, PruningParams, SharedParams};
|
||||
use crate::CliConfiguration;
|
||||
use sc_service::chain_ops::revert_chain;
|
||||
use sp_runtime::traits::{Block as BlockT, Header as HeaderT};
|
||||
@@ -32,7 +32,7 @@ use sc_client_api::{Backend, UsageProvider};
|
||||
pub struct RevertCmd {
|
||||
/// Number of blocks to revert.
|
||||
#[structopt(default_value = "256")]
|
||||
pub num: BlockNumber,
|
||||
pub num: GenericNumber,
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[structopt(flatten)]
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2018-2020 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/>.
|
||||
|
||||
//! Implementation of the `sign` subcommand
|
||||
use crate::{error, utils, with_crypto_scheme, CryptoSchemeFlag, KeystoreParams};
|
||||
use structopt::StructOpt;
|
||||
use sp_core::crypto::SecretString;
|
||||
|
||||
/// The `sign` command
|
||||
#[derive(Debug, StructOpt)]
|
||||
#[structopt(
|
||||
name = "sign",
|
||||
about = "Sign a message, with a given (secret) key"
|
||||
)]
|
||||
pub struct SignCmd {
|
||||
/// The secret key URI.
|
||||
/// If the value is a file, the file content is used as URI.
|
||||
/// If not given, you will be prompted for the URI.
|
||||
#[structopt(long)]
|
||||
suri: Option<String>,
|
||||
|
||||
/// Message to sign, if not provided you will be prompted to
|
||||
/// pass the message via STDIN
|
||||
#[structopt(long)]
|
||||
message: Option<String>,
|
||||
|
||||
/// The message on STDIN is hex-encoded data
|
||||
#[structopt(long)]
|
||||
hex: bool,
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[structopt(flatten)]
|
||||
pub keystore_params: KeystoreParams,
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[structopt(flatten)]
|
||||
pub crypto_scheme: CryptoSchemeFlag,
|
||||
}
|
||||
|
||||
|
||||
impl SignCmd {
|
||||
/// Run the command
|
||||
pub fn run(&self) -> error::Result<()> {
|
||||
let message = utils::read_message(self.message.as_ref(), self.hex)?;
|
||||
let suri = utils::read_uri(self.suri.as_ref())?;
|
||||
let password = self.keystore_params.read_password()?;
|
||||
|
||||
let signature = with_crypto_scheme!(
|
||||
self.crypto_scheme.scheme,
|
||||
sign(&suri, password, message)
|
||||
)?;
|
||||
|
||||
println!("{}", signature);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn sign<P: sp_core::Pair>(suri: &str, password: Option<SecretString>, message: Vec<u8>) -> error::Result<String> {
|
||||
let pair = utils::pair_from_suri::<P>(suri, password)?;
|
||||
Ok(format!("{}", hex::encode(pair.sign(&message))))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::SignCmd;
|
||||
use structopt::StructOpt;
|
||||
|
||||
#[test]
|
||||
fn sign() {
|
||||
let seed = "0xad1fb77243b536b90cfe5f0d351ab1b1ac40e3890b41dc64f766ee56340cfca5";
|
||||
|
||||
let sign = SignCmd::from_iter(&[
|
||||
"sign",
|
||||
"--suri",
|
||||
seed,
|
||||
"--message",
|
||||
&seed[2..],
|
||||
"--password",
|
||||
"12345"
|
||||
]);
|
||||
assert!(sign.run().is_ok());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2019-2020 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/>.
|
||||
|
||||
//! subcommand utilities
|
||||
use std::{io::Read, path::PathBuf};
|
||||
use sp_core::{
|
||||
Pair, hexdisplay::HexDisplay,
|
||||
crypto::{Ss58Codec, Ss58AddressFormat},
|
||||
};
|
||||
use sp_runtime::{MultiSigner, traits::IdentifyAccount};
|
||||
use crate::{OutputType, error::{self, Error}};
|
||||
use serde_json::json;
|
||||
use sp_core::crypto::{SecretString, Zeroize, ExposeSecret};
|
||||
|
||||
/// Public key type for Runtime
|
||||
pub type PublicFor<P> = <P as sp_core::Pair>::Public;
|
||||
/// Seed type for Runtime
|
||||
pub type SeedFor<P> = <P as sp_core::Pair>::Seed;
|
||||
|
||||
/// helper method to fetch uri from `Option<String>` either as a file or read from stdin
|
||||
pub fn read_uri(uri: Option<&String>) -> error::Result<String> {
|
||||
let uri = if let Some(uri) = uri {
|
||||
let file = PathBuf::from(&uri);
|
||||
if file.is_file() {
|
||||
std::fs::read_to_string(uri)?
|
||||
.trim_end()
|
||||
.to_owned()
|
||||
} else {
|
||||
uri.into()
|
||||
}
|
||||
} else {
|
||||
rpassword::read_password_from_tty(Some("URI: "))?
|
||||
};
|
||||
|
||||
Ok(uri)
|
||||
}
|
||||
|
||||
/// print formatted pair from uri
|
||||
pub fn print_from_uri<Pair>(
|
||||
uri: &str,
|
||||
password: Option<SecretString>,
|
||||
network_override: Ss58AddressFormat,
|
||||
output: OutputType,
|
||||
)
|
||||
where
|
||||
Pair: sp_core::Pair,
|
||||
Pair::Public: Into<MultiSigner>,
|
||||
{
|
||||
let password = password.as_ref().map(|s| s.expose_secret().as_str());
|
||||
if let Ok((pair, seed)) = Pair::from_phrase(uri, password.clone()) {
|
||||
let public_key = pair.public();
|
||||
|
||||
match output {
|
||||
OutputType::Json => {
|
||||
let json = json!({
|
||||
"secretPhrase": uri,
|
||||
"secretSeed": format_seed::<Pair>(seed),
|
||||
"publicKey": format_public_key::<Pair>(public_key.clone()),
|
||||
"accountId": format_account_id::<Pair>(public_key),
|
||||
"ss58Address": pair.public().into().into_account().to_ss58check(),
|
||||
});
|
||||
println!("{}", serde_json::to_string_pretty(&json).expect("Json pretty print failed"));
|
||||
},
|
||||
OutputType::Text => {
|
||||
println!("Secret phrase `{}` is account:\n \
|
||||
Secret seed: {}\n \
|
||||
Public key (hex): {}\n \
|
||||
Account ID: {}\n \
|
||||
SS58 Address: {}",
|
||||
uri,
|
||||
format_seed::<Pair>(seed),
|
||||
format_public_key::<Pair>(public_key.clone()),
|
||||
format_account_id::<Pair>(public_key),
|
||||
pair.public().into().into_account().to_ss58check(),
|
||||
);
|
||||
},
|
||||
}
|
||||
} else if let Ok((pair, seed)) = Pair::from_string_with_seed(uri, password.clone()) {
|
||||
let public_key = pair.public();
|
||||
|
||||
match output {
|
||||
OutputType::Json => {
|
||||
let json = json!({
|
||||
"secretKeyUri": uri,
|
||||
"secretSeed": if let Some(seed) = seed { format_seed::<Pair>(seed) } else { "n/a".into() },
|
||||
"publicKey": format_public_key::<Pair>(public_key.clone()),
|
||||
"accountId": format_account_id::<Pair>(public_key),
|
||||
"ss58Address": pair.public().into().into_account().to_ss58check(),
|
||||
});
|
||||
println!("{}", serde_json::to_string_pretty(&json).expect("Json pretty print failed"));
|
||||
},
|
||||
OutputType::Text => {
|
||||
println!("Secret Key URI `{}` is account:\n \
|
||||
Secret seed: {}\n \
|
||||
Public key (hex): {}\n \
|
||||
Account ID: {}\n \
|
||||
SS58 Address: {}",
|
||||
uri,
|
||||
if let Some(seed) = seed { format_seed::<Pair>(seed) } else { "n/a".into() },
|
||||
format_public_key::<Pair>(public_key.clone()),
|
||||
format_account_id::<Pair>(public_key),
|
||||
pair.public().into().into_account().to_ss58check(),
|
||||
);
|
||||
},
|
||||
}
|
||||
} else if let Ok((public_key, _v)) = Pair::Public::from_string_with_version(uri) {
|
||||
let v = network_override;
|
||||
|
||||
match output {
|
||||
OutputType::Json => {
|
||||
let json = json!({
|
||||
"publicKeyUri": uri,
|
||||
"networkId": String::from(v),
|
||||
"publicKey": format_public_key::<Pair>(public_key.clone()),
|
||||
"accountId": format_account_id::<Pair>(public_key.clone()),
|
||||
"ss58Address": public_key.to_ss58check_with_version(v),
|
||||
});
|
||||
println!("{}", serde_json::to_string_pretty(&json).expect("Json pretty print failed"));
|
||||
},
|
||||
OutputType::Text => {
|
||||
println!("Public Key URI `{}` is account:\n \
|
||||
Network ID/version: {}\n \
|
||||
Public key (hex): {}\n \
|
||||
Account ID: {}\n \
|
||||
SS58 Address: {}",
|
||||
uri,
|
||||
String::from(v),
|
||||
format_public_key::<Pair>(public_key.clone()),
|
||||
format_account_id::<Pair>(public_key.clone()),
|
||||
public_key.to_ss58check_with_version(v),
|
||||
);
|
||||
},
|
||||
}
|
||||
} else {
|
||||
println!("Invalid phrase/URI given");
|
||||
}
|
||||
}
|
||||
|
||||
/// generate a pair from suri
|
||||
pub fn pair_from_suri<P: Pair>(suri: &str, password: Option<SecretString>) -> Result<P, Error> {
|
||||
let result = if let Some(pass) = password {
|
||||
let mut pass_str = pass.expose_secret().clone();
|
||||
let pair = P::from_string(suri, Some(&pass_str));
|
||||
pass_str.zeroize();
|
||||
pair
|
||||
} else {
|
||||
P::from_string(suri, None)
|
||||
};
|
||||
|
||||
Ok(result.map_err(|err| format!("Invalid phrase {:?}", err))?)
|
||||
}
|
||||
|
||||
/// formats seed as hex
|
||||
pub fn format_seed<P: sp_core::Pair>(seed: SeedFor<P>) -> String {
|
||||
format!("0x{}", HexDisplay::from(&seed.as_ref()))
|
||||
}
|
||||
|
||||
/// formats public key as hex
|
||||
fn format_public_key<P: sp_core::Pair>(public_key: PublicFor<P>) -> String {
|
||||
format!("0x{}", HexDisplay::from(&public_key.as_ref()))
|
||||
}
|
||||
|
||||
/// formats public key as accountId as hex
|
||||
fn format_account_id<P: sp_core::Pair>(public_key: PublicFor<P>) -> String
|
||||
where
|
||||
PublicFor<P>: Into<MultiSigner>,
|
||||
{
|
||||
format!("0x{}", HexDisplay::from(&public_key.into().into_account().as_ref()))
|
||||
}
|
||||
|
||||
/// helper method for decoding hex
|
||||
pub fn decode_hex<T: AsRef<[u8]>>(message: T) -> Result<Vec<u8>, Error> {
|
||||
let mut message = message.as_ref();
|
||||
if message[..2] == [b'0', b'x'] {
|
||||
message = &message[2..]
|
||||
}
|
||||
hex::decode(message)
|
||||
.map_err(|e| Error::Other(format!("Invalid hex ({})", e)))
|
||||
}
|
||||
|
||||
/// checks if message is Some, otherwise reads message from stdin and optionally decodes hex
|
||||
pub fn read_message(msg: Option<&String>, should_decode: bool) -> Result<Vec<u8>, Error> {
|
||||
let mut message = vec![];
|
||||
match msg {
|
||||
Some(m) => {
|
||||
message = decode_hex(m)?;
|
||||
},
|
||||
None => {
|
||||
std::io::stdin().lock().read_to_end(&mut message)?;
|
||||
if should_decode {
|
||||
message = decode_hex(&message)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(message)
|
||||
}
|
||||
|
||||
|
||||
/// Allows for calling $method with appropriate crypto impl.
|
||||
#[macro_export]
|
||||
macro_rules! with_crypto_scheme {
|
||||
($scheme:expr, $method:ident($($params:expr),*)) => {
|
||||
with_crypto_scheme!($scheme, $method<>($($params),*))
|
||||
};
|
||||
($scheme:expr, $method:ident<$($generics:ty),*>($($params:expr),*)) => {
|
||||
match $scheme {
|
||||
$crate::CryptoScheme::Ecdsa => {
|
||||
$method::<sp_core::ecdsa::Pair, $($generics),*>($($params),*)
|
||||
}
|
||||
$crate::CryptoScheme::Sr25519 => {
|
||||
$method::<sp_core::sr25519::Pair, $($generics),*>($($params),*)
|
||||
}
|
||||
$crate::CryptoScheme::Ed25519 => {
|
||||
$method::<sp_core::ed25519::Pair, $($generics),*>($($params),*)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2018-2020 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/>.
|
||||
|
||||
//! implementation of the `vanity` subcommand
|
||||
|
||||
use crate::{
|
||||
error, utils, with_crypto_scheme,
|
||||
CryptoSchemeFlag, NetworkSchemeFlag, OutputTypeFlag,
|
||||
};
|
||||
use sp_core::crypto::Ss58Codec;
|
||||
use structopt::StructOpt;
|
||||
use rand::{rngs::OsRng, RngCore};
|
||||
use sp_runtime::traits::IdentifyAccount;
|
||||
|
||||
/// The `vanity` command
|
||||
#[derive(Debug, StructOpt)]
|
||||
#[structopt(
|
||||
name = "vanity",
|
||||
about = "Generate a seed that provides a vanity address"
|
||||
)]
|
||||
pub struct VanityCmd {
|
||||
/// Desired pattern
|
||||
#[structopt(long, parse(try_from_str = assert_non_empty_string))]
|
||||
pattern: String,
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[structopt(flatten)]
|
||||
network_scheme: NetworkSchemeFlag,
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[structopt(flatten)]
|
||||
output_scheme: OutputTypeFlag,
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[structopt(flatten)]
|
||||
crypto_scheme: CryptoSchemeFlag,
|
||||
}
|
||||
|
||||
impl VanityCmd {
|
||||
/// Run the command
|
||||
pub fn run(&self) -> error::Result<()> {
|
||||
let formated_seed = with_crypto_scheme!(self.crypto_scheme.scheme, generate_key(&self.pattern))?;
|
||||
use utils::print_from_uri;
|
||||
with_crypto_scheme!(
|
||||
self.crypto_scheme.scheme,
|
||||
print_from_uri(
|
||||
&formated_seed,
|
||||
None,
|
||||
self.network_scheme.network.clone(),
|
||||
self.output_scheme.output_type.clone()
|
||||
)
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// genertae a key based on given pattern
|
||||
fn generate_key<Pair>(desired: &str) -> Result<String, &'static str>
|
||||
where
|
||||
Pair: sp_core::Pair,
|
||||
Pair::Public: IdentifyAccount,
|
||||
<Pair::Public as IdentifyAccount>::AccountId: Ss58Codec,
|
||||
{
|
||||
println!("Generating key containing pattern '{}'", desired);
|
||||
|
||||
let top = 45 + (desired.len() * 48);
|
||||
let mut best = 0;
|
||||
let mut seed = Pair::Seed::default();
|
||||
let mut done = 0;
|
||||
|
||||
loop {
|
||||
if done % 100000 == 0 {
|
||||
OsRng.fill_bytes(seed.as_mut());
|
||||
} else {
|
||||
next_seed(seed.as_mut());
|
||||
}
|
||||
|
||||
let p = Pair::from_seed(&seed);
|
||||
let ss58 = p.public().into_account().to_ss58check();
|
||||
let score = calculate_score(&desired, &ss58);
|
||||
if score > best || desired.len() < 2 {
|
||||
best = score;
|
||||
if best >= top {
|
||||
println!("best: {} == top: {}", best, top);
|
||||
return Ok(utils::format_seed::<Pair>(seed.clone()));
|
||||
}
|
||||
}
|
||||
done += 1;
|
||||
|
||||
if done % good_waypoint(done) == 0 {
|
||||
println!("{} keys searched; best is {}/{} complete", done, best, top);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn good_waypoint(done: u64) -> u64 {
|
||||
match done {
|
||||
0..=1_000_000 => 100_000,
|
||||
0..=10_000_000 => 1_000_000,
|
||||
0..=100_000_000 => 10_000_000,
|
||||
_ => 100_000_000,
|
||||
}
|
||||
}
|
||||
|
||||
fn next_seed(seed: &mut [u8]) {
|
||||
for i in 0..seed.len() {
|
||||
match seed[i] {
|
||||
255 => {
|
||||
seed[i] = 0;
|
||||
}
|
||||
_ => {
|
||||
seed[i] += 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate the score of a key based on the desired
|
||||
/// input.
|
||||
fn calculate_score(_desired: &str, key: &str) -> usize {
|
||||
for truncate in 0.._desired.len() {
|
||||
let snip_size = _desired.len() - truncate;
|
||||
let truncated = &_desired[0..snip_size];
|
||||
if let Some(pos) = key.find(truncated) {
|
||||
return (47 - pos) + (snip_size * 48);
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
/// checks that `pattern` is non-empty
|
||||
fn assert_non_empty_string(pattern: &str) -> Result<String, &'static str> {
|
||||
if pattern.is_empty() {
|
||||
Err("Pattern must not be empty")
|
||||
} else {
|
||||
Ok(pattern.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use sp_core::{crypto::Ss58Codec, Pair};
|
||||
use sp_core::sr25519;
|
||||
#[cfg(feature = "bench")]
|
||||
use test::Bencher;
|
||||
use structopt::StructOpt;
|
||||
|
||||
#[test]
|
||||
fn vanity() {
|
||||
let vanity = VanityCmd::from_iter(&["vanity", "--pattern", "j"]);
|
||||
assert!(vanity.run().is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generation_with_single_char() {
|
||||
let seed = generate_key::<sr25519::Pair>("j").unwrap();
|
||||
assert!(
|
||||
sr25519::Pair::from_seed_slice(&hex::decode(&seed[2..]).unwrap())
|
||||
.unwrap()
|
||||
.public()
|
||||
.to_ss58check()
|
||||
.contains("j"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_score_1_char_100() {
|
||||
let score = calculate_score("j", "5jolkadotwHY5k9GpdTgpqs9xjuNvtv8EcwCFpEeyEf3KHim");
|
||||
assert_eq!(score, 94);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_score_100() {
|
||||
let score = calculate_score(
|
||||
"Polkadot",
|
||||
"5PolkadotwHY5k9GpdTgpqs9xjuNvtv8EcwCFpEeyEf3KHim",
|
||||
);
|
||||
assert_eq!(score, 430);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_score_50_2() {
|
||||
// 50% for the position + 50% for the size
|
||||
assert_eq!(
|
||||
calculate_score(
|
||||
"Polkadot",
|
||||
"5PolkXXXXwHY5k9GpdTgpqs9xjuNvtv8EcwCFpEeyEf3KHim"
|
||||
),
|
||||
238
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_score_0() {
|
||||
assert_eq!(
|
||||
calculate_score(
|
||||
"Polkadot",
|
||||
"5GUWv4bLCchGUHJrzULXnh4JgXsMpTKRnjuXTY7Qo1Kh9uYK"
|
||||
),
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "bench")]
|
||||
#[bench]
|
||||
fn bench_paranoiac(b: &mut Bencher) {
|
||||
b.iter(|| generate_key("polk"));
|
||||
}
|
||||
|
||||
#[cfg(feature = "bench")]
|
||||
#[bench]
|
||||
fn bench_not_paranoiac(b: &mut Bencher) {
|
||||
b.iter(|| generate_key("polk"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2019-2020 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/>.
|
||||
|
||||
//! implementation of the `verify` subcommand
|
||||
|
||||
use crate::{error, utils, with_crypto_scheme, CryptoSchemeFlag};
|
||||
use sp_core::{Public, crypto::Ss58Codec};
|
||||
use structopt::StructOpt;
|
||||
|
||||
/// The `verify` command
|
||||
#[derive(Debug, StructOpt)]
|
||||
#[structopt(
|
||||
name = "verify",
|
||||
about = "Verify a signature for a message, provided on STDIN, with a given (public or secret) key"
|
||||
)]
|
||||
pub struct VerifyCmd {
|
||||
/// Signature, hex-encoded.
|
||||
sig: String,
|
||||
|
||||
/// The public or secret key URI.
|
||||
/// If the value is a file, the file content is used as URI.
|
||||
/// If not given, you will be prompted for the URI.
|
||||
uri: Option<String>,
|
||||
|
||||
/// Message to verify, if not provided you will be prompted to
|
||||
/// pass the message via STDIN
|
||||
#[structopt(long)]
|
||||
message: Option<String>,
|
||||
|
||||
/// The message on STDIN is hex-encoded data
|
||||
#[structopt(long)]
|
||||
hex: bool,
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[structopt(flatten)]
|
||||
pub crypto_scheme: CryptoSchemeFlag,
|
||||
}
|
||||
|
||||
impl VerifyCmd {
|
||||
/// Run the command
|
||||
pub fn run(&self) -> error::Result<()> {
|
||||
let message = utils::read_message(self.message.as_ref(), self.hex)?;
|
||||
let sig_data = utils::decode_hex(&self.sig)?;
|
||||
let uri = utils::read_uri(self.uri.as_ref())?;
|
||||
let uri = if uri.starts_with("0x") {
|
||||
&uri[2..]
|
||||
} else {
|
||||
&uri
|
||||
};
|
||||
|
||||
with_crypto_scheme!(
|
||||
self.crypto_scheme.scheme,
|
||||
verify(sig_data, message, uri)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn verify<Pair>(sig_data: Vec<u8>, message: Vec<u8>, uri: &str) -> error::Result<()>
|
||||
where
|
||||
Pair: sp_core::Pair,
|
||||
Pair::Signature: Default + AsMut<[u8]>,
|
||||
{
|
||||
let mut signature = Pair::Signature::default();
|
||||
if sig_data.len() != signature.as_ref().len() {
|
||||
return Err(error::Error::Other(format!(
|
||||
"signature has an invalid length. read {} bytes, expected {} bytes",
|
||||
sig_data.len(),
|
||||
signature.as_ref().len(),
|
||||
)));
|
||||
}
|
||||
signature.as_mut().copy_from_slice(&sig_data);
|
||||
|
||||
let pubkey = if let Ok(pubkey_vec) = hex::decode(uri) {
|
||||
Pair::Public::from_slice(pubkey_vec.as_slice())
|
||||
} else {
|
||||
Pair::Public::from_string(uri)
|
||||
.map_err(|_| {
|
||||
error::Error::Other(format!("Invalid URI; expecting either a secret URI or a public URI."))
|
||||
})?
|
||||
};
|
||||
|
||||
if Pair::verify(&signature, &message, &pubkey) {
|
||||
println!("Signature verifies correctly.");
|
||||
} else {
|
||||
return Err(error::Error::Other("Signature invalid.".into()))
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user