From 5bd8b754dfc379505303402fd707acd2c06da39c Mon Sep 17 00:00:00 2001 From: Aditya Kulkarni Date: Sat, 7 Sep 2019 22:27:35 -0700 Subject: [PATCH] Derive addresses from seed --- rust-lightclient/Cargo.toml | 3 +- rust-lightclient/src/lightclient.rs | 2 +- rust-lightclient/src/lightwallet.rs | 98 +++++++++++++++++++++-------- 3 files changed, 76 insertions(+), 27 deletions(-) diff --git a/rust-lightclient/Cargo.toml b/rust-lightclient/Cargo.toml index 743c89a..37189b0 100644 --- a/rust-lightclient/Cargo.toml +++ b/rust-lightclient/Cargo.toml @@ -21,6 +21,7 @@ hex = "0.3" protobuf = "2" rustyline = "5.0.2" byteorder = "1" +rand = "0.5.6" [dependencies.bellman] path = "../../librustzcash/bellman" @@ -50,4 +51,4 @@ features = ["ff_derive"] tower-grpc-build = { git = "https://github.com/tower-rs/tower-grpc", features = ["tower-hyper"] } [profile.release] -debug = true \ No newline at end of file +#debug = true \ No newline at end of file diff --git a/rust-lightclient/src/lightclient.rs b/rust-lightclient/src/lightclient.rs index a296e1b..1774e70 100644 --- a/rust-lightclient/src/lightclient.rs +++ b/rust-lightclient/src/lightclient.rs @@ -68,7 +68,7 @@ impl LightClient { } pub fn do_address(&self) { - println!("Address: {}", self.wallet.address()); + println!("Address: {}", self.wallet.address(0)); // TODO: This is showing only the default address println!("Balance: {}", self.wallet.balance()); } diff --git a/rust-lightclient/src/lightwallet.rs b/rust-lightclient/src/lightwallet.rs index d71f4fa..0dd2cb6 100644 --- a/rust-lightclient/src/lightwallet.rs +++ b/rust-lightclient/src/lightwallet.rs @@ -28,7 +28,7 @@ use zcash_primitives::{ TxId, Transaction }, note_encryption::{Memo, try_sapling_note_decryption}, - zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}, + zip32::{ExtendedFullViewingKey, ExtendedSpendingKey, ChildIndex}, JUBJUB, primitives::{Diversifier, Note, PaymentAddress}, jubjub::{ @@ -349,9 +349,15 @@ impl SpendableNote { } pub struct LightWallet { - extsks: [ExtendedSpendingKey; 1], - extfvks: [ExtendedFullViewingKey; 1], - address: PaymentAddress, + seed: [u8; 32], // Seed phrase for this wallet. + + // List of keys, actually in this wallet. This may include more + // than keys derived from the seed, for example, if user imports + // a private key + extsks: Vec, + extfvks: Vec, + address: Vec>, + blocks: Arc>>, pub txs: Arc>>, } @@ -361,17 +367,42 @@ impl LightWallet { return 1; } - pub fn new() -> Self { - let extsk = ExtendedSpendingKey::master(&[1; 32]); // New key - let extfvk = ExtendedFullViewingKey::from(&extsk); + fn get_pk_from_seed(seed: &[u8; 32]) -> + (ExtendedSpendingKey, ExtendedFullViewingKey, PaymentAddress) { + let extsk: ExtendedSpendingKey = ExtendedSpendingKey::from_path( + &ExtendedSpendingKey::master(seed), + &[ + ChildIndex::Hardened(32), + ChildIndex::Hardened(1), // TODO: Cointype should be 133 for mainnet + ChildIndex::Hardened(0) + ], + ); + let extfvk = ExtendedFullViewingKey::from(&extsk); let address = extfvk.default_address().unwrap().1; + (extsk, extfvk, address) + } + + pub fn new() -> Self { + use rand::{FromEntropy, ChaChaRng, Rng}; + + // Create a random seed. + let mut system_rng = ChaChaRng::from_entropy(); + let mut seed_bytes = [0u8; 32]; + system_rng.fill(&mut seed_bytes); + + // Derive only the first address + // 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 + let (extsk, extfvk, address) = LightWallet::get_pk_from_seed(&seed_bytes); + LightWallet { - extsks: [extsk], - extfvks: [extfvk], - address, - blocks: Arc::new(RwLock::new(vec![])), - txs: Arc::new(RwLock::new(HashMap::new())), + seed: seed_bytes, + extsks: vec![extsk], + extfvks: vec![extfvk], + address: vec![address], + blocks: Arc::new(RwLock::new(vec![])), + txs: Arc::new(RwLock::new(HashMap::new())), } } @@ -379,6 +410,21 @@ impl LightWallet { let version = reader.read_u64::()?; assert_eq!(version, LightWallet::serialized_version()); + // Seed + let mut seed_bytes = [0u8; 32]; + reader.read_exact(&mut seed_bytes)?; + + // Read the spending keys + let extsks = Vector::read(&mut reader, |r| ExtendedSpendingKey::read(r))?; + + // Calculate the viewing keys + let extfvks = extsks.iter().map(|sk| ExtendedFullViewingKey::from(sk)) + .collect::>(); + + // Calculate the addresses + let addresses = extfvks.iter().map( |fvk| fvk.default_address().unwrap().1 ) + .collect::>>(); + let blocks = Vector::read(&mut reader, |r| BlockData::read(r))?; let txs_tuples = Vector::read(&mut reader, |r| { @@ -389,17 +435,13 @@ impl LightWallet { })?; let txs = txs_tuples.into_iter().collect::>(); - - let extsk = ExtendedSpendingKey::master(&[1; 32]); // New key - let extfvk = ExtendedFullViewingKey::from(&extsk); - let address = extfvk.default_address().unwrap().1; - Ok(LightWallet{ - extsks: [extsk], - extfvks: [extfvk], - address, - blocks: Arc::new(RwLock::new(blocks)), - txs: Arc::new(RwLock::new(txs)) + seed: seed_bytes, + extsks: extsks, + extfvks: extfvks, + address: addresses, + blocks: Arc::new(RwLock::new(blocks)), + txs: Arc::new(RwLock::new(txs)) }) } @@ -407,7 +449,13 @@ impl LightWallet { // Write the version writer.write_u64::(LightWallet::serialized_version())?; - // TODO: Write the keys properly. Right now, they're just hardcoded + // Write the seed + writer.write_all(&self.seed)?; + + // Write all the spending keys + Vector::write(&mut writer, &self.extsks, + |w, sk| sk.write(w) + )?; Vector::write(&mut writer, &self.blocks.read().unwrap(), |w, b| b.write(w))?; @@ -487,8 +535,8 @@ impl LightWallet { } } - pub fn address(&self) -> String { - encode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, &self.address) + pub fn address(&self, account: usize) -> String { + encode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, &self.address[account]) } pub fn balance(&self) -> u64 {