// This file is part of Substrate.
// Copyright (C) 2018-2020 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
#![cfg_attr(feature = "bench", feature(test))]
#[cfg(feature = "bench")]
extern crate test;
use bip39::{Language, Mnemonic, MnemonicType};
use clap::{App, ArgMatches, SubCommand};
use codec::{Decode, Encode};
use hex_literal::hex;
use itertools::Itertools;
use libp2p::identity::{ed25519 as libp2p_ed25519, PublicKey};
use node_primitives::{Balance, Hash, Index, AccountId, Signature};
use node_runtime::{BalancesCall, Call, Runtime, SignedPayload, UncheckedExtrinsic, VERSION};
use serde_json::json;
use sp_core::{
crypto::{set_default_ss58_version, Ss58AddressFormat, Ss58Codec},
ed25519, sr25519, ecdsa, Pair, Public, H256, hexdisplay::HexDisplay,
};
use sp_runtime::{traits::{AccountIdConversion, IdentifyAccount, Verify}, generic::Era, ModuleId};
use std::{
convert::{TryInto, TryFrom}, io::{stdin, Read}, str::FromStr, path::PathBuf, fs, fmt,
};
mod rpc;
mod vanity;
enum OutputType {
Json,
Text,
}
impl<'a> TryFrom<&'a str> for OutputType {
type Error = ();
fn try_from(s: &'a str) -> Result {
match s {
"json" => Ok(OutputType::Json),
"text" => Ok(OutputType::Text),
_ => Err(()),
}
}
}
trait Crypto: Sized {
type Pair: Pair;
type Public: Public + Ss58Codec + AsRef<[u8]> + std::hash::Hash;
fn pair_from_suri(suri: &str, password: Option<&str>) -> Self::Pair {
Self::Pair::from_string(suri, password).expect("Invalid phrase")
}
fn ss58_from_pair(pair: &Self::Pair) -> String where
::Public: PublicT,
{
pair.public().into_runtime().into_account().to_ss58check()
}
fn public_from_pair(pair: &Self::Pair) -> Self::Public {
pair.public()
}
fn print_from_uri(
uri: &str,
password: Option<&str>,
network_override: Option,
output: OutputType,
) where
::Public: PublicT,
{
let v = network_override.unwrap_or_default();
if let Ok((pair, seed)) = Self::Pair::from_phrase(uri, password) {
let public_key = Self::public_from_pair(&pair);
match output {
OutputType::Json => {
let json = json!({
"secretPhrase": uri,
"networkId": String::from(v),
"secretSeed": format_seed::(seed),
"publicKey": format_public_key::(public_key.clone()),
"accountId": format_account_id::(public_key),
"ss58Address": Self::ss58_from_pair(&pair),
});
println!("{}", serde_json::to_string_pretty(&json).expect("Json pretty print failed"));
},
OutputType::Text => {
println!("Secret phrase `{}` is account:\n \
Network ID/version: {}\n \
Secret seed: {}\n \
Public key (hex): {}\n \
Account ID: {}\n \
SS58 Address: {}",
uri,
String::from(v),
format_seed::(seed),
format_public_key::(public_key.clone()),
format_account_id::(public_key),
Self::ss58_from_pair(&pair),
);
},
}
} else if let Ok((pair, seed)) = Self::Pair::from_string_with_seed(uri, password) {
let public_key = Self::public_from_pair(&pair);
match output {
OutputType::Json => {
let json = json!({
"secretKeyUri": uri,
"networkId": String::from(v),
"secretSeed": if let Some(seed) = seed { format_seed::(seed) } else { "n/a".into() },
"publicKey": format_public_key::(public_key.clone()),
"accountId": format_account_id::(public_key),
"ss58Address": Self::ss58_from_pair(&pair),
});
println!("{}", serde_json::to_string_pretty(&json).expect("Json pretty print failed"));
},
OutputType::Text => {
println!("Secret Key URI `{}` is account:\n \
Network ID/version: {}\n \
Secret seed: {}\n \
Public key (hex): {}\n \
Account ID: {}\n \
SS58 Address: {}",
uri,
String::from(v),
if let Some(seed) = seed { format_seed::(seed) } else { "n/a".into() },
format_public_key::(public_key.clone()),
format_account_id::(public_key),
Self::ss58_from_pair(&pair),
);
},
}
} else if let Ok((public_key, v)) =
::Public::from_string_with_version(uri)
{
let v = network_override.unwrap_or(v);
match output {
OutputType::Json => {
let json = json!({
"publicKeyUri": uri,
"networkId": String::from(v),
"publicKey": format_public_key::(public_key.clone()),
"accountId": format_account_id::(public_key.clone()),
"ss58Address": public_key.to_ss58check_with_version(v),
});
println!("{}", serde_json::to_string_pretty(&json).expect("Json pretty print failed"));
},
OutputType::Text => {
println!("Public Key URI `{}` is account:\n \
Network ID/version: {}\n \
Public key (hex): {}\n \
Account ID: {}\n \
SS58 Address: {}",
uri,
String::from(v),
format_public_key::(public_key.clone()),
format_account_id::(public_key.clone()),
public_key.to_ss58check_with_version(v),
);
},
}
} else {
eprintln!("Invalid phrase/URI given");
}
}
}
struct Ed25519;
impl Crypto for Ed25519 {
type Pair = ed25519::Pair;
type Public = ed25519::Public;
fn pair_from_suri(suri: &str, password_override: Option<&str>) -> Self::Pair {
ed25519::Pair::from_legacy_string(suri, password_override)
}
}
struct Sr25519;
impl Crypto for Sr25519 {
type Pair = sr25519::Pair;
type Public = sr25519::Public;
}
struct Ecdsa;
impl Crypto for Ecdsa {
type Pair = ecdsa::Pair;
type Public = ecdsa::Public;
}
type SignatureOf = <::Pair as Pair>::Signature;
type PublicOf = <::Pair as Pair>::Public;
type SeedOf = <::Pair as Pair>::Seed;
type AccountPublic = ::Signer;
trait SignatureT: AsRef<[u8]> + AsMut<[u8]> + Default {
/// Converts the signature into a runtime account signature, if possible. If not possible, bombs out.
fn into_runtime(self) -> Signature {
panic!("This cryptography isn't supported for this runtime.")
}
}
trait PublicT: Sized + AsRef<[u8]> + Ss58Codec {
/// Converts the public key into a runtime account public key, if possible. If not possible, bombs out.
fn into_runtime(self) -> AccountPublic {
panic!("This cryptography isn't supported for this runtime.")
}
}
impl SignatureT for sr25519::Signature { fn into_runtime(self) -> Signature { self.into() } }
impl SignatureT for ed25519::Signature { fn into_runtime(self) -> Signature { self.into() } }
impl SignatureT for ecdsa::Signature { fn into_runtime(self) -> Signature { self.into() } }
impl PublicT for sr25519::Public { fn into_runtime(self) -> AccountPublic { self.into() } }
impl PublicT for ed25519::Public { fn into_runtime(self) -> AccountPublic { self.into() } }
impl PublicT for ecdsa::Public { fn into_runtime(self) -> AccountPublic { self.into() } }
fn get_usage() -> String {
let networks = Ss58AddressFormat::all().iter().cloned().map(String::from).join("/");
let default_network = String::from(Ss58AddressFormat::default());
format!("
-e, --ed25519 'Use Ed25519/BIP39 cryptography'
-k, --secp256k1 'Use SECP256k1/ECDSA/BIP39 cryptography'
-s, --sr25519 'Use Schnorr/Ristretto x25519/BIP39 cryptography'
[network] -n, --network 'Specify a network. One of {}. Default is {}'
[password] -p, --password 'The password for the key'
--password-interactive 'You will be prompted for the password for the key.'
[output] -o, --output