Browse Source

Vanity Addresses (#3)

* Diversified addr search

* Add error handling

* Vanity addresses
duke
adityapk00 5 years ago
committed by GitHub
parent
commit
6d6a04ac27
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      cli/src/main.rs
  2. 2
      lib/Cargo.toml
  3. 141
      lib/src/paper.rs

8
cli/src/main.rs

@ -114,7 +114,13 @@ fn main() {
let prefix = matches.value_of("vanity").unwrap().to_string();
println!("Generating address starting with \"{}\"", prefix);
let addresses = generate_vanity_wallet(is_testnet, num_threads, prefix);
let addresses = match generate_vanity_wallet(is_testnet, num_threads, prefix) {
Ok(w) => w,
Err(e) => {
eprintln!("{}", e);
return;
}
};
// return
addresses

2
lib/Cargo.toml

@ -8,7 +8,7 @@ edition = "2018"
rand = "0.5"
hex = "0.3"
bech32 = "0.6"
zip32 = { git = "https://github.com/zcash/librustzcash", rev="3b6f5e3d5ede6469f6ae85357f0b03d4c1b45cfe" }
zip32 = { git = "https://github.com/adityapk00/librustzcash", branch = "forked/paperpub" }
json = "0.11.14"
qrcode = { version = "0.8", default-features = false }
printpdf = "0.2.8"

141
lib/src/paper.rs

@ -3,7 +3,6 @@ use hex;
use secp256k1;
use ripemd160::{Ripemd160, Digest};
use base58::{ToBase58};
use zip32::{ChildIndex, ExtendedSpendingKey, ExtendedFullViewingKey};
use bech32::{Bech32, u5, ToBase32};
use rand::{Rng, ChaChaRng, FromEntropy, SeedableRng};
use json::{array, object};
@ -14,6 +13,8 @@ use std::sync::mpsc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::time::{SystemTime};
use zip32::{DiversifierIndex, DiversifierKey};
use zip32::{ChildIndex, ExtendedSpendingKey, ExtendedFullViewingKey};
/// A trait for converting a [u8] to base58 encoded string.
pub trait ToBase58Check {
@ -78,25 +79,91 @@ pub fn params(is_testnet: bool) -> CoinParams {
}
}
pub fn increment(s: &mut [u8; 32]) -> Result<(), ()> {
for k in 0..32 {
s[k] = s[k].wrapping_add(1);
if s[k] != 0 {
// No overflow
return Ok(());
}
}
// Overflow
Err(())
}
// Turn the prefix into Vec<u5>, so it can be matched directly without any encoding overhead.
fn get_bech32_for_prefix(prefix: String) -> Result<Vec<u5>, String> {
// Reverse character set. Maps ASCII byte -> CHARSET index on [0,31]
const CHARSET_REV: [i8; 128] = [
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1,
-1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1,
-1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1
];
let mut ans = Vec::new();
for c in prefix.chars() {
if CHARSET_REV[c as usize] == -1 {
return Err(format!("Invalid character in prefix: '{}'", c));
}
ans.push(u5::try_from_u8(CHARSET_REV[c as usize] as u8).expect("Should be able to convert to u5"));
}
return Ok(ans);
}
/// A single thread that grinds through the Diversifiers to find the defualt key that matches the prefix
pub fn vanity_thread(is_testnet: bool, entropy: &[u8], prefix: String, tx: mpsc::Sender<String>, please_stop: Arc<AtomicBool>) {
let prefix_str : String = params(is_testnet).zaddress_prefix + "1" + &prefix;
let mut seed: [u8; 32] = [0; 32];
seed.copy_from_slice(&entropy[0..32]);
let msk = ExtendedSpendingKey::master(&entropy);
let mut i: u32 = 0;
let di = DiversifierIndex::new();
let vanity_bytes = get_bech32_for_prefix(prefix).expect("Bad char in prefix");
let mut v = vec![0; 43];
let master_spk = ExtendedSpendingKey::from_path(&ExtendedSpendingKey::master(&seed),
&[ChildIndex::Hardened(32), ChildIndex::Hardened(params(is_testnet).cointype), ChildIndex::Hardened(0)]);
let mut spkv = vec![];
master_spk.write(&mut spkv).unwrap();
let mut i: u32 = 0;
loop {
let spk: ExtendedSpendingKey = ExtendedSpendingKey::from_path(&msk, &[ ChildIndex::NonHardened(i) ]);
let (_d, addr) = spk.default_address().expect("Cannot get result");
// Address is encoded as a bech32 string
v.get_mut(..11).unwrap().copy_from_slice(&addr.diversifier.0);
addr.pk_d.write(v.get_mut(11..).unwrap()).expect("Cannot write!");
let checked_data: Vec<u5> = v.to_base32();
let encoded : String = Bech32::new(params(is_testnet).zaddress_prefix.into(), checked_data).expect("bech32 failed").to_string();
if encoded.starts_with(&prefix_str) {
if increment(&mut seed).is_err() {
return;
}
let dk = DiversifierKey::master(&seed);
let (_ndk, nd) = dk.diversifier(di).unwrap();
// test for nd
let mut isequal = true;
for i in 0..vanity_bytes.len() {
if vanity_bytes[i] != nd.0.to_base32()[i] {
isequal = false;
break;
}
}
if isequal {
let len = spkv.len();
spkv[(len-32)..len].copy_from_slice(&dk.0[0..32]);
let spk = ExtendedSpendingKey::read(&spkv[..]).unwrap();
let (_d, addr) = spk.default_address().expect("Cannot get result");
// Address is encoded as a bech32 string
let mut v = vec![0; 43];
v.get_mut(..11).unwrap().copy_from_slice(&addr.diversifier.0);
addr.pk_d.write(v.get_mut(11..).unwrap()).expect("Cannot write!");
let checked_data: Vec<u5> = v.to_base32();
let encoded : String = Bech32::new(params(is_testnet).zaddress_prefix.into(), checked_data).expect("bech32 failed").to_string();
// Private Key is encoded as bech32 string
let mut vp = Vec::new();
spk.write(&mut vp).expect("Can't write private key");
@ -111,14 +178,14 @@ pub fn vanity_thread(is_testnet: bool, entropy: &[u8], prefix: String, tx: mpsc:
tx.send(json::stringify_pretty(wallet, 2)).unwrap();
return;
}
}
i = i + 1;
if i%1000 == 0 {
if i%5000 == 0 {
if please_stop.load(Ordering::Relaxed) {
return;
}
tx.send("Processed:1000".to_string()).unwrap();
tx.send("Processed:5000".to_string()).unwrap();
}
if i == 0 { return; }
@ -126,7 +193,13 @@ pub fn vanity_thread(is_testnet: bool, entropy: &[u8], prefix: String, tx: mpsc:
}
/// Generate a vanity address with the given prefix.
pub fn generate_vanity_wallet(is_testnet: bool, num_threads: u32, prefix: String) -> String {
pub fn generate_vanity_wallet(is_testnet: bool, num_threads: u32, prefix: String) -> Result<String, String> {
// Test the prefix first
match get_bech32_for_prefix(prefix.clone()) {
Ok(_) => (),
Err(e) => return Err(format!("{}. Note that ['b', 'i', 'o', '1'] are not allowed in addresses.", e))
};
// Get 32 bytes of system entropy
let mut system_rng = ChaChaRng::from_entropy();
@ -158,7 +231,7 @@ pub fn generate_vanity_wallet(is_testnet: bool, num_threads: u32, prefix: String
loop {
let recv = rx.recv().unwrap();
if recv.starts_with(&"Processed") {
processed = processed + 1000;
processed = processed + 5000;
let timeelapsed = now.elapsed().unwrap().as_secs() + 1; // Add one second to prevent any divide by zero problems.
print!("Checking addresses at {}/sec on {} CPU threads\r", (processed / timeelapsed), num_threads);
@ -177,7 +250,7 @@ pub fn generate_vanity_wallet(is_testnet: bool, num_threads: u32, prefix: String
handle.join().unwrap();
}
return wallet;
return Ok(wallet);
}
/// Generate a series of `count` addresses and private keys.
@ -472,6 +545,32 @@ mod tests {
}
}
#[test]
fn test_vanity() {
use crate::paper::generate_vanity_wallet;
// Single thread
let td = json::parse(&generate_vanity_wallet(false, 1, "te".to_string()).unwrap()).unwrap();
assert_eq!(td.len(), 1);
assert!(td[0]["address"].as_str().unwrap().starts_with("zs1te"));
// Multi thread
let td = json::parse(&generate_vanity_wallet(false, 4, "tt".to_string()).unwrap()).unwrap();
assert_eq!(td.len(), 1);
assert!(td[0]["address"].as_str().unwrap().starts_with("zs1tt"));
// Testnet
let td = json::parse(&generate_vanity_wallet(true, 4, "ts".to_string()).unwrap()).unwrap();
assert_eq!(td.len(), 1);
assert!(td[0]["address"].as_str().unwrap().starts_with("ztestsapling1ts"));
// Test for invalid chars
generate_vanity_wallet(false, 1, "b".to_string()).expect_err("b is not allowed");
generate_vanity_wallet(false, 1, "o".to_string()).expect_err("o is not allowed");
generate_vanity_wallet(false, 1, "i".to_string()).expect_err("i is not allowed");
generate_vanity_wallet(false, 1, "1".to_string()).expect_err("1 is not allowed");
}
#[test]
fn test_taddr_testnet() {
use crate::paper::get_taddress;

Loading…
Cancel
Save