mirror of
https://github.com/Qortal/piratewallet-light-cli.git
synced 2025-01-30 02:22:15 +00:00
Implement support for detection of diversified addressess
This commit is contained in:
parent
ddada7676b
commit
e41874df40
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -3155,7 +3155,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zecwallet-cli"
|
name = "zecwallet-cli"
|
||||||
version = "1.4.1"
|
version = "1.4.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"clap",
|
"clap",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "zecwallet-cli"
|
name = "zecwallet-cli"
|
||||||
version = "1.4.1"
|
version = "1.4.2"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
@ -15,4 +15,3 @@ byteorder = "1"
|
|||||||
tiny-bip39 = "0.6.2"
|
tiny-bip39 = "0.6.2"
|
||||||
|
|
||||||
zecwalletlitelib = { path = "../lib/" }
|
zecwalletlitelib = { path = "../lib/" }
|
||||||
|
|
||||||
|
@ -1 +1 @@
|
|||||||
pub const VERSION:&str = "1.4.1";
|
pub const VERSION:&str = "1.4.2";
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use crate::lightwallet::LightWallet;
|
use crate::lightwallet::LightWallet;
|
||||||
|
use crate::lightwallet::walletzkey::WalletDiversifiers;
|
||||||
|
|
||||||
use rand::{rngs::OsRng, seq::SliceRandom};
|
use rand::{rngs::OsRng, seq::SliceRandom};
|
||||||
|
|
||||||
@ -6,7 +7,7 @@ use std::sync::{Arc, RwLock, Mutex, mpsc::channel};
|
|||||||
use std::sync::atomic::{AtomicI32, AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicI32, AtomicUsize, Ordering};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::collections::{HashSet, HashMap};
|
use std::collections::{HashMap};
|
||||||
use std::cmp::{max, min};
|
use std::cmp::{max, min};
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
@ -584,6 +585,33 @@ impl LightClient {
|
|||||||
sync_status : Arc::new(RwLock::new(WalletStatus::new())),
|
sync_status : Arc::new(RwLock::new(WalletStatus::new())),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//Load Diversified Addresses from SaplingNotes
|
||||||
|
{
|
||||||
|
let note_wallet = lc.wallet.write().unwrap();
|
||||||
|
let txs = note_wallet.txs.read().unwrap();
|
||||||
|
for (_t, tx) in txs.iter() {
|
||||||
|
for n in tx.notes.iter() {
|
||||||
|
match LightWallet::note_address(lc.config.hrp_sapling_address(), &n) {
|
||||||
|
Some(a) => {
|
||||||
|
//Add diversified addresses to address list
|
||||||
|
let mut zaddrs = note_wallet.zaddresses.write().unwrap();
|
||||||
|
let mut found = false;
|
||||||
|
for z in zaddrs.iter() {
|
||||||
|
if z.zaddress == a {
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
zaddrs.push(WalletDiversifiers{extfvk: n.extfvk.clone(), diversifier: n.diversifier.clone(), zaddress: a});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "embed_params")]
|
#[cfg(feature = "embed_params")]
|
||||||
lc.read_sapling_params();
|
lc.read_sapling_params();
|
||||||
|
|
||||||
@ -611,6 +639,33 @@ impl LightClient {
|
|||||||
sync_status : Arc::new(RwLock::new(WalletStatus::new())),
|
sync_status : Arc::new(RwLock::new(WalletStatus::new())),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//Load Diversified Addresses from SaplingNotes
|
||||||
|
{
|
||||||
|
let note_wallet = lc.wallet.write().unwrap();
|
||||||
|
let txs = note_wallet.txs.read().unwrap();
|
||||||
|
for (_t, tx) in txs.iter() {
|
||||||
|
for n in tx.notes.iter() {
|
||||||
|
match LightWallet::note_address(lc.config.hrp_sapling_address(), &n) {
|
||||||
|
Some(a) => {
|
||||||
|
//Add diversified addresses to address list
|
||||||
|
let mut zaddrs = note_wallet.zaddresses.write().unwrap();
|
||||||
|
let mut found = false;
|
||||||
|
for z in zaddrs.iter() {
|
||||||
|
if z.zaddress == a {
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
zaddrs.push(WalletDiversifiers{extfvk: n.extfvk.clone(), diversifier: n.diversifier.clone(), zaddress: a});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "embed_params")]
|
#[cfg(feature = "embed_params")]
|
||||||
lc.read_sapling_params();
|
lc.read_sapling_params();
|
||||||
|
|
||||||
@ -905,29 +960,24 @@ impl LightClient {
|
|||||||
let mut spent_notes : Vec<JsonValue> = vec![];
|
let mut spent_notes : Vec<JsonValue> = vec![];
|
||||||
let mut pending_notes: Vec<JsonValue> = vec![];
|
let mut pending_notes: Vec<JsonValue> = vec![];
|
||||||
|
|
||||||
let anchor_height: i32 = self.wallet.read().unwrap().get_anchor_height() as i32;
|
|
||||||
|
|
||||||
{
|
{
|
||||||
let wallet = self.wallet.read().unwrap();
|
let wallet = self.wallet.read().unwrap();
|
||||||
|
let all_zkeys = wallet.zkeys.read().unwrap();
|
||||||
// First, collect all extfvk's that are spendable (i.e., we have the private key)
|
|
||||||
let spendable_address: HashSet<String> = wallet.get_all_zaddresses().iter()
|
|
||||||
.filter(|address| wallet.have_spending_key_for_zaddress(address))
|
|
||||||
.map(|address| address.clone())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// Collect Sapling notes
|
// Collect Sapling notes
|
||||||
wallet.txs.read().unwrap().iter()
|
wallet.txs.read().unwrap().iter()
|
||||||
.flat_map( |(txid, wtx)| {
|
.flat_map( |(txid, wtx)| {
|
||||||
let spendable_address = spendable_address.clone();
|
let zkeys = all_zkeys.clone();
|
||||||
wtx.notes.iter().filter_map(move |nd|
|
wtx.notes.iter().filter_map(move |nd|
|
||||||
if !all_notes && nd.spent.is_some() {
|
if !all_notes && nd.spent.is_some() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
let address = LightWallet::note_address(self.config.hrp_sapling_address(), nd);
|
let address = LightWallet::note_address(self.config.hrp_sapling_address(), nd);
|
||||||
let spendable = address.is_some() &&
|
|
||||||
spendable_address.contains(&address.clone().unwrap()) &&
|
let spendable = match zkeys.iter().find(|zk| zk.extfvk == nd.extfvk) {
|
||||||
wtx.block <= anchor_height && nd.spent.is_none() && nd.unconfirmed_spent.is_none();
|
None => false,
|
||||||
|
Some(zk) => zk.have_spending_key()
|
||||||
|
};
|
||||||
|
|
||||||
Some(object!{
|
Some(object!{
|
||||||
"created_in_block" => wtx.block,
|
"created_in_block" => wtx.block,
|
||||||
|
@ -55,11 +55,11 @@ mod extended_key;
|
|||||||
mod utils;
|
mod utils;
|
||||||
mod address;
|
mod address;
|
||||||
mod prover;
|
mod prover;
|
||||||
mod walletzkey;
|
pub mod walletzkey;
|
||||||
|
|
||||||
use data::{BlockData, WalletTx, Utxo, SaplingNoteData, SpendableNote, OutgoingTxMetadata};
|
use data::{BlockData, WalletTx, Utxo, SaplingNoteData, SpendableNote, OutgoingTxMetadata};
|
||||||
use extended_key::{KeyIndex, ExtendedPrivKey};
|
use extended_key::{KeyIndex, ExtendedPrivKey};
|
||||||
use walletzkey::{WalletZKey, WalletZKeyType};
|
use walletzkey::{WalletZKey, WalletZKeyType, WalletDiversifiers};
|
||||||
|
|
||||||
pub const MAX_REORG: usize = 100;
|
pub const MAX_REORG: usize = 100;
|
||||||
|
|
||||||
@ -116,7 +116,8 @@ pub struct LightWallet {
|
|||||||
|
|
||||||
// List of keys, actually in this wallet. This is a combination of HD keys derived from the seed,
|
// List of keys, actually in this wallet. This is a combination of HD keys derived from the seed,
|
||||||
// viewing keys and imported spending keys.
|
// viewing keys and imported spending keys.
|
||||||
zkeys: Arc<RwLock<Vec<WalletZKey>>>,
|
pub zkeys: Arc<RwLock<Vec<WalletZKey>>>,
|
||||||
|
pub zaddresses: Arc<RwLock<Vec<WalletDiversifiers>>>,
|
||||||
|
|
||||||
// Transparent keys. If the wallet is locked, then the secret keys will be encrypted,
|
// Transparent keys. If the wallet is locked, then the secret keys will be encrypted,
|
||||||
// but the addresses will be present.
|
// but the addresses will be present.
|
||||||
@ -229,6 +230,7 @@ impl LightWallet {
|
|||||||
nonce: vec![],
|
nonce: vec![],
|
||||||
seed: seed_bytes,
|
seed: seed_bytes,
|
||||||
zkeys: Arc::new(RwLock::new(vec![WalletZKey::new_hdkey(hdkey_num, extsk)])),
|
zkeys: Arc::new(RwLock::new(vec![WalletZKey::new_hdkey(hdkey_num, extsk)])),
|
||||||
|
zaddresses: Arc::new(RwLock::new(vec![])),
|
||||||
tkeys: Arc::new(RwLock::new(vec![])),
|
tkeys: Arc::new(RwLock::new(vec![])),
|
||||||
taddresses: Arc::new(RwLock::new(vec![])),
|
taddresses: Arc::new(RwLock::new(vec![])),
|
||||||
blocks: Arc::new(RwLock::new(vec![])),
|
blocks: Arc::new(RwLock::new(vec![])),
|
||||||
@ -345,18 +347,18 @@ impl LightWallet {
|
|||||||
Vector::read(&mut reader, |r| WalletZKey::read(r))?
|
Vector::read(&mut reader, |r| WalletZKey::read(r))?
|
||||||
};
|
};
|
||||||
|
|
||||||
let tkeys = Vector::read(&mut reader, |r| {
|
let _tkeys = Vector::read(&mut reader, |r| {
|
||||||
let mut tpk_bytes = [0u8; 32];
|
let mut tpk_bytes = [0u8; 32];
|
||||||
r.read_exact(&mut tpk_bytes)?;
|
r.read_exact(&mut tpk_bytes)?;
|
||||||
secp256k1::SecretKey::from_slice(&tpk_bytes).map_err(|e| io::Error::new(ErrorKind::InvalidData, e))
|
secp256k1::SecretKey::from_slice(&tpk_bytes).map_err(|e| io::Error::new(ErrorKind::InvalidData, e))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let taddresses = if version >= 4 {
|
let _taddresses = if version >= 4 {
|
||||||
// Read the addresses
|
// Read the addresses
|
||||||
Vector::read(&mut reader, |r| utils::read_string(r))?
|
Vector::read(&mut reader, |r| utils::read_string(r))?
|
||||||
} else {
|
} else {
|
||||||
// Calculate the addresses
|
// Calculate the addresses
|
||||||
tkeys.iter().map(|sk| LightWallet::address_from_prefix_sk(&config.base58_pubkey_address(), sk)).collect()
|
_tkeys.iter().map(|sk| LightWallet::address_from_prefix_sk(&config.base58_pubkey_address(), sk)).collect()
|
||||||
};
|
};
|
||||||
|
|
||||||
let blocks = Vector::read(&mut reader, |r| BlockData::read(r))?;
|
let blocks = Vector::read(&mut reader, |r| BlockData::read(r))?;
|
||||||
@ -385,6 +387,7 @@ impl LightWallet {
|
|||||||
nonce: nonce,
|
nonce: nonce,
|
||||||
seed: seed_bytes,
|
seed: seed_bytes,
|
||||||
zkeys: Arc::new(RwLock::new(zkeys)),
|
zkeys: Arc::new(RwLock::new(zkeys)),
|
||||||
|
zaddresses: Arc::new(RwLock::new(vec![])),
|
||||||
tkeys: Arc::new(RwLock::new(vec![])),
|
tkeys: Arc::new(RwLock::new(vec![])),
|
||||||
taddresses: Arc::new(RwLock::new(vec![])),
|
taddresses: Arc::new(RwLock::new(vec![])),
|
||||||
blocks: Arc::new(RwLock::new(blocks)),
|
blocks: Arc::new(RwLock::new(blocks)),
|
||||||
@ -753,9 +756,23 @@ impl LightWallet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_all_zaddresses(&self) -> Vec<String> {
|
pub fn get_all_zaddresses(&self) -> Vec<String> {
|
||||||
self.zkeys.read().unwrap().iter().map( |zk| {
|
let mut zaddrs: Vec<String> = self.zkeys.read().unwrap().iter().map( |zk| {
|
||||||
encode_payment_address(self.config.hrp_sapling_address(), &zk.zaddress)
|
encode_payment_address(self.config.hrp_sapling_address(), &zk.zaddress)
|
||||||
}).collect()
|
}).collect();
|
||||||
|
|
||||||
|
let dzaddrs = self.zaddresses.read().unwrap();
|
||||||
|
for z in dzaddrs.iter() {
|
||||||
|
let mut found = false;
|
||||||
|
for ud in zaddrs.iter() {
|
||||||
|
if ud == &z.zaddress.clone() {
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
zaddrs.push(z.zaddress.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
zaddrs
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn address_from_prefix_sk(prefix: &[u8; 2], sk: &secp256k1::SecretKey) -> String {
|
pub fn address_from_prefix_sk(prefix: &[u8; 2], sk: &secp256k1::SecretKey) -> String {
|
||||||
@ -869,7 +886,7 @@ impl LightWallet {
|
|||||||
let bip39_seed = bip39::Seed::new(&Mnemonic::from_entropy(&seed, Language::English).unwrap(), "");
|
let bip39_seed = bip39::Seed::new(&Mnemonic::from_entropy(&seed, Language::English).unwrap(), "");
|
||||||
|
|
||||||
// Transparent keys
|
// Transparent keys
|
||||||
let mut tkeys = vec![];
|
let tkeys = vec![];
|
||||||
// for pos in 0..self.taddresses.read().unwrap().len() {
|
// for pos in 0..self.taddresses.read().unwrap().len() {
|
||||||
// let sk = LightWallet::get_taddr_from_bip39seed(&self.config, &bip39_seed.as_bytes(), pos as u32);
|
// let sk = LightWallet::get_taddr_from_bip39seed(&self.config, &bip39_seed.as_bytes(), pos as u32);
|
||||||
// let address = self.address_from_sk(&sk);
|
// let address = self.address_from_sk(&sk);
|
||||||
@ -1965,7 +1982,21 @@ impl LightWallet {
|
|||||||
match LightWallet::note_address(self.config.hrp_sapling_address(), &new_note) {
|
match LightWallet::note_address(self.config.hrp_sapling_address(), &new_note) {
|
||||||
Some(a) => {
|
Some(a) => {
|
||||||
info!("Received sapling output to {}", a);
|
info!("Received sapling output to {}", a);
|
||||||
self.ensure_hd_zaddresses(&a);
|
// self.ensure_hd_zaddresses(&a);
|
||||||
|
|
||||||
|
//Add diversified addresses to address list
|
||||||
|
let mut zaddrs = self.zaddresses.write().unwrap();
|
||||||
|
let mut found = false;
|
||||||
|
for z in zaddrs.iter() {
|
||||||
|
if z.zaddress == a {
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
zaddrs.push(WalletDiversifiers{extfvk: new_note.extfvk.clone(), diversifier: new_note.diversifier.clone(), zaddress: a});
|
||||||
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,7 @@ impl BlockData {
|
|||||||
|
|
||||||
pub struct SaplingNoteData {
|
pub struct SaplingNoteData {
|
||||||
pub(super) account: usize,
|
pub(super) account: usize,
|
||||||
pub(super) extfvk: ExtendedFullViewingKey, // Technically, this should be recoverable from the account number, but we're going to refactor this in the future, so I'll write it again here.
|
pub extfvk: ExtendedFullViewingKey, // Technically, this should be recoverable from the account number, but we're going to refactor this in the future, so I'll write it again here.
|
||||||
pub diversifier: Diversifier,
|
pub diversifier: Diversifier,
|
||||||
pub note: Note<Bls12>,
|
pub note: Note<Bls12>,
|
||||||
pub(super) witnesses: Vec<IncrementalWitness<Node>>,
|
pub(super) witnesses: Vec<IncrementalWitness<Node>>,
|
||||||
|
@ -9,7 +9,7 @@ use sodiumoxide::crypto::secretbox;
|
|||||||
use zcash_primitives::{
|
use zcash_primitives::{
|
||||||
serialize::{Vector, Optional},
|
serialize::{Vector, Optional},
|
||||||
zip32::{ExtendedFullViewingKey, ExtendedSpendingKey},
|
zip32::{ExtendedFullViewingKey, ExtendedSpendingKey},
|
||||||
primitives::{PaymentAddress},
|
primitives::{Diversifier, PaymentAddress},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::lightclient::{LightClientConfig};
|
use crate::lightclient::{LightClientConfig};
|
||||||
@ -22,13 +22,21 @@ pub enum WalletZKeyType {
|
|||||||
ImportedViewKey = 2
|
ImportedViewKey = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A struct that holds diversified addresses
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub struct WalletDiversifiers {
|
||||||
|
pub extfvk: ExtendedFullViewingKey,
|
||||||
|
pub diversifier: Diversifier,
|
||||||
|
pub zaddress: String
|
||||||
|
}
|
||||||
|
|
||||||
// A struct that holds z-address private keys or view keys
|
// A struct that holds z-address private keys or view keys
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct WalletZKey {
|
pub struct WalletZKey {
|
||||||
pub(super) keytype: WalletZKeyType,
|
pub(super) keytype: WalletZKeyType,
|
||||||
locked: bool,
|
locked: bool,
|
||||||
pub(super) extsk: Option<ExtendedSpendingKey>,
|
pub(super) extsk: Option<ExtendedSpendingKey>,
|
||||||
pub(super) extfvk: ExtendedFullViewingKey,
|
pub extfvk: ExtendedFullViewingKey,
|
||||||
pub(super) zaddress: PaymentAddress<Bls12>,
|
pub(super) zaddress: PaymentAddress<Bls12>,
|
||||||
|
|
||||||
// If this is a HD key, what is the key number
|
// If this is a HD key, what is the key number
|
||||||
|
Loading…
Reference in New Issue
Block a user