mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 11:41:04 +00:00
Make subkey support Sr25519 crypto (#1933)
* Make subkey support Sr25519 crypto. * Rebuild runtime. * Build and rejig locks. * Fix grumbles * Derivations * Introduce tests
This commit is contained in:
@@ -1,6 +1,28 @@
|
||||
name: subkey
|
||||
author: "Parity Team <admin@parity.io>"
|
||||
about: A substrate key utility
|
||||
args:
|
||||
- ed25519-legacy:
|
||||
short: o
|
||||
long: legacy
|
||||
help: Use legacy, outdated Ed25519 cryptography
|
||||
takes_value: false
|
||||
- ed25519:
|
||||
short: e
|
||||
long: ed25519
|
||||
help: Use Ed25519/BIP39 cryptography
|
||||
takes_value: false
|
||||
- sr25519:
|
||||
short: s
|
||||
long: sr25519
|
||||
help: Use Schnorr/Ristretto x25519/BIP39 cryptography
|
||||
takes_value: false
|
||||
- password:
|
||||
short: p
|
||||
long: password
|
||||
takes_value: true
|
||||
required: false
|
||||
help: The password for the key
|
||||
subcommands:
|
||||
- generate:
|
||||
about: Generate a random account
|
||||
@@ -14,7 +36,7 @@ subcommands:
|
||||
it will be right-padded with 0x20 bytes (ASCII space). If the provided seed is longer than
|
||||
32 bytes then seed will be truncated.
|
||||
- vanity:
|
||||
about: Generate vanity address
|
||||
about: Generate a seed that provides a vanity address
|
||||
args:
|
||||
- pattern:
|
||||
index: 1
|
||||
@@ -25,3 +47,9 @@ subcommands:
|
||||
help: Number of keys to generate
|
||||
takes_value: true
|
||||
default_value: "1"
|
||||
- query:
|
||||
about: Query an account by its seed
|
||||
args:
|
||||
- seed:
|
||||
index: 1
|
||||
help: The 0x prefixed seed
|
||||
|
||||
+161
-41
@@ -18,58 +18,178 @@
|
||||
#[cfg(feature = "bench")]
|
||||
extern crate test;
|
||||
|
||||
extern crate substrate_bip39;
|
||||
extern crate rustc_hex;
|
||||
|
||||
use clap::load_yaml;
|
||||
use rand::{RngCore, rngs::OsRng};
|
||||
use substrate_primitives::{ed25519::Pair, hexdisplay::HexDisplay};
|
||||
use substrate_bip39::{mini_secret_from_entropy};
|
||||
use bip39::{Mnemonic, Language, MnemonicType};
|
||||
use substrate_primitives::{ed25519, sr25519, hexdisplay::HexDisplay};
|
||||
use schnorrkel::keys::MiniSecretKey;
|
||||
use rustc_hex::FromHex;
|
||||
|
||||
mod vanity;
|
||||
|
||||
fn print_account(seed: &[u8; 32]) {
|
||||
let pair = Pair::from_seed(seed);
|
||||
println!("Seed 0x{} is account:\n Public key (hex): 0x{}\n Address (SS58): {}",
|
||||
HexDisplay::from(seed),
|
||||
HexDisplay::from(&pair.public().0),
|
||||
pair.public().to_ss58check()
|
||||
);
|
||||
trait Crypto {
|
||||
type Seed: AsRef<[u8]> + AsMut<[u8]> + Sized + Default;
|
||||
type Pair;
|
||||
fn generate_phrase() -> String {
|
||||
Mnemonic::new(MnemonicType::Words12, Language::English).phrase().to_owned()
|
||||
}
|
||||
fn generate_seed() -> Self::Seed;
|
||||
fn seed_from_phrase(phrase: &str, password: Option<&str>) -> Self::Seed;
|
||||
fn pair_from_seed(seed: &Self::Seed) -> Self::Pair;
|
||||
fn pair_from_phrase(phrase: &str, password: Option<&str>) -> Self::Pair {
|
||||
Self::pair_from_seed(&Self::seed_from_phrase(phrase, password))
|
||||
}
|
||||
fn ss58_from_pair(pair: &Self::Pair) -> String;
|
||||
fn public_from_pair(pair: &Self::Pair) -> Vec<u8>;
|
||||
fn print_from_seed(seed: &Self::Seed) {
|
||||
let pair = Self::pair_from_seed(seed);
|
||||
println!("Seed 0x{} is account:\n Public key (hex): 0x{}\n Address (SS58): {}",
|
||||
HexDisplay::from(&seed.as_ref()),
|
||||
HexDisplay::from(&Self::public_from_pair(&pair)),
|
||||
Self::ss58_from_pair(&pair)
|
||||
);
|
||||
}
|
||||
fn print_from_phrase(phrase: &str, password: Option<&str>) {
|
||||
let seed = Self::seed_from_phrase(phrase, password);
|
||||
let pair = Self::pair_from_seed(&seed);
|
||||
println!("Phrase `{}` is account:\n Seed: 0x{}\n Public key (hex): 0x{}\n Address (SS58): {}",
|
||||
phrase,
|
||||
HexDisplay::from(&seed.as_ref()),
|
||||
HexDisplay::from(&Self::public_from_pair(&pair)),
|
||||
Self::ss58_from_pair(&pair)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
struct OriginalEd25519;
|
||||
|
||||
impl Crypto for OriginalEd25519 {
|
||||
type Seed = [u8; 32];
|
||||
type Pair = ed25519::Pair;
|
||||
|
||||
fn generate_seed() -> Self::Seed {
|
||||
let mut seed = [0u8; 32];
|
||||
OsRng::new().unwrap().fill_bytes(&mut seed[..]);
|
||||
seed
|
||||
}
|
||||
|
||||
fn seed_from_phrase(phrase: &str, password: Option<&str>) -> Self::Seed {
|
||||
if password.is_some() {
|
||||
panic!("Ed25519 original doesn't support passwords")
|
||||
}
|
||||
|
||||
let mut raw_seed = phrase.as_bytes();
|
||||
|
||||
if raw_seed.len() > 32 {
|
||||
raw_seed = &raw_seed[..32];
|
||||
println!("seed is too long and will be truncated to: {}", HexDisplay::from(&raw_seed));
|
||||
}
|
||||
|
||||
// Copy the raw_seed into a buffer that already contains ' ' 0x20.
|
||||
// This will effectively get us padding for seeds shorter than 32.
|
||||
let mut seed = [' ' as u8; 32];
|
||||
let len = raw_seed.len().min(32);
|
||||
seed[..len].copy_from_slice(&raw_seed[..len]);
|
||||
seed
|
||||
}
|
||||
|
||||
fn pair_from_seed(seed: &Self::Seed) -> Self::Pair { ed25519::Pair::from_seed(seed) }
|
||||
fn ss58_from_pair(pair: &Self::Pair) -> String { pair.public().to_ss58check() }
|
||||
fn public_from_pair(pair: &Self::Pair) -> Vec<u8> { (&pair.public().0[..]).to_owned() }
|
||||
}
|
||||
|
||||
struct Sr25519;
|
||||
|
||||
impl Crypto for Sr25519 {
|
||||
type Seed = [u8; 32];
|
||||
type Pair = sr25519::Pair;
|
||||
|
||||
fn generate_seed() -> Self::Seed {
|
||||
let mut seed = [0u8; 32];
|
||||
OsRng::new().unwrap().fill_bytes(&mut seed[..]);
|
||||
seed
|
||||
}
|
||||
|
||||
fn seed_from_phrase(phrase: &str, password: Option<&str>) -> Self::Seed {
|
||||
mini_secret_from_entropy(
|
||||
Mnemonic::from_phrase(phrase, Language::English)
|
||||
.unwrap_or_else(|_|
|
||||
panic!("Phrase is not a valid BIP-39 phrase: \n {}", phrase)
|
||||
)
|
||||
.entropy(),
|
||||
password.unwrap_or("")
|
||||
)
|
||||
.expect("32 bytes can always build a key; qed")
|
||||
.to_bytes()
|
||||
}
|
||||
|
||||
fn pair_from_phrase(phrase: &str, password: Option<&str>) -> Self::Pair {
|
||||
sr25519::Pair::from_phrase(phrase, password)
|
||||
.unwrap_or_else(||
|
||||
panic!("Phrase is not a valid BIP-39 phrase: \n {}", phrase)
|
||||
)
|
||||
}
|
||||
|
||||
fn pair_from_seed(seed: &Self::Seed) -> Self::Pair {
|
||||
MiniSecretKey::from_bytes(seed)
|
||||
.expect("32 bytes can always build a key; qed")
|
||||
.into()
|
||||
}
|
||||
fn ss58_from_pair(pair: &Self::Pair) -> String { pair.public().to_ss58check() }
|
||||
fn public_from_pair(pair: &Self::Pair) -> Vec<u8> { (&pair.public().0[..]).to_owned() }
|
||||
}
|
||||
|
||||
fn execute<C: Crypto<Seed=[u8; 32]>>(matches: clap::ArgMatches) {
|
||||
let password = matches.value_of("password");
|
||||
match matches.subcommand() {
|
||||
("generate", Some(_matches)) => {
|
||||
// create a new randomly generated mnemonic phrase
|
||||
let mnemonic = Mnemonic::new(MnemonicType::Words12, Language::English);
|
||||
C::print_from_phrase(mnemonic.phrase(), password);
|
||||
},
|
||||
("vanity", Some(matches)) => {
|
||||
let desired: String = matches.value_of("pattern").map(str::to_string).unwrap_or_default();
|
||||
let key = vanity::generate_key::<C>(&desired).expect("Key generation failed");
|
||||
C::print_from_seed(&key.seed);
|
||||
}
|
||||
("restore", Some(matches)) => {
|
||||
let phrase = matches.value_of("seed")
|
||||
.expect("seed parameter is required; thus it can't be None; qed");
|
||||
C::print_from_phrase(phrase, password);
|
||||
},
|
||||
("query", Some(matches)) => {
|
||||
let seed_data = matches.value_of("seed")
|
||||
.expect("seed parameter is required; thus it can't be None; qed");
|
||||
let seed_data = if seed_data.starts_with("0x") {
|
||||
&seed_data[2..]
|
||||
} else {
|
||||
seed_data
|
||||
};
|
||||
let seed_data: Vec<u8> = seed_data.from_hex().expect("seed is not valid hex");
|
||||
let correct_size = ::std::mem::size_of::<C::Seed>();
|
||||
if seed_data.len() != correct_size {
|
||||
panic!("Seed is incorrect size. It must be {} bytes for this cryptography", correct_size);
|
||||
}
|
||||
let mut seed = C::Seed::default();
|
||||
seed.as_mut().copy_from_slice(&seed_data);
|
||||
C::print_from_seed(&seed);
|
||||
},
|
||||
_ => print_usage(&matches),
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let yaml = load_yaml!("cli.yml");
|
||||
let matches = clap::App::from_yaml(yaml).get_matches();
|
||||
|
||||
match matches.subcommand() {
|
||||
("generate", Some(_matches)) => {
|
||||
let mut seed = [0u8; 32];
|
||||
OsRng::new().unwrap().fill_bytes(&mut seed[..]);
|
||||
print_account(&seed);
|
||||
}
|
||||
("vanity", Some(matches)) => {
|
||||
let desired: String = matches.value_of("pattern").map(str::to_string).unwrap_or_default();
|
||||
let key = vanity::generate_key(&desired).expect("Key generation failed");
|
||||
println!("Found account with score {}%", key.score);
|
||||
print_account(&key.seed);
|
||||
}
|
||||
("restore", Some(matches)) => {
|
||||
// This subcommand is probably obsolete, see
|
||||
// https://github.com/paritytech/substrate/issues/1063
|
||||
|
||||
let mut raw_seed = matches.value_of("seed")
|
||||
.map(str::as_bytes)
|
||||
.expect("seed parameter is required; thus it can't be None; qed");
|
||||
|
||||
if raw_seed.len() > 32 {
|
||||
raw_seed = &raw_seed[..32];
|
||||
println!("seed is too long and will be truncated to: {}", HexDisplay::from(&raw_seed));
|
||||
}
|
||||
|
||||
// Copy the raw_seed into a buffer that already contains ' ' 0x20.
|
||||
// This will effectively get us padding for seeds shorter than 32.
|
||||
let mut seed = [' ' as u8; 32];
|
||||
let len = raw_seed.len().min(32);
|
||||
seed[..len].copy_from_slice(&raw_seed[..len]);
|
||||
print_account(&seed);
|
||||
},
|
||||
_ => print_usage(&matches),
|
||||
if matches.is_present("ed25519original") {
|
||||
execute::<OriginalEd25519>(matches)
|
||||
} else {
|
||||
execute::<Sr25519>(matches)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use rand::{rngs::OsRng, RngCore};
|
||||
use substrate_primitives::ed25519::Pair;
|
||||
use super::Crypto;
|
||||
|
||||
fn good_waypoint(done: u64) -> u64 {
|
||||
match done {
|
||||
@@ -38,9 +38,9 @@ fn next_seed(mut seed: [u8; 32]) -> [u8; 32] {
|
||||
|
||||
/// A structure used to carry both Pair and seed.
|
||||
/// This should usually NOT been used. If unsure, use Pair.
|
||||
pub struct KeyPair {
|
||||
pub pair: Pair,
|
||||
pub seed: [u8; 32],
|
||||
pub(super) struct KeyPair<C: Crypto> {
|
||||
pub pair: C::Pair,
|
||||
pub seed: C::Seed,
|
||||
pub score: usize,
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ fn calculate_score(_desired: &str, key: &str) -> usize {
|
||||
0
|
||||
}
|
||||
|
||||
pub fn generate_key(desired: &str) -> Result<KeyPair, &str> {
|
||||
pub(super) fn generate_key<C: Crypto<Seed=[u8; 32]>>(desired: &str) -> Result<KeyPair<C>, &str> {
|
||||
if desired.is_empty() {
|
||||
return Err("Pattern must not be empty");
|
||||
}
|
||||
@@ -77,8 +77,8 @@ pub fn generate_key(desired: &str) -> Result<KeyPair, &str> {
|
||||
OsRng::new().unwrap().fill_bytes(&mut seed[..]);
|
||||
}
|
||||
|
||||
let p = Pair::from_seed(&seed);
|
||||
let ss58 = p.public().to_ss58check();
|
||||
let p = C::pair_from_seed(&seed);
|
||||
let ss58 = C::ss58_from_pair(&p);
|
||||
let score = calculate_score(&desired, &ss58);
|
||||
if score > best || desired.len() < 2 {
|
||||
best = score;
|
||||
@@ -104,12 +104,13 @@ pub fn generate_key(desired: &str) -> Result<KeyPair, &str> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use super::super::OriginalEd25519;
|
||||
#[cfg(feature = "bench")]
|
||||
use test::Bencher;
|
||||
|
||||
#[test]
|
||||
fn test_generation_with_single_char() {
|
||||
assert!(generate_key("j").unwrap().pair.public().to_ss58check().contains("j"));
|
||||
assert!(generate_key::<OriginalEd25519>("j").unwrap().pair.public().to_ss58check().contains("j"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user