rustfmt ghost client cli and fix typos
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
This commit is contained in:
parent
a74e42369b
commit
767161ac9c
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ghost-client-cli"
|
name = "ghost-client-cli"
|
||||||
version = "0.1.3"
|
version = "0.1.4"
|
||||||
description = "Ghost CLI interface"
|
description = "Ghost CLI interface"
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
authors.workspace = true
|
authors.workspace = true
|
||||||
|
@ -3,13 +3,10 @@
|
|||||||
use bip39::Mnemonic;
|
use bip39::Mnemonic;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use sc_cli::{
|
use sc_cli::{with_crypto_scheme, CryptoSchemeFlag, Error, KeystoreParams, OutputTypeFlag};
|
||||||
with_crypto_scheme, KeystoreParams, OutputTypeFlag,
|
|
||||||
CryptoSchemeFlag, Error,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::params::NetworkSchemeFlag;
|
|
||||||
use crate::commands::utils::print_from_uri;
|
use crate::commands::utils::print_from_uri;
|
||||||
|
use crate::params::NetworkSchemeFlag;
|
||||||
|
|
||||||
/// The `generate` command
|
/// The `generate` command
|
||||||
#[derive(Debug, Clone, Parser)]
|
#[derive(Debug, Clone, Parser)]
|
||||||
@ -17,58 +14,58 @@ use crate::commands::utils::print_from_uri;
|
|||||||
pub struct GenerateCmd {
|
pub struct GenerateCmd {
|
||||||
/// The number of words in the phrase to generate. One of 12 (default),
|
/// The number of words in the phrase to generate. One of 12 (default),
|
||||||
/// 15, 18, 21 and 24.
|
/// 15, 18, 21 and 24.
|
||||||
#[arg(short = 'w', long, value_name = "WORDS")]
|
#[arg(short = 'w', long, value_name = "WORDS")]
|
||||||
words: Option<usize>,
|
words: Option<usize>,
|
||||||
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[clap(flatten)]
|
#[clap(flatten)]
|
||||||
pub keystore_params: KeystoreParams,
|
pub keystore_params: KeystoreParams,
|
||||||
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[clap(flatten)]
|
#[clap(flatten)]
|
||||||
pub network_scheme: NetworkSchemeFlag,
|
pub network_scheme: NetworkSchemeFlag,
|
||||||
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[clap(flatten)]
|
#[clap(flatten)]
|
||||||
pub output_scheme: OutputTypeFlag,
|
pub output_scheme: OutputTypeFlag,
|
||||||
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[clap(flatten)]
|
#[clap(flatten)]
|
||||||
pub crypto_scheme: CryptoSchemeFlag,
|
pub crypto_scheme: CryptoSchemeFlag,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GenerateCmd {
|
impl GenerateCmd {
|
||||||
/// Run the command
|
/// Run the command
|
||||||
pub fn run(&self) -> Result<(), Error> {
|
pub fn run(&self) -> Result<(), Error> {
|
||||||
let words = match self.words {
|
let words = match self.words {
|
||||||
Some(words_count) if [12, 15, 18, 21, 24].contains(&words_count) => Ok(words_count),
|
Some(words_count) if [12, 15, 18, 21, 24].contains(&words_count) => Ok(words_count),
|
||||||
Some(_) => Err(Error::Input(
|
Some(_) => Err(Error::Input(
|
||||||
"Invalid number of words given for phrase: must be 12/15/18/21/24".into(),
|
"Invalid number of words given for phrase: must be 12/15/18/21/24".into(),
|
||||||
)),
|
)),
|
||||||
None => Ok(12),
|
None => Ok(12),
|
||||||
}?;
|
}?;
|
||||||
let mnemonic = Mnemonic::generate(words)
|
let mnemonic = Mnemonic::generate(words)
|
||||||
.map_err(|e| Error::Input(format!("Mnemonic generation failed: {e}").into()))?;
|
.map_err(|e| Error::Input(format!("Mnemonic generation failed: {e}").into()))?;
|
||||||
let password = self.keystore_params.read_password()?;
|
let password = self.keystore_params.read_password()?;
|
||||||
let output = self.output_scheme.output_type;
|
let output = self.output_scheme.output_type;
|
||||||
|
|
||||||
let phrase = mnemonic.words().join(" ");
|
let phrase = mnemonic.words().join(" ");
|
||||||
|
|
||||||
with_crypto_scheme!(
|
with_crypto_scheme!(
|
||||||
self.crypto_scheme.scheme,
|
self.crypto_scheme.scheme,
|
||||||
print_from_uri(&phrase, password, self.network_scheme.network, output)
|
print_from_uri(&phrase, password, self.network_scheme.network, output)
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn generate() {
|
fn generate() {
|
||||||
let generate = GenerateCmd::parse_from(&["generate", "--password", "12345"]);
|
let generate = GenerateCmd::parse_from(&["generate", "--password", "12345"]);
|
||||||
assert!(generate.run().is_ok())
|
assert!(generate.run().is_ok())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,102 +1,101 @@
|
|||||||
//! Implementation of the `inspect` subcommand
|
//! Implementation of the `inspect` subcommand
|
||||||
|
|
||||||
|
use crate::commands::utils::{print_from_public, print_from_uri};
|
||||||
|
use crate::params::NetworkSchemeFlag;
|
||||||
use clap::Parser;
|
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 sp_core::crypto::{ExposeSecret, SecretString, SecretUri, Ss58Codec};
|
||||||
use std::str::FromStr;
|
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
|
/// The `inspect` command
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
#[command(
|
#[command(
|
||||||
name = "inspect",
|
name = "inspect",
|
||||||
about = "Gets a public key and a SS58 address from the provided Secret URI"
|
about = "Gets a public key and a SS58 address from the provided Secret URI"
|
||||||
)]
|
)]
|
||||||
pub struct InspectKeyCmd {
|
pub struct InspectKeyCmd {
|
||||||
/// A Key URI to be inspected. May be a secret seed, secret URI
|
/// 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.
|
/// (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 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
|
/// If the given value is a file, the file content will be used
|
||||||
/// as URI.
|
/// as URI.
|
||||||
///
|
///
|
||||||
/// If omitted, you will be prompted for the URI.
|
/// If omitted, you will be prompted for the URI.
|
||||||
uri: Option<String>,
|
uri: Option<String>,
|
||||||
|
|
||||||
/// Is the given `uri` a hex encoded public key?
|
/// Is the given `uri` a hex encoded public key?
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
public: bool,
|
public: bool,
|
||||||
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[clap(flatten)]
|
#[clap(flatten)]
|
||||||
pub keystore_params: KeystoreParams,
|
pub keystore_params: KeystoreParams,
|
||||||
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[clap(flatten)]
|
#[clap(flatten)]
|
||||||
pub network_scheme: NetworkSchemeFlag,
|
pub network_scheme: NetworkSchemeFlag,
|
||||||
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[clap(flatten)]
|
#[clap(flatten)]
|
||||||
pub output_scheme: OutputTypeFlag,
|
pub output_scheme: OutputTypeFlag,
|
||||||
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[clap(flatten)]
|
#[clap(flatten)]
|
||||||
pub crypto_scheme: CryptoSchemeFlag,
|
pub crypto_scheme: CryptoSchemeFlag,
|
||||||
|
|
||||||
/// Expect that `--uri` has the given public key/account-id.
|
/// 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
|
/// 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
|
/// `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
|
/// given by `--password`, it will be used to decrypt `uri` before comparing the public
|
||||||
/// key/account-id.
|
/// key/account-id.
|
||||||
///
|
///
|
||||||
/// If there is no derivation in `--uri`, the public key will be checked against the public key
|
/// If there is no derivation in `--uri`, the public key will be checked against the public key
|
||||||
/// of `--uri` directly.
|
/// of `--uri` directly.
|
||||||
#[arg(long, conflicts_with = "public")]
|
#[arg(long, conflicts_with = "public")]
|
||||||
pub expect_public: Option<String>,
|
pub expect_public: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InspectKeyCmd {
|
impl InspectKeyCmd {
|
||||||
/// Run the command
|
/// Run the command
|
||||||
pub fn run(&self) -> Result<(), Error> {
|
pub fn run(&self) -> Result<(), Error> {
|
||||||
let uri = read_uri(self.uri.as_ref())?;
|
let uri = read_uri(self.uri.as_ref())?;
|
||||||
let password = self.keystore_params.read_password()?;
|
let password = self.keystore_params.read_password()?;
|
||||||
|
|
||||||
if self.public {
|
if self.public {
|
||||||
with_crypto_scheme!(
|
with_crypto_scheme!(
|
||||||
self.crypto_scheme.scheme,
|
self.crypto_scheme.scheme,
|
||||||
print_from_public(
|
print_from_public(
|
||||||
&uri,
|
&uri,
|
||||||
self.network_scheme.network,
|
self.network_scheme.network,
|
||||||
self.output_scheme.output_type,
|
self.output_scheme.output_type,
|
||||||
)
|
)
|
||||||
)?;
|
)?;
|
||||||
} else {
|
} else {
|
||||||
if let Some(ref expect_public) = self.expect_public {
|
if let Some(ref expect_public) = self.expect_public {
|
||||||
with_crypto_scheme!(
|
with_crypto_scheme!(
|
||||||
self.crypto_scheme.scheme,
|
self.crypto_scheme.scheme,
|
||||||
expect_public_from_phrase(expect_public, &uri, password.as_ref())
|
expect_public_from_phrase(expect_public, &uri, password.as_ref())
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
with_crypto_scheme!(
|
with_crypto_scheme!(
|
||||||
self.crypto_scheme.scheme,
|
self.crypto_scheme.scheme,
|
||||||
print_from_uri(
|
print_from_uri(
|
||||||
&uri,
|
&uri,
|
||||||
password,
|
password,
|
||||||
self.network_scheme.network,
|
self.network_scheme.network,
|
||||||
self.output_scheme.output_type,
|
self.output_scheme.output_type,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks that `expect_public` is the public key of `suri`.
|
/// 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.
|
/// Returns an error if the public key does not match.
|
||||||
fn expect_public_from_phrase<Pair: sp_core::Pair>(
|
fn expect_public_from_phrase<Pair: sp_core::Pair>(
|
||||||
expect_public: &str,
|
expect_public: &str,
|
||||||
suri: &str,
|
suri: &str,
|
||||||
password: Option<&SecretString>,
|
password: Option<&SecretString>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let secret_uri = SecretUri::from_str(suri).map_err(|e| format!("{:?}", e))?;
|
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 expected_public = if let Some(public) = expect_public.strip_prefix("0x") {
|
||||||
let hex_public = array_bytes::hex2bytes(public)
|
let hex_public = array_bytes::hex2bytes(public)
|
||||||
.map_err(|_| format!("Invalid expected public key hex: `{}`", expect_public))?;
|
.map_err(|_| format!("Invalid expected public key hex: `{}`", expect_public))?;
|
||||||
Pair::Public::try_from(&hex_public)
|
Pair::Public::try_from(&hex_public)
|
||||||
.map_err(|_| format!("Invalid expected public key: `{}`", expect_public))?
|
.map_err(|_| format!("Invalid expected public key: `{}`", expect_public))?
|
||||||
} else {
|
} else {
|
||||||
Pair::Public::from_string_with_version(expect_public)
|
Pair::Public::from_string_with_version(expect_public)
|
||||||
.map_err(|_| format!("Invalid expected account id: `{}`", expect_public))?
|
.map_err(|_| format!("Invalid expected account id: `{}`", expect_public))?
|
||||||
.0
|
.0
|
||||||
};
|
};
|
||||||
|
|
||||||
let pair = Pair::from_string_with_seed(
|
let pair = Pair::from_string_with_seed(
|
||||||
secret_uri.phrase.expose_secret().as_str(),
|
secret_uri.phrase.expose_secret().as_str(),
|
||||||
password
|
password
|
||||||
.or_else(|| secret_uri.password.as_ref())
|
.or_else(|| secret_uri.password.as_ref())
|
||||||
.map(|p| p.expose_secret().as_str()),
|
.map(|p| p.expose_secret().as_str()),
|
||||||
)
|
)
|
||||||
.map_err(|_| format!("Invalid secret uri: {}", suri))?
|
.map_err(|_| format!("Invalid secret uri: {}", suri))?
|
||||||
.0;
|
.0;
|
||||||
|
|
||||||
if pair.public() == expected_public {
|
if pair.public() == expected_public {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(format!("Expected public ({}) key does not match.", expect_public).into())
|
Err(format!("Expected public ({}) key does not match.", expect_public).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use sp_core::crypto::{ByteArray, Pair};
|
use sp_core::crypto::{ByteArray, Pair};
|
||||||
use sp_runtime::traits::IdentifyAccount;
|
use sp_runtime::traits::IdentifyAccount;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn inspect() {
|
fn inspect() {
|
||||||
let words =
|
let words =
|
||||||
"remember fiber forum demise paper uniform squirrel feel access exclude casual effort";
|
"remember fiber forum demise paper uniform squirrel feel access exclude casual effort";
|
||||||
let seed = "0xad1fb77243b536b90cfe5f0d351ab1b1ac40e3890b41dc64f766ee56340cfca5";
|
let seed = "0xad1fb77243b536b90cfe5f0d351ab1b1ac40e3890b41dc64f766ee56340cfca5";
|
||||||
|
|
||||||
let inspect = InspectKeyCmd::parse_from(&["inspect-key", words, "--password", "12345"]);
|
let inspect = InspectKeyCmd::parse_from(&["inspect-key", words, "--password", "12345"]);
|
||||||
assert!(inspect.run().is_ok());
|
assert!(inspect.run().is_ok());
|
||||||
|
|
||||||
let inspect = InspectKeyCmd::parse_from(&["inspect-key", seed]);
|
let inspect = InspectKeyCmd::parse_from(&["inspect-key", seed]);
|
||||||
assert!(inspect.run().is_ok());
|
assert!(inspect.run().is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn inspect_public_key() {
|
fn inspect_public_key() {
|
||||||
let public = "0x12e76e0ae8ce41b6516cce52b3f23a08dcb4cfeed53c6ee8f5eb9f7367341069";
|
let public = "0x12e76e0ae8ce41b6516cce52b3f23a08dcb4cfeed53c6ee8f5eb9f7367341069";
|
||||||
|
|
||||||
let inspect = InspectKeyCmd::parse_from(&["inspect-key", "--public", public]);
|
let inspect = InspectKeyCmd::parse_from(&["inspect-key", "--public", public]);
|
||||||
assert!(inspect.run().is_ok());
|
assert!(inspect.run().is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn inspect_with_expected_public_key() {
|
fn inspect_with_expected_public_key() {
|
||||||
let check_cmd = |seed, expected_public, success| {
|
let check_cmd = |seed, expected_public, success| {
|
||||||
let inspect = InspectKeyCmd::parse_from(&[
|
let inspect = InspectKeyCmd::parse_from(&[
|
||||||
"inspect-key",
|
"inspect-key",
|
||||||
"--expect-public",
|
"--expect-public",
|
||||||
expected_public,
|
expected_public,
|
||||||
seed,
|
seed,
|
||||||
]);
|
]);
|
||||||
let res = inspect.run();
|
let res = inspect.run();
|
||||||
|
|
||||||
if success {
|
if success {
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
} else {
|
} else {
|
||||||
assert!(res.unwrap_err().to_string().contains(&format!(
|
assert!(res.unwrap_err().to_string().contains(&format!(
|
||||||
"Expected public ({}) key does not match.",
|
"Expected public ({}) key does not match.",
|
||||||
expected_public
|
expected_public
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let seed =
|
let seed =
|
||||||
"remember fiber forum demise paper uniform squirrel feel access exclude casual effort";
|
"remember fiber forum demise paper uniform squirrel feel access exclude casual effort";
|
||||||
let invalid_public = "0x12e76e0ae8ce41b6516cce52b3f23a08dcb4cfeed53c6ee8f5eb9f7367341069";
|
let invalid_public = "0x12e76e0ae8ce41b6516cce52b3f23a08dcb4cfeed53c6ee8f5eb9f7367341069";
|
||||||
let valid_public = sp_core::sr25519::Pair::from_string_with_seed(seed, None)
|
let valid_public = sp_core::sr25519::Pair::from_string_with_seed(seed, None)
|
||||||
.expect("Valid")
|
.expect("Valid")
|
||||||
.0
|
.0
|
||||||
.public();
|
.public();
|
||||||
let valid_public_hex = array_bytes::bytes2hex("0x", valid_public.as_slice());
|
let valid_public_hex = array_bytes::bytes2hex("0x", valid_public.as_slice());
|
||||||
let valid_accountid = format!("{}", valid_public.into_account());
|
let valid_accountid = format!("{}", valid_public.into_account());
|
||||||
|
|
||||||
// It should fail with the invalid public key
|
// It should fail with the invalid public key
|
||||||
check_cmd(seed, invalid_public, false);
|
check_cmd(seed, invalid_public, false);
|
||||||
|
|
||||||
// It should work with the valid public key & account id
|
// It should work with the valid public key & account id
|
||||||
check_cmd(seed, &valid_public_hex, true);
|
check_cmd(seed, &valid_public_hex, true);
|
||||||
check_cmd(seed, &valid_accountid, true);
|
check_cmd(seed, &valid_accountid, true);
|
||||||
|
|
||||||
let password = "test12245";
|
let password = "test12245";
|
||||||
let seed_with_password = format!("{}///{}", seed, password);
|
let seed_with_password = format!("{}///{}", seed, password);
|
||||||
let valid_public_with_password =
|
let valid_public_with_password =
|
||||||
sp_core::sr25519::Pair::from_string_with_seed(&seed_with_password, Some(password))
|
sp_core::sr25519::Pair::from_string_with_seed(&seed_with_password, Some(password))
|
||||||
.expect("Valid")
|
.expect("Valid")
|
||||||
.0
|
.0
|
||||||
.public();
|
.public();
|
||||||
let valid_public_hex_with_password =
|
let valid_public_hex_with_password =
|
||||||
array_bytes::bytes2hex("0x", valid_public_with_password.as_slice());
|
array_bytes::bytes2hex("0x", valid_public_with_password.as_slice());
|
||||||
let valid_accountid_with_password =
|
let valid_accountid_with_password =
|
||||||
format!("{}", &valid_public_with_password.into_account());
|
format!("{}", &valid_public_with_password.into_account());
|
||||||
|
|
||||||
// Only the public key that corresponds to the seed with password should be accepted.
|
// 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_public_hex, false);
|
||||||
check_cmd(&seed_with_password, &valid_accountid, 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_public_hex_with_password, true);
|
||||||
check_cmd(&seed_with_password, &valid_accountid_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 =
|
let valid_public_with_password_and_derivation =
|
||||||
sp_core::sr25519::Pair::from_string_with_seed(
|
sp_core::sr25519::Pair::from_string_with_seed(
|
||||||
&seed_with_password_and_derivation,
|
&seed_with_password_and_derivation,
|
||||||
Some(password),
|
Some(password),
|
||||||
)
|
)
|
||||||
.expect("Valid")
|
.expect("Valid")
|
||||||
.0
|
.0
|
||||||
.public();
|
.public();
|
||||||
let valid_public_hex_with_password_and_derivation =
|
let valid_public_hex_with_password_and_derivation =
|
||||||
array_bytes::bytes2hex("0x", valid_public_with_password_and_derivation.as_slice());
|
array_bytes::bytes2hex("0x", valid_public_with_password_and_derivation.as_slice());
|
||||||
|
|
||||||
// They should still be valid, because we check the base secret key.
|
// 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(
|
||||||
check_cmd(&seed_with_password_and_derivation, &valid_accountid_with_password, true);
|
&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.
|
// And these should be invalid.
|
||||||
check_cmd(&seed_with_password_and_derivation, &valid_public_hex, false);
|
check_cmd(&seed_with_password_and_derivation, &valid_public_hex, false);
|
||||||
check_cmd(&seed_with_password_and_derivation, &valid_accountid, false);
|
check_cmd(&seed_with_password_and_derivation, &valid_accountid, false);
|
||||||
|
|
||||||
// The public of the derived account should fail.
|
// The public of the derived account should fail.
|
||||||
check_cmd(
|
check_cmd(
|
||||||
&seed_with_password_and_derivation,
|
&seed_with_password_and_derivation,
|
||||||
&valid_public_hex_with_password_and_derivation,
|
&valid_public_hex_with_password_and_derivation,
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,40 +1,37 @@
|
|||||||
//! Key related CLI utilities
|
//! Key related CLI utilities
|
||||||
|
|
||||||
use super::{generate::GenerateCmd, inspect_key::InspectKeyCmd};
|
use super::{generate::GenerateCmd, inspect_key::InspectKeyCmd};
|
||||||
use sc_cli::{
|
use sc_cli::{Error, GenerateKeyCmdCommon, InsertKeyCmd, InspectNodeKeyCmd, SubstrateCli};
|
||||||
GenerateKeyCmdCommon, InsertKeyCmd, InspectNodeKeyCmd, Error,
|
|
||||||
SubstrateCli,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Key utilities for the cli.
|
/// Key utilities for the cli.
|
||||||
#[derive(Debug, clap::Subcommand)]
|
#[derive(Debug, clap::Subcommand)]
|
||||||
pub enum KeySubcommand {
|
pub enum KeySubcommand {
|
||||||
/// Generate a random node key, write it to a file or stdout and write the
|
/// Generate a random node key, write it to a file or stdout and write the
|
||||||
/// corresponding peer-id to stderr
|
/// corresponding peer-id to stderr
|
||||||
GenerateNodeKey(GenerateKeyCmdCommon),
|
GenerateNodeKey(GenerateKeyCmdCommon),
|
||||||
|
|
||||||
/// Generate a Substrate-based random account
|
/// Generate a Substrate-based random account
|
||||||
Generate(GenerateCmd),
|
Generate(GenerateCmd),
|
||||||
|
|
||||||
/// Gets a public key and a SS58 address from the provided Secret URI
|
/// Gets a public key and a SS58 address from the provided Secret URI
|
||||||
Inspect(InspectKeyCmd),
|
Inspect(InspectKeyCmd),
|
||||||
|
|
||||||
/// Load a node key from a file or stdin and print the corresponding peer-id
|
/// Load a node key from a file or stdin and print the corresponding peer-id
|
||||||
InspectNodeKey(InspectNodeKeyCmd),
|
InspectNodeKey(InspectNodeKeyCmd),
|
||||||
|
|
||||||
/// Insert a key to the keystore of a node.
|
/// Insert a key to the keystore of a node.
|
||||||
Insert(InsertKeyCmd),
|
Insert(InsertKeyCmd),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KeySubcommand {
|
impl KeySubcommand {
|
||||||
/// run the key subcommands
|
/// run the key subcommands
|
||||||
pub fn run<C: SubstrateCli>(&self, cli: &C) -> Result<(), Error> {
|
pub fn run<C: SubstrateCli>(&self, cli: &C) -> Result<(), Error> {
|
||||||
match self {
|
match self {
|
||||||
KeySubcommand::GenerateNodeKey(cmd) => cmd.run(),
|
KeySubcommand::GenerateNodeKey(cmd) => cmd.run(),
|
||||||
KeySubcommand::Generate(cmd) => cmd.run(),
|
KeySubcommand::Generate(cmd) => cmd.run(),
|
||||||
KeySubcommand::Inspect(cmd) => cmd.run(),
|
KeySubcommand::Inspect(cmd) => cmd.run(),
|
||||||
KeySubcommand::Insert(cmd) => cmd.run(cli),
|
KeySubcommand::Insert(cmd) => cmd.run(cli),
|
||||||
KeySubcommand::InspectNodeKey(cmd) => cmd.run(),
|
KeySubcommand::InspectNodeKey(cmd) => cmd.run(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
mod key;
|
|
||||||
mod generate;
|
mod generate;
|
||||||
mod vanity;
|
|
||||||
mod inspect_key;
|
mod inspect_key;
|
||||||
|
mod key;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
mod vanity;
|
||||||
|
|
||||||
pub use self::{
|
pub use self::{
|
||||||
key::KeySubcommand, vanity::VanityCmd, inspect_key::InspectKeyCmd,
|
|
||||||
generate::GenerateCmd,
|
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,
|
||||||
};
|
};
|
||||||
|
@ -1,194 +1,206 @@
|
|||||||
use serde_json::json;
|
|
||||||
use sc_cli::{
|
use sc_cli::{
|
||||||
OutputType,
|
|
||||||
utils::{PublicFor, SeedFor},
|
utils::{PublicFor, SeedFor},
|
||||||
|
OutputType,
|
||||||
};
|
};
|
||||||
use sp_runtime::{traits::IdentifyAccount, MultiSigner};
|
use serde_json::json;
|
||||||
use sp_core::{
|
use sp_core::{
|
||||||
crypto::{
|
crypto::{
|
||||||
unwrap_or_default_ss58_version,
|
unwrap_or_default_ss58_version, ExposeSecret, SecretString, Ss58AddressFormat, Ss58Codec,
|
||||||
Ss58Codec, ExposeSecret, Ss58AddressFormat, SecretString,
|
|
||||||
},
|
},
|
||||||
hexdisplay::HexDisplay,
|
hexdisplay::HexDisplay,
|
||||||
};
|
};
|
||||||
|
use sp_runtime::{traits::IdentifyAccount, MultiSigner};
|
||||||
|
|
||||||
pub fn print_from_uri<Pair>(
|
pub fn print_from_uri<Pair>(
|
||||||
uri: &str,
|
uri: &str,
|
||||||
password: Option<SecretString>,
|
password: Option<SecretString>,
|
||||||
network_override: Option<Ss58AddressFormat>,
|
network_override: Option<Ss58AddressFormat>,
|
||||||
output: OutputType,
|
output: OutputType,
|
||||||
) where
|
) where
|
||||||
Pair: sp_core::Pair,
|
Pair: sp_core::Pair,
|
||||||
Pair::Public: Into<MultiSigner>,
|
Pair::Public: Into<MultiSigner>,
|
||||||
{
|
{
|
||||||
let password = password.as_ref().map(|s| s.expose_secret().as_str());
|
let password = password.as_ref().map(|s| s.expose_secret().as_str());
|
||||||
let network_id = unwrap_or_default_ss58_name(network_override);
|
let network_id = unwrap_or_default_ss58_name(network_override);
|
||||||
|
|
||||||
if let Ok((pair, seed)) = Pair::from_phrase(uri, password) {
|
if let Ok((pair, seed)) = Pair::from_phrase(uri, password) {
|
||||||
let public_key = pair.public();
|
let public_key = pair.public();
|
||||||
let network_override = unwrap_or_default_ss58_version(network_override);
|
let network_override = unwrap_or_default_ss58_version(network_override);
|
||||||
|
|
||||||
match output {
|
match output {
|
||||||
OutputType::Json => {
|
OutputType::Json => {
|
||||||
let json = json!({
|
let json = json!({
|
||||||
"secretPhrase": uri,
|
"secretPhrase": uri,
|
||||||
"networkId": network_id,
|
"networkId": network_id,
|
||||||
"secretSeed": format_seed::<Pair>(seed),
|
"secretSeed": format_seed::<Pair>(seed),
|
||||||
"publicKey": format_public_key::<Pair>(public_key.clone()),
|
"publicKey": format_public_key::<Pair>(public_key.clone()),
|
||||||
"ss58PublicKey": public_key.to_ss58check_with_version(network_override),
|
"ss58PublicKey": public_key.to_ss58check_with_version(network_override),
|
||||||
"accountId": format_account_id::<Pair>(public_key),
|
"accountId": format_account_id::<Pair>(public_key),
|
||||||
"ss58Address": pair.public().into().into_account().to_ss58check_with_version(network_override),
|
"ss58Address": pair.public().into().into_account().to_ss58check_with_version(network_override),
|
||||||
});
|
});
|
||||||
println!(
|
println!(
|
||||||
"{}",
|
"{}",
|
||||||
serde_json::to_string_pretty(&json).expect("Json pretty print failed")
|
serde_json::to_string_pretty(&json).expect("Json pretty print failed")
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
OutputType::Text => {
|
OutputType::Text => {
|
||||||
println!(
|
println!(
|
||||||
"Secret phrase: {}\n \
|
"Secret phrase: {}\n \
|
||||||
Network ID: {}\n \
|
Network ID: {}\n \
|
||||||
Secret seed: {}\n \
|
Secret seed: {}\n \
|
||||||
Public key (hex): {}\n \
|
Public key (hex): {}\n \
|
||||||
Account ID: {}\n \
|
Account ID: {}\n \
|
||||||
Public key (SS58): {}\n \
|
Public key (SS58): {}\n \
|
||||||
SS58 Address: {}",
|
SS58 Address: {}",
|
||||||
uri,
|
uri,
|
||||||
network_id,
|
network_id,
|
||||||
format_seed::<Pair>(seed),
|
format_seed::<Pair>(seed),
|
||||||
format_public_key::<Pair>(public_key.clone()),
|
format_public_key::<Pair>(public_key.clone()),
|
||||||
format_account_id::<Pair>(public_key.clone()),
|
format_account_id::<Pair>(public_key.clone()),
|
||||||
public_key.to_ss58check_with_version(network_override),
|
public_key.to_ss58check_with_version(network_override),
|
||||||
pair.public().into().into_account().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);
|
}
|
||||||
|
} 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 {
|
match output {
|
||||||
OutputType::Json => {
|
OutputType::Json => {
|
||||||
let json = json!({
|
let json = json!({
|
||||||
"secretKeyUri": uri,
|
"secretKeyUri": uri,
|
||||||
"networkId": network_id,
|
"networkId": network_id,
|
||||||
"secretSeed": if let Some(seed) = seed { format_seed::<Pair>(seed) } else { "n/a".into() },
|
"secretSeed": if let Some(seed) = seed { format_seed::<Pair>(seed) } else { "n/a".into() },
|
||||||
"publicKey": format_public_key::<Pair>(public_key.clone()),
|
"publicKey": format_public_key::<Pair>(public_key.clone()),
|
||||||
"ss58PublicKey": public_key.to_ss58check_with_version(network_override),
|
"ss58PublicKey": public_key.to_ss58check_with_version(network_override),
|
||||||
"accountId": format_account_id::<Pair>(public_key),
|
"accountId": format_account_id::<Pair>(public_key),
|
||||||
"ss58Address": pair.public().into().into_account().to_ss58check_with_version(network_override),
|
"ss58Address": pair.public().into().into_account().to_ss58check_with_version(network_override),
|
||||||
});
|
});
|
||||||
println!(
|
println!(
|
||||||
"{}",
|
"{}",
|
||||||
serde_json::to_string_pretty(&json).expect("Json pretty print failed")
|
serde_json::to_string_pretty(&json).expect("Json pretty print failed")
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
OutputType::Text => {
|
OutputType::Text => {
|
||||||
println!(
|
println!(
|
||||||
"Secret Key URI `{}` is account:\n \
|
"Secret Key URI `{}` is account:\n \
|
||||||
Network ID: {}\n \
|
Network ID: {}\n \
|
||||||
Secret seed: {}\n \
|
Secret seed: {}\n \
|
||||||
Public key (hex): {}\n \
|
Public key (hex): {}\n \
|
||||||
Account ID: {}\n \
|
Account ID: {}\n \
|
||||||
Public key (SS58): {}\n \
|
Public key (SS58): {}\n \
|
||||||
SS58 Address: {}",
|
SS58 Address: {}",
|
||||||
uri,
|
uri,
|
||||||
network_id,
|
network_id,
|
||||||
if let Some(seed) = seed { format_seed::<Pair>(seed) } else { "n/a".into() },
|
if let Some(seed) = seed {
|
||||||
format_public_key::<Pair>(public_key.clone()),
|
format_seed::<Pair>(seed)
|
||||||
format_account_id::<Pair>(public_key.clone()),
|
} else {
|
||||||
public_key.to_ss58check_with_version(network_override),
|
"n/a".into()
|
||||||
pair.public().into().into_account().to_ss58check_with_version(network_override),
|
},
|
||||||
);
|
format_public_key::<Pair>(public_key.clone()),
|
||||||
},
|
format_account_id::<Pair>(public_key.clone()),
|
||||||
}
|
public_key.to_ss58check_with_version(network_override),
|
||||||
} else if let Ok((public_key, network)) = Pair::Public::from_string_with_version(uri) {
|
pair.public()
|
||||||
let network_override = network_override.unwrap_or(network);
|
.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 {
|
match output {
|
||||||
OutputType::Json => {
|
OutputType::Json => {
|
||||||
let json = json!({
|
let json = json!({
|
||||||
"publicKeyUri": uri,
|
"publicKeyUri": uri,
|
||||||
"networkId": String::from(network_override),
|
"networkId": String::from(network_override),
|
||||||
"publicKey": format_public_key::<Pair>(public_key.clone()),
|
"publicKey": format_public_key::<Pair>(public_key.clone()),
|
||||||
"accountId": format_account_id::<Pair>(public_key.clone()),
|
"accountId": format_account_id::<Pair>(public_key.clone()),
|
||||||
"ss58PublicKey": public_key.to_ss58check_with_version(network_override),
|
"ss58PublicKey": public_key.to_ss58check_with_version(network_override),
|
||||||
"ss58Address": public_key.to_ss58check_with_version(network_override),
|
"ss58Address": public_key.to_ss58check_with_version(network_override),
|
||||||
});
|
});
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"{}",
|
"{}",
|
||||||
serde_json::to_string_pretty(&json).expect("Json pretty print failed")
|
serde_json::to_string_pretty(&json).expect("Json pretty print failed")
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
OutputType::Text => {
|
OutputType::Text => {
|
||||||
println!(
|
println!(
|
||||||
"Public Key URI `{}` is account:\n \
|
"Public Key URI `{}` is account:\n \
|
||||||
Network ID/Version: {}\n \
|
Network ID/Version: {}\n \
|
||||||
Public key (hex): {}\n \
|
Public key (hex): {}\n \
|
||||||
Account ID: {}\n \
|
Account ID: {}\n \
|
||||||
Public key (SS58): {}\n \
|
Public key (SS58): {}\n \
|
||||||
SS58 Address: {}",
|
SS58 Address: {}",
|
||||||
uri,
|
uri,
|
||||||
String::from(network_override),
|
String::from(network_override),
|
||||||
format_public_key::<Pair>(public_key.clone()),
|
format_public_key::<Pair>(public_key.clone()),
|
||||||
format_account_id::<Pair>(public_key.clone()),
|
format_account_id::<Pair>(public_key.clone()),
|
||||||
public_key.to_ss58check_with_version(network_override),
|
public_key.to_ss58check_with_version(network_override),
|
||||||
public_key.to_ss58check_with_version(network_override),
|
public_key.to_ss58check_with_version(network_override),
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
println!("Invalid phrase/URI given");
|
println!("Invalid phrase/URI given");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Try to parse given `public` as hex encoded public key and print relevant information.
|
/// Try to parse given `public` as hex encoded public key and print relevant information.
|
||||||
pub fn print_from_public<Pair>(
|
pub fn print_from_public<Pair>(
|
||||||
public_str: &str,
|
public_str: &str,
|
||||||
network_override: Option<Ss58AddressFormat>,
|
network_override: Option<Ss58AddressFormat>,
|
||||||
output: OutputType,
|
output: OutputType,
|
||||||
) -> Result<(), sc_cli::Error>
|
) -> Result<(), sc_cli::Error>
|
||||||
where
|
where
|
||||||
Pair: sp_core::Pair,
|
Pair: sp_core::Pair,
|
||||||
Pair::Public: Into<MultiSigner>,
|
Pair::Public: Into<MultiSigner>,
|
||||||
{
|
{
|
||||||
let public = array_bytes::hex2bytes(public_str)?;
|
let public = array_bytes::hex2bytes(public_str)?;
|
||||||
|
|
||||||
let public_key = Pair::Public::try_from(&public)
|
let public_key = Pair::Public::try_from(&public)
|
||||||
.map_err(|_| "Failed to construct public key from given hex")?;
|
.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 {
|
match output {
|
||||||
OutputType::Json => {
|
OutputType::Json => {
|
||||||
let json = json!({
|
let json = json!({
|
||||||
"networkId": String::from(network_override),
|
"networkId": String::from(network_override),
|
||||||
"publicKey": format_public_key::<Pair>(public_key.clone()),
|
"publicKey": format_public_key::<Pair>(public_key.clone()),
|
||||||
"accountId": format_account_id::<Pair>(public_key.clone()),
|
"accountId": format_account_id::<Pair>(public_key.clone()),
|
||||||
"ss58PublicKey": public_key.to_ss58check_with_version(network_override),
|
"ss58PublicKey": public_key.to_ss58check_with_version(network_override),
|
||||||
"ss58Address": 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"));
|
println!(
|
||||||
},
|
"{}",
|
||||||
OutputType::Text => {
|
serde_json::to_string_pretty(&json).expect("Json pretty print failed")
|
||||||
println!(
|
);
|
||||||
"Network ID/Version: {}\n \
|
}
|
||||||
|
OutputType::Text => {
|
||||||
|
println!(
|
||||||
|
"Network ID/Version: {}\n \
|
||||||
Public key (hex): {}\n \
|
Public key (hex): {}\n \
|
||||||
Account ID: {}\n \
|
Account ID: {}\n \
|
||||||
Public key (SS58): {}\n \
|
Public key (SS58): {}\n \
|
||||||
SS58 Address: {}",
|
SS58 Address: {}",
|
||||||
String::from(network_override),
|
String::from(network_override),
|
||||||
format_public_key::<Pair>(public_key.clone()),
|
format_public_key::<Pair>(public_key.clone()),
|
||||||
format_account_id::<Pair>(public_key.clone()),
|
format_account_id::<Pair>(public_key.clone()),
|
||||||
public_key.to_ss58check_with_version(network_override),
|
public_key.to_ss58check_with_version(network_override),
|
||||||
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<Ss58AddressFormat>) -> String {
|
pub fn unwrap_or_default_ss58_name(x: Option<Ss58AddressFormat>) -> String {
|
||||||
@ -216,5 +228,8 @@ fn format_account_id<P: sp_core::Pair>(public_key: PublicFor<P>) -> String
|
|||||||
where
|
where
|
||||||
PublicFor<P>: Into<MultiSigner>,
|
PublicFor<P>: Into<MultiSigner>,
|
||||||
{
|
{
|
||||||
format!("0x{}", HexDisplay::from(&public_key.into().into_account().as_ref()))
|
format!(
|
||||||
|
"0x{}",
|
||||||
|
HexDisplay::from(&public_key.into().into_account().as_ref())
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -2,213 +2,218 @@
|
|||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use rand::{rngs::OsRng, RngCore};
|
use rand::{rngs::OsRng, RngCore};
|
||||||
use sp_core::crypto::{
|
use sc_cli::{utils::format_seed, with_crypto_scheme, CryptoSchemeFlag, Error, OutputTypeFlag};
|
||||||
unwrap_or_default_ss58_version, Ss58AddressFormat, Ss58Codec,
|
use sp_core::crypto::{unwrap_or_default_ss58_version, Ss58AddressFormat, Ss58Codec};
|
||||||
};
|
|
||||||
use sp_runtime::traits::IdentifyAccount;
|
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::commands::utils::print_from_uri;
|
||||||
use crate::params::NetworkSchemeFlag;
|
use crate::params::NetworkSchemeFlag;
|
||||||
|
|
||||||
/// The `vanity` command
|
/// The `vanity` command
|
||||||
#[derive(Debug, Clone, Parser)]
|
#[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 {
|
pub struct VanityCmd {
|
||||||
/// Desired pattern
|
/// Desired pattern
|
||||||
#[arg(long, value_parser = assert_non_empty_string)]
|
#[arg(long, value_parser = assert_non_empty_string)]
|
||||||
pattern: String,
|
pattern: String,
|
||||||
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[clap(flatten)]
|
#[clap(flatten)]
|
||||||
network_scheme: NetworkSchemeFlag,
|
network_scheme: NetworkSchemeFlag,
|
||||||
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[clap(flatten)]
|
#[clap(flatten)]
|
||||||
output_scheme: OutputTypeFlag,
|
output_scheme: OutputTypeFlag,
|
||||||
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[clap(flatten)]
|
#[clap(flatten)]
|
||||||
crypto_scheme: CryptoSchemeFlag,
|
crypto_scheme: CryptoSchemeFlag,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VanityCmd {
|
impl VanityCmd {
|
||||||
/// Run the command
|
/// Run the command
|
||||||
pub fn run(&self) -> Result<(), Error> {
|
pub fn run(&self) -> Result<(), Error> {
|
||||||
let formated_seed = with_crypto_scheme!(
|
let formated_seed = with_crypto_scheme!(
|
||||||
self.crypto_scheme.scheme,
|
self.crypto_scheme.scheme,
|
||||||
generate_key(
|
generate_key(
|
||||||
&self.pattern,
|
&self.pattern,
|
||||||
unwrap_or_default_ss58_version(self.network_scheme.network)
|
unwrap_or_default_ss58_version(self.network_scheme.network)
|
||||||
),
|
),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
with_crypto_scheme!(
|
with_crypto_scheme!(
|
||||||
self.crypto_scheme.scheme,
|
self.crypto_scheme.scheme,
|
||||||
print_from_uri(
|
print_from_uri(
|
||||||
&formated_seed,
|
&formated_seed,
|
||||||
None,
|
None,
|
||||||
self.network_scheme.network,
|
self.network_scheme.network,
|
||||||
self.output_scheme.output_type,
|
self.output_scheme.output_type,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// genertae a key based on given pattern
|
/// genertae a key based on given pattern
|
||||||
fn generate_key<Pair>(
|
fn generate_key<Pair>(
|
||||||
desired: &str,
|
desired: &str,
|
||||||
network_override: Ss58AddressFormat,
|
network_override: Ss58AddressFormat,
|
||||||
) -> Result<String, &'static str>
|
) -> Result<String, &'static str>
|
||||||
where
|
where
|
||||||
Pair: sp_core::Pair,
|
Pair: sp_core::Pair,
|
||||||
Pair::Public: IdentifyAccount,
|
Pair::Public: IdentifyAccount,
|
||||||
<Pair::Public as IdentifyAccount>::AccountId: Ss58Codec,
|
<Pair::Public as IdentifyAccount>::AccountId: Ss58Codec,
|
||||||
{
|
{
|
||||||
println!("Generating key containing pattern '{}'", desired);
|
println!("Generating key containing pattern '{}'", desired);
|
||||||
|
|
||||||
let top = 45 + (desired.len() * 48);
|
let top = 45 + (desired.len() * 48);
|
||||||
let mut best = 0;
|
let mut best = 0;
|
||||||
let mut seed = Pair::Seed::default();
|
let mut seed = Pair::Seed::default();
|
||||||
let mut done = 0;
|
let mut done = 0;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if done % 100000 == 0 {
|
if done % 100000 == 0 {
|
||||||
OsRng.fill_bytes(seed.as_mut());
|
OsRng.fill_bytes(seed.as_mut());
|
||||||
} else {
|
} else {
|
||||||
next_seed(seed.as_mut());
|
next_seed(seed.as_mut());
|
||||||
}
|
}
|
||||||
|
|
||||||
let p = Pair::from_seed(&seed);
|
let p = Pair::from_seed(&seed);
|
||||||
let ss58 = p.public().into_account().to_ss58check_with_version(network_override);
|
let ss58 = p
|
||||||
|
.public()
|
||||||
|
.into_account()
|
||||||
|
.to_ss58check_with_version(network_override);
|
||||||
println!("{:?}", ss58);
|
println!("{:?}", ss58);
|
||||||
let score = calculate_score(desired, &ss58);
|
let score = calculate_score(desired, &ss58);
|
||||||
if score > best || desired.len() < 2 {
|
if score > best || desired.len() < 2 {
|
||||||
best = score;
|
best = score;
|
||||||
if best >= top {
|
if best >= top {
|
||||||
println!("best: {} == top: {}", best, top);
|
println!("best: {} == top: {}", best, top);
|
||||||
return Ok(format_seed::<Pair>(seed.clone()))
|
return Ok(format_seed::<Pair>(seed.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
done += 1;
|
done += 1;
|
||||||
|
|
||||||
if done % good_waypoint(done) == 0 {
|
if done % good_waypoint(done) == 0 {
|
||||||
println!("{} keys searched; best is {}/{} complete", done, best, top);
|
println!("{} keys searched; best is {}/{} complete", done, best, top);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn good_waypoint(done: u64) -> u64 {
|
fn good_waypoint(done: u64) -> u64 {
|
||||||
match done {
|
match done {
|
||||||
0..=1_000_000 => 100_000,
|
0..=1_000_000 => 100_000,
|
||||||
1_000_001..=10_000_000 => 1_000_000,
|
1_000_001..=10_000_000 => 1_000_000,
|
||||||
10_000_001..=100_000_000 => 10_000_000,
|
10_000_001..=100_000_000 => 10_000_000,
|
||||||
100_000_001.. => 100_000_000,
|
100_000_001.. => 100_000_000,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_seed(seed: &mut [u8]) {
|
fn next_seed(seed: &mut [u8]) {
|
||||||
for s in seed {
|
for s in seed {
|
||||||
match s {
|
match s {
|
||||||
255 => {
|
255 => {
|
||||||
*s = 0;
|
*s = 0;
|
||||||
},
|
}
|
||||||
_ => {
|
_ => {
|
||||||
*s += 1;
|
*s += 1;
|
||||||
break
|
break;
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate the score of a key based on the desired
|
/// Calculate the score of a key based on the desired
|
||||||
/// input.
|
/// input.
|
||||||
fn calculate_score(_desired: &str, key: &str) -> usize {
|
fn calculate_score(_desired: &str, key: &str) -> usize {
|
||||||
for truncate in 0.._desired.len() {
|
for truncate in 0.._desired.len() {
|
||||||
let snip_size = _desired.len() - truncate;
|
let snip_size = _desired.len() - truncate;
|
||||||
let truncated = &_desired[0..snip_size];
|
let truncated = &_desired[0..snip_size];
|
||||||
if let Some(pos) = key.find(truncated) {
|
if let Some(pos) = key.find(truncated) {
|
||||||
return (47 - pos) + (snip_size * 48)
|
return (47 - pos) + (snip_size * 48);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// checks that `pattern` is non-empty
|
/// checks that `pattern` is non-empty
|
||||||
fn assert_non_empty_string(pattern: &str) -> Result<String, &'static str> {
|
fn assert_non_empty_string(pattern: &str) -> Result<String, &'static str> {
|
||||||
if pattern.is_empty() {
|
if pattern.is_empty() {
|
||||||
Err("Pattern must not be empty")
|
Err("Pattern must not be empty")
|
||||||
} else {
|
} else {
|
||||||
Ok(pattern.to_string())
|
Ok(pattern.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use sp_core::{
|
use sp_core::{
|
||||||
crypto::{default_ss58_version, Ss58AddressFormatRegistry, Ss58Codec},
|
crypto::{default_ss58_version, Ss58AddressFormatRegistry, Ss58Codec},
|
||||||
sr25519, Pair,
|
sr25519, Pair,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn vanity() {
|
fn vanity() {
|
||||||
let vanity = VanityCmd::parse_from(&["vanity", "--pattern", "j"]);
|
let vanity = VanityCmd::parse_from(&["vanity", "--pattern", "j"]);
|
||||||
assert!(vanity.run().is_ok());
|
assert!(vanity.run().is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_generation_with_single_char() {
|
fn test_generation_with_single_char() {
|
||||||
let seed = generate_key::<sr25519::Pair>("ab", default_ss58_version()).unwrap();
|
let seed = generate_key::<sr25519::Pair>("ab", default_ss58_version()).unwrap();
|
||||||
assert!(sr25519::Pair::from_seed_slice(&array_bytes::hex2bytes_unchecked(&seed))
|
assert!(
|
||||||
.unwrap()
|
sr25519::Pair::from_seed_slice(&array_bytes::hex2bytes_unchecked(&seed))
|
||||||
.public()
|
.unwrap()
|
||||||
.to_ss58check()
|
.public()
|
||||||
.contains("ab"));
|
.to_ss58check()
|
||||||
}
|
.contains("ab")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn generate_key_respects_network_override() {
|
fn generate_key_respects_network_override() {
|
||||||
let seed =
|
let seed =
|
||||||
generate_key::<sr25519::Pair>("ab", Ss58AddressFormatRegistry::PolkadotAccount.into())
|
generate_key::<sr25519::Pair>("ab", Ss58AddressFormatRegistry::PolkadotAccount.into())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(sr25519::Pair::from_seed_slice(&array_bytes::hex2bytes_unchecked(&seed))
|
assert!(
|
||||||
.unwrap()
|
sr25519::Pair::from_seed_slice(&array_bytes::hex2bytes_unchecked(&seed))
|
||||||
.public()
|
.unwrap()
|
||||||
.to_ss58check_with_version(Ss58AddressFormatRegistry::PolkadotAccount.into())
|
.public()
|
||||||
.contains("ab"));
|
.to_ss58check_with_version(Ss58AddressFormatRegistry::PolkadotAccount.into())
|
||||||
}
|
.contains("ab")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_score_1_char_100() {
|
fn test_score_1_char_100() {
|
||||||
let score = calculate_score("j", "sYrjWiZkEP1PQe2kKEZiC1Bi9L8yFSsB5RPEkzEPd5NThsB5H");
|
let score = calculate_score("j", "sYrjWiZkEP1PQe2kKEZiC1Bi9L8yFSsB5RPEkzEPd5NThsB5H");
|
||||||
assert_eq!(score, 96);
|
assert_eq!(score, 96);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_score_100() {
|
fn test_score_100() {
|
||||||
let score = calculate_score("ghst", "sYsghstuhYd9p6unUC3kPxjD2gv2zRCztYQaEDCMJpYrPTqTG");
|
let score = calculate_score("ghst", "sYsghstuhYd9p6unUC3kPxjD2gv2zRCztYQaEDCMJpYrPTqTG");
|
||||||
assert_eq!(score, 246);
|
assert_eq!(score, 246);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_score_50_2() {
|
fn test_score_50_2() {
|
||||||
// 50% for the position + 50% for the size
|
// 50% for the position + 50% for the size
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
calculate_score("ghst", "sYsghXXuhYd9p6unUC3kPxjD2gv2zRCztYQaEDCMJpYrPTqTG"),
|
calculate_score("ghst", "sYsghXXuhYd9p6unUC3kPxjD2gv2zRCztYQaEDCMJpYrPTqTG"),
|
||||||
146
|
146
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_score_0() {
|
fn test_score_0() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
calculate_score("ghst", "sYrjWiZkEP1PQe2kKEZiC1Bi9L8yFSsB5RPEkzEPd5NThsB5H"),
|
calculate_score("ghst", "sYrjWiZkEP1PQe2kKEZiC1Bi9L8yFSsB5RPEkzEPd5NThsB5H"),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
pub mod params;
|
|
||||||
pub mod commands;
|
pub mod commands;
|
||||||
|
pub mod params;
|
||||||
|
|
||||||
pub use commands::KeySubcommand;
|
pub use commands::KeySubcommand;
|
||||||
pub use commands::VanityCmd;
|
pub use commands::VanityCmd;
|
||||||
|
@ -21,19 +21,25 @@ pub struct InnerSs58AddressFormat(Ss58AddressFormat);
|
|||||||
impl InnerSs58AddressFormat {
|
impl InnerSs58AddressFormat {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn custom(prefix: u16) -> Self {
|
pub fn custom(prefix: u16) -> Self {
|
||||||
Self { 0: Ss58AddressFormat::custom(prefix) }
|
Self {
|
||||||
|
0: Ss58AddressFormat::custom(prefix),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for InnerSs58AddressFormat {
|
impl std::fmt::Display for InnerSs58AddressFormat {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
write!(f, "{} network", ALL_POSSIBLE_IDS
|
write!(
|
||||||
.binary_search(&u16::from(self.0))
|
f,
|
||||||
.expect("always be found"))
|
"{} 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;
|
type Error = ParseError;
|
||||||
|
|
||||||
fn try_from(x: &'a str) -> Result<Self, Self::Error> {
|
fn try_from(x: &'a str) -> Result<Self, Self::Error> {
|
||||||
@ -44,13 +50,13 @@ impl<'a>TryFrom<&'a str> for InnerSs58AddressFormat {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_s58_prefix_address_format(x: &str,) -> Result<Ss58AddressFormat, String> {
|
pub fn parse_s58_prefix_address_format(x: &str) -> Result<Ss58AddressFormat, String> {
|
||||||
match InnerSs58AddressFormat::try_from(x) {
|
match InnerSs58AddressFormat::try_from(x) {
|
||||||
Ok(format_registry) => Ok(format_registry.0.into()),
|
Ok(format_registry) => Ok(format_registry.0.into()),
|
||||||
Err(_) => Err(format!(
|
Err(_) => Err(format!(
|
||||||
"Unable to parse variant. Known variants: {:?}",
|
"Unable to parse variant. Known variants: {:?}",
|
||||||
&ALL_POSSIBLE_NAMES
|
&ALL_POSSIBLE_NAMES
|
||||||
))
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user