Browse Source

Add nohd support

taddr
Aditya Kulkarni 5 years ago
parent
commit
20b519e7bc
  1. 8
      cli/src/main.rs
  2. 91
      lib/src/paper.rs

8
cli/src/main.rs

@ -23,6 +23,10 @@ fn main() {
.value_name("FORMAT")
.possible_values(&["pdf", "json"])
.default_value("json"))
.arg(Arg::with_name("nohd")
.short("n")
.long("nohd")
.help("Don't reuse HD keys. Normally, zecpaperwallet will use the same HD key to derive multiple addresses. This flag will use a new seed for each address"))
.arg(Arg::with_name("output")
.short("o")
.long("output")
@ -42,6 +46,8 @@ fn main() {
let testnet: bool = matches.is_present("testnet");
let nohd: bool = matches.is_present("nohd");
let filename = matches.value_of("output");
let format = matches.value_of("format").unwrap();
@ -55,7 +61,7 @@ fn main() {
print!("Generating {} Sapling addresses.........", num_addresses);
io::stdout().flush().ok();
let addresses = generate_wallet(testnet, num_addresses);
let addresses = generate_wallet(testnet, nohd, num_addresses);
println!("[OK]");
// If the default format is present, write to the console if the filename is absent

91
lib/src/paper.rs

@ -7,12 +7,25 @@ use json::{array, object};
/**
* Generate a series of `count` addresses and private keys.
*/
pub fn generate_wallet(testnet: bool, count: u32) -> String {
let mut rng = ChaChaRng::from_entropy();
let mut seed:[u8; 32] = [0; 32];
rng.fill(&mut seed);
return gen_addresses_with_seed_as_json(testnet, count, &seed);
pub fn generate_wallet(testnet: bool, nohd: bool, count: u32) -> String {
if !nohd {
// Allow HD addresses, so use only 1 seed
let mut rng = ChaChaRng::from_entropy();
let mut seed:[u8; 32] = [0; 32];
rng.fill(&mut seed);
return gen_addresses_with_seed_as_json(testnet, count, |i| (seed.to_vec(), i));
} else {
// Not using HD addresses, so derive a new seed every time
return gen_addresses_with_seed_as_json(testnet, count, |_| {
let mut rng = ChaChaRng::from_entropy();
let mut seed:[u8; 32] = [0; 32];
rng.fill(&mut seed);
return (seed.to_vec(), 0);
});
}
}
/**
@ -20,12 +33,20 @@ pub fn generate_wallet(testnet: bool, count: u32) -> String {
* index is 0..count
*
* Note that cointype is 1 for testnet and 133 for mainnet
*
* get_seed is a closure that will take the address number being derived, and return a tuple cointaining the
* seed and child number to use to derive this wallet.
*
* It is useful if we want to reuse (or not) the seed across multiple wallets.
*/
fn gen_addresses_with_seed_as_json(testnet: bool, count: u32, seed: &[u8]) -> String {
fn gen_addresses_with_seed_as_json<F>(testnet: bool, count: u32, get_seed: F) -> String
where F: Fn(u32) -> (Vec<u8>, u32)
{
let mut ans = array![];
for i in 0..count {
let (addr, pk, path) = get_address(testnet, &seed, i);
let (seed, child) = get_seed(i);
let (addr, pk, path) = get_address(testnet, &seed, child);
ans.push(object!{
"num" => i,
"address" => addr,
@ -92,7 +113,7 @@ mod tests {
use std::collections::HashSet;
// Testnet wallet
let w = generate_wallet(true, 1);
let w = generate_wallet(true, false, 1);
let j = json::parse(&w).unwrap();
assert_eq!(j.len(), 1);
assert!(j[0]["address"].as_str().unwrap().starts_with("ztestsapling"));
@ -101,7 +122,7 @@ mod tests {
// Mainnet wallet
let w = generate_wallet(false, 1);
let w = generate_wallet(false, false, 1);
let j = json::parse(&w).unwrap();
assert_eq!(j.len(), 1);
assert!(j[0]["address"].as_str().unwrap().starts_with("zs"));
@ -109,19 +130,57 @@ mod tests {
assert_eq!(j[0]["seed"]["path"].as_str().unwrap(), "m/32'/133'/0'");
// Check if all the addresses are the same
let w = generate_wallet(true, 3);
let w = generate_wallet(true, false, 3);
let j = json::parse(&w).unwrap();
assert_eq!(j.len(), 3);
let mut s = HashSet::new();
let mut set1 = HashSet::new();
let mut set2 = HashSet::new();
for i in 0..3 {
assert!(j[i]["address"].as_str().unwrap().starts_with("ztestsapling"));
assert_eq!(j[i]["seed"]["path"].as_str().unwrap(), format!("m/32'/1'/{}'", i).as_str());
s.insert(j[i]["address"].as_str().unwrap());
s.insert(j[i]["private_key"].as_str().unwrap());
set1.insert(j[i]["address"].as_str().unwrap());
set1.insert(j[i]["private_key"].as_str().unwrap());
set2.insert(j[i]["seed"]["HDSeed"].as_str().unwrap());
}
// There should be 3 + 3 distinct addresses and private keys
assert_eq!(set1.len(), 6);
// ...but only 1 seed
assert_eq!(set2.len(), 1);
}
/**
* Test nohd address generation, which does not use the same sed.
*/
#[test]
fn test_nohd() {
use crate::paper::generate_wallet;
use std::collections::HashSet;
// Check if all the addresses use a different seed
let w = generate_wallet(true, true, 3);
let j = json::parse(&w).unwrap();
assert_eq!(j.len(), 3);
let mut set1 = HashSet::new();
let mut set2 = HashSet::new();
for i in 0..3 {
assert!(j[i]["address"].as_str().unwrap().starts_with("ztestsapling"));
assert_eq!(j[i]["seed"]["path"].as_str().unwrap(), "m/32'/1'/0'"); // All of them should use the same path
set1.insert(j[i]["address"].as_str().unwrap());
set1.insert(j[i]["private_key"].as_str().unwrap());
set2.insert(j[i]["seed"]["HDSeed"].as_str().unwrap());
}
// There should be 3 + 3 distinct addresses and private keys
assert_eq!(s.len(), 6);
assert_eq!(set1.len(), 6);
// ...and 3 different seeds
assert_eq!(set2.len(), 3);
}
// Test the address derivation against the test data (see below)
@ -133,7 +192,7 @@ mod tests {
let seed = hex::decode(i["seed"].as_str().unwrap()).unwrap();
let num = i["num"].as_u32().unwrap();
let addresses = gen_addresses_with_seed_as_json(testnet, num+1, &seed[..]);
let addresses = gen_addresses_with_seed_as_json(testnet, num+1, |child| (seed.clone(), child));
let j = json::parse(&addresses).unwrap();
assert_eq!(j[num as usize]["address"], i["addr"]);

Loading…
Cancel
Save