Browse Source

Add seed phrases

master
Aditya Kulkarni 5 years ago
parent
commit
c08b51fde7
  1. 2
      rust-lightclient/Cargo.toml
  2. 19
      rust-lightclient/src/commands.rs
  3. 87
      rust-lightclient/src/lightclient.rs
  4. 28
      rust-lightclient/src/lightwallet.rs
  5. 15
      rust-lightclient/src/main.rs

2
rust-lightclient/Cargo.toml

@ -23,6 +23,8 @@ rustyline = "5.0.2"
byteorder = "1" byteorder = "1"
rand = "0.5.6" rand = "0.5.6"
json = "0.12.0" json = "0.12.0"
bip39 = "0.6.0-beta.1"
clap = "2.33"
[dependencies.bellman] [dependencies.bellman]
path = "../../librustzcash/bellman" path = "../../librustzcash/bellman"

19
rust-lightclient/src/commands.rs

@ -110,18 +110,23 @@ impl Command for SaveCommand {
} }
} }
struct ReadCommand {} struct SeedCommand {}
impl Command for ReadCommand { impl Command for SeedCommand {
fn help(&self) { fn help(&self) {
println!("Read wallet from disk"); println!("Show the seed phrase for the wallet");
} }
fn short_help(&self) -> String { fn short_help(&self) -> String {
"Read wallet file from disk".to_string() "Display the seed phrase".to_string()
} }
fn exec(&self, _args: &[String], lightclient: &mut LightClient) { fn exec(&self, _args: &[String], lightclient: &mut LightClient) {
lightclient.do_read(); let phrase = lightclient.do_seed_phrase();
println!("Current seed phrase. PLEASE SAVE THIS CAREFULLY AND DO NOT SHARE IT");
println!();
println!("{}", phrase);
println!();
} }
} }
@ -167,9 +172,9 @@ pub fn get_commands() -> Box<HashMap<String, Box<dyn Command>>> {
map.insert("info".to_string(), Box::new(InfoCommand{})); map.insert("info".to_string(), Box::new(InfoCommand{}));
map.insert("send".to_string(), Box::new(SendCommand{})); map.insert("send".to_string(), Box::new(SendCommand{}));
map.insert("save".to_string(), Box::new(SaveCommand{})); map.insert("save".to_string(), Box::new(SaveCommand{}));
map.insert("read".to_string(), Box::new(ReadCommand{}));
map.insert("quit".to_string(), Box::new(QuitCommand{})); map.insert("quit".to_string(), Box::new(QuitCommand{}));
map.insert("list".to_string(), Box::new(TransactionsCommand{})); map.insert("list".to_string(), Box::new(TransactionsCommand{}));
map.insert("seed".to_string(), Box::new(SeedCommand{}));
Box::new(map) Box::new(map)
} }

87
rust-lightclient/src/lightclient.rs

@ -39,24 +39,44 @@ pub struct LightClient {
} }
impl LightClient { impl LightClient {
pub fn new() -> Self { pub fn new(seed_phrase: Option<&str>) -> io::Result<Self> {
let mut w = LightClient {
wallet : Arc::new(LightWallet::new()),
sapling_output : vec![],
sapling_spend : vec![]
};
// Read Sapling Params let mut lc = if Path::new("wallet.dat").exists() {
let mut f = File::open("/home/adityapk/.zcash-params/sapling-output.params").unwrap(); // Make sure that if a wallet exists, there is no seed phrase being attempted
f.read_to_end(&mut w.sapling_output).unwrap(); if !seed_phrase.is_none() {
let mut f = File::open("/home/adityapk/.zcash-params/sapling-spend.params").unwrap(); return Err(io::Error::new(io::ErrorKind::AlreadyExists,
f.read_to_end(&mut w.sapling_spend).unwrap(); "Cannot restore from seed, because a wallet already exists"));
}
let mut file_buffer = BufReader::new(File::open("wallet.dat")?);
let wallet = LightWallet::read(&mut file_buffer)?;
LightClient {
wallet : Arc::new(wallet),
sapling_output : vec![],
sapling_spend : vec![]
}
} else {
let l = LightClient {
wallet : Arc::new(LightWallet::new(seed_phrase).unwrap()),
sapling_output : vec![],
sapling_spend : vec![]
};
w.wallet.set_initial_block(500000, l.wallet.set_initial_block(500000,
"004fada8d4dbc5e80b13522d2c6bd0116113c9b7197f0c6be69bc7a62f2824cd", "004fada8d4dbc5e80b13522d2c6bd0116113c9b7197f0c6be69bc7a62f2824cd",
"01b733e839b5f844287a6a491409a991ec70277f39a50c99163ed378d23a829a0700100001916db36dfb9a0cf26115ed050b264546c0fa23459433c31fd72f63d188202f2400011f5f4e3bd18da479f48d674dbab64454f6995b113fa21c9d8853a9e764fb3e1f01df9d2c233ca60360e3c2bb73caf5839a1be634c8b99aea22d02abda2e747d9100001970d41722c078288101acd0a75612acfb4c434f2a55aab09fb4e812accc2ba7301485150f0deac7774dcd0fe32043bde9ba2b6bbfff787ad074339af68e88ee70101601324f1421e00a43ef57f197faf385ee4cac65aab58048016ecbd94e022973701e1b17f4bd9d1b6ca1107f619ac6d27b53dd3350d5be09b08935923cbed97906c0000000000011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39"); "01b733e839b5f844287a6a491409a991ec70277f39a50c99163ed378d23a829a0700100001916db36dfb9a0cf26115ed050b264546c0fa23459433c31fd72f63d188202f2400011f5f4e3bd18da479f48d674dbab64454f6995b113fa21c9d8853a9e764fb3e1f01df9d2c233ca60360e3c2bb73caf5839a1be634c8b99aea22d02abda2e747d9100001970d41722c078288101acd0a75612acfb4c434f2a55aab09fb4e812accc2ba7301485150f0deac7774dcd0fe32043bde9ba2b6bbfff787ad074339af68e88ee70101601324f1421e00a43ef57f197faf385ee4cac65aab58048016ecbd94e022973701e1b17f4bd9d1b6ca1107f619ac6d27b53dd3350d5be09b08935923cbed97906c0000000000011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39");
return w; l
};
// Read Sapling Params
let mut f = File::open("/home/adityapk/.zcash-params/sapling-output.params")?;
f.read_to_end(&mut lc.sapling_output)?;
let mut f = File::open("/home/adityapk/.zcash-params/sapling-spend.params")?;
f.read_to_end(&mut lc.sapling_spend)?;
Ok(lc)
} }
pub fn last_scanned_height(&self) -> u64 { pub fn last_scanned_height(&self) -> u64 {
@ -80,27 +100,6 @@ impl LightClient {
} }
} }
pub fn do_read(&mut self) {
if !Path::new("wallet.dat").exists() {
println!("No existing wallet");
return;
}
print!("Reading wallet...");
io::stdout().flush().ok().expect("Could not flush stdout");
let mut file_buffer = match File::open("wallet.dat") {
Ok(f) => BufReader::new(f),
Err(e) => {
println!("[Error: {}]", e.description());
return;
}
};
let lw = LightWallet::read(&mut file_buffer).unwrap();
self.wallet = Arc::new(lw);
println!("[OK]");
}
pub fn do_save(&self) { pub fn do_save(&self) {
print!("Saving wallet..."); print!("Saving wallet...");
io::stdout().flush().ok().expect("Could not flush stdout"); io::stdout().flush().ok().expect("Could not flush stdout");
@ -133,6 +132,10 @@ impl LightClient {
tokio::runtime::current_thread::Runtime::new().unwrap().block_on(say_hello).unwrap() tokio::runtime::current_thread::Runtime::new().unwrap().block_on(say_hello).unwrap()
} }
pub fn do_seed_phrase(&self) -> String {
self.wallet.get_seed_phrase()
}
pub fn do_list_transactions(&self) -> JsonValue { pub fn do_list_transactions(&self) -> JsonValue {
// Create a list of TransactionItems // Create a list of TransactionItems
let mut tx_list = self.wallet.txs.read().unwrap().iter() let mut tx_list = self.wallet.txs.read().unwrap().iter()
@ -168,14 +171,14 @@ impl LightClient {
"amount" => nd.note.value as i64, "amount" => nd.note.value as i64,
"address" => nd.note_address().unwrap(), "address" => nd.note_address().unwrap(),
"memo" => match &nd.memo { "memo" => match &nd.memo {
Some(memo) => { Some(memo) => {
match memo.to_utf8() { match memo.to_utf8() {
Some(Ok(memo_str)) => Some(memo_str), Some(Ok(memo_str)) => Some(memo_str),
_ => None _ => None
}
} }
_ => None
} }
_ => None
}
}) })
); );

28
rust-lightclient/src/lightwallet.rs

@ -6,7 +6,9 @@ use std::cmp;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};
use protobuf::*; use protobuf::parse_from_bytes;
use bip39::{Mnemonic, Language};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use pairing::bls12_381::{Bls12}; use pairing::bls12_381::{Bls12};
@ -383,27 +385,33 @@ impl LightWallet {
(extsk, extfvk, address) (extsk, extfvk, address)
} }
pub fn new() -> Self { pub fn new(seed_phrase: Option<&str>) -> io::Result<Self> {
use rand::{FromEntropy, ChaChaRng, Rng}; use rand::{FromEntropy, ChaChaRng, Rng};
// Create a random seed.
let mut system_rng = ChaChaRng::from_entropy();
let mut seed_bytes = [0u8; 32]; let mut seed_bytes = [0u8; 32];
system_rng.fill(&mut seed_bytes);
if seed_phrase.is_none() {
// Create a random seed.
let mut system_rng = ChaChaRng::from_entropy();
system_rng.fill(&mut seed_bytes);
} else {
seed_bytes.copy_from_slice(&Mnemonic::from_phrase(seed_phrase.expect("should have a seed phrase"),
Language::English).unwrap().entropy());
}
// Derive only the first address // Derive only the first address
// TODO: We need to monitor addresses, and always keep 1 "free" address, so // TODO: We need to monitor addresses, and always keep 1 "free" address, so
// users can import a seed phrase and automatically get all used addresses // users can import a seed phrase and automatically get all used addresses
let (extsk, extfvk, address) = LightWallet::get_pk_from_seed(&seed_bytes); let (extsk, extfvk, address) = LightWallet::get_pk_from_seed(&seed_bytes);
LightWallet { Ok(LightWallet {
seed: seed_bytes, seed: seed_bytes,
extsks: vec![extsk], extsks: vec![extsk],
extfvks: vec![extfvk], extfvks: vec![extfvk],
address: vec![address], address: vec![address],
blocks: Arc::new(RwLock::new(vec![])), blocks: Arc::new(RwLock::new(vec![])),
txs: Arc::new(RwLock::new(HashMap::new())), txs: Arc::new(RwLock::new(HashMap::new())),
} })
} }
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> { pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
@ -539,6 +547,12 @@ impl LightWallet {
encode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, &self.address[account]) encode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, &self.address[account])
} }
pub fn get_seed_phrase(&self) -> String {
Mnemonic::from_entropy(&self.seed,
Language::English,
).unwrap().phrase().to_string()
}
pub fn balance(&self, addr: Option<String>) -> u64 { pub fn balance(&self, addr: Option<String>) -> u64 {
self.txs self.txs
.read() .read()

15
rust-lightclient/src/main.rs

@ -16,8 +16,21 @@ pub mod grpc_client {
pub fn main() { pub fn main() {
use clap::{Arg, App};
let matches = App::new("Light Client")
.version("1.0")
.arg(Arg::with_name("seed")
.short("s")
.long("seed")
.value_name("seed_phrase")
.help("Create a new wallet with the given 24-word seed phrase. Will fail if wallet already exists")
.takes_value(true))
.get_matches();
let mut lightclient = LightClient::new(matches.value_of("seed")).unwrap();
println!("Starting Light Client"); println!("Starting Light Client");
let mut lightclient = LightClient::new();
// At startup, read the wallet.dat // At startup, read the wallet.dat
commands::do_user_command(&"read".to_string(), &mut lightclient); commands::do_user_command(&"read".to_string(), &mut lightclient);

Loading…
Cancel
Save