merged new updates
This commit is contained in:
126
lib/src/lightwallet/bugs.rs
Normal file
126
lib/src/lightwallet/bugs.rs
Normal file
@@ -0,0 +1,126 @@
|
||||
///
|
||||
/// In v1.0 of zecwallet-cli, there was a bug that incorrectly derived HD wallet keys after the first key. That is, the
|
||||
/// first key, address was correct, but subsequent ones were not.
|
||||
///
|
||||
/// The issue was that the 32-byte seed was directly being used to derive then subsequent addresses instead of the
|
||||
/// 64-byte pkdf2(seed). The issue affected both t and z addresses
|
||||
///
|
||||
/// To fix the bug, we need to:
|
||||
/// 1. Check if the wallet has more than 1 address for t or z addresses
|
||||
/// 2. Move any funds in these addresses to the first address
|
||||
/// 3. Re-derive the addresses
|
||||
|
||||
use super::LightWallet;
|
||||
use crate::lightclient::LightClient;
|
||||
|
||||
use json::object;
|
||||
use bip39::{Mnemonic, Language};
|
||||
|
||||
pub struct BugBip39Derivation {}
|
||||
|
||||
impl BugBip39Derivation {
|
||||
|
||||
/// Check if this bug exists in the wallet
|
||||
pub fn has_bug(client: &LightClient) -> bool {
|
||||
let wallet = client.wallet.read().unwrap();
|
||||
|
||||
if wallet.zaddress.read().unwrap().len() <= 1 {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The seed bytes is the raw entropy. To pass it to HD wallet generation,
|
||||
// we need to get the 64 byte bip39 entropy
|
||||
let bip39_seed = bip39::Seed::new(&Mnemonic::from_entropy(&wallet.seed, Language::English).unwrap(), "");
|
||||
|
||||
// Check z addresses
|
||||
for pos in 0..wallet.zaddress.read().unwrap().len() {
|
||||
let (_, _, address) =
|
||||
LightWallet::get_zaddr_from_bip39seed(&wallet.config, &bip39_seed.as_bytes(), pos as u32);
|
||||
|
||||
if address != wallet.zaddress.read().unwrap()[pos] {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check t addresses
|
||||
for pos in 0..wallet.taddresses.read().unwrap().len() {
|
||||
let sk = LightWallet::get_taddr_from_bip39seed(&wallet.config, &bip39_seed.as_bytes(), pos as u32);
|
||||
let address = wallet.address_from_sk(&sk);
|
||||
|
||||
if address != wallet.taddresses.read().unwrap()[pos] {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Automatically fix the bug if it exists in the wallet
|
||||
pub fn fix_bug(client: &LightClient) -> String {
|
||||
use zcash_primitives::transaction::components::amount::DEFAULT_FEE;
|
||||
use std::convert::TryInto;
|
||||
|
||||
if !BugBip39Derivation::has_bug(client) {
|
||||
let r = object!{
|
||||
"has_bug" => false
|
||||
};
|
||||
|
||||
return r.pretty(2);
|
||||
}
|
||||
|
||||
// Tranfer money
|
||||
// 1. The desination is z address #0
|
||||
let zaddr = client.do_address()["z_addresses"][0].as_str().unwrap().to_string();
|
||||
let balance_json = client.do_balance();
|
||||
let fee: u64 = DEFAULT_FEE.try_into().unwrap();
|
||||
let amount: u64 = balance_json["zbalance"].as_u64().unwrap()
|
||||
+ balance_json["tbalance"].as_u64().unwrap()
|
||||
- fee;
|
||||
|
||||
let txid = if amount > 0 {
|
||||
match client.do_send(vec![(&zaddr, amount, None)]) {
|
||||
Ok(txid) => txid,
|
||||
Err(e) => {
|
||||
let r = object!{
|
||||
"has_bug" => true,
|
||||
"fixed" => false,
|
||||
"error" => e,
|
||||
};
|
||||
|
||||
return r.pretty(2);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
|
||||
|
||||
// regen addresses
|
||||
let wallet = client.wallet.read().unwrap();
|
||||
let num_zaddrs = wallet.zaddress.read().unwrap().len();
|
||||
let num_taddrs = wallet.taddresses.read().unwrap().len();
|
||||
|
||||
wallet.extsks.write().unwrap().truncate(1);
|
||||
wallet.extfvks.write().unwrap().truncate(1);
|
||||
wallet.zaddress.write().unwrap().truncate(1);
|
||||
|
||||
wallet.tkeys.write().unwrap().truncate(1);
|
||||
wallet.taddresses.write().unwrap().truncate(1);
|
||||
|
||||
for _ in 1..num_zaddrs {
|
||||
wallet.add_zaddr();
|
||||
}
|
||||
|
||||
for _ in 1..num_taddrs {
|
||||
wallet.add_taddr();
|
||||
}
|
||||
|
||||
let r = object!{
|
||||
"has_bug" => true,
|
||||
"fixed" => true,
|
||||
"txid" => txid,
|
||||
};
|
||||
|
||||
return r.pretty(2);
|
||||
}
|
||||
}
|
||||
@@ -267,7 +267,7 @@ impl Utxo {
|
||||
let mut address_bytes = vec![0; address_len as usize];
|
||||
reader.read_exact(&mut address_bytes)?;
|
||||
let address = String::from_utf8(address_bytes).unwrap();
|
||||
assert_eq!(address.chars().take(1).collect::<Vec<char>>()[0], 'R');
|
||||
assert_eq!(address.chars().take(1).collect::<Vec<char>>()[0], 't');
|
||||
|
||||
let mut txid_bytes = [0; 32];
|
||||
reader.read_exact(&mut txid_bytes)?;
|
||||
@@ -362,8 +362,12 @@ impl OutgoingTxMetadata {
|
||||
}
|
||||
|
||||
pub struct WalletTx {
|
||||
// Block in which this tx was included
|
||||
pub block: i32,
|
||||
|
||||
// Timestamp of Tx. Added in v4
|
||||
pub datetime: u64,
|
||||
|
||||
// Txid of this transaction. It's duplicated here (It is also the Key in the HashMap that points to this
|
||||
// WalletTx in LightWallet::txs)
|
||||
pub txid: TxId,
|
||||
@@ -386,17 +390,19 @@ pub struct WalletTx {
|
||||
// All outgoing sapling sends to addresses outside this wallet
|
||||
pub outgoing_metadata: Vec<OutgoingTxMetadata>,
|
||||
|
||||
// Whether this TxID was downloaded from the server and scanned for Memos
|
||||
pub full_tx_scanned: bool,
|
||||
}
|
||||
|
||||
impl WalletTx {
|
||||
pub fn serialized_version() -> u64 {
|
||||
return 3;
|
||||
return 4;
|
||||
}
|
||||
|
||||
pub fn new(height: i32, txid: &TxId) -> Self {
|
||||
pub fn new(height: i32, datetime: u64, txid: &TxId) -> Self {
|
||||
WalletTx {
|
||||
block: height,
|
||||
datetime,
|
||||
txid: txid.clone(),
|
||||
notes: vec![],
|
||||
utxos: vec![],
|
||||
@@ -413,6 +419,12 @@ impl WalletTx {
|
||||
|
||||
let block = reader.read_i32::<LittleEndian>()?;
|
||||
|
||||
let datetime = if version >= 4 {
|
||||
reader.read_u64::<LittleEndian>()?
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
let mut txid_bytes = [0u8; 32];
|
||||
reader.read_exact(&mut txid_bytes)?;
|
||||
|
||||
@@ -431,6 +443,7 @@ impl WalletTx {
|
||||
|
||||
Ok(WalletTx{
|
||||
block,
|
||||
datetime,
|
||||
txid,
|
||||
notes,
|
||||
utxos,
|
||||
@@ -446,6 +459,8 @@ impl WalletTx {
|
||||
|
||||
writer.write_i32::<LittleEndian>(self.block)?;
|
||||
|
||||
writer.write_u64::<LittleEndian>(self.datetime)?;
|
||||
|
||||
writer.write_all(&self.txid.0)?;
|
||||
|
||||
Vector::write(&mut writer, &self.notes, |w, nd| nd.write(w))?;
|
||||
@@ -475,7 +490,8 @@ pub struct SpendableNote {
|
||||
impl SpendableNote {
|
||||
pub fn from(txid: TxId, nd: &SaplingNoteData, anchor_offset: usize, extsk: &ExtendedSpendingKey) -> Option<Self> {
|
||||
// Include only notes that haven't been spent, or haven't been included in an unconfirmed spend yet.
|
||||
if nd.spent.is_none() && nd.unconfirmed_spent.is_none() {
|
||||
if nd.spent.is_none() && nd.unconfirmed_spent.is_none() &&
|
||||
nd.witnesses.len() >= (anchor_offset + 1) {
|
||||
let witness = nd.witnesses.get(nd.witnesses.len() - anchor_offset - 1);
|
||||
|
||||
witness.map(|w| SpendableNote {
|
||||
|
||||
20
lib/src/lightwallet/startup_helpers.rs
Normal file
20
lib/src/lightwallet/startup_helpers.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
pub fn report_permission_error() {
|
||||
let user = std::env::var("USER").expect(
|
||||
"Unexpected error reading value of $USER!");
|
||||
let home = std::env::var("HOME").expect(
|
||||
"Unexpected error reading value of $HOME!");
|
||||
let current_executable = std::env::current_exe()
|
||||
.expect("Unexpected error reporting executable path!");
|
||||
eprintln!("USER: {}", user);
|
||||
eprintln!("HOME: {}", home);
|
||||
eprintln!("Executable: {}", current_executable.display());
|
||||
if home == "/" {
|
||||
eprintln!("User {} must have permission to write to '{}.komodo/HUSH3/' .",
|
||||
user,
|
||||
home);
|
||||
} else {
|
||||
eprintln!("User {} must have permission to write to '{}/.komodo/HUSH3/ .",
|
||||
user,
|
||||
home);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user