add wif import support
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -1744,6 +1744,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"bellman 0.1.0 (git+https://github.com/MyHush/librustzcash.git?rev=1a0204113d487cdaaf183c2967010e5214ff9e37)",
|
"bellman 0.1.0 (git+https://github.com/MyHush/librustzcash.git?rev=1a0204113d487cdaaf183c2967010e5214ff9e37)",
|
||||||
|
"bs58 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ embed_params = []
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
base58 = "0.1.0"
|
base58 = "0.1.0"
|
||||||
|
bs58 = { version = "0.2", features = ["check"] }
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
log4rs = "0.8.3"
|
log4rs = "0.8.3"
|
||||||
dirs = "2.0.2"
|
dirs = "2.0.2"
|
||||||
|
|||||||
@@ -701,6 +701,42 @@ impl Command for ImportCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct TImportCommand {}
|
||||||
|
impl Command for TImportCommand {
|
||||||
|
fn help(&self) -> String {
|
||||||
|
let mut h = vec![];
|
||||||
|
h.push("Import an external WIF");
|
||||||
|
h.push("Usage:");
|
||||||
|
h.push("timport wif (Begins with U");
|
||||||
|
h.push("");
|
||||||
|
|
||||||
|
h.join("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn short_help(&self) -> String {
|
||||||
|
"Import wif to the wallet".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exec(&self, args: &[&str], lightclient: &LightClient) -> String {
|
||||||
|
|
||||||
|
let key = args[0];
|
||||||
|
|
||||||
|
let r = match lightclient.do_import_tk(key.to_string()){
|
||||||
|
Ok(r) => r.pretty(2),
|
||||||
|
Err(e) => return format!("Error: {}", e),
|
||||||
|
};
|
||||||
|
|
||||||
|
match lightclient.do_rescan() {
|
||||||
|
Ok(_) => {},
|
||||||
|
Err(e) => return format!("Error: Rescan failed: {}", e),
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct HeightCommand {}
|
struct HeightCommand {}
|
||||||
impl Command for HeightCommand {
|
impl Command for HeightCommand {
|
||||||
fn help(&self) -> String {
|
fn help(&self) -> String {
|
||||||
@@ -864,6 +900,7 @@ pub fn get_commands() -> Box<HashMap<String, Box<dyn Command>>> {
|
|||||||
map.insert("addresses".to_string(), Box::new(AddressCommand{}));
|
map.insert("addresses".to_string(), Box::new(AddressCommand{}));
|
||||||
map.insert("height".to_string(), Box::new(HeightCommand{}));
|
map.insert("height".to_string(), Box::new(HeightCommand{}));
|
||||||
map.insert("import".to_string(), Box::new(ImportCommand{}));
|
map.insert("import".to_string(), Box::new(ImportCommand{}));
|
||||||
|
map.insert("timport".to_string(), Box::new(TImportCommand{}));
|
||||||
map.insert("export".to_string(), Box::new(ExportCommand{}));
|
map.insert("export".to_string(), Box::new(ExportCommand{}));
|
||||||
map.insert("info".to_string(), Box::new(InfoCommand{}));
|
map.insert("info".to_string(), Box::new(InfoCommand{}));
|
||||||
map.insert("coinsupply".to_string(), Box::new(CoinsupplyCommand{}));
|
map.insert("coinsupply".to_string(), Box::new(CoinsupplyCommand{}));
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
use crate::lightwallet::LightWallet;
|
use crate::lightwallet::LightWallet;
|
||||||
|
|
||||||
use rand::{rngs::OsRng, seq::SliceRandom};
|
|
||||||
|
|
||||||
use std::sync::{Arc, RwLock, Mutex, mpsc::channel};
|
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};
|
||||||
@@ -284,7 +282,7 @@ impl LightClientConfig {
|
|||||||
|
|
||||||
pub fn base58_secretkey_prefix(&self) -> [u8; 1] {
|
pub fn base58_secretkey_prefix(&self) -> [u8; 1] {
|
||||||
match &self.chain_name[..] {
|
match &self.chain_name[..] {
|
||||||
"main" => [0x80],
|
"main" => [0xBC],
|
||||||
"test" => [0xEF],
|
"test" => [0xEF],
|
||||||
"regtest" => [0xEF],
|
"regtest" => [0xEF],
|
||||||
c => panic!("Unknown chain {}", c)
|
c => panic!("Unknown chain {}", c)
|
||||||
@@ -612,7 +610,7 @@ impl LightClient {
|
|||||||
let z_addresses = wallet.get_all_zaddresses();
|
let z_addresses = wallet.get_all_zaddresses();
|
||||||
|
|
||||||
// Collect t addresses
|
// 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>>();
|
.collect::<Vec<String>>();
|
||||||
|
|
||||||
object!{
|
object!{
|
||||||
@@ -635,7 +633,7 @@ impl LightClient {
|
|||||||
}).collect::<Vec<JsonValue>>();
|
}).collect::<Vec<JsonValue>>();
|
||||||
|
|
||||||
// Collect t addresses
|
// 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
|
// Get the balance for this address
|
||||||
let balance = wallet.tbalance(Some(address.clone())) ;
|
let balance = wallet.tbalance(Some(address.clone())) ;
|
||||||
|
|
||||||
@@ -1012,6 +1010,31 @@ impl LightClient {
|
|||||||
Ok(array![new_address])
|
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
|
/// 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> {
|
pub fn do_import_key(&self, key: String, birthday: u64) -> Result<JsonValue, String> {
|
||||||
if key.starts_with(self.config.hrp_sapling_private_key()) {
|
if key.starts_with(self.config.hrp_sapling_private_key()) {
|
||||||
@@ -1303,53 +1326,18 @@ impl LightClient {
|
|||||||
// So, reset the total_reorg
|
// So, reset the total_reorg
|
||||||
total_reorg = 0;
|
total_reorg = 0;
|
||||||
|
|
||||||
// We'll also fetch all the txids that our transparent addresses are involved with
|
// If this is the first pass after a retry, fetch older t address txids too, becuse
|
||||||
{
|
// they might have been missed last time.
|
||||||
// Copy over addresses so as to not lock up the wallet, which we'll use inside the callback below.
|
let transparent_start_height = if pass == 1 && retry_count > 0 {
|
||||||
let addresses = self.wallet.read().unwrap()
|
start_height - scan_batch_size
|
||||||
.taddresses.read().unwrap().iter().map(|a| a.clone())
|
} else {
|
||||||
.collect::<Vec<String>>();
|
start_height
|
||||||
|
};
|
||||||
|
|
||||||
// Create a channel so the fetch_transparent_txids can send the results back
|
let no_cert = true;
|
||||||
let (ctx, crx) = channel();
|
|
||||||
let num_addresses = addresses.len();
|
|
||||||
|
|
||||||
for address in addresses {
|
// We'll also fetch all the txids that our transparent addresses are involved with
|
||||||
let wallet = self.wallet.clone();
|
self.scan_taddress_txids(&pool, block_times, transparent_start_height, end_height, no_cert)?;
|
||||||
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>>()?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do block height accounting
|
// Do block height accounting
|
||||||
last_scanned_height = end_height;
|
last_scanned_height = end_height;
|
||||||
@@ -1376,6 +1364,61 @@ impl LightClient {
|
|||||||
|
|
||||||
// Get the Raw transaction for all the wallet transactions
|
// 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 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)
|
// 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()
|
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))
|
.map(|wtx| (wtx.txid.clone(), wtx.block))
|
||||||
.collect::<Vec<(TxId, i32)>>();
|
.collect::<Vec<(TxId, i32)>>();
|
||||||
|
|
||||||
info!("Fetching {} new txids, total {} with decoy", txids_to_fetch.len(), all_new_txs.read().unwrap().len());
|
info!("Fetching {} new txids, total {} with decoy", txids_to_fetch.len(), decoy_txids.len());
|
||||||
txids_to_fetch.extend_from_slice(&all_new_txs.read().unwrap()[..]);
|
txids_to_fetch.extend_from_slice(&decoy_txids[..]);
|
||||||
txids_to_fetch.sort();
|
txids_to_fetch.sort();
|
||||||
txids_to_fetch.dedup();
|
txids_to_fetch.dedup();
|
||||||
|
|
||||||
let mut rng = OsRng;
|
|
||||||
txids_to_fetch.shuffle(&mut rng);
|
|
||||||
|
|
||||||
let num_fetches = txids_to_fetch.len();
|
let num_fetches = txids_to_fetch.len();
|
||||||
let (ctx, crx) = channel();
|
let (ctx, crx) = channel();
|
||||||
|
|
||||||
@@ -1420,15 +1460,7 @@ impl LightClient {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Wait for all the fetches to finish.
|
// Wait for all the fetches to finish.
|
||||||
let result = crx.iter().take(num_fetches).collect::<Result<Vec<()>, String>>();
|
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))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn do_send(&self, addrs: Vec<(&str, u64, Option<String>)>) -> Result<String, 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_export(None).is_err());
|
||||||
assert!(!lc.do_seed_phrase().is_err());
|
assert!(!lc.do_seed_phrase().is_err());
|
||||||
|
|
||||||
// This will lock the wallet again, so after this, we'll need to unlock again
|
// Can't add keys while unlocked but encrypted
|
||||||
assert!(!lc.do_new_address("R").is_err());
|
assert!(lc.do_new_address("R").is_err());
|
||||||
lc.wallet.write().unwrap().unlock("password".to_string()).unwrap();
|
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]
|
#[test]
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ 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, WalletTKey, WalletZKeyType};
|
||||||
|
|
||||||
pub const MAX_REORG: usize = 100;
|
pub const MAX_REORG: usize = 100;
|
||||||
pub const GAP_RULE_UNUSED_ADDRESSES: usize = 5;
|
pub const GAP_RULE_UNUSED_ADDRESSES: usize = 5;
|
||||||
@@ -124,11 +124,8 @@ pub struct LightWallet {
|
|||||||
// viewing keys and imported spending keys.
|
// viewing keys and imported spending keys.
|
||||||
zkeys: Arc<RwLock<Vec<WalletZKey>>>,
|
zkeys: Arc<RwLock<Vec<WalletZKey>>>,
|
||||||
|
|
||||||
|
// Transparent keys.
|
||||||
// Transparent keys. If the wallet is locked, then the secret keys will be encrypted,
|
tkeys: Arc<RwLock<Vec<WalletTKey>>>,
|
||||||
// but the addresses will be present.
|
|
||||||
tkeys: Arc<RwLock<Vec<secp256k1::SecretKey>>>,
|
|
||||||
pub taddresses: Arc<RwLock<Vec<String>>>,
|
|
||||||
|
|
||||||
blocks: Arc<RwLock<Vec<BlockData>>>,
|
blocks: Arc<RwLock<Vec<BlockData>>>,
|
||||||
pub txs: Arc<RwLock<HashMap<TxId, WalletTx>>>,
|
pub txs: Arc<RwLock<HashMap<TxId, WalletTx>>>,
|
||||||
@@ -149,7 +146,7 @@ pub struct LightWallet {
|
|||||||
|
|
||||||
impl LightWallet {
|
impl LightWallet {
|
||||||
pub fn serialized_version() -> u64 {
|
pub fn serialized_version() -> u64 {
|
||||||
return 7;
|
return 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_taddr_from_bip39seed(config: &LightClientConfig, bip39_seed: &[u8], pos: u32) -> SecretKey {
|
fn get_taddr_from_bip39seed(config: &LightClientConfig, bip39_seed: &[u8], pos: u32) -> SecretKey {
|
||||||
@@ -255,8 +252,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)])),
|
||||||
tkeys: Arc::new(RwLock::new(vec![tpk])),
|
tkeys: Arc::new(RwLock::new(vec![WalletTKey::new_hdkey(tpk, taddr)])),
|
||||||
taddresses: Arc::new(RwLock::new(vec![taddr])),
|
|
||||||
blocks: Arc::new(RwLock::new(vec![])),
|
blocks: Arc::new(RwLock::new(vec![])),
|
||||||
txs: Arc::new(RwLock::new(HashMap::new())),
|
txs: Arc::new(RwLock::new(HashMap::new())),
|
||||||
mempool_txs: Arc::new(RwLock::new(HashMap::new())),
|
mempool_txs: Arc::new(RwLock::new(HashMap::new())),
|
||||||
@@ -374,18 +370,29 @@ impl LightWallet {
|
|||||||
|
|
||||||
// Calculate the addresses
|
// Calculate the addresses
|
||||||
|
|
||||||
let tkeys = Vector::read(&mut reader, |r| {
|
let wallet_tkeys = if version >= 9 {
|
||||||
let mut tpk_bytes = [0u8; 32];
|
Vector::read(&mut reader, |r| {
|
||||||
r.read_exact(&mut tpk_bytes)?;
|
WalletTKey::read(r)
|
||||||
secp256k1::SecretKey::from_slice(&tpk_bytes).map_err(|e| io::Error::new(ErrorKind::InvalidData, e))
|
})?
|
||||||
})?;
|
|
||||||
|
|
||||||
let taddresses = if version >= 4 {
|
|
||||||
// Read the addresses
|
|
||||||
Vector::read(&mut reader, |r| utils::read_string(r))?
|
|
||||||
} else {
|
} else {
|
||||||
// Calculate the addresses
|
|
||||||
tkeys.iter().map(|sk| LightWallet::address_from_prefix_sk(&config.base58_pubkey_address(), sk)).collect()
|
let tkeys = Vector::read(&mut reader, |r| {
|
||||||
|
let mut tpk_bytes = [0u8; 32];
|
||||||
|
r.read_exact(&mut tpk_bytes)?;
|
||||||
|
secp256k1::SecretKey::from_slice(&tpk_bytes).map_err(|e| io::Error::new(ErrorKind::InvalidData, e))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let taddresses = if version >= 4 {
|
||||||
|
// Read the addresses
|
||||||
|
Vector::read(&mut reader, |r| utils::read_string(r))?
|
||||||
|
} else {
|
||||||
|
// Calculate the addresses
|
||||||
|
tkeys.iter().map(|sk| LightWallet::address_from_prefix_sk(&config.base58_pubkey_address(), sk)).collect()
|
||||||
|
};
|
||||||
|
|
||||||
|
tkeys.iter().zip(taddresses.iter()).map(|(k, a)|
|
||||||
|
WalletTKey::new_hdkey(*k, a.clone())
|
||||||
|
).collect()
|
||||||
};
|
};
|
||||||
|
|
||||||
let blocks = Vector::read(&mut reader, |r| BlockData::read(r))?;
|
let blocks = Vector::read(&mut reader, |r| BlockData::read(r))?;
|
||||||
@@ -414,8 +421,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)),
|
||||||
tkeys: Arc::new(RwLock::new(tkeys)),
|
tkeys: Arc::new(RwLock::new(wallet_tkeys)),
|
||||||
taddresses: Arc::new(RwLock::new(taddresses)),
|
|
||||||
blocks: Arc::new(RwLock::new(blocks)),
|
blocks: Arc::new(RwLock::new(blocks)),
|
||||||
txs: Arc::new(RwLock::new(txs)),
|
txs: Arc::new(RwLock::new(txs)),
|
||||||
mempool_txs: Arc::new(RwLock::new(HashMap::new())),
|
mempool_txs: Arc::new(RwLock::new(HashMap::new())),
|
||||||
@@ -456,12 +462,7 @@ impl LightWallet {
|
|||||||
|
|
||||||
// Write the transparent private keys
|
// Write the transparent private keys
|
||||||
Vector::write(&mut writer, &self.tkeys.read().unwrap(),
|
Vector::write(&mut writer, &self.tkeys.read().unwrap(),
|
||||||
|w, pk| w.write_all(&pk[..])
|
|w, tk| tk.write(w)
|
||||||
)?;
|
|
||||||
|
|
||||||
// Write the transparent addresses
|
|
||||||
Vector::write(&mut writer, &self.taddresses.read().unwrap(),
|
|
||||||
|w, a| utils::write_string(w, a)
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Vector::write(&mut writer, &self.blocks.read().unwrap(), |w, b| b.write(w))?;
|
Vector::write(&mut writer, &self.blocks.read().unwrap(), |w, b| b.write(w))?;
|
||||||
@@ -533,9 +534,14 @@ impl LightWallet {
|
|||||||
|
|
||||||
/// Get all t-address private keys. Returns a Vector of (address, secretkey)
|
/// Get all t-address private keys. Returns a Vector of (address, secretkey)
|
||||||
pub fn get_t_secret_keys(&self) -> Vec<(String, String)> {
|
pub fn get_t_secret_keys(&self) -> Vec<(String, String)> {
|
||||||
self.tkeys.read().unwrap().iter().map(|sk| {
|
self.tkeys.read().unwrap().iter().map(|wtk| {
|
||||||
(self.address_from_sk(sk),
|
let sk = if wtk.tkey.is_some() {
|
||||||
sk[..].to_base58check(&self.config.base58_secretkey_prefix(), &[0x01]))
|
wtk.tkey.unwrap()[..].to_base58check(&self.config.base58_secretkey_prefix(), &[0x01])
|
||||||
|
} else {
|
||||||
|
"".to_string()
|
||||||
|
};
|
||||||
|
|
||||||
|
(wtk.address.clone(), sk)
|
||||||
}).collect::<Vec<(String, String)>>()
|
}).collect::<Vec<(String, String)>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -547,6 +553,10 @@ impl LightWallet {
|
|||||||
return "Error: Can't add key while wallet is locked".to_string();
|
return "Error: Can't add key while wallet is locked".to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.encrypted {
|
||||||
|
return "Error: Can't add key while wallet is encrypted".to_string();
|
||||||
|
}
|
||||||
|
|
||||||
// Find the highest pos we have
|
// Find the highest pos we have
|
||||||
let pos = self.zkeys.read().unwrap().iter()
|
let pos = self.zkeys.read().unwrap().iter()
|
||||||
.filter(|zk| zk.hdkey_num.is_some())
|
.filter(|zk| zk.hdkey_num.is_some())
|
||||||
@@ -603,14 +613,49 @@ impl LightWallet {
|
|||||||
return "Error: Can't add key while wallet is locked".to_string();
|
return "Error: Can't add key while wallet is locked".to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.encrypted {
|
||||||
|
return "Error: Can't add key while wallet is encrypted".to_string();
|
||||||
|
}
|
||||||
|
|
||||||
let pos = self.tkeys.read().unwrap().len() as u32;
|
let pos = self.tkeys.read().unwrap().len() as u32;
|
||||||
let bip39_seed = bip39::Seed::new(&Mnemonic::from_entropy(&self.seed, Language::English).unwrap(), "");
|
let bip39_seed = bip39::Seed::new(&Mnemonic::from_entropy(&self.seed, Language::English).unwrap(), "");
|
||||||
|
|
||||||
let sk = LightWallet::get_taddr_from_bip39seed(&self.config, &bip39_seed.as_bytes(), pos);
|
let sk = LightWallet::get_taddr_from_bip39seed(&self.config, &bip39_seed.as_bytes(), pos);
|
||||||
let address = self.address_from_sk(&sk);
|
let address = self.address_from_sk(&sk);
|
||||||
|
|
||||||
self.tkeys.write().unwrap().push(sk);
|
self.tkeys.write().unwrap().push(WalletTKey::new_hdkey(sk, address.clone()));
|
||||||
self.taddresses.write().unwrap().push(address.clone());
|
|
||||||
|
address
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn import_taddr(&self, sk: String) -> String {
|
||||||
|
if !self.unlocked {
|
||||||
|
return "Error: Can't add key while wallet is locked".to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
//// Decode Wif to base58 to hex
|
||||||
|
let sk_to_bs58 = bs58::decode(sk).into_vec().unwrap();
|
||||||
|
|
||||||
|
let bs58_to_hex = hex::encode(sk_to_bs58);
|
||||||
|
|
||||||
|
//// Manipulate string, to exclude last 4 bytes (checksum bytes), first 2 bytes (secretkey prefix) and the compressed flag (works only for compressed Wifs!)
|
||||||
|
|
||||||
|
let slice_sk = &bs58_to_hex[2..66];
|
||||||
|
|
||||||
|
//// Get the SecretKey from slice
|
||||||
|
let secret_key = SecretKey::from_slice(&hex::decode(slice_sk).unwrap());
|
||||||
|
|
||||||
|
let sk_raw = secret_key.unwrap();
|
||||||
|
|
||||||
|
//// Make sure the key doesn't already exist
|
||||||
|
if self.tkeys.read().unwrap().iter().find(|&wk| wk.tkey.is_some() && wk.tkey.as_ref().unwrap() == &sk_raw.clone()).is_some() {
|
||||||
|
return "Error: Key already exists".to_string();
|
||||||
|
}
|
||||||
|
//// Get the taddr from key
|
||||||
|
let address = self.address_from_sk(&sk_raw);
|
||||||
|
|
||||||
|
//// Add to tkeys
|
||||||
|
self.tkeys.write().unwrap().push(WalletTKey::import_hdkey(sk_raw , address.clone()));
|
||||||
|
|
||||||
address
|
address
|
||||||
}
|
}
|
||||||
@@ -791,6 +836,12 @@ impl LightWallet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_all_taddresses(&self) -> Vec<String> {
|
||||||
|
self.tkeys.read().unwrap()
|
||||||
|
.iter()
|
||||||
|
.map(|wtx| wtx.address.clone()).collect()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_all_zaddresses(&self) -> Vec<String> {
|
pub fn get_all_zaddresses(&self) -> Vec<String> {
|
||||||
self.zkeys.read().unwrap().iter().map( |zk| {
|
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)
|
||||||
@@ -863,6 +914,11 @@ impl LightWallet {
|
|||||||
self.nonce = nonce.as_ref().to_vec();
|
self.nonce = nonce.as_ref().to_vec();
|
||||||
|
|
||||||
// Encrypt the individual keys
|
// Encrypt the individual keys
|
||||||
|
|
||||||
|
self.tkeys.write().unwrap().iter_mut()
|
||||||
|
.map(|k| k.encrypt(&key))
|
||||||
|
.collect::<io::Result<Vec<()>>>()?;
|
||||||
|
|
||||||
self.zkeys.write().unwrap().iter_mut()
|
self.zkeys.write().unwrap().iter_mut()
|
||||||
.map(|k| k.encrypt(&key))
|
.map(|k| k.encrypt(&key))
|
||||||
.collect::<io::Result<Vec<()>>>()?;
|
.collect::<io::Result<Vec<()>>>()?;
|
||||||
@@ -884,7 +940,12 @@ impl LightWallet {
|
|||||||
|
|
||||||
// Empty the seed and the secret keys
|
// Empty the seed and the secret keys
|
||||||
self.seed.copy_from_slice(&[0u8; 32]);
|
self.seed.copy_from_slice(&[0u8; 32]);
|
||||||
self.tkeys = Arc::new(RwLock::new(vec![]));
|
|
||||||
|
// Remove all the private key from the tkeys
|
||||||
|
self.tkeys.write().unwrap().iter_mut().map(|tk| {
|
||||||
|
tk.lock()
|
||||||
|
}).collect::<io::Result<Vec<_>>>()?;
|
||||||
|
|
||||||
// Remove all the private key from the zkeys
|
// Remove all the private key from the zkeys
|
||||||
self.zkeys.write().unwrap().iter_mut().map(|zk| {
|
self.zkeys.write().unwrap().iter_mut().map(|zk| {
|
||||||
zk.lock()
|
zk.lock()
|
||||||
@@ -922,19 +983,10 @@ impl LightWallet {
|
|||||||
// we need to get the 64 byte bip39 entropy
|
// we need to get the 64 byte bip39 entropy
|
||||||
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
|
// Go over the tkeys, and add the keys again
|
||||||
let mut tkeys = vec![];
|
self.tkeys.write().unwrap().iter_mut().map(|tk| {
|
||||||
for pos in 0..self.taddresses.read().unwrap().len() {
|
tk.unlock(&key)
|
||||||
let sk = LightWallet::get_taddr_from_bip39seed(&self.config, &bip39_seed.as_bytes(), pos as u32);
|
}).collect::<io::Result<Vec<()>>>()?;
|
||||||
let address = self.address_from_sk(&sk);
|
|
||||||
|
|
||||||
if address != self.taddresses.read().unwrap()[pos] {
|
|
||||||
return Err(io::Error::new(ErrorKind::InvalidData,
|
|
||||||
format!("taddress mismatch at {}. {} vs {}", pos, address, self.taddresses.read().unwrap()[pos])));
|
|
||||||
}
|
|
||||||
|
|
||||||
tkeys.push(sk);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Go over the zkeys, and add the spending keys again
|
// Go over the zkeys, and add the spending keys again
|
||||||
self.zkeys.write().unwrap().iter_mut().map(|zk| {
|
self.zkeys.write().unwrap().iter_mut().map(|zk| {
|
||||||
@@ -942,7 +994,6 @@ impl LightWallet {
|
|||||||
}).collect::<io::Result<Vec<()>>>()?;
|
}).collect::<io::Result<Vec<()>>>()?;
|
||||||
|
|
||||||
// Everything checks out, so we'll update our wallet with the decrypted values
|
// Everything checks out, so we'll update our wallet with the decrypted values
|
||||||
self.tkeys = Arc::new(RwLock::new(tkeys));
|
|
||||||
self.seed.copy_from_slice(&seed);
|
self.seed.copy_from_slice(&seed);
|
||||||
|
|
||||||
self.encrypted = true;
|
self.encrypted = true;
|
||||||
@@ -963,8 +1014,13 @@ impl LightWallet {
|
|||||||
self.unlock(passwd)?;
|
self.unlock(passwd)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove encryption from individual tkeys
|
||||||
|
self.tkeys.write().unwrap().iter_mut().map(|tk| {
|
||||||
|
tk.remove_encryption()
|
||||||
|
}).collect::<io::Result<Vec<()>>>()?;
|
||||||
|
|
||||||
// Remove encryption from individual zkeys
|
// Remove encryption from individual zkeys
|
||||||
self.zkeys.write().unwrap().iter_mut().map(|zk| {
|
self.zkeys.write().unwrap().iter_mut().map(|zk| {
|
||||||
zk.remove_encryption()
|
zk.remove_encryption()
|
||||||
}).collect::<io::Result<Vec<()>>>()?;
|
}).collect::<io::Result<Vec<()>>>()?;
|
||||||
|
|
||||||
@@ -1159,7 +1215,12 @@ impl LightWallet {
|
|||||||
// If one of the last 'n' taddress was used, ensure we add the next HD taddress to the wallet.
|
// If one of the last 'n' taddress was used, ensure we add the next HD taddress to the wallet.
|
||||||
pub fn ensure_hd_taddresses(&self, address: &String) {
|
pub fn ensure_hd_taddresses(&self, address: &String) {
|
||||||
let last_addresses = {
|
let last_addresses = {
|
||||||
self.taddresses.read().unwrap().iter().rev().take(GAP_RULE_UNUSED_ADDRESSES).map(|s| s.clone()).collect::<Vec<String>>()
|
self.tkeys.read().unwrap()
|
||||||
|
.iter()
|
||||||
|
.map(|t| t.address.clone())
|
||||||
|
.rev().take(GAP_RULE_UNUSED_ADDRESSES).map(|s|
|
||||||
|
s.clone())
|
||||||
|
.collect::<Vec<String>>()
|
||||||
};
|
};
|
||||||
|
|
||||||
match last_addresses.iter().position(|s| *s == *address) {
|
match last_addresses.iter().position(|s| *s == *address) {
|
||||||
@@ -1252,7 +1313,8 @@ impl LightWallet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Scan for t outputs
|
// Scan for t outputs
|
||||||
let all_taddresses = self.taddresses.read().unwrap().iter()
|
let all_taddresses = self.tkeys.read().unwrap().iter()
|
||||||
|
.map(|wtx| wtx.address.clone())
|
||||||
.map(|a| a.clone())
|
.map(|a| a.clone())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
for address in all_taddresses {
|
for address in all_taddresses {
|
||||||
@@ -1279,7 +1341,8 @@ impl LightWallet {
|
|||||||
// outgoing metadata
|
// outgoing metadata
|
||||||
|
|
||||||
// Collect our t-addresses
|
// Collect our t-addresses
|
||||||
let wallet_taddrs = self.taddresses.read().unwrap().iter()
|
let wallet_taddrs = self.tkeys.read().unwrap().iter()
|
||||||
|
.map(|wtx| wtx.address.clone())
|
||||||
.map(|a| a.clone())
|
.map(|a| a.clone())
|
||||||
.collect::<HashSet<String>>();
|
.collect::<HashSet<String>>();
|
||||||
|
|
||||||
@@ -2037,7 +2100,8 @@ impl LightWallet {
|
|||||||
// Create a map from address -> sk for all taddrs, so we can spend from the
|
// Create a map from address -> sk for all taddrs, so we can spend from the
|
||||||
// right address
|
// right address
|
||||||
let address_to_sk = self.tkeys.read().unwrap().iter()
|
let address_to_sk = self.tkeys.read().unwrap().iter()
|
||||||
.map(|sk| (self.address_from_sk(&sk), sk.clone()))
|
.filter(|wtk| wtk.tkey.is_some())
|
||||||
|
.map(|wtk| (wtk.address.clone(), wtk.tkey.unwrap().clone()))
|
||||||
.collect::<HashMap<_,_>>();
|
.collect::<HashMap<_,_>>();
|
||||||
|
|
||||||
// Add all tinputs
|
// Add all tinputs
|
||||||
@@ -2050,15 +2114,11 @@ impl LightWallet {
|
|||||||
script_pubkey: Script { 0: utxo.script.clone() },
|
script_pubkey: Script { 0: utxo.script.clone() },
|
||||||
};
|
};
|
||||||
|
|
||||||
match address_to_sk.get(&utxo.address) {
|
if let Some(sk) = address_to_sk.get(&utxo.address) {
|
||||||
Some(sk) => builder.add_transparent_input(*sk, outpoint.clone(), coin.clone()),
|
return builder.add_transparent_input(*sk, outpoint.clone(), coin.clone())
|
||||||
None => {
|
} else {
|
||||||
// Something is very wrong
|
info!("Not adding a UTXO because secret key is absent.");
|
||||||
let e = format!("Couldn't find the secreykey for taddr {}", utxo.address);
|
return Ok(())
|
||||||
error!("{}", e);
|
|
||||||
|
|
||||||
Err(zcash_primitives::transaction::builder::Error::InvalidAddress)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -13,7 +13,167 @@ use zcash_primitives::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::lightclient::{LightClientConfig};
|
use crate::lightclient::{LightClientConfig};
|
||||||
use crate::lightwallet::LightWallet;
|
use crate::lightwallet::{LightWallet, utils};
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug, Clone)]
|
||||||
|
pub enum WalletTKeyType {
|
||||||
|
HdKey = 0,
|
||||||
|
ImportedKey = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// A struct that holds z-address private keys or view keys
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub struct WalletTKey {
|
||||||
|
pub(super) keytype: WalletTKeyType,
|
||||||
|
locked: bool,
|
||||||
|
pub(super) address: String,
|
||||||
|
pub(super) tkey: Option<secp256k1::SecretKey>,
|
||||||
|
|
||||||
|
// If locked, the encrypted key is here
|
||||||
|
enc_key: Option<Vec<u8>>,
|
||||||
|
nonce: Option<Vec<u8>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WalletTKey {
|
||||||
|
pub fn new_hdkey(key: secp256k1::SecretKey, address: String) -> Self {
|
||||||
|
WalletTKey {
|
||||||
|
keytype: WalletTKeyType::HdKey,
|
||||||
|
locked: false,
|
||||||
|
address,
|
||||||
|
tkey: Some(key),
|
||||||
|
|
||||||
|
enc_key: None,
|
||||||
|
nonce: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn import_hdkey(key: secp256k1::SecretKey, address: String) -> Self {
|
||||||
|
WalletTKey {
|
||||||
|
keytype: WalletTKeyType::ImportedKey,
|
||||||
|
locked: false,
|
||||||
|
address,
|
||||||
|
tkey: Some(key),
|
||||||
|
|
||||||
|
enc_key: None,
|
||||||
|
nonce: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialized_version() -> u8 {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read<R: Read>(mut inp: R) -> io::Result<Self> {
|
||||||
|
let version = inp.read_u8()?;
|
||||||
|
assert!(version <= Self::serialized_version());
|
||||||
|
|
||||||
|
let keytype: WalletTKeyType = match inp.read_u32::<LittleEndian>()? {
|
||||||
|
0 => Ok(WalletTKeyType::HdKey),
|
||||||
|
1 => Ok(WalletTKeyType::ImportedKey),
|
||||||
|
n => Err(io::Error::new(ErrorKind::InvalidInput, format!("Unknown tkey type {}", n)))
|
||||||
|
}?;
|
||||||
|
|
||||||
|
let locked = inp.read_u8()? > 0;
|
||||||
|
|
||||||
|
let address = utils::read_string(&mut inp)?;
|
||||||
|
let tkey = Optional::read(&mut inp, |r| {
|
||||||
|
let mut tpk_bytes = [0u8; 32];
|
||||||
|
r.read_exact(&mut tpk_bytes)?;
|
||||||
|
secp256k1::SecretKey::from_slice(&tpk_bytes).map_err(|e| io::Error::new(ErrorKind::InvalidData, e))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let enc_key = Optional::read(&mut inp, |r|
|
||||||
|
Vector::read(r, |r| r.read_u8()))?;
|
||||||
|
let nonce = Optional::read(&mut inp, |r|
|
||||||
|
Vector::read(r, |r| r.read_u8()))?;
|
||||||
|
|
||||||
|
Ok(WalletTKey {
|
||||||
|
keytype,
|
||||||
|
locked,
|
||||||
|
address,
|
||||||
|
tkey,
|
||||||
|
enc_key,
|
||||||
|
nonce,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write<W: Write>(&self, mut out: W) -> io::Result<()> {
|
||||||
|
out.write_u8(Self::serialized_version())?;
|
||||||
|
|
||||||
|
out.write_u32::<LittleEndian>(self.keytype.clone() as u32)?;
|
||||||
|
|
||||||
|
out.write_u8(self.locked as u8)?;
|
||||||
|
|
||||||
|
utils::write_string(&mut out, &self.address)?;
|
||||||
|
Optional::write(&mut out, &self.tkey, |w, pk|
|
||||||
|
w.write_all(&pk[..])
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Write enc_key
|
||||||
|
Optional::write(&mut out, &self.enc_key, |o, v|
|
||||||
|
Vector::write(o, v, |o,n| o.write_u8(*n)))?;
|
||||||
|
|
||||||
|
// Write nonce
|
||||||
|
Optional::write(&mut out, &self.nonce, |o, v|
|
||||||
|
Vector::write(o, v, |o,n| o.write_u8(*n)))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn lock(&mut self) -> io::Result<()> {
|
||||||
|
// For keys, encrypt the key into enckey
|
||||||
|
// assert that we have the encrypted key.
|
||||||
|
if self.enc_key.is_none() {
|
||||||
|
return Err(Error::new(ErrorKind::InvalidInput, "Can't lock when t-addr private key is not encrypted"));
|
||||||
|
}
|
||||||
|
self.tkey = None;
|
||||||
|
self.locked = true;
|
||||||
|
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unlock(&mut self, key: &secretbox::Key) -> io::Result<()> {
|
||||||
|
// For imported keys, we need to decrypt from the encrypted key
|
||||||
|
let nonce = secretbox::Nonce::from_slice(&self.nonce.as_ref().unwrap()).unwrap();
|
||||||
|
let sk_bytes = match secretbox::open(&self.enc_key.as_ref().unwrap(), &nonce, &key) {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(_) => {return Err(io::Error::new(ErrorKind::InvalidData, "Decryption failed. Is your password correct?"));}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.tkey = Some(secp256k1::SecretKey::from_slice(&sk_bytes[..]).map_err(|e|
|
||||||
|
io::Error::new(ErrorKind::InvalidData, format!("{}", e))
|
||||||
|
)?);
|
||||||
|
|
||||||
|
self.locked = false;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encrypt(&mut self, key: &secretbox::Key) -> io::Result<()> {
|
||||||
|
// For keys, encrypt the key into enckey
|
||||||
|
let nonce = secretbox::gen_nonce();
|
||||||
|
|
||||||
|
let sk_bytes = &self.tkey.unwrap()[..];
|
||||||
|
|
||||||
|
self.enc_key = Some(secretbox::seal(&sk_bytes, &nonce, &key));
|
||||||
|
self.nonce = Some(nonce.as_ref().to_vec());
|
||||||
|
|
||||||
|
self.tkey = None;
|
||||||
|
|
||||||
|
// Also lock after encrypt
|
||||||
|
self.lock()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_encryption(&mut self) -> io::Result<()> {
|
||||||
|
if self.locked {
|
||||||
|
return Err(Error::new(ErrorKind::InvalidInput, "Can't remove encryption while locked"));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.enc_key = None;
|
||||||
|
self.nonce = None;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Debug, Clone)]
|
#[derive(PartialEq, Debug, Clone)]
|
||||||
pub enum WalletZKeyType {
|
pub enum WalletZKeyType {
|
||||||
|
|||||||
Reference in New Issue
Block a user