extern crate clap; extern crate silentdragonpaper; mod version; use clap::{Arg, App}; use silentdragonpaper::paper::*; use silentdragonpaper::pdf; use std::io; use std::io::prelude::*; fn main() { let matches = App::new("SilentDragonPaper") .version(version::version()) .about("A command line Hush paper wallet generator") .arg(Arg::with_name("format") .short("f") .long("format") .help("What format to generate the output in: json or pdf") .takes_value(true) .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, SilentDragonPaper 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") .index(1) .help("Name of output file.")) .arg(Arg::with_name("entropy") .short("e") .long("entropy") .takes_value(true) .help("Provide additional entropy to the random number generator. Any random string, containing 32-64 characters")) .arg(Arg::with_name("vanity_prefix") .long("vanity") .help("Generate a vanity address with the given prefix. Note that ['b', 'i', 'o', '1'] are not allowed in addresses.") .takes_value(true)) .arg(Arg::with_name("threads") .long("threads") .help("Number of threads to use for the vanity address generator. Set this to the number of CPUs you have") .takes_value(true) .default_value("1")) .arg(Arg::with_name("t_addresses") .short("t") .long("taddrs") .help("Number of t-addresses to generate") .takes_value(true) .default_value("0") .validator(|i:String| match i.parse::() { Ok(_) => return Ok(()), Err(_) => return Err(format!("Number of addresses '{}' is not a number", i)) })) .arg(Arg::with_name("z_addresses") .short("z") .long("zaddrs") .help("Number of z-addresses (Sapling) to generate") .takes_value(true) .default_value("1") .validator(|i:String| match i.parse::() { Ok(_) => return Ok(()), Err(_) => return Err(format!("Number of addresses '{}' is not a number", i)) })) .get_matches(); let nohd: bool = matches.is_present("nohd"); // Get the filename and output format let filename = matches.value_of("output"); let format = matches.value_of("format").unwrap(); // Writing to PDF requires a filename if format == "pdf" && filename.is_none() { eprintln!("Need an output file name when writing to PDF"); return; } // Number of t addresses to generate let t_addresses = matches.value_of("t_addresses").unwrap().parse::().unwrap(); // Number of z addresses to generate let z_addresses = matches.value_of("z_addresses").unwrap().parse::().unwrap(); let addresses = if !matches.value_of("vanity_prefix").is_none() { if z_addresses != 1 { eprintln!("Can only generate 1 z-address in vanity mode. You specified {}", z_addresses); return; } if t_addresses != 0 { eprintln!("Can't generate vanity t-addresses yet"); return; } let num_threads = matches.value_of("threads").unwrap().parse::().unwrap(); let prefix = matches.value_of("vanity_prefix").unwrap().to_string(); println!("Generating z-address starting with \"{}\"...", prefix); let addresses = match generate_vanity_wallet(num_threads, prefix) { Ok(w) => w, Err(e) => { eprintln!("{}", e); return; } }; // return addresses } else { // Get user entropy. let mut entropy: Vec = Vec::new(); // If the user hasn't specified any, read from the stdin if matches.value_of("entropy").is_none() { // Read from stdin println!("Provide additional entropy for generating random numbers. Type in a string of random characters, press [ENTER] when done."); let mut buffer = String::new(); let stdin = io::stdin(); stdin.lock().read_line(&mut buffer).unwrap(); entropy.extend_from_slice(buffer.as_bytes()); } else { // Use provided entropy. entropy.extend(matches.value_of("entropy").unwrap().as_bytes()); } print!("Generating {} z-addresses and {} t-addresses...", z_addresses, t_addresses); io::stdout().flush().ok(); let addresses = generate_wallet(nohd, z_addresses, t_addresses, &entropy); println!("[OK]"); addresses }; // If the default format is present, write to the console if the filename is absent if format == "json" { if filename.is_none() { println!("{}", addresses); } else { std::fs::write(filename.unwrap(), addresses).expect("Couldn't write to file!"); println!("Wrote {:?} as a plaintext file", filename); } } else if format == "pdf" { // We already know the output file name was specified print!("Writing {:?} as a PDF file...", filename.unwrap()); io::stdout().flush().ok(); match pdf::save_to_pdf(&addresses, filename.unwrap()) { Ok(_) => { println!("[OK]");}, Err(e) => { eprintln!("[ERROR]"); eprintln!("{}", e); } }; } }