diff --git a/src/address.rs b/src/address.rs index 14a0a1b..6e4df02 100644 --- a/src/address.rs +++ b/src/address.rs @@ -6,7 +6,7 @@ use zcash_client_backend::encoding::{decode_payment_address, decode_transparent_ use zcash_primitives::legacy::TransparentAddress; use zcash_client_backend::constants::testnet::{ - B58_PUBKEY_ADDRESS_PREFIX, B58_SCRIPT_ADDRESS_PREFIX, HRP_SAPLING_PAYMENT_ADDRESS, + B58_PUBKEY_ADDRESS_PREFIX, B58_SCRIPT_ADDRESS_PREFIX, }; /// An address that funds can be sent to. @@ -28,9 +28,9 @@ impl From for RecipientAddress { } impl RecipientAddress { - pub fn from_str(s: &str) -> Option { + pub fn from_str(s: &str, hrp_sapling_address: &str) -> Option { // Try to match a sapling z address - if let Some(pa) = match decode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, s) { + if let Some(pa) = match decode_payment_address(hrp_sapling_address, s) { Ok(ret) => ret, Err(_) => None } diff --git a/src/lightclient.rs b/src/lightclient.rs index 1a4b24e..8c905dd 100644 --- a/src/lightclient.rs +++ b/src/lightclient.rs @@ -16,7 +16,7 @@ use json::{object, JsonValue}; use zcash_primitives::transaction::{TxId, Transaction}; use zcash_primitives::note_encryption::Memo; use zcash_client_backend::{ - constants::testnet::HRP_SAPLING_PAYMENT_ADDRESS, encoding::encode_payment_address, + constants::testnet, constants::mainnet, constants::regtest, encoding::encode_payment_address, }; use futures::Future; @@ -97,6 +97,16 @@ impl LightClientConfig { log_path.into_boxed_path() } + pub fn get_initial_state(&self) -> Option<(u64, &str, &str)> { + match &self.chain_name[..] { + "test" => Some((600000, + "0107385846c7451480912c294b6ce1ee1feba6c2619079fd9104f6e71e4d8fe7", + "01690698411e3f8badea7da885e556d7aba365a797e9b20b44ac0946dced14b23c001001ab2a18a5a86aa5d77e43b69071b21770b6fe6b3c26304dcaf7f96c0bb3fed74d000186482712fa0f2e5aa2f2700c4ed49ef360820f323d34e2b447b78df5ec4dfa0401a332e89a21afb073cb1db7d6f07396b56a95e97454b9bca5a63d0ebc575d3a33000000000001c9d3564eff54ebc328eab2e4f1150c3637f4f47516f879a0cfebdf49fe7b1d5201c104705fac60a85596010e41260d07f3a64f38f37a112eaef41cd9d736edc5270145e3d4899fcd7f0f1236ae31eafb3f4b65ad6b11a17eae1729cec09bd3afa01a000000011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39" + )), + _ => None + } + } + pub fn get_server_or_default(server: Option) -> String { match server { Some(s) => if s.starts_with("http://") {s} else { "http://".to_string() + &s} @@ -104,6 +114,15 @@ impl LightClientConfig { } } + pub fn hrp_sapling_address(&self) -> &str { + match &self.chain_name[..] { + "main" => mainnet::HRP_SAPLING_PAYMENT_ADDRESS, + "test" => testnet::HRP_SAPLING_PAYMENT_ADDRESS, + "regtest" => regtest::HRP_SAPLING_PAYMENT_ADDRESS, + c => panic!("Unknown chain {}", c) + } + } + } pub struct LightClient { @@ -119,9 +138,14 @@ pub struct LightClient { impl LightClient { pub fn set_wallet_initial_state(&self) { - self.wallet.set_initial_block(600000, - "0107385846c7451480912c294b6ce1ee1feba6c2619079fd9104f6e71e4d8fe7", - "01690698411e3f8badea7da885e556d7aba365a797e9b20b44ac0946dced14b23c001001ab2a18a5a86aa5d77e43b69071b21770b6fe6b3c26304dcaf7f96c0bb3fed74d000186482712fa0f2e5aa2f2700c4ed49ef360820f323d34e2b447b78df5ec4dfa0401a332e89a21afb073cb1db7d6f07396b56a95e97454b9bca5a63d0ebc575d3a33000000000001c9d3564eff54ebc328eab2e4f1150c3637f4f47516f879a0cfebdf49fe7b1d5201c104705fac60a85596010e41260d07f3a64f38f37a112eaef41cd9d736edc5270145e3d4899fcd7f0f1236ae31eafb3f4b65ad6b11a17eae1729cec09bd3afa01a000000011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39"); + use std::convert::TryInto; + + let state = self.config.get_initial_state(); + + match state { + Some((height, hash, tree)) => self.wallet.set_initial_block(height.try_into().unwrap(), hash, tree), + _ => true, + }; } pub fn new(seed_phrase: Option, config: &LightClientConfig) -> io::Result { @@ -134,7 +158,7 @@ impl LightClient { let mut file_buffer = BufReader::new(File::open(config.get_wallet_path())?); - let wallet = LightWallet::read(&mut file_buffer)?; + let wallet = LightWallet::read(&mut file_buffer, config)?; LightClient { wallet : Arc::new(wallet), config : config.clone(), @@ -143,7 +167,7 @@ impl LightClient { } } else { let l = LightClient { - wallet : Arc::new(LightWallet::new(seed_phrase)?), + wallet : Arc::new(LightWallet::new(seed_phrase, config)?), config : config.clone(), sapling_output : vec![], sapling_spend : vec![] @@ -183,7 +207,7 @@ impl LightClient { pub fn do_address(&self) -> json::JsonValue { // Collect z addresses let z_addresses = self.wallet.address.iter().map( |ad| { - encode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, &ad) + encode_payment_address(self.config.hrp_sapling_address(), &ad) }).collect::>(); // Collect t addresses @@ -200,7 +224,7 @@ impl LightClient { pub fn do_balance(&self) -> json::JsonValue { // Collect z addresses let z_addresses = self.wallet.address.iter().map( |ad| { - let address = encode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, &ad); + let address = encode_payment_address(self.config.hrp_sapling_address(), &ad); object!{ "address" => address.clone(), "zbalance" => self.wallet.zbalance(Some(address.clone())), @@ -296,7 +320,7 @@ impl LightClient { "created_in_txid" => format!("{}", txid), "value" => nd.note.value, "is_change" => nd.is_change, - "address" => nd.note_address(), + "address" => self.wallet.note_address(nd), "spent" => nd.spent.map(|spent_txid| format!("{}", spent_txid)), "unconfirmed_spent" => nd.unconfirmed_spent.map(|spent_txid| format!("{}", spent_txid)), }) @@ -418,7 +442,7 @@ impl LightClient { "block_height" => v.block, "txid" => format!("{}", v.txid), "amount" => nd.note.value as i64, - "address" => nd.note_address().unwrap(), + "address" => self.wallet.note_address(nd), "memo" => match &nd.memo { Some(memo) => { match memo.to_utf8() { diff --git a/src/lightwallet/data.rs b/src/lightwallet/data.rs index 5cd4f2f..877345e 100644 --- a/src/lightwallet/data.rs +++ b/src/lightwallet/data.rs @@ -4,11 +4,6 @@ use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use pairing::bls12_381::{Bls12}; use ff::{PrimeField, PrimeFieldRepr}; -use zcash_client_backend::{ - constants::testnet::{HRP_SAPLING_PAYMENT_ADDRESS,}, - encoding::encode_payment_address, -}; - use zcash_primitives::{ block::BlockHash, merkle_tree::{CommitmentTree, IncrementalWitness}, @@ -239,12 +234,6 @@ impl SaplingNoteData { Ok(()) } - pub fn note_address(&self) -> Option { - match self.extfvk.fvk.vk.into_payment_address(self.diversifier, &JUBJUB) { - Some(pa) => Some(encode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, &pa)), - None => None - } - } } #[derive(Clone, Debug)] diff --git a/src/lightwallet/mod.rs b/src/lightwallet/mod.rs index b658599..a25faa5 100644 --- a/src/lightwallet/mod.rs +++ b/src/lightwallet/mod.rs @@ -14,7 +14,7 @@ use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use pairing::bls12_381::{Bls12}; use zcash_client_backend::{ - constants::testnet::{HRP_SAPLING_PAYMENT_ADDRESS,B58_PUBKEY_ADDRESS_PREFIX,}, + constants::testnet::{B58_PUBKEY_ADDRESS_PREFIX,}, encoding::encode_payment_address, proto::compact_formats::CompactBlock, welding_rig::scan_block, }; @@ -35,21 +35,21 @@ use zcash_primitives::{ primitives::{PaymentAddress}, }; -pub mod data; -pub mod extended_key; - use data::{BlockData, WalletTx, Utxo, SaplingNoteData, SpendableNote}; use crate::address; use crate::prover; - +use crate::LightClientConfig; use sha2::{Sha256, Digest}; -const ANCHOR_OFFSET: u32 = 1; -const SAPLING_ACTIVATION_HEIGHT: i32 = 280_000; +pub mod data; +pub mod extended_key; + + +const ANCHOR_OFFSET: u32 = 1; fn now() -> f64 { SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs() as f64 @@ -122,6 +122,9 @@ pub struct LightWallet { blocks: Arc>>, pub txs: Arc>>, + + // Non-serialized fields + config: LightClientConfig, } impl LightWallet { @@ -145,7 +148,7 @@ impl LightWallet { (extsk, extfvk, address) } - pub fn new(seed_phrase: Option) -> io::Result { + pub fn new(seed_phrase: Option, config: &LightClientConfig) -> io::Result { use rand::{FromEntropy, ChaChaRng, Rng}; let mut seed_bytes = [0u8; 32]; @@ -176,12 +179,13 @@ impl LightWallet { tkeys: vec![tpk], blocks: Arc::new(RwLock::new(vec![])), txs: Arc::new(RwLock::new(HashMap::new())), + config: config.clone(), }) } - pub fn read(mut reader: R) -> io::Result { + pub fn read(mut reader: R, config: &LightClientConfig) -> io::Result { let version = reader.read_u64::()?; - assert_eq!(version, LightWallet::serialized_version()); + assert!(version <= LightWallet::serialized_version()); info!("Reading wallet version {}", version); // Seed @@ -220,7 +224,8 @@ impl LightWallet { address: addresses, tkeys: vec![tpk], blocks: Arc::new(RwLock::new(blocks)), - txs: Arc::new(RwLock::new(txs)) + txs: Arc::new(RwLock::new(txs)), + config: config.clone(), }) } @@ -251,7 +256,12 @@ impl LightWallet { Ok(()) } - + pub fn note_address(&self, note: &SaplingNoteData) -> Option { + match note.extfvk.fvk.vk.into_payment_address(note.diversifier, &JUBJUB) { + Some(pa) => Some(encode_payment_address(self.config.hrp_sapling_address(), &pa)), + None => None + } + } // Clears all the downloaded blocks and resets the state back to the inital block. // After this, the wallet's initial state will need to be set @@ -315,12 +325,10 @@ impl LightWallet { } pub fn last_scanned_height(&self) -> i32 { - self.blocks - .read() - .unwrap() + self.blocks.read().unwrap() .last() .map(|block| block.height) - .unwrap_or(SAPLING_ACTIVATION_HEIGHT - 1) + .unwrap_or(self.config.sapling_activation_height as i32 - 1) } /// Determines the target height for a transaction, and the offset from which to @@ -382,7 +390,7 @@ impl LightWallet { .filter(|nd| { // TODO, this whole section is shared with verified_balance. Refactor it. match addr.clone() { Some(a) => a == encode_payment_address( - HRP_SAPLING_PAYMENT_ADDRESS, + self.config.hrp_sapling_address(), &nd.extfvk.fvk.vk .into_payment_address(nd.diversifier, &JUBJUB).unwrap() ), @@ -436,7 +444,7 @@ impl LightWallet { .filter(|nd| { // TODO, this whole section is shared with verified_balance. Refactor it. match addr.clone() { Some(a) => a == encode_payment_address( - HRP_SAPLING_PAYMENT_ADDRESS, + self.config.hrp_sapling_address(), &nd.extfvk.fvk.vk .into_payment_address(nd.diversifier, &JUBJUB).unwrap() ), @@ -598,7 +606,7 @@ impl LightWallet { // This could be a chane or an outgoing transaction println!("Recovered outgoing for {} to {} :{:?}", note.value, - encode_payment_address(HRP_SAPLING_PAYMENT_ADDRESS, &address), + encode_payment_address(self.config.hrp_sapling_address(), &address), memo.to_utf8()) }, None => {} @@ -787,7 +795,7 @@ impl LightWallet { let extfvk = &self.extfvks[0]; let ovk = extfvk.fvk.ovk; - let to = match address::RecipientAddress::from_str(to) { + let to = match address::RecipientAddress::from_str(to, self.config.hrp_sapling_address()) { Some(to) => to, None => { eprintln!("Invalid recipient address");