add wif import support

This commit is contained in:
DenioD
2020-09-30 21:17:04 +02:00
parent 7637fba279
commit 856f1a1ea7
6 changed files with 426 additions and 131 deletions

View File

@@ -1,7 +1,5 @@
use crate::lightwallet::LightWallet;
use rand::{rngs::OsRng, seq::SliceRandom};
use std::sync::{Arc, RwLock, Mutex, mpsc::channel};
use std::sync::atomic::{AtomicI32, AtomicUsize, Ordering};
use std::path::{Path, PathBuf};
@@ -284,7 +282,7 @@ impl LightClientConfig {
pub fn base58_secretkey_prefix(&self) -> [u8; 1] {
match &self.chain_name[..] {
"main" => [0x80],
"main" => [0xBC],
"test" => [0xEF],
"regtest" => [0xEF],
c => panic!("Unknown chain {}", c)
@@ -612,7 +610,7 @@ impl LightClient {
let z_addresses = wallet.get_all_zaddresses();
// Collect t addresses
let t_addresses = wallet.taddresses.read().unwrap().iter().map( |a| a.clone() )
let t_addresses = wallet.get_all_taddresses().iter().map( |a| a.clone() )
.collect::<Vec<String>>();
object!{
@@ -635,7 +633,7 @@ impl LightClient {
}).collect::<Vec<JsonValue>>();
// Collect t addresses
let t_addresses = wallet.taddresses.read().unwrap().iter().map( |address| {
let t_addresses = wallet.get_all_taddresses().iter().map( |address| {
// Get the balance for this address
let balance = wallet.tbalance(Some(address.clone())) ;
@@ -1012,6 +1010,31 @@ impl LightClient {
Ok(array![new_address])
}
/// Import a new private key
pub fn do_import_tk(&self, sk: String) -> Result<JsonValue, String> {
if !self.wallet.read().unwrap().is_unlocked_for_spending() {
error!("Wallet is locked");
return Err("Wallet is locked".to_string());
}
let new_address = {
let wallet = self.wallet.write().unwrap();
let addr = wallet.import_taddr(sk);
if addr.starts_with("Error") {
let e = format!("Error creating new address{}", addr);
error!("{}", e);
return Err(e);
}
addr
};
self.do_save()?;
Ok(array![new_address])
}
/// Convinence function to determine what type of key this is and import it
pub fn do_import_key(&self, key: String, birthday: u64) -> Result<JsonValue, String> {
if key.starts_with(self.config.hrp_sapling_private_key()) {
@@ -1303,53 +1326,18 @@ impl LightClient {
// So, reset the total_reorg
total_reorg = 0;
// We'll also fetch all the txids that our transparent addresses are involved with
{
// Copy over addresses so as to not lock up the wallet, which we'll use inside the callback below.
let addresses = self.wallet.read().unwrap()
.taddresses.read().unwrap().iter().map(|a| a.clone())
.collect::<Vec<String>>();
// If this is the first pass after a retry, fetch older t address txids too, becuse
// they might have been missed last time.
let transparent_start_height = if pass == 1 && retry_count > 0 {
start_height - scan_batch_size
} else {
start_height
};
// Create a channel so the fetch_transparent_txids can send the results back
let (ctx, crx) = channel();
let num_addresses = addresses.len();
let no_cert = true;
for address in addresses {
let wallet = self.wallet.clone();
let block_times_inner = block_times.clone();
// If this is the first pass after a retry, fetch older t address txids too, becuse
// they might have been missed last time.
let transparent_start_height = if pass == 1 && retry_count > 0 {
start_height - scan_batch_size
} else {
start_height
};
let pool = pool.clone();
let server_uri = self.get_server_uri();
let ctx = ctx.clone();
let no_cert = self.config.no_cert_verification;
pool.execute(move || {
// Fetch the transparent transactions for this address, and send the results
// via the channel
let r = fetch_transparent_txids(&server_uri, address, transparent_start_height, end_height, no_cert,
move |tx_bytes: &[u8], height: u64| {
let tx = Transaction::read(tx_bytes).unwrap();
// Scan this Tx for transparent inputs and outputs
let datetime = block_times_inner.read().unwrap().get(&height).map(|v| *v).unwrap_or(0);
wallet.read().unwrap().scan_full_tx(&tx, height as i32, datetime as u64);
});
ctx.send(r).unwrap();
});
}
// Collect all results from the transparent fetches, and make sure everything was OK.
// If it was not, we return an error, which will go back to the retry
crx.iter().take(num_addresses).collect::<Result<Vec<()>, String>>()?;
}
// We'll also fetch all the txids that our transparent addresses are involved with
self.scan_taddress_txids(&pool, block_times, transparent_start_height, end_height, no_cert)?;
// Do block height accounting
last_scanned_height = end_height;
@@ -1376,6 +1364,61 @@ impl LightClient {
// Get the Raw transaction for all the wallet transactions
{
let decoy_txids = all_new_txs.read().unwrap();
match self.scan_fill_fulltxs(&pool, decoy_txids.to_vec()) {
Ok(_) => Ok(object!{
"result" => "success",
"latest_block" => latest_block,
"downloaded_bytes" => bytes_downloaded.load(Ordering::SeqCst)
}),
Err(e) => Err(format!("Error fetching all txns for memos: {}", e))
}
}
}
fn scan_taddress_txids(&self, pool: &ThreadPool, block_times: Arc<RwLock<HashMap<u64, u32>>>, start_height: u64, end_height: u64, no_cert: bool) -> Result<Vec<()>, String> {
// Copy over addresses so as to not lock up the wallet, which we'll use inside the callback below.
let addresses = self.wallet.read().unwrap()
.get_all_taddresses().iter()
.map(|a| a.clone())
.collect::<Vec<String>>();
// Create a channel so the fetch_transparent_txids can send the results back
let (ctx, crx) = channel();
let num_addresses = addresses.len();
for address in addresses {
let wallet = self.wallet.clone();
let pool = pool.clone();
let server_uri = self.get_server_uri();
let ctx = ctx.clone();
let block_times = block_times.clone();
pool.execute(move || {
// Fetch the transparent transactions for this address, and send the results
// via the channel
let r = fetch_transparent_txids(&server_uri, address, start_height, end_height,no_cert,
move |tx_bytes: &[u8], height: u64| {
let tx = Transaction::read(tx_bytes).unwrap();
// Scan this Tx for transparent inputs and outputs
let datetime = block_times.read().unwrap().get(&height).map(|v| *v).unwrap_or(0);
wallet.read().unwrap().scan_full_tx(&tx, height as i32, datetime as u64);
});
ctx.send(r).unwrap();
});
}
// Collect all results from the transparent fetches, and make sure everything was OK.
// If it was not, we return an error, which will go back to the retry
crx.iter().take(num_addresses).collect::<Result<Vec<()>, String>>()
}
fn scan_fill_fulltxs(&self, pool: &ThreadPool, decoy_txids: Vec<(TxId, i32)>) -> Result<Vec<()>, String> {
// We need to first copy over the Txids from the wallet struct, because
// we need to free the read lock from here (Because we'll self.wallet.txs later)
let mut txids_to_fetch: Vec<(TxId, i32)> = self.wallet.read().unwrap().txs.read().unwrap().values()
@@ -1383,14 +1426,11 @@ impl LightClient {
.map(|wtx| (wtx.txid.clone(), wtx.block))
.collect::<Vec<(TxId, i32)>>();
info!("Fetching {} new txids, total {} with decoy", txids_to_fetch.len(), all_new_txs.read().unwrap().len());
txids_to_fetch.extend_from_slice(&all_new_txs.read().unwrap()[..]);
info!("Fetching {} new txids, total {} with decoy", txids_to_fetch.len(), decoy_txids.len());
txids_to_fetch.extend_from_slice(&decoy_txids[..]);
txids_to_fetch.sort();
txids_to_fetch.dedup();
let mut rng = OsRng;
txids_to_fetch.shuffle(&mut rng);
let num_fetches = txids_to_fetch.len();
let (ctx, crx) = channel();
@@ -1420,15 +1460,7 @@ impl LightClient {
};
// Wait for all the fetches to finish.
let result = crx.iter().take(num_fetches).collect::<Result<Vec<()>, String>>();
match result {
Ok(_) => Ok(object!{
"result" => "success",
"latest_block" => latest_block,
"downloaded_bytes" => bytes_downloaded.load(Ordering::SeqCst)
}),
Err(e) => Err(format!("Error fetching all txns for memos: {}", e))
}
crx.iter().take(num_fetches).collect::<Result<Vec<()>, String>>()
}
pub fn do_send(&self, addrs: Vec<(&str, u64, Option<String>)>) -> Result<String, String> {
@@ -1486,11 +1518,15 @@ pub mod tests {
assert!(!lc.do_export(None).is_err());
assert!(!lc.do_seed_phrase().is_err());
// This will lock the wallet again, so after this, we'll need to unlock again
assert!(!lc.do_new_address("R").is_err());
lc.wallet.write().unwrap().unlock("password".to_string()).unwrap();
// Can't add keys while unlocked but encrypted
assert!(lc.do_new_address("R").is_err());
assert!(lc.do_new_address("zs").is_err());
// Remove encryption, which will allow adding
lc.wallet.write().unwrap().remove_encryption("password".to_string()).unwrap();
assert!(!lc.do_new_address("zs").is_err());
assert!(lc.do_new_address("R").is_ok());
assert!(lc.do_new_address("zs").is_ok());
}
#[test]