diff --git a/client/cli/Cargo.toml b/client/cli/Cargo.toml index 7227a04..536341b 100644 --- a/client/cli/Cargo.toml +++ b/client/cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ghost-client-cli" -version = "0.1.3" +version = "0.1.4" description = "Ghost CLI interface" license.workspace = true authors.workspace = true diff --git a/client/cli/src/commands/generate.rs b/client/cli/src/commands/generate.rs index 1f89119..e5e093a 100755 --- a/client/cli/src/commands/generate.rs +++ b/client/cli/src/commands/generate.rs @@ -3,13 +3,10 @@ use bip39::Mnemonic; use clap::Parser; use itertools::Itertools; -use sc_cli::{ - with_crypto_scheme, KeystoreParams, OutputTypeFlag, - CryptoSchemeFlag, Error, -}; +use sc_cli::{with_crypto_scheme, CryptoSchemeFlag, Error, KeystoreParams, OutputTypeFlag}; -use crate::params::NetworkSchemeFlag; use crate::commands::utils::print_from_uri; +use crate::params::NetworkSchemeFlag; /// The `generate` command #[derive(Debug, Clone, Parser)] @@ -17,58 +14,58 @@ use crate::commands::utils::print_from_uri; pub struct GenerateCmd { /// The number of words in the phrase to generate. One of 12 (default), /// 15, 18, 21 and 24. - #[arg(short = 'w', long, value_name = "WORDS")] - words: Option, + #[arg(short = 'w', long, value_name = "WORDS")] + words: Option, - #[allow(missing_docs)] - #[clap(flatten)] - pub keystore_params: KeystoreParams, + #[allow(missing_docs)] + #[clap(flatten)] + pub keystore_params: KeystoreParams, - #[allow(missing_docs)] - #[clap(flatten)] - pub network_scheme: NetworkSchemeFlag, + #[allow(missing_docs)] + #[clap(flatten)] + pub network_scheme: NetworkSchemeFlag, - #[allow(missing_docs)] - #[clap(flatten)] - pub output_scheme: OutputTypeFlag, + #[allow(missing_docs)] + #[clap(flatten)] + pub output_scheme: OutputTypeFlag, - #[allow(missing_docs)] - #[clap(flatten)] - pub crypto_scheme: CryptoSchemeFlag, + #[allow(missing_docs)] + #[clap(flatten)] + pub crypto_scheme: CryptoSchemeFlag, } impl GenerateCmd { - /// Run the command - pub fn run(&self) -> Result<(), Error> { - let words = match self.words { + /// Run the command + pub fn run(&self) -> Result<(), Error> { + let words = match self.words { Some(words_count) if [12, 15, 18, 21, 24].contains(&words_count) => Ok(words_count), Some(_) => Err(Error::Input( "Invalid number of words given for phrase: must be 12/15/18/21/24".into(), )), None => Ok(12), - }?; - let mnemonic = Mnemonic::generate(words) + }?; + let mnemonic = Mnemonic::generate(words) .map_err(|e| Error::Input(format!("Mnemonic generation failed: {e}").into()))?; - let password = self.keystore_params.read_password()?; - let output = self.output_scheme.output_type; + let password = self.keystore_params.read_password()?; + let output = self.output_scheme.output_type; let phrase = mnemonic.words().join(" "); - with_crypto_scheme!( - self.crypto_scheme.scheme, - print_from_uri(&phrase, password, self.network_scheme.network, output) - ); - Ok(()) - } + with_crypto_scheme!( + self.crypto_scheme.scheme, + print_from_uri(&phrase, password, self.network_scheme.network, output) + ); + Ok(()) + } } #[cfg(test)] mod tests { - use super::*; + use super::*; - #[test] - fn generate() { - let generate = GenerateCmd::parse_from(&["generate", "--password", "12345"]); - assert!(generate.run().is_ok()) - } + #[test] + fn generate() { + let generate = GenerateCmd::parse_from(&["generate", "--password", "12345"]); + assert!(generate.run().is_ok()) + } } diff --git a/client/cli/src/commands/inspect_key.rs b/client/cli/src/commands/inspect_key.rs index bcb94f0..216f077 100755 --- a/client/cli/src/commands/inspect_key.rs +++ b/client/cli/src/commands/inspect_key.rs @@ -1,102 +1,101 @@ //! Implementation of the `inspect` subcommand +use crate::commands::utils::{print_from_public, print_from_uri}; +use crate::params::NetworkSchemeFlag; use clap::Parser; +use sc_cli::{ + utils::read_uri, with_crypto_scheme, CryptoSchemeFlag, Error, KeystoreParams, OutputTypeFlag, +}; use sp_core::crypto::{ExposeSecret, SecretString, SecretUri, Ss58Codec}; use std::str::FromStr; -use sc_cli::{ - with_crypto_scheme, KeystoreParams, OutputTypeFlag, CryptoSchemeFlag, Error, - utils::read_uri, -}; -use crate::params::NetworkSchemeFlag; -use crate::commands::utils::{print_from_public, print_from_uri}; /// The `inspect` command #[derive(Debug, Parser)] #[command( - name = "inspect", - about = "Gets a public key and a SS58 address from the provided Secret URI" + name = "inspect", + about = "Gets a public key and a SS58 address from the provided Secret URI" )] pub struct InspectKeyCmd { - /// A Key URI to be inspected. May be a secret seed, secret URI - /// (with derivation paths and password), SS58, public URI or a hex encoded public key. - /// - /// If it is a hex encoded public key, `--public` needs to be given as argument. - /// - /// If the given value is a file, the file content will be used - /// as URI. - /// - /// If omitted, you will be prompted for the URI. - uri: Option, + /// A Key URI to be inspected. May be a secret seed, secret URI + /// (with derivation paths and password), SS58, public URI or a hex encoded public key. + /// + /// If it is a hex encoded public key, `--public` needs to be given as argument. + /// + /// If the given value is a file, the file content will be used + /// as URI. + /// + /// If omitted, you will be prompted for the URI. + uri: Option, - /// Is the given `uri` a hex encoded public key? - #[arg(long)] - public: bool, + /// Is the given `uri` a hex encoded public key? + #[arg(long)] + public: bool, - #[allow(missing_docs)] - #[clap(flatten)] - pub keystore_params: KeystoreParams, + #[allow(missing_docs)] + #[clap(flatten)] + pub keystore_params: KeystoreParams, - #[allow(missing_docs)] - #[clap(flatten)] - pub network_scheme: NetworkSchemeFlag, + #[allow(missing_docs)] + #[clap(flatten)] + pub network_scheme: NetworkSchemeFlag, - #[allow(missing_docs)] - #[clap(flatten)] - pub output_scheme: OutputTypeFlag, + #[allow(missing_docs)] + #[clap(flatten)] + pub output_scheme: OutputTypeFlag, - #[allow(missing_docs)] - #[clap(flatten)] - pub crypto_scheme: CryptoSchemeFlag, + #[allow(missing_docs)] + #[clap(flatten)] + pub crypto_scheme: CryptoSchemeFlag, - /// Expect that `--uri` has the given public key/account-id. - /// - /// If `--uri` has any derivations, the public key is checked against the base `uri`, i.e. the - /// `uri` without any derivation applied. However, if `uri` has a password or there is one - /// given by `--password`, it will be used to decrypt `uri` before comparing the public - /// key/account-id. - /// - /// If there is no derivation in `--uri`, the public key will be checked against the public key - /// of `--uri` directly. - #[arg(long, conflicts_with = "public")] - pub expect_public: Option, + /// Expect that `--uri` has the given public key/account-id. + /// + /// If `--uri` has any derivations, the public key is checked against the base `uri`, i.e. the + /// `uri` without any derivation applied. However, if `uri` has a password or there is one + /// given by `--password`, it will be used to decrypt `uri` before comparing the public + /// key/account-id. + /// + /// If there is no derivation in `--uri`, the public key will be checked against the public key + /// of `--uri` directly. + #[arg(long, conflicts_with = "public")] + pub expect_public: Option, } impl InspectKeyCmd { - /// Run the command - pub fn run(&self) -> Result<(), Error> { - let uri = read_uri(self.uri.as_ref())?; - let password = self.keystore_params.read_password()?; + /// Run the command + pub fn run(&self) -> Result<(), Error> { + let uri = read_uri(self.uri.as_ref())?; + let password = self.keystore_params.read_password()?; - if self.public { - with_crypto_scheme!( - self.crypto_scheme.scheme, - print_from_public( - &uri, - self.network_scheme.network, - self.output_scheme.output_type, - ) - )?; - } else { - if let Some(ref expect_public) = self.expect_public { - with_crypto_scheme!( - self.crypto_scheme.scheme, - expect_public_from_phrase(expect_public, &uri, password.as_ref()) - )?; - } + if self.public { + with_crypto_scheme!( + self.crypto_scheme.scheme, + print_from_public( + &uri, + self.network_scheme.network, + self.output_scheme.output_type, + ) + )?; + } else { + if let Some(ref expect_public) = self.expect_public { + with_crypto_scheme!( + self.crypto_scheme.scheme, + expect_public_from_phrase(expect_public, &uri, password.as_ref()) + )?; + } - with_crypto_scheme!( - self.crypto_scheme.scheme, - print_from_uri( - &uri, - password, - self.network_scheme.network, - self.output_scheme.output_type, - ) - ); - } + with_crypto_scheme!( + self.crypto_scheme.scheme, + print_from_uri( + &uri, + password, + self.network_scheme.network, + self.output_scheme.output_type, + ) + ); + } - Ok(()) - } + Ok(()) + } } /// Checks that `expect_public` is the public key of `suri`. @@ -106,148 +105,156 @@ impl InspectKeyCmd { /// /// Returns an error if the public key does not match. fn expect_public_from_phrase( - expect_public: &str, - suri: &str, - password: Option<&SecretString>, + expect_public: &str, + suri: &str, + password: Option<&SecretString>, ) -> Result<(), Error> { - let secret_uri = SecretUri::from_str(suri).map_err(|e| format!("{:?}", e))?; - let expected_public = if let Some(public) = expect_public.strip_prefix("0x") { - let hex_public = array_bytes::hex2bytes(public) - .map_err(|_| format!("Invalid expected public key hex: `{}`", expect_public))?; - Pair::Public::try_from(&hex_public) - .map_err(|_| format!("Invalid expected public key: `{}`", expect_public))? - } else { - Pair::Public::from_string_with_version(expect_public) - .map_err(|_| format!("Invalid expected account id: `{}`", expect_public))? - .0 - }; + let secret_uri = SecretUri::from_str(suri).map_err(|e| format!("{:?}", e))?; + let expected_public = if let Some(public) = expect_public.strip_prefix("0x") { + let hex_public = array_bytes::hex2bytes(public) + .map_err(|_| format!("Invalid expected public key hex: `{}`", expect_public))?; + Pair::Public::try_from(&hex_public) + .map_err(|_| format!("Invalid expected public key: `{}`", expect_public))? + } else { + Pair::Public::from_string_with_version(expect_public) + .map_err(|_| format!("Invalid expected account id: `{}`", expect_public))? + .0 + }; - let pair = Pair::from_string_with_seed( - secret_uri.phrase.expose_secret().as_str(), - password - .or_else(|| secret_uri.password.as_ref()) - .map(|p| p.expose_secret().as_str()), - ) - .map_err(|_| format!("Invalid secret uri: {}", suri))? - .0; + let pair = Pair::from_string_with_seed( + secret_uri.phrase.expose_secret().as_str(), + password + .or_else(|| secret_uri.password.as_ref()) + .map(|p| p.expose_secret().as_str()), + ) + .map_err(|_| format!("Invalid secret uri: {}", suri))? + .0; - if pair.public() == expected_public { - Ok(()) - } else { - Err(format!("Expected public ({}) key does not match.", expect_public).into()) - } + if pair.public() == expected_public { + Ok(()) + } else { + Err(format!("Expected public ({}) key does not match.", expect_public).into()) + } } #[cfg(test)] mod tests { - use super::*; - use sp_core::crypto::{ByteArray, Pair}; - use sp_runtime::traits::IdentifyAccount; + use super::*; + use sp_core::crypto::{ByteArray, Pair}; + use sp_runtime::traits::IdentifyAccount; - #[test] - fn inspect() { - let words = - "remember fiber forum demise paper uniform squirrel feel access exclude casual effort"; - let seed = "0xad1fb77243b536b90cfe5f0d351ab1b1ac40e3890b41dc64f766ee56340cfca5"; + #[test] + fn inspect() { + let words = + "remember fiber forum demise paper uniform squirrel feel access exclude casual effort"; + let seed = "0xad1fb77243b536b90cfe5f0d351ab1b1ac40e3890b41dc64f766ee56340cfca5"; - let inspect = InspectKeyCmd::parse_from(&["inspect-key", words, "--password", "12345"]); - assert!(inspect.run().is_ok()); + let inspect = InspectKeyCmd::parse_from(&["inspect-key", words, "--password", "12345"]); + assert!(inspect.run().is_ok()); - let inspect = InspectKeyCmd::parse_from(&["inspect-key", seed]); - assert!(inspect.run().is_ok()); - } + let inspect = InspectKeyCmd::parse_from(&["inspect-key", seed]); + assert!(inspect.run().is_ok()); + } - #[test] - fn inspect_public_key() { - let public = "0x12e76e0ae8ce41b6516cce52b3f23a08dcb4cfeed53c6ee8f5eb9f7367341069"; + #[test] + fn inspect_public_key() { + let public = "0x12e76e0ae8ce41b6516cce52b3f23a08dcb4cfeed53c6ee8f5eb9f7367341069"; - let inspect = InspectKeyCmd::parse_from(&["inspect-key", "--public", public]); - assert!(inspect.run().is_ok()); - } + let inspect = InspectKeyCmd::parse_from(&["inspect-key", "--public", public]); + assert!(inspect.run().is_ok()); + } - #[test] - fn inspect_with_expected_public_key() { - let check_cmd = |seed, expected_public, success| { - let inspect = InspectKeyCmd::parse_from(&[ - "inspect-key", - "--expect-public", - expected_public, - seed, - ]); - let res = inspect.run(); + #[test] + fn inspect_with_expected_public_key() { + let check_cmd = |seed, expected_public, success| { + let inspect = InspectKeyCmd::parse_from(&[ + "inspect-key", + "--expect-public", + expected_public, + seed, + ]); + let res = inspect.run(); - if success { - assert!(res.is_ok()); - } else { - assert!(res.unwrap_err().to_string().contains(&format!( - "Expected public ({}) key does not match.", - expected_public - ))); - } - }; + if success { + assert!(res.is_ok()); + } else { + assert!(res.unwrap_err().to_string().contains(&format!( + "Expected public ({}) key does not match.", + expected_public + ))); + } + }; - let seed = - "remember fiber forum demise paper uniform squirrel feel access exclude casual effort"; - let invalid_public = "0x12e76e0ae8ce41b6516cce52b3f23a08dcb4cfeed53c6ee8f5eb9f7367341069"; - let valid_public = sp_core::sr25519::Pair::from_string_with_seed(seed, None) - .expect("Valid") - .0 - .public(); - let valid_public_hex = array_bytes::bytes2hex("0x", valid_public.as_slice()); - let valid_accountid = format!("{}", valid_public.into_account()); + let seed = + "remember fiber forum demise paper uniform squirrel feel access exclude casual effort"; + let invalid_public = "0x12e76e0ae8ce41b6516cce52b3f23a08dcb4cfeed53c6ee8f5eb9f7367341069"; + let valid_public = sp_core::sr25519::Pair::from_string_with_seed(seed, None) + .expect("Valid") + .0 + .public(); + let valid_public_hex = array_bytes::bytes2hex("0x", valid_public.as_slice()); + let valid_accountid = format!("{}", valid_public.into_account()); - // It should fail with the invalid public key - check_cmd(seed, invalid_public, false); + // It should fail with the invalid public key + check_cmd(seed, invalid_public, false); - // It should work with the valid public key & account id - check_cmd(seed, &valid_public_hex, true); - check_cmd(seed, &valid_accountid, true); + // It should work with the valid public key & account id + check_cmd(seed, &valid_public_hex, true); + check_cmd(seed, &valid_accountid, true); - let password = "test12245"; - let seed_with_password = format!("{}///{}", seed, password); - let valid_public_with_password = - sp_core::sr25519::Pair::from_string_with_seed(&seed_with_password, Some(password)) - .expect("Valid") - .0 - .public(); - let valid_public_hex_with_password = - array_bytes::bytes2hex("0x", valid_public_with_password.as_slice()); - let valid_accountid_with_password = - format!("{}", &valid_public_with_password.into_account()); + let password = "test12245"; + let seed_with_password = format!("{}///{}", seed, password); + let valid_public_with_password = + sp_core::sr25519::Pair::from_string_with_seed(&seed_with_password, Some(password)) + .expect("Valid") + .0 + .public(); + let valid_public_hex_with_password = + array_bytes::bytes2hex("0x", valid_public_with_password.as_slice()); + let valid_accountid_with_password = + format!("{}", &valid_public_with_password.into_account()); - // Only the public key that corresponds to the seed with password should be accepted. - check_cmd(&seed_with_password, &valid_public_hex, false); - check_cmd(&seed_with_password, &valid_accountid, false); + // Only the public key that corresponds to the seed with password should be accepted. + check_cmd(&seed_with_password, &valid_public_hex, false); + check_cmd(&seed_with_password, &valid_accountid, false); - check_cmd(&seed_with_password, &valid_public_hex_with_password, true); - check_cmd(&seed_with_password, &valid_accountid_with_password, true); + check_cmd(&seed_with_password, &valid_public_hex_with_password, true); + check_cmd(&seed_with_password, &valid_accountid_with_password, true); - let seed_with_password_and_derivation = format!("{}//test//account///{}", seed, password); + let seed_with_password_and_derivation = format!("{}//test//account///{}", seed, password); - let valid_public_with_password_and_derivation = - sp_core::sr25519::Pair::from_string_with_seed( - &seed_with_password_and_derivation, - Some(password), - ) - .expect("Valid") - .0 - .public(); - let valid_public_hex_with_password_and_derivation = - array_bytes::bytes2hex("0x", valid_public_with_password_and_derivation.as_slice()); + let valid_public_with_password_and_derivation = + sp_core::sr25519::Pair::from_string_with_seed( + &seed_with_password_and_derivation, + Some(password), + ) + .expect("Valid") + .0 + .public(); + let valid_public_hex_with_password_and_derivation = + array_bytes::bytes2hex("0x", valid_public_with_password_and_derivation.as_slice()); - // They should still be valid, because we check the base secret key. - check_cmd(&seed_with_password_and_derivation, &valid_public_hex_with_password, true); - check_cmd(&seed_with_password_and_derivation, &valid_accountid_with_password, true); + // They should still be valid, because we check the base secret key. + check_cmd( + &seed_with_password_and_derivation, + &valid_public_hex_with_password, + true, + ); + check_cmd( + &seed_with_password_and_derivation, + &valid_accountid_with_password, + true, + ); - // And these should be invalid. - check_cmd(&seed_with_password_and_derivation, &valid_public_hex, false); - check_cmd(&seed_with_password_and_derivation, &valid_accountid, false); + // And these should be invalid. + check_cmd(&seed_with_password_and_derivation, &valid_public_hex, false); + check_cmd(&seed_with_password_and_derivation, &valid_accountid, false); - // The public of the derived account should fail. - check_cmd( - &seed_with_password_and_derivation, - &valid_public_hex_with_password_and_derivation, - false, - ); - } + // The public of the derived account should fail. + check_cmd( + &seed_with_password_and_derivation, + &valid_public_hex_with_password_and_derivation, + false, + ); + } } diff --git a/client/cli/src/commands/key.rs b/client/cli/src/commands/key.rs index 0b1181d..f28ecd4 100755 --- a/client/cli/src/commands/key.rs +++ b/client/cli/src/commands/key.rs @@ -1,40 +1,37 @@ //! Key related CLI utilities use super::{generate::GenerateCmd, inspect_key::InspectKeyCmd}; -use sc_cli::{ - GenerateKeyCmdCommon, InsertKeyCmd, InspectNodeKeyCmd, Error, - SubstrateCli, -}; +use sc_cli::{Error, GenerateKeyCmdCommon, InsertKeyCmd, InspectNodeKeyCmd, SubstrateCli}; /// Key utilities for the cli. #[derive(Debug, clap::Subcommand)] pub enum KeySubcommand { - /// Generate a random node key, write it to a file or stdout and write the - /// corresponding peer-id to stderr - GenerateNodeKey(GenerateKeyCmdCommon), + /// Generate a random node key, write it to a file or stdout and write the + /// corresponding peer-id to stderr + GenerateNodeKey(GenerateKeyCmdCommon), - /// Generate a Substrate-based random account - Generate(GenerateCmd), + /// Generate a Substrate-based random account + Generate(GenerateCmd), - /// Gets a public key and a SS58 address from the provided Secret URI - Inspect(InspectKeyCmd), + /// Gets a public key and a SS58 address from the provided Secret URI + Inspect(InspectKeyCmd), - /// Load a node key from a file or stdin and print the corresponding peer-id - InspectNodeKey(InspectNodeKeyCmd), + /// Load a node key from a file or stdin and print the corresponding peer-id + InspectNodeKey(InspectNodeKeyCmd), - /// Insert a key to the keystore of a node. - Insert(InsertKeyCmd), + /// Insert a key to the keystore of a node. + Insert(InsertKeyCmd), } impl KeySubcommand { - /// run the key subcommands - pub fn run(&self, cli: &C) -> Result<(), Error> { - match self { - KeySubcommand::GenerateNodeKey(cmd) => cmd.run(), - KeySubcommand::Generate(cmd) => cmd.run(), - KeySubcommand::Inspect(cmd) => cmd.run(), - KeySubcommand::Insert(cmd) => cmd.run(cli), - KeySubcommand::InspectNodeKey(cmd) => cmd.run(), - } - } + /// run the key subcommands + pub fn run(&self, cli: &C) -> Result<(), Error> { + match self { + KeySubcommand::GenerateNodeKey(cmd) => cmd.run(), + KeySubcommand::Generate(cmd) => cmd.run(), + KeySubcommand::Inspect(cmd) => cmd.run(), + KeySubcommand::Insert(cmd) => cmd.run(cli), + KeySubcommand::InspectNodeKey(cmd) => cmd.run(), + } + } } diff --git a/client/cli/src/commands/mod.rs b/client/cli/src/commands/mod.rs index a916150..2621fe5 100755 --- a/client/cli/src/commands/mod.rs +++ b/client/cli/src/commands/mod.rs @@ -1,11 +1,13 @@ -mod key; mod generate; -mod vanity; mod inspect_key; +mod key; mod utils; +mod vanity; pub use self::{ - key::KeySubcommand, vanity::VanityCmd, inspect_key::InspectKeyCmd, generate::GenerateCmd, - utils::{unwrap_or_default_ss58_name, print_from_uri, print_from_public}, + inspect_key::InspectKeyCmd, + key::KeySubcommand, + utils::{print_from_public, print_from_uri, unwrap_or_default_ss58_name}, + vanity::VanityCmd, }; diff --git a/client/cli/src/commands/utils.rs b/client/cli/src/commands/utils.rs index 73eec92..22b9554 100755 --- a/client/cli/src/commands/utils.rs +++ b/client/cli/src/commands/utils.rs @@ -1,194 +1,206 @@ -use serde_json::json; use sc_cli::{ - OutputType, utils::{PublicFor, SeedFor}, + OutputType, }; -use sp_runtime::{traits::IdentifyAccount, MultiSigner}; +use serde_json::json; use sp_core::{ crypto::{ - unwrap_or_default_ss58_version, - Ss58Codec, ExposeSecret, Ss58AddressFormat, SecretString, + unwrap_or_default_ss58_version, ExposeSecret, SecretString, Ss58AddressFormat, Ss58Codec, }, hexdisplay::HexDisplay, }; +use sp_runtime::{traits::IdentifyAccount, MultiSigner}; pub fn print_from_uri( - uri: &str, - password: Option, - network_override: Option, - output: OutputType, + uri: &str, + password: Option, + network_override: Option, + output: OutputType, ) where - Pair: sp_core::Pair, - Pair::Public: Into, + Pair: sp_core::Pair, + Pair::Public: Into, { - let password = password.as_ref().map(|s| s.expose_secret().as_str()); - let network_id = unwrap_or_default_ss58_name(network_override); + let password = password.as_ref().map(|s| s.expose_secret().as_str()); + let network_id = unwrap_or_default_ss58_name(network_override); - if let Ok((pair, seed)) = Pair::from_phrase(uri, password) { - let public_key = pair.public(); - let network_override = unwrap_or_default_ss58_version(network_override); + if let Ok((pair, seed)) = Pair::from_phrase(uri, password) { + let public_key = pair.public(); + let network_override = unwrap_or_default_ss58_version(network_override); - match output { - OutputType::Json => { - let json = json!({ - "secretPhrase": uri, - "networkId": network_id, - "secretSeed": format_seed::(seed), - "publicKey": format_public_key::(public_key.clone()), - "ss58PublicKey": public_key.to_ss58check_with_version(network_override), - "accountId": format_account_id::(public_key), - "ss58Address": pair.public().into().into_account().to_ss58check_with_version(network_override), - }); - println!( - "{}", - serde_json::to_string_pretty(&json).expect("Json pretty print failed") - ); - }, - OutputType::Text => { - println!( - "Secret phrase: {}\n \ + match output { + OutputType::Json => { + let json = json!({ + "secretPhrase": uri, + "networkId": network_id, + "secretSeed": format_seed::(seed), + "publicKey": format_public_key::(public_key.clone()), + "ss58PublicKey": public_key.to_ss58check_with_version(network_override), + "accountId": format_account_id::(public_key), + "ss58Address": pair.public().into().into_account().to_ss58check_with_version(network_override), + }); + println!( + "{}", + serde_json::to_string_pretty(&json).expect("Json pretty print failed") + ); + } + OutputType::Text => { + println!( + "Secret phrase: {}\n \ Network ID: {}\n \ Secret seed: {}\n \ Public key (hex): {}\n \ Account ID: {}\n \ Public key (SS58): {}\n \ SS58 Address: {}", - uri, - network_id, - format_seed::(seed), - format_public_key::(public_key.clone()), - format_account_id::(public_key.clone()), - public_key.to_ss58check_with_version(network_override), - pair.public().into().into_account().to_ss58check_with_version(network_override), - ); - }, - } - } else if let Ok((pair, seed)) = Pair::from_string_with_seed(uri, password) { - let public_key = pair.public(); - let network_override = unwrap_or_default_ss58_version(network_override); + uri, + network_id, + format_seed::(seed), + format_public_key::(public_key.clone()), + format_account_id::(public_key.clone()), + public_key.to_ss58check_with_version(network_override), + pair.public() + .into() + .into_account() + .to_ss58check_with_version(network_override), + ); + } + } + } else if let Ok((pair, seed)) = Pair::from_string_with_seed(uri, password) { + let public_key = pair.public(); + let network_override = unwrap_or_default_ss58_version(network_override); - match output { - OutputType::Json => { - let json = json!({ - "secretKeyUri": uri, - "networkId": network_id, - "secretSeed": if let Some(seed) = seed { format_seed::(seed) } else { "n/a".into() }, - "publicKey": format_public_key::(public_key.clone()), - "ss58PublicKey": public_key.to_ss58check_with_version(network_override), - "accountId": format_account_id::(public_key), - "ss58Address": pair.public().into().into_account().to_ss58check_with_version(network_override), - }); - println!( - "{}", - serde_json::to_string_pretty(&json).expect("Json pretty print failed") - ); - }, - OutputType::Text => { - println!( - "Secret Key URI `{}` is account:\n \ + match output { + OutputType::Json => { + let json = json!({ + "secretKeyUri": uri, + "networkId": network_id, + "secretSeed": if let Some(seed) = seed { format_seed::(seed) } else { "n/a".into() }, + "publicKey": format_public_key::(public_key.clone()), + "ss58PublicKey": public_key.to_ss58check_with_version(network_override), + "accountId": format_account_id::(public_key), + "ss58Address": pair.public().into().into_account().to_ss58check_with_version(network_override), + }); + println!( + "{}", + serde_json::to_string_pretty(&json).expect("Json pretty print failed") + ); + } + OutputType::Text => { + println!( + "Secret Key URI `{}` is account:\n \ Network ID: {}\n \ Secret seed: {}\n \ Public key (hex): {}\n \ Account ID: {}\n \ Public key (SS58): {}\n \ SS58 Address: {}", - uri, - network_id, - if let Some(seed) = seed { format_seed::(seed) } else { "n/a".into() }, - format_public_key::(public_key.clone()), - format_account_id::(public_key.clone()), - public_key.to_ss58check_with_version(network_override), - pair.public().into().into_account().to_ss58check_with_version(network_override), - ); - }, - } - } else if let Ok((public_key, network)) = Pair::Public::from_string_with_version(uri) { - let network_override = network_override.unwrap_or(network); + uri, + network_id, + if let Some(seed) = seed { + format_seed::(seed) + } else { + "n/a".into() + }, + format_public_key::(public_key.clone()), + format_account_id::(public_key.clone()), + public_key.to_ss58check_with_version(network_override), + pair.public() + .into() + .into_account() + .to_ss58check_with_version(network_override), + ); + } + } + } else if let Ok((public_key, network)) = Pair::Public::from_string_with_version(uri) { + let network_override = network_override.unwrap_or(network); - match output { - OutputType::Json => { - let json = json!({ - "publicKeyUri": uri, - "networkId": String::from(network_override), - "publicKey": format_public_key::(public_key.clone()), - "accountId": format_account_id::(public_key.clone()), - "ss58PublicKey": public_key.to_ss58check_with_version(network_override), - "ss58Address": public_key.to_ss58check_with_version(network_override), - }); + match output { + OutputType::Json => { + let json = json!({ + "publicKeyUri": uri, + "networkId": String::from(network_override), + "publicKey": format_public_key::(public_key.clone()), + "accountId": format_account_id::(public_key.clone()), + "ss58PublicKey": public_key.to_ss58check_with_version(network_override), + "ss58Address": public_key.to_ss58check_with_version(network_override), + }); - println!( - "{}", - serde_json::to_string_pretty(&json).expect("Json pretty print failed") - ); - }, - OutputType::Text => { - println!( - "Public Key URI `{}` is account:\n \ + 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 \ Public key (SS58): {}\n \ SS58 Address: {}", - uri, - String::from(network_override), - format_public_key::(public_key.clone()), - format_account_id::(public_key.clone()), - public_key.to_ss58check_with_version(network_override), - public_key.to_ss58check_with_version(network_override), - ); - }, - } - } else { - println!("Invalid phrase/URI given"); - } + uri, + String::from(network_override), + format_public_key::(public_key.clone()), + format_account_id::(public_key.clone()), + public_key.to_ss58check_with_version(network_override), + public_key.to_ss58check_with_version(network_override), + ); + } + } + } else { + println!("Invalid phrase/URI given"); + } } /// Try to parse given `public` as hex encoded public key and print relevant information. pub fn print_from_public( - public_str: &str, - network_override: Option, - output: OutputType, + public_str: &str, + network_override: Option, + output: OutputType, ) -> Result<(), sc_cli::Error> where - Pair: sp_core::Pair, - Pair::Public: Into, + Pair: sp_core::Pair, + Pair::Public: Into, { - let public = array_bytes::hex2bytes(public_str)?; + let public = array_bytes::hex2bytes(public_str)?; - let public_key = Pair::Public::try_from(&public) - .map_err(|_| "Failed to construct public key from given hex")?; + let public_key = Pair::Public::try_from(&public) + .map_err(|_| "Failed to construct public key from given hex")?; - let network_override = unwrap_or_default_ss58_version(network_override); + let network_override = unwrap_or_default_ss58_version(network_override); - match output { - OutputType::Json => { - let json = json!({ - "networkId": String::from(network_override), - "publicKey": format_public_key::(public_key.clone()), - "accountId": format_account_id::(public_key.clone()), - "ss58PublicKey": public_key.to_ss58check_with_version(network_override), - "ss58Address": public_key.to_ss58check_with_version(network_override), - }); + match output { + OutputType::Json => { + let json = json!({ + "networkId": String::from(network_override), + "publicKey": format_public_key::(public_key.clone()), + "accountId": format_account_id::(public_key.clone()), + "ss58PublicKey": public_key.to_ss58check_with_version(network_override), + "ss58Address": public_key.to_ss58check_with_version(network_override), + }); - println!("{}", serde_json::to_string_pretty(&json).expect("Json pretty print failed")); - }, - OutputType::Text => { - println!( - "Network ID/Version: {}\n \ + println!( + "{}", + serde_json::to_string_pretty(&json).expect("Json pretty print failed") + ); + } + OutputType::Text => { + println!( + "Network ID/Version: {}\n \ Public key (hex): {}\n \ Account ID: {}\n \ Public key (SS58): {}\n \ SS58 Address: {}", - String::from(network_override), - format_public_key::(public_key.clone()), - format_account_id::(public_key.clone()), - public_key.to_ss58check_with_version(network_override), - public_key.to_ss58check_with_version(network_override), - ); - }, - } + String::from(network_override), + format_public_key::(public_key.clone()), + format_account_id::(public_key.clone()), + public_key.to_ss58check_with_version(network_override), + public_key.to_ss58check_with_version(network_override), + ); + } + } - Ok(()) + Ok(()) } pub fn unwrap_or_default_ss58_name(x: Option) -> String { @@ -216,5 +228,8 @@ fn format_account_id(public_key: PublicFor

) -> String where PublicFor

: Into, { - format!("0x{}", HexDisplay::from(&public_key.into().into_account().as_ref())) + format!( + "0x{}", + HexDisplay::from(&public_key.into().into_account().as_ref()) + ) } diff --git a/client/cli/src/commands/vanity.rs b/client/cli/src/commands/vanity.rs index 2204336..8e2ad01 100755 --- a/client/cli/src/commands/vanity.rs +++ b/client/cli/src/commands/vanity.rs @@ -2,213 +2,218 @@ use clap::Parser; use rand::{rngs::OsRng, RngCore}; -use sp_core::crypto::{ - unwrap_or_default_ss58_version, Ss58AddressFormat, Ss58Codec, -}; +use sc_cli::{utils::format_seed, with_crypto_scheme, CryptoSchemeFlag, Error, OutputTypeFlag}; +use sp_core::crypto::{unwrap_or_default_ss58_version, Ss58AddressFormat, Ss58Codec}; use sp_runtime::traits::IdentifyAccount; -use sc_cli::{ - with_crypto_scheme, Error, OutputTypeFlag, CryptoSchemeFlag, - utils::format_seed, -}; use crate::commands::utils::print_from_uri; use crate::params::NetworkSchemeFlag; /// The `vanity` command #[derive(Debug, Clone, Parser)] -#[command(name = "vanity", about = "Generate a seed that provides a vanity address")] +#[command( + name = "vanity", + about = "Generate a seed that provides a vanity address" +)] pub struct VanityCmd { - /// Desired pattern - #[arg(long, value_parser = assert_non_empty_string)] - pattern: String, + /// Desired pattern + #[arg(long, value_parser = assert_non_empty_string)] + pattern: String, - #[allow(missing_docs)] - #[clap(flatten)] - network_scheme: NetworkSchemeFlag, + #[allow(missing_docs)] + #[clap(flatten)] + network_scheme: NetworkSchemeFlag, - #[allow(missing_docs)] - #[clap(flatten)] - output_scheme: OutputTypeFlag, + #[allow(missing_docs)] + #[clap(flatten)] + output_scheme: OutputTypeFlag, - #[allow(missing_docs)] - #[clap(flatten)] - crypto_scheme: CryptoSchemeFlag, + #[allow(missing_docs)] + #[clap(flatten)] + crypto_scheme: CryptoSchemeFlag, } impl VanityCmd { - /// Run the command - pub fn run(&self) -> Result<(), Error> { - let formated_seed = with_crypto_scheme!( - self.crypto_scheme.scheme, - generate_key( - &self.pattern, - unwrap_or_default_ss58_version(self.network_scheme.network) - ), - )?; + /// Run the command + pub fn run(&self) -> Result<(), Error> { + let formated_seed = with_crypto_scheme!( + self.crypto_scheme.scheme, + generate_key( + &self.pattern, + unwrap_or_default_ss58_version(self.network_scheme.network) + ), + )?; - with_crypto_scheme!( - self.crypto_scheme.scheme, - print_from_uri( - &formated_seed, - None, - self.network_scheme.network, - self.output_scheme.output_type, - ), - ); - Ok(()) - } + with_crypto_scheme!( + self.crypto_scheme.scheme, + print_from_uri( + &formated_seed, + None, + self.network_scheme.network, + self.output_scheme.output_type, + ), + ); + Ok(()) + } } /// genertae a key based on given pattern fn generate_key( - desired: &str, - network_override: Ss58AddressFormat, + desired: &str, + network_override: Ss58AddressFormat, ) -> Result where - Pair: sp_core::Pair, - Pair::Public: IdentifyAccount, - ::AccountId: Ss58Codec, + Pair: sp_core::Pair, + Pair::Public: IdentifyAccount, + ::AccountId: Ss58Codec, { - println!("Generating key containing pattern '{}'", desired); + 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; + 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()); - } + 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_with_version(network_override); + let p = Pair::from_seed(&seed); + let ss58 = p + .public() + .into_account() + .to_ss58check_with_version(network_override); println!("{:?}", ss58); - let score = calculate_score(desired, &ss58); - if score > best || desired.len() < 2 { - best = score; - if best >= top { - println!("best: {} == top: {}", best, top); - return Ok(format_seed::(seed.clone())) - } - } - done += 1; + let score = calculate_score(desired, &ss58); + if score > best || desired.len() < 2 { + best = score; + if best >= top { + println!("best: {} == top: {}", best, top); + return Ok(format_seed::(seed.clone())); + } + } + done += 1; - if done % good_waypoint(done) == 0 { - println!("{} keys searched; best is {}/{} complete", done, best, top); - } - } + 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, - 1_000_001..=10_000_000 => 1_000_000, - 10_000_001..=100_000_000 => 10_000_000, - 100_000_001.. => 100_000_000, - } + match done { + 0..=1_000_000 => 100_000, + 1_000_001..=10_000_000 => 1_000_000, + 10_000_001..=100_000_000 => 10_000_000, + 100_000_001.. => 100_000_000, + } } fn next_seed(seed: &mut [u8]) { - for s in seed { - match s { - 255 => { - *s = 0; - }, - _ => { - *s += 1; - break - }, - } - } + for s in seed { + match s { + 255 => { + *s = 0; + } + _ => { + *s += 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 + 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 { - if pattern.is_empty() { - Err("Pattern must not be empty") - } else { - Ok(pattern.to_string()) - } + 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::{default_ss58_version, Ss58AddressFormatRegistry, Ss58Codec}, - sr25519, Pair, - }; + use super::*; + use sp_core::{ + crypto::{default_ss58_version, Ss58AddressFormatRegistry, Ss58Codec}, + sr25519, Pair, + }; - #[test] - fn vanity() { - let vanity = VanityCmd::parse_from(&["vanity", "--pattern", "j"]); - assert!(vanity.run().is_ok()); - } + #[test] + fn vanity() { + let vanity = VanityCmd::parse_from(&["vanity", "--pattern", "j"]); + assert!(vanity.run().is_ok()); + } - #[test] - fn test_generation_with_single_char() { - let seed = generate_key::("ab", default_ss58_version()).unwrap(); - assert!(sr25519::Pair::from_seed_slice(&array_bytes::hex2bytes_unchecked(&seed)) - .unwrap() - .public() - .to_ss58check() - .contains("ab")); - } + #[test] + fn test_generation_with_single_char() { + let seed = generate_key::("ab", default_ss58_version()).unwrap(); + assert!( + sr25519::Pair::from_seed_slice(&array_bytes::hex2bytes_unchecked(&seed)) + .unwrap() + .public() + .to_ss58check() + .contains("ab") + ); + } - #[test] - fn generate_key_respects_network_override() { - let seed = - generate_key::("ab", Ss58AddressFormatRegistry::PolkadotAccount.into()) - .unwrap(); - assert!(sr25519::Pair::from_seed_slice(&array_bytes::hex2bytes_unchecked(&seed)) - .unwrap() - .public() - .to_ss58check_with_version(Ss58AddressFormatRegistry::PolkadotAccount.into()) - .contains("ab")); - } + #[test] + fn generate_key_respects_network_override() { + let seed = + generate_key::("ab", Ss58AddressFormatRegistry::PolkadotAccount.into()) + .unwrap(); + assert!( + sr25519::Pair::from_seed_slice(&array_bytes::hex2bytes_unchecked(&seed)) + .unwrap() + .public() + .to_ss58check_with_version(Ss58AddressFormatRegistry::PolkadotAccount.into()) + .contains("ab") + ); + } - #[test] - fn test_score_1_char_100() { - let score = calculate_score("j", "sYrjWiZkEP1PQe2kKEZiC1Bi9L8yFSsB5RPEkzEPd5NThsB5H"); - assert_eq!(score, 96); - } + #[test] + fn test_score_1_char_100() { + let score = calculate_score("j", "sYrjWiZkEP1PQe2kKEZiC1Bi9L8yFSsB5RPEkzEPd5NThsB5H"); + assert_eq!(score, 96); + } - #[test] - fn test_score_100() { - let score = calculate_score("ghst", "sYsghstuhYd9p6unUC3kPxjD2gv2zRCztYQaEDCMJpYrPTqTG"); - assert_eq!(score, 246); - } + #[test] + fn test_score_100() { + let score = calculate_score("ghst", "sYsghstuhYd9p6unUC3kPxjD2gv2zRCztYQaEDCMJpYrPTqTG"); + assert_eq!(score, 246); + } - #[test] - fn test_score_50_2() { - // 50% for the position + 50% for the size - assert_eq!( - calculate_score("ghst", "sYsghXXuhYd9p6unUC3kPxjD2gv2zRCztYQaEDCMJpYrPTqTG"), - 146 - ); - } + #[test] + fn test_score_50_2() { + // 50% for the position + 50% for the size + assert_eq!( + calculate_score("ghst", "sYsghXXuhYd9p6unUC3kPxjD2gv2zRCztYQaEDCMJpYrPTqTG"), + 146 + ); + } - #[test] - fn test_score_0() { - assert_eq!( - calculate_score("ghst", "sYrjWiZkEP1PQe2kKEZiC1Bi9L8yFSsB5RPEkzEPd5NThsB5H"), - 0 - ); - } + #[test] + fn test_score_0() { + assert_eq!( + calculate_score("ghst", "sYrjWiZkEP1PQe2kKEZiC1Bi9L8yFSsB5RPEkzEPd5NThsB5H"), + 0 + ); + } } diff --git a/client/cli/src/lib.rs b/client/cli/src/lib.rs index 02258a0..23ff2fd 100644 --- a/client/cli/src/lib.rs +++ b/client/cli/src/lib.rs @@ -1,5 +1,5 @@ -pub mod params; pub mod commands; +pub mod params; pub use commands::KeySubcommand; pub use commands::VanityCmd; diff --git a/client/cli/src/params/mod.rs b/client/cli/src/params/mod.rs index 86e2e27..f5f47b7 100644 --- a/client/cli/src/params/mod.rs +++ b/client/cli/src/params/mod.rs @@ -21,19 +21,25 @@ pub struct InnerSs58AddressFormat(Ss58AddressFormat); impl InnerSs58AddressFormat { #[inline] pub fn custom(prefix: u16) -> Self { - Self { 0: Ss58AddressFormat::custom(prefix) } + Self { + 0: Ss58AddressFormat::custom(prefix), + } } } impl std::fmt::Display for InnerSs58AddressFormat { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "{} network", ALL_POSSIBLE_IDS - .binary_search(&u16::from(self.0)) - .expect("always be found")) + write!( + f, + "{} network", + ALL_POSSIBLE_IDS + .binary_search(&u16::from(self.0)) + .expect("always be found") + ) } } -impl<'a>TryFrom<&'a str> for InnerSs58AddressFormat { +impl<'a> TryFrom<&'a str> for InnerSs58AddressFormat { type Error = ParseError; fn try_from(x: &'a str) -> Result { @@ -44,13 +50,13 @@ impl<'a>TryFrom<&'a str> for InnerSs58AddressFormat { } } -pub fn parse_s58_prefix_address_format(x: &str,) -> Result { +pub fn parse_s58_prefix_address_format(x: &str) -> Result { match InnerSs58AddressFormat::try_from(x) { Ok(format_registry) => Ok(format_registry.0.into()), Err(_) => Err(format!( "Unable to parse variant. Known variants: {:?}", &ALL_POSSIBLE_NAMES - )) + )), } }