From da8e932ff53893f07c257d150b9b0fcc829808eb Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 12 Jul 2019 20:40:38 +0200 Subject: [PATCH 1/7] Introduce network IDs for SS58 --- Cargo.lock | 2 + core/primitives/Cargo.toml | 4 ++ core/primitives/src/crypto.rs | 111 +++++++++++++++++++++++++++++++--- subkey/src/cli.yml | 6 ++ subkey/src/main.rs | 19 ++++-- 5 files changed, 127 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 93cb8a6f23448..34b50ae3bd33f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4614,8 +4614,10 @@ dependencies = [ "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "hex-literal 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "impl-serde 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "primitive-types 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/core/primitives/Cargo.toml b/core/primitives/Cargo.toml index 94dac26bec96c..1d55f82fab230 100644 --- a/core/primitives/Cargo.toml +++ b/core/primitives/Cargo.toml @@ -28,6 +28,8 @@ hex = { version = "0.3", optional = true } regex = { version = "1.1", optional = true } num-traits = { version = "0.2", default-features = false } zeroize = { version = "0.9.2", default-features = false } +lazy_static = { version = "1.3", optional = true } +parking_lot = { version = "0.8", optional = true } [dev-dependencies] substrate-serializer = { path = "../serializer" } @@ -47,6 +49,8 @@ bench = false default = ["std"] std = [ "wasmi", + "lazy_static", + "parking_lot", "primitive-types/std", "primitive-types/serde", "primitive-types/byteorder", diff --git a/core/primitives/src/crypto.rs b/core/primitives/src/crypto.rs index 6aac4e08bcdc6..f46d63198ffb4 100644 --- a/core/primitives/src/crypto.rs +++ b/core/primitives/src/crypto.rs @@ -18,6 +18,10 @@ //! Cryptographic utilities. // end::description[] +#[cfg(feature = "std")] +use std::convert::{TryFrom, TryInto}; +#[cfg(feature = "std")] +use parking_lot::Mutex; #[cfg(feature = "std")] use rand::{RngCore, rngs::OsRng}; #[cfg(feature = "std")] @@ -243,12 +247,23 @@ pub enum PublicError { #[cfg(feature = "std")] pub trait Ss58Codec: Sized { /// Some if the string is a properly encoded SS58Check address. - fn from_ss58check(s: &str) -> Result; + fn from_ss58check(s: &str) -> Result { + Self::from_ss58check_with_version(s) + .and_then(|(r, v)| match v { + Ss58AddressFormat::SubstrateAccountDirect => Ok(r), + v if v == *DEFAULT_VERSION.lock() => Ok(r), + _ => Err(PublicError::UnknownVersion), + }) + } + /// Some if the string is a properly encoded SS58Check address. + fn from_ss58check_with_version(s: &str) -> Result<(Self, Ss58AddressFormat), PublicError>; /// Some if the string is a properly encoded SS58Check address, optionally with /// a derivation path following. fn from_string(s: &str) -> Result { Self::from_ss58check(s) } /// Return the ss58-check string for this key. - fn to_ss58check(&self) -> String; + fn to_ss58check_with_version(&self, version: Ss58AddressFormat) -> String; + /// Return the ss58-check string for this key. + fn to_ss58check(&self) -> String { self.to_ss58check_with_version(*DEFAULT_VERSION.lock()) } } #[cfg(feature = "std")] @@ -273,9 +288,88 @@ fn ss58hash(data: &[u8]) -> blake2_rfc::blake2b::Blake2bResult { context.finalize() } +#[cfg(feature = "std")] +lazy_static::lazy_static! { + static ref DEFAULT_VERSION: Mutex + = Mutex::new(Ss58AddressFormat::SubstrateAccountDirect); +} + +/// A known address (sub)format/network ID for SS58. +#[cfg(feature = "std")] +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum Ss58AddressFormat { + /// Any Substrate network, direct checksum, standard account (*25519). + SubstrateAccountDirect, + /// Polkadot Relay-chain, direct checksum, standard account (*25519). + PolkadotAccountDirect, + /// Kusama Relay-chain, direct checksum, standard account (*25519). + KusamaAccountDirect, +} + +#[cfg(feature = "std")] +impl From for u8 { + fn from(x: Ss58AddressFormat) -> u8 { + match x { + Ss58AddressFormat::SubstrateAccountDirect => 42, + Ss58AddressFormat::PolkadotAccountDirect => 0, + Ss58AddressFormat::KusamaAccountDirect => 2, + } + } +} + +#[cfg(feature = "std")] +impl TryFrom for Ss58AddressFormat { + type Error = (); + fn try_from(x: u8) -> Result { + match x { + 42 => Ok(Ss58AddressFormat::SubstrateAccountDirect), + 0 => Ok(Ss58AddressFormat::PolkadotAccountDirect), + 2 => Ok(Ss58AddressFormat::KusamaAccountDirect), + _ => Err(()), + } + } +} + +#[cfg(feature = "std")] +impl<'a> TryFrom<&'a str> for Ss58AddressFormat { + type Error = (); + fn try_from(x: &'a str) -> Result { + match x { + "substrate" => Ok(Ss58AddressFormat::SubstrateAccountDirect), + "polkadot" => Ok(Ss58AddressFormat::PolkadotAccountDirect), + "kusama" => Ok(Ss58AddressFormat::KusamaAccountDirect), + _ => Err(()), + } + } +} + +#[cfg(feature = "std")] +impl From for &'static str { + fn from(x: Ss58AddressFormat) -> &'static str { + match x { + Ss58AddressFormat::SubstrateAccountDirect => "substrate", + Ss58AddressFormat::PolkadotAccountDirect => "polkadot", + Ss58AddressFormat::KusamaAccountDirect => "kusama", + } + } +} + +/// Set the default "version" (actually, this is a bit of a misnomer and the version byte is +/// typically used not just to encode format/version but also network identity) that is used for +/// encoding and decoding SS58 addresses. If an unknown version is provided then it fails. +/// +/// Current known "versions" are: +/// - 0 direct (payload) checksum for 32-byte *25519 Polkadot addresses. +/// - 2 direct (payload) checksum for 32-byte *25519 Polkadot Milestone 'K' addresses. +/// - 42 direct (payload) checksum for 32-byte *25519 addresses on any Substrate-based network. +#[cfg(feature = "std")] +pub fn set_default_ss58_version(version: Ss58AddressFormat) { + *DEFAULT_VERSION.lock() = version +} + #[cfg(feature = "std")] impl + AsRef<[u8]> + Default + Derive> Ss58Codec for T { - fn from_ss58check(s: &str) -> Result { + fn from_ss58check_with_version(s: &str) -> Result<(Self, Ss58AddressFormat), PublicError> { let mut res = T::default(); let len = res.as_mut().len(); let d = s.from_base58().map_err(|_| PublicError::BadBase58)?; // failure here would be invalid encoding. @@ -283,21 +377,18 @@ impl + AsRef<[u8]> + Default + Derive> Ss58Codec for T { // Invalid length. return Err(PublicError::BadLength); } - if d[0] != 42 { - // Invalid version. - return Err(PublicError::UnknownVersion); - } + let ver = d[0].try_into().map_err(|_: ()| PublicError::UnknownVersion)?; if d[len+1..len+3] != ss58hash(&d[0..len+1]).as_bytes()[0..2] { // Invalid checksum. return Err(PublicError::InvalidChecksum); } res.as_mut().copy_from_slice(&d[1..len+1]); - Ok(res) + Ok((res, ver)) } - fn to_ss58check(&self) -> String { - let mut v = vec![42u8]; + fn to_ss58check_with_version(&self, version: Ss58AddressFormat) -> String { + let mut v = vec![version.into()]; v.extend(self.as_ref()); let r = ss58hash(&v); v.extend(&r.as_bytes()[0..2]); diff --git a/subkey/src/cli.yml b/subkey/src/cli.yml index 89190df3624f5..91cff6a3ddb3a 100644 --- a/subkey/src/cli.yml +++ b/subkey/src/cli.yml @@ -18,6 +18,12 @@ args: takes_value: true required: false help: The password for the key + - format: + short: n + long: network + takes_value: true + required: false + help: Specify a network. One of Substrate (default), Polkadot and Kusama. subcommands: - generate: about: Generate a random account diff --git a/subkey/src/main.rs b/subkey/src/main.rs index 7cff0d6414f26..e2f1d04606d26 100644 --- a/subkey/src/main.rs +++ b/subkey/src/main.rs @@ -18,15 +18,18 @@ #[cfg(feature = "bench")] extern crate test; -use std::{str::FromStr, io::{stdin, Read}}; +use std::{str::FromStr, io::{stdin, Read}, convert::TryInto}; use hex_literal::hex; use clap::load_yaml; use bip39::{Mnemonic, Language, MnemonicType}; -use substrate_primitives::{ed25519, sr25519, hexdisplay::HexDisplay, Pair, Public, crypto::Ss58Codec, blake2_256}; +use substrate_primitives::{ + ed25519, sr25519, hexdisplay::HexDisplay, Pair, Public, + crypto::{Ss58Codec, set_default_ss58_version}, blake2_256 +}; use parity_codec::{Encode, Decode, Compact}; use sr_primitives::generic::Era; use node_primitives::{Balance, Index, Hash}; -use node_runtime::{Call, UncheckedExtrinsic, BalancesCall}; +use node_runtime::{Call, UncheckedExtrinsic, CheckNonce, TakeFees, BalancesCall}; mod vanity; @@ -87,6 +90,12 @@ fn execute(matches: clap::ArgMatches) where <::Pair as Pair>::Public: Sized + AsRef<[u8]> + Ss58Codec + AsRef<<::Pair as Pair>::Public>, { let password = matches.value_of("password"); + let maybe_network = matches.value_of("network"); + if let Some(network) = maybe_network { + let v = network.try_into() + .expect("Invalid network name: must be polkadot/substrate/kusama"); + set_default_ss58_version(v); + } match matches.subcommand() { ("generate", Some(matches)) => { // create a new randomly generated mnemonic phrase @@ -161,11 +170,11 @@ fn execute(matches: clap::ArgMatches) where signer.sign(payload) }); let extrinsic = UncheckedExtrinsic::new_signed( - index, raw_payload.1, signer.public().into(), signature.into(), era, + (CheckNonce(index), TakeFees(0)), ); println!("0x{}", hex::encode(&extrinsic.encode())); } @@ -202,11 +211,11 @@ fn execute(matches: clap::ArgMatches) where ); let extrinsic = UncheckedExtrinsic::new_signed( - index, raw_payload.1, signer.public().into(), signature.into(), era, + (CheckNonce(index), TakeFees(0)), ); println!("0x{}", hex::encode(&extrinsic.encode())); From e3ff6f2dd622947352f274f5c8db76f4e123834a Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 12 Jul 2019 20:46:53 +0200 Subject: [PATCH 2/7] Fix --- subkey/src/cli.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/subkey/src/cli.yml b/subkey/src/cli.yml index 91cff6a3ddb3a..b4f99f8743fa5 100644 --- a/subkey/src/cli.yml +++ b/subkey/src/cli.yml @@ -18,12 +18,12 @@ args: takes_value: true required: false help: The password for the key - - format: + - network: short: n long: network takes_value: true required: false - help: Specify a network. One of Substrate (default), Polkadot and Kusama. + help: Specify a network. One of substrate (default), polkadot and kusama. subcommands: - generate: about: Generate a random account From 1e16de3c2d29758f232ff68efdd8db7c1b67f631 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 18 Jul 2019 17:27:04 +0800 Subject: [PATCH 3/7] Allow numeric overrides. --- core/primitives/src/crypto.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/core/primitives/src/crypto.rs b/core/primitives/src/crypto.rs index f46d63198ffb4..4a7a4fd28ef2f 100644 --- a/core/primitives/src/crypto.rs +++ b/core/primitives/src/crypto.rs @@ -304,6 +304,8 @@ pub enum Ss58AddressFormat { PolkadotAccountDirect, /// Kusama Relay-chain, direct checksum, standard account (*25519). KusamaAccountDirect, + /// Override + Override(u8), } #[cfg(feature = "std")] @@ -312,7 +314,8 @@ impl From for u8 { match x { Ss58AddressFormat::SubstrateAccountDirect => 42, Ss58AddressFormat::PolkadotAccountDirect => 0, - Ss58AddressFormat::KusamaAccountDirect => 2, + Ss58AddressFormat::KusamaAccountDirect => 4, + Ss58AddressFormat::Override(n) => n, } } } @@ -338,7 +341,7 @@ impl<'a> TryFrom<&'a str> for Ss58AddressFormat { "substrate" => Ok(Ss58AddressFormat::SubstrateAccountDirect), "polkadot" => Ok(Ss58AddressFormat::PolkadotAccountDirect), "kusama" => Ok(Ss58AddressFormat::KusamaAccountDirect), - _ => Err(()), + a => a.parse::().map(Ss58AddressFormat::Override).map_err(|_| ()), } } } @@ -350,6 +353,7 @@ impl From for &'static str { Ss58AddressFormat::SubstrateAccountDirect => "substrate", Ss58AddressFormat::PolkadotAccountDirect => "polkadot", Ss58AddressFormat::KusamaAccountDirect => "kusama", + Ss58AddressFormat::Override(_) => "(override)", } } } From 60f05b07bfec6c4bf969b303ca1e16a779ad0d7a Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 18 Jul 2019 17:27:40 +0800 Subject: [PATCH 4/7] Improve docs --- core/primitives/src/crypto.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/primitives/src/crypto.rs b/core/primitives/src/crypto.rs index 4a7a4fd28ef2f..9654a948afd74 100644 --- a/core/primitives/src/crypto.rs +++ b/core/primitives/src/crypto.rs @@ -304,7 +304,7 @@ pub enum Ss58AddressFormat { PolkadotAccountDirect, /// Kusama Relay-chain, direct checksum, standard account (*25519). KusamaAccountDirect, - /// Override + /// Override with a manually provided numeric value. Override(u8), } From a98a8eb99693a745525954f1bb3bec68b510ddc6 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 19 Jul 2019 17:35:40 +0800 Subject: [PATCH 5/7] String rather than str --- core/primitives/src/crypto.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/core/primitives/src/crypto.rs b/core/primitives/src/crypto.rs index 9654a948afd74..c3b13a87a7c78 100644 --- a/core/primitives/src/crypto.rs +++ b/core/primitives/src/crypto.rs @@ -304,8 +304,8 @@ pub enum Ss58AddressFormat { PolkadotAccountDirect, /// Kusama Relay-chain, direct checksum, standard account (*25519). KusamaAccountDirect, - /// Override with a manually provided numeric value. - Override(u8), + /// Use a manually provided numeric value. + Custom(u8), } #[cfg(feature = "std")] @@ -314,8 +314,8 @@ impl From for u8 { match x { Ss58AddressFormat::SubstrateAccountDirect => 42, Ss58AddressFormat::PolkadotAccountDirect => 0, - Ss58AddressFormat::KusamaAccountDirect => 4, - Ss58AddressFormat::Override(n) => n, + Ss58AddressFormat::KusamaAccountDirect => 2, + Ss58AddressFormat::Custom(n) => n, } } } @@ -341,19 +341,19 @@ impl<'a> TryFrom<&'a str> for Ss58AddressFormat { "substrate" => Ok(Ss58AddressFormat::SubstrateAccountDirect), "polkadot" => Ok(Ss58AddressFormat::PolkadotAccountDirect), "kusama" => Ok(Ss58AddressFormat::KusamaAccountDirect), - a => a.parse::().map(Ss58AddressFormat::Override).map_err(|_| ()), + a => a.parse::().map(Ss58AddressFormat::Custom).map_err(|_| ()), } } } #[cfg(feature = "std")] -impl From for &'static str { - fn from(x: Ss58AddressFormat) -> &'static str { +impl From for String { + fn from(x: Ss58AddressFormat) -> String { match x { - Ss58AddressFormat::SubstrateAccountDirect => "substrate", - Ss58AddressFormat::PolkadotAccountDirect => "polkadot", - Ss58AddressFormat::KusamaAccountDirect => "kusama", - Ss58AddressFormat::Override(_) => "(override)", + Ss58AddressFormat::SubstrateAccountDirect => "substrate".into(), + Ss58AddressFormat::PolkadotAccountDirect => "polkadot".into(), + Ss58AddressFormat::KusamaAccountDirect => "kusama".into(), + Ss58AddressFormat::Custom(x) => x.to_string(), } } } From 0d4dbc21c3dabca3c3ad7cc953012eb42d8cd0d3 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 19 Jul 2019 18:35:44 +0800 Subject: [PATCH 6/7] Comment out code that will become valid after other PR --- subkey/src/main.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/subkey/src/main.rs b/subkey/src/main.rs index e2f1d04606d26..10b9f52018fb8 100644 --- a/subkey/src/main.rs +++ b/subkey/src/main.rs @@ -29,7 +29,7 @@ use substrate_primitives::{ use parity_codec::{Encode, Decode, Compact}; use sr_primitives::generic::Era; use node_primitives::{Balance, Index, Hash}; -use node_runtime::{Call, UncheckedExtrinsic, CheckNonce, TakeFees, BalancesCall}; +use node_runtime::{Call, UncheckedExtrinsic, /*CheckNonce, TakeFees, */BalancesCall}; mod vanity; @@ -129,7 +129,7 @@ fn execute(matches: clap::ArgMatches) where let sig = pair.sign(&message); println!("{}", hex::encode(&sig)); } - ("transfer", Some(matches)) => { + /*("transfer", Some(matches)) => { let signer = matches.value_of("from") .expect("parameter is required; thus it can't be None; qed"); let signer = Sr25519::pair_from_suri(signer, password); @@ -156,7 +156,7 @@ fn execute(matches: clap::ArgMatches) where "elm" => hex!["10c08714a10c7da78f40a60f6f732cf0dba97acfb5e2035445b032386157d5c3"].into(), "alex" => hex!["dcd1346701ca8396496e52aa2785b1748deb6db09551b72159dcb3e08991025b"].into(), h => hex::decode(h).ok().and_then(|x| Decode::decode(&mut &x[..])) - .expect("Invalid genesis hash or unrecognised chain identifier"), + .expect("Invalid genesis hash or unrecognised chain identifier"), }; println!("Using a genesis hash of {}", HexDisplay::from(&genesis_hash.as_ref())); @@ -219,7 +219,7 @@ fn execute(matches: clap::ArgMatches) where ); println!("0x{}", hex::encode(&extrinsic.encode())); - } + }*/ ("verify", Some(matches)) => { let sig_data = matches.value_of("sig") .expect("signature parameter is required; thus it can't be None; qed"); From 3e992832232d3a6f8931eec27ed882aed5e5cdf9 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 19 Jul 2019 19:09:35 +0800 Subject: [PATCH 7/7] Fix --- core/primitives/src/crypto.rs | 37 ++++++++++++++++++++++++++++++++++- subkey/src/main.rs | 9 +++++---- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/core/primitives/src/crypto.rs b/core/primitives/src/crypto.rs index c3b13a87a7c78..3578ea9027f91 100644 --- a/core/primitives/src/crypto.rs +++ b/core/primitives/src/crypto.rs @@ -259,11 +259,24 @@ pub trait Ss58Codec: Sized { fn from_ss58check_with_version(s: &str) -> Result<(Self, Ss58AddressFormat), PublicError>; /// Some if the string is a properly encoded SS58Check address, optionally with /// a derivation path following. - fn from_string(s: &str) -> Result { Self::from_ss58check(s) } + fn from_string(s: &str) -> Result { + Self::from_string_with_version(s) + .and_then(|(r, v)| match v { + Ss58AddressFormat::SubstrateAccountDirect => Ok(r), + v if v == *DEFAULT_VERSION.lock() => Ok(r), + _ => Err(PublicError::UnknownVersion), + }) + } + /// Return the ss58-check string for this key. fn to_ss58check_with_version(&self, version: Ss58AddressFormat) -> String; /// Return the ss58-check string for this key. fn to_ss58check(&self) -> String { self.to_ss58check_with_version(*DEFAULT_VERSION.lock()) } + /// Some if the string is a properly encoded SS58Check address, optionally with + /// a derivation path following. + fn from_string_with_version(s: &str) -> Result<(Self, Ss58AddressFormat), PublicError> { + Self::from_ss58check_with_version(s) + } } #[cfg(feature = "std")] @@ -419,6 +432,28 @@ impl + AsRef<[u8]> + Default + Derive> Ss58Codec for T { .ok_or(PublicError::InvalidPath) } } + + fn from_string_with_version(s: &str) -> Result<(Self, Ss58AddressFormat), PublicError> { + let re = Regex::new(r"^(?P[\w\d]+)?(?P(//?[^/]+)*)$") + .expect("constructed from known-good static value; qed"); + let cap = re.captures(s).ok_or(PublicError::InvalidFormat)?; + let re_junction = Regex::new(r"/(/?[^/]+)") + .expect("constructed from known-good static value; qed"); + let (addr, v) = Self::from_ss58check_with_version( + cap.name("ss58") + .map(|r| r.as_str()) + .unwrap_or(DEV_ADDRESS) + )?; + if cap["path"].is_empty() { + Ok((addr, v)) + } else { + let path = re_junction.captures_iter(&cap["path"]) + .map(|f| DeriveJunction::from(&f[1])); + addr.derive(path) + .ok_or(PublicError::InvalidPath) + .map(|a| (a, v)) + } + } } /// Trait suitable for typical cryptographic PKI key public type. diff --git a/subkey/src/main.rs b/subkey/src/main.rs index 10b9f52018fb8..b0b8adaabf228 100644 --- a/subkey/src/main.rs +++ b/subkey/src/main.rs @@ -24,7 +24,7 @@ use clap::load_yaml; use bip39::{Mnemonic, Language, MnemonicType}; use substrate_primitives::{ ed25519, sr25519, hexdisplay::HexDisplay, Pair, Public, - crypto::{Ss58Codec, set_default_ss58_version}, blake2_256 + crypto::{Ss58Codec, set_default_ss58_version, Ss58AddressFormat}, blake2_256 }; use parity_codec::{Encode, Decode, Compact}; use sr_primitives::generic::Era; @@ -55,11 +55,12 @@ trait Crypto { HexDisplay::from(&Self::public_from_pair(&pair)), Self::ss58_from_pair(&pair) ); - } else if let Ok(public) = ::Public::from_string(uri) { - println!("Public Key URI `{}` is account:\n Public key (hex): 0x{}\n Address (SS58): {}", + } else if let Ok((public, v)) = ::Public::from_string_with_version(uri) { + println!("Public Key URI `{}` is account:\n Network ID/version: {}\n Public key (hex): 0x{}\n Address (SS58): {}", uri, + String::from(Ss58AddressFormat::from(v)), HexDisplay::from(&public.as_ref()), - public.to_ss58check() + public.to_ss58check_with_version(v) ); } else { println!("Invalid phrase/URI given");