diff --git a/substrate/client/cli/src/commands/generate.rs b/substrate/client/cli/src/commands/generate.rs index 4664e17551c17ed48f690a08819586ef1a4a522c..86b039ce6a4c5bd8f3f15fabcb03dfb2c6f42c6d 100644 --- a/substrate/client/cli/src/commands/generate.rs +++ b/substrate/client/cli/src/commands/generate.rs @@ -70,7 +70,7 @@ impl GenerateCmd { mnemonic.phrase(), password, self.network_scheme.network.clone(), - output + output, ) ); Ok(()) diff --git a/substrate/client/cli/src/commands/inspect.rs b/substrate/client/cli/src/commands/inspect_key.rs similarity index 67% rename from substrate/client/cli/src/commands/inspect.rs rename to substrate/client/cli/src/commands/inspect_key.rs index 0c9e54d118533320492d0623fd0fbb5731c97c96..fb3a7ef4f3b4413856ac738abc639d98f10378bc 100644 --- a/substrate/client/cli/src/commands/inspect.rs +++ b/substrate/client/cli/src/commands/inspect_key.rs @@ -18,8 +18,8 @@ //! Implementation of the `inspect` subcommand use crate::{ - utils, KeystoreParams, with_crypto_scheme, NetworkSchemeFlag, - OutputTypeFlag, CryptoSchemeFlag, Error, + utils::{self, print_from_uri, print_from_public}, KeystoreParams, + with_crypto_scheme, NetworkSchemeFlag, OutputTypeFlag, CryptoSchemeFlag, Error, }; use structopt::StructOpt; /// The `inspect` command @@ -30,7 +30,9 @@ use structopt::StructOpt; )] pub struct InspectKeyCmd { /// A Key URI to be inspected. May be a secret seed, secret URI - /// (with derivation paths and password), SS58 or public 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. @@ -38,6 +40,10 @@ pub struct InspectKeyCmd { /// If omitted, you will be prompted for the URI. uri: Option<String>, + /// Is the given `uri` a hex encoded public key? + #[structopt(long)] + public: bool, + #[allow(missing_docs)] #[structopt(flatten)] pub keystore_params: KeystoreParams, @@ -61,16 +67,26 @@ impl InspectKeyCmd { let uri = utils::read_uri(self.uri.as_ref())?; let password = self.keystore_params.read_password()?; - use utils::print_from_uri; - with_crypto_scheme!( - self.crypto_scheme.scheme, - print_from_uri( - &uri, - password, - self.network_scheme.network.clone(), - self.output_scheme.output_type.clone() - ) - ); + if self.public { + with_crypto_scheme!( + self.crypto_scheme.scheme, + print_from_public( + &uri, + self.network_scheme.network.clone(), + self.output_scheme.output_type.clone(), + ) + )?; + } else { + with_crypto_scheme!( + self.crypto_scheme.scheme, + print_from_uri( + &uri, + password, + self.network_scheme.network.clone(), + self.output_scheme.output_type.clone(), + ) + ); + } Ok(()) } @@ -94,4 +110,12 @@ mod tests { let inspect = InspectKeyCmd::from_iter(&["inspect-key", seed]); assert!(inspect.run().is_ok()); } + + #[test] + fn inspect_public_key() { + let public = "0x12e76e0ae8ce41b6516cce52b3f23a08dcb4cfeed53c6ee8f5eb9f7367341069"; + + let inspect = InspectKeyCmd::from_iter(&["inspect-key", "--public", public]); + assert!(inspect.run().is_ok()); + } } diff --git a/substrate/client/cli/src/commands/key.rs b/substrate/client/cli/src/commands/key.rs index 930acd7925ac6cd2936b6126425233ca6044c1b1..e5bce08145cb8bd482b1b150b81be8e3b2a1b5e2 100644 --- a/substrate/client/cli/src/commands/key.rs +++ b/substrate/client/cli/src/commands/key.rs @@ -22,7 +22,7 @@ use structopt::StructOpt; use super::{ insert::InsertCmd, - inspect::InspectKeyCmd, + inspect_key::InspectKeyCmd, generate::GenerateCmd, inspect_node_key::InspectNodeKeyCmd, generate_node_key::GenerateNodeKeyCmd, diff --git a/substrate/client/cli/src/commands/mod.rs b/substrate/client/cli/src/commands/mod.rs index 7b740d10032381640b06cf33e4cf1ddc099a46d1..9867f61cd277fc626144d5ba8d3aedbe73032c3e 100644 --- a/substrate/client/cli/src/commands/mod.rs +++ b/substrate/client/cli/src/commands/mod.rs @@ -30,7 +30,7 @@ mod generate_node_key; mod generate; mod insert; mod inspect_node_key; -mod inspect; +mod inspect_key; mod key; pub mod utils; @@ -44,7 +44,7 @@ pub use self::{ sign::SignCmd, generate::GenerateCmd, insert::InsertCmd, - inspect::InspectKeyCmd, + inspect_key::InspectKeyCmd, generate_node_key::GenerateNodeKeyCmd, inspect_node_key::InspectNodeKeyCmd, key::KeySubcommand, diff --git a/substrate/client/cli/src/commands/utils.rs b/substrate/client/cli/src/commands/utils.rs index 8da71ec95540e43371da8ad9e19fa28aeaf26f55..38263af50cfbb9783275ce1297ebaacd7e19486a 100644 --- a/substrate/client/cli/src/commands/utils.rs +++ b/substrate/client/cli/src/commands/utils.rs @@ -17,7 +17,7 @@ // along with this program. If not, see <https://www.gnu.org/licenses/>. //! subcommand utilities -use std::{io::Read, path::PathBuf}; +use std::{io::Read, path::PathBuf, convert::TryFrom}; use sp_core::{ Pair, hexdisplay::HexDisplay, crypto::{Ss58Codec, Ss58AddressFormat}, @@ -50,13 +50,23 @@ pub fn read_uri(uri: Option<&String>) -> error::Result<String> { Ok(uri) } -/// print formatted pair from uri +/// Try to parse given `uri` and print relevant information. +/// +/// 1. Try to construct the `Pair` while using `uri` as input for [`sp_core::Pair::from_phrase`]. +/// +/// 2. Try to construct the `Pair` while using `uri` as input for [`sp_core::Pair::from_string_with_seed`]. +/// +/// 3. Try to construct the `Pair::Public` while using `uri` as input for +/// [`sp_core::Pair::Public::from_string_with_version`]. pub fn print_from_uri<Pair>( uri: &str, password: Option<SecretString>, network_override: Option<Ss58AddressFormat>, output: OutputType, -) where Pair: sp_core::Pair, Pair::Public: Into<MultiSigner> { +) where + Pair: sp_core::Pair, + Pair::Public: Into<MultiSigner>, +{ let password = password.as_ref().map(|s| s.expose_secret().as_str()); if let Ok((pair, seed)) = Pair::from_phrase(uri, password.clone()) { let public_key = pair.public(); @@ -130,28 +140,74 @@ pub fn print_from_uri<Pair>( "accountId": format_account_id::<Pair>(public_key.clone()), "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 \ - Network ID/version: {}\n \ - Public key (hex): {}\n \ - Account ID: {}\n \ - SS58 Address: {}", + Network ID/version: {}\n \ + Public key (hex): {}\n \ + Account ID: {}\n \ + SS58 Address: {}", uri, String::from(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 { println!("Invalid phrase/URI given"); } } +/// Try to parse given `public` as hex encoded public key and print relevant information. +pub fn print_from_public<Pair>( + public_str: &str, + network_override: Option<Ss58AddressFormat>, + output: OutputType, +) -> Result<(), Error> +where + Pair: sp_core::Pair, + Pair::Public: Into<MultiSigner>, +{ + let public = decode_hex(public_str)?; + + let public_key = Pair::Public::try_from(&public) + .map_err(|_| "Failed to construct public key from given hex")?; + + let network_override = network_override.unwrap_or_default(); + + match output { + OutputType::Json => { + let json = json!({ + "networkId": String::from(network_override), + "publicKey": format_public_key::<Pair>(public_key.clone()), + "accountId": format_account_id::<Pair>(public_key.clone()), + "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 \ + Public key (hex): {}\n \ + Account ID: {}\n \ + SS58 Address: {}", + String::from(network_override), + format_public_key::<Pair>(public_key.clone()), + format_account_id::<Pair>(public_key.clone()), + public_key.to_ss58check_with_version(network_override), + ); + } + } + + Ok(()) +} + /// generate a pair from suri pub fn pair_from_suri<P: Pair>(suri: &str, password: Option<SecretString>) -> Result<P, Error> { let result = if let Some(pass) = password { diff --git a/substrate/primitives/application-crypto/src/lib.rs b/substrate/primitives/application-crypto/src/lib.rs index 6b5cf8857fb5b754dd27613089e275c4f03ed50f..12e11d690541ae812226bf1c4d352c3c58b0d2e5 100644 --- a/substrate/primitives/application-crypto/src/lib.rs +++ b/substrate/primitives/application-crypto/src/lib.rs @@ -323,6 +323,14 @@ macro_rules! app_crypto_public_common { ) } } + + impl<'a> $crate::TryFrom<&'a [u8]> for Public { + type Error = (); + + fn try_from(data: &'a [u8]) -> Result<Self, Self::Error> { + <$public>::try_from(data).map(Into::into) + } + } } } diff --git a/substrate/primitives/core/src/crypto.rs b/substrate/primitives/core/src/crypto.rs index b569f4cb8ef513aab6dbdc34fa76443eee0c9651..b685c28c67fd3516db6471509dfc645e4c5e838a 100644 --- a/substrate/primitives/core/src/crypto.rs +++ b/substrate/primitives/core/src/crypto.rs @@ -583,7 +583,17 @@ impl<T: Sized + AsMut<[u8]> + AsRef<[u8]> + Default + Derive> Ss58Codec for T { /// Trait suitable for typical cryptographic PKI key public type. pub trait Public: - AsRef<[u8]> + AsMut<[u8]> + Default + Derive + CryptoType + PartialEq + Eq + Clone + Send + Sync + AsRef<[u8]> + + AsMut<[u8]> + + Default + + Derive + + CryptoType + + PartialEq + + Eq + + Clone + + Send + + Sync + + for<'a> TryFrom<&'a [u8]> { /// A new instance from the given slice. /// @@ -751,6 +761,14 @@ mod dummy { } } + impl<'a> TryFrom<&'a [u8]> for Dummy { + type Error = (); + + fn try_from(_: &'a [u8]) -> Result<Self, ()> { + Ok(Self) + } + } + impl CryptoType for Dummy { type Pair = Dummy; } @@ -1108,6 +1126,13 @@ mod tests { &mut [] } } + impl<'a> TryFrom<&'a [u8]> for TestPublic { + type Error = (); + + fn try_from(_: &'a [u8]) -> Result<Self, ()> { + Ok(Self) + } + } impl CryptoType for TestPublic { type Pair = TestPair; }