mirror of
https://github.com/Qortal/piratewallet-light-cli.git
synced 2025-02-12 02:05:47 +00:00
RwLock for secret keys
This commit is contained in:
parent
e0725684b8
commit
e358448e45
1
.gitignore
vendored
1
.gitignore
vendored
@ -4,3 +4,4 @@ Cargo.lock
|
|||||||
history.txt
|
history.txt
|
||||||
/.idea/
|
/.idea/
|
||||||
tarpaulin-report.html
|
tarpaulin-report.html
|
||||||
|
/log*
|
@ -311,6 +311,32 @@ impl Command for TransactionsCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct NewAddressCommand {}
|
||||||
|
impl Command for NewAddressCommand {
|
||||||
|
fn help(&self) -> String {
|
||||||
|
let mut h = vec![];
|
||||||
|
h.push("Create a new address in this wallet");
|
||||||
|
h.push("Usage:");
|
||||||
|
h.push("new [z | t]");
|
||||||
|
h.push("");
|
||||||
|
h.push("Example:");
|
||||||
|
h.push("To create a new z address:");
|
||||||
|
h.push("new z");
|
||||||
|
h.join("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn short_help(&self) -> String {
|
||||||
|
"Create a new address in this wallet".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exec(&self, args: &[&str], lightclient: &LightClient) -> String {
|
||||||
|
if args.len() != 1 {
|
||||||
|
return format!("No address type specified\n{}", self.help());
|
||||||
|
}
|
||||||
|
|
||||||
|
format!("{}", lightclient.do_new_address(args[0]).pretty(2))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct NotesCommand {}
|
struct NotesCommand {}
|
||||||
impl Command for NotesCommand {
|
impl Command for NotesCommand {
|
||||||
@ -388,6 +414,7 @@ pub fn get_commands() -> Box<HashMap<String, Box<dyn Command>>> {
|
|||||||
map.insert("quit".to_string(), Box::new(QuitCommand{}));
|
map.insert("quit".to_string(), Box::new(QuitCommand{}));
|
||||||
map.insert("list".to_string(), Box::new(TransactionsCommand{}));
|
map.insert("list".to_string(), Box::new(TransactionsCommand{}));
|
||||||
map.insert("notes".to_string(), Box::new(NotesCommand{}));
|
map.insert("notes".to_string(), Box::new(NotesCommand{}));
|
||||||
|
map.insert("new".to_string(), Box::new(NewAddressCommand{}));
|
||||||
map.insert("seed".to_string(), Box::new(SeedCommand{}));
|
map.insert("seed".to_string(), Box::new(SeedCommand{}));
|
||||||
|
|
||||||
Box::new(map)
|
Box::new(map)
|
||||||
|
@ -10,7 +10,7 @@ use std::io;
|
|||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
use std::io::{BufReader, BufWriter, Error, ErrorKind};
|
use std::io::{BufReader, BufWriter, Error, ErrorKind};
|
||||||
|
|
||||||
use json::{object, JsonValue};
|
use json::{object, array, JsonValue};
|
||||||
use zcash_primitives::transaction::{TxId, Transaction};
|
use zcash_primitives::transaction::{TxId, Transaction};
|
||||||
use zcash_client_backend::{
|
use zcash_client_backend::{
|
||||||
constants::testnet, constants::mainnet, constants::regtest, encoding::encode_payment_address,
|
constants::testnet, constants::mainnet, constants::regtest, encoding::encode_payment_address,
|
||||||
@ -225,7 +225,7 @@ impl LightClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Export private keys
|
// Export private keys
|
||||||
pub fn do_export(&self, addr: Option<String>) -> json::JsonValue {
|
pub fn do_export(&self, addr: Option<String>) -> JsonValue {
|
||||||
// Clone address so it can be moved into the closure
|
// Clone address so it can be moved into the closure
|
||||||
let address = addr.clone();
|
let address = addr.clone();
|
||||||
|
|
||||||
@ -259,14 +259,14 @@ impl LightClient {
|
|||||||
all_keys.into()
|
all_keys.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn do_address(&self) -> json::JsonValue {
|
pub fn do_address(&self) -> JsonValue {
|
||||||
// Collect z addresses
|
// Collect z addresses
|
||||||
let z_addresses = self.wallet.address.iter().map( |ad| {
|
let z_addresses = self.wallet.address.read().unwrap().iter().map( |ad| {
|
||||||
encode_payment_address(self.config.hrp_sapling_address(), &ad)
|
encode_payment_address(self.config.hrp_sapling_address(), &ad)
|
||||||
}).collect::<Vec<String>>();
|
}).collect::<Vec<String>>();
|
||||||
|
|
||||||
// Collect t addresses
|
// Collect t addresses
|
||||||
let t_addresses = self.wallet.tkeys.iter().map( |sk| {
|
let t_addresses = self.wallet.tkeys.read().unwrap().iter().map( |sk| {
|
||||||
self.wallet.address_from_sk(&sk)
|
self.wallet.address_from_sk(&sk)
|
||||||
}).collect::<Vec<String>>();
|
}).collect::<Vec<String>>();
|
||||||
|
|
||||||
@ -276,9 +276,9 @@ impl LightClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn do_balance(&self) -> json::JsonValue {
|
pub fn do_balance(&self) -> JsonValue {
|
||||||
// Collect z addresses
|
// Collect z addresses
|
||||||
let z_addresses = self.wallet.address.iter().map( |ad| {
|
let z_addresses = self.wallet.address.read().unwrap().iter().map( |ad| {
|
||||||
let address = encode_payment_address(self.config.hrp_sapling_address(), &ad);
|
let address = encode_payment_address(self.config.hrp_sapling_address(), &ad);
|
||||||
object!{
|
object!{
|
||||||
"address" => address.clone(),
|
"address" => address.clone(),
|
||||||
@ -288,7 +288,7 @@ impl LightClient {
|
|||||||
}).collect::<Vec<JsonValue>>();
|
}).collect::<Vec<JsonValue>>();
|
||||||
|
|
||||||
// Collect t addresses
|
// Collect t addresses
|
||||||
let t_addresses = self.wallet.tkeys.iter().map( |sk| {
|
let t_addresses = self.wallet.tkeys.read().unwrap().iter().map( |sk| {
|
||||||
let address = self.wallet.address_from_sk(&sk);
|
let address = self.wallet.address_from_sk(&sk);
|
||||||
|
|
||||||
// Get the balance for this address
|
// Get the balance for this address
|
||||||
@ -516,6 +516,23 @@ impl LightClient {
|
|||||||
JsonValue::Array(tx_list)
|
JsonValue::Array(tx_list)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new address, deriving it from the seed.
|
||||||
|
pub fn do_new_address(&self, addr_type: &str) -> JsonValue {
|
||||||
|
let new_address = match addr_type {
|
||||||
|
"z" => self.wallet.add_zaddr(),
|
||||||
|
"t" => self.wallet.add_taddr(),
|
||||||
|
_ => {
|
||||||
|
let e = format!("Unrecognized address type: {}", addr_type);
|
||||||
|
error!("{}", e);
|
||||||
|
return object!{
|
||||||
|
"error" => e
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
array![new_address]
|
||||||
|
}
|
||||||
|
|
||||||
pub fn do_rescan(&self) -> String {
|
pub fn do_rescan(&self) -> String {
|
||||||
info!("Rescan starting");
|
info!("Rescan starting");
|
||||||
// First, clear the state from the wallet
|
// First, clear the state from the wallet
|
||||||
@ -630,7 +647,7 @@ impl LightClient {
|
|||||||
|
|
||||||
// We'll also fetch all the txids that our transparent addresses are involved with
|
// We'll also fetch all the txids that our transparent addresses are involved with
|
||||||
// TODO: Use for all t addresses
|
// TODO: Use for all t addresses
|
||||||
let address = self.wallet.address_from_sk(&self.wallet.tkeys[0]);
|
let address = self.wallet.address_from_sk(&self.wallet.tkeys.read().unwrap()[0]);
|
||||||
let wallet = self.wallet.clone();
|
let wallet = self.wallet.clone();
|
||||||
fetch_transparent_txids(&self.get_server_uri(), address, start_height, end_height,
|
fetch_transparent_txids(&self.get_server_uri(), address, start_height, end_height,
|
||||||
move |tx_bytes: &[u8], height: u64 | {
|
move |tx_bytes: &[u8], height: u64 | {
|
||||||
|
@ -9,6 +9,7 @@ use log::{info, warn, error};
|
|||||||
|
|
||||||
use protobuf::parse_from_bytes;
|
use protobuf::parse_from_bytes;
|
||||||
|
|
||||||
|
use secp256k1::SecretKey;
|
||||||
use bip39::{Mnemonic, Language};
|
use bip39::{Mnemonic, Language};
|
||||||
|
|
||||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||||
@ -62,7 +63,7 @@ pub fn double_sha256(payload: &[u8]) -> Vec<u8> {
|
|||||||
h2.to_vec()
|
h2.to_vec()
|
||||||
}
|
}
|
||||||
|
|
||||||
use base58::{ToBase58, FromBase58};
|
use base58::{ToBase58};
|
||||||
|
|
||||||
/// A trait for converting a [u8] to base58 encoded string.
|
/// A trait for converting a [u8] to base58 encoded string.
|
||||||
pub trait ToBase58Check {
|
pub trait ToBase58Check {
|
||||||
@ -85,25 +86,25 @@ impl ToBase58Check for [u8] {
|
|||||||
payload.to_base58()
|
payload.to_base58()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//
|
||||||
pub trait FromBase58Check {
|
//pub trait FromBase58Check {
|
||||||
fn from_base58check(&self, version: &[u8], suffix: &[u8]) -> Vec<u8>;
|
// fn from_base58check(&self, version: &[u8], suffix: &[u8]) -> Vec<u8>;
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
|
//
|
||||||
impl FromBase58Check for str {
|
//impl FromBase58Check for str {
|
||||||
fn from_base58check(&self, version: &[u8], suffix: &[u8]) -> Vec<u8> {
|
// fn from_base58check(&self, version: &[u8], suffix: &[u8]) -> Vec<u8> {
|
||||||
let mut payload: Vec<u8> = Vec::new();
|
// let mut payload: Vec<u8> = Vec::new();
|
||||||
let bytes = self.from_base58().unwrap();
|
// let bytes = self.from_base58().unwrap();
|
||||||
|
//
|
||||||
let start = version.len();
|
// let start = version.len();
|
||||||
let end = bytes.len() - (4 + suffix.len());
|
// let end = bytes.len() - (4 + suffix.len());
|
||||||
|
//
|
||||||
payload.extend(&bytes[start..end]);
|
// payload.extend(&bytes[start..end]);
|
||||||
|
//
|
||||||
payload
|
// payload
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
|
||||||
|
|
||||||
pub struct LightWallet {
|
pub struct LightWallet {
|
||||||
@ -112,12 +113,12 @@ pub struct LightWallet {
|
|||||||
// List of keys, actually in this wallet. This may include more
|
// List of keys, actually in this wallet. This may include more
|
||||||
// than keys derived from the seed, for example, if user imports
|
// than keys derived from the seed, for example, if user imports
|
||||||
// a private key
|
// a private key
|
||||||
extsks: Vec<ExtendedSpendingKey>,
|
extsks: Arc<RwLock<Vec<ExtendedSpendingKey>>>,
|
||||||
extfvks: Vec<ExtendedFullViewingKey>,
|
extfvks: Arc<RwLock<Vec<ExtendedFullViewingKey>>>,
|
||||||
pub address: Vec<PaymentAddress<Bls12>>,
|
pub address: Arc<RwLock<Vec<PaymentAddress<Bls12>>>>,
|
||||||
|
|
||||||
// Transparent keys. TODO: Make it not pubic
|
// Transparent keys. TODO: Make it not pubic
|
||||||
pub tkeys: Vec<secp256k1::SecretKey>,
|
pub tkeys: Arc<RwLock<Vec<secp256k1::SecretKey>>>,
|
||||||
|
|
||||||
blocks: Arc<RwLock<Vec<BlockData>>>,
|
blocks: Arc<RwLock<Vec<BlockData>>>,
|
||||||
pub txs: Arc<RwLock<HashMap<TxId, WalletTx>>>,
|
pub txs: Arc<RwLock<HashMap<TxId, WalletTx>>>,
|
||||||
@ -135,14 +136,26 @@ impl LightWallet {
|
|||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_pk_from_bip39seed(config: LightClientConfig, bip39seed: &[u8]) ->
|
fn get_taddr_from_bip39seed(config: &LightClientConfig, bip39_seed: &[u8], pos: u32) -> SecretKey {
|
||||||
|
let ext_t_key = ExtendedPrivKey::with_seed(bip39_seed).unwrap();
|
||||||
|
ext_t_key
|
||||||
|
.derive_private_key(KeyIndex::hardened_from_normalize_index(44).unwrap()).unwrap()
|
||||||
|
.derive_private_key(KeyIndex::hardened_from_normalize_index(config.get_coin_type()).unwrap()).unwrap()
|
||||||
|
.derive_private_key(KeyIndex::hardened_from_normalize_index(0).unwrap()).unwrap()
|
||||||
|
.derive_private_key(KeyIndex::Normal(0)).unwrap()
|
||||||
|
.derive_private_key(KeyIndex::Normal(pos)).unwrap()
|
||||||
|
.private_key
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn get_zaddr_from_bip39seed(config: &LightClientConfig, bip39seed: &[u8], pos: u32) ->
|
||||||
(ExtendedSpendingKey, ExtendedFullViewingKey, PaymentAddress<Bls12>) {
|
(ExtendedSpendingKey, ExtendedFullViewingKey, PaymentAddress<Bls12>) {
|
||||||
let extsk: ExtendedSpendingKey = ExtendedSpendingKey::from_path(
|
let extsk: ExtendedSpendingKey = ExtendedSpendingKey::from_path(
|
||||||
&ExtendedSpendingKey::master(bip39seed),
|
&ExtendedSpendingKey::master(bip39seed),
|
||||||
&[
|
&[
|
||||||
ChildIndex::Hardened(32),
|
ChildIndex::Hardened(32),
|
||||||
ChildIndex::Hardened(config.get_coin_type()),
|
ChildIndex::Hardened(config.get_coin_type()),
|
||||||
ChildIndex::Hardened(0)
|
ChildIndex::Hardened(pos)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
let extfvk = ExtendedFullViewingKey::from(&extsk);
|
let extfvk = ExtendedFullViewingKey::from(&extsk);
|
||||||
@ -170,27 +183,20 @@ 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_bytes, Language::English).unwrap(), "");
|
let bip39_seed = bip39::Seed::new(&Mnemonic::from_entropy(&seed_bytes, Language::English).unwrap(), "");
|
||||||
|
|
||||||
// TODO: This only reads one key for now
|
|
||||||
let ext_t_key = ExtendedPrivKey::with_seed(&bip39_seed.as_bytes()).unwrap();
|
|
||||||
let tpk = ext_t_key
|
|
||||||
.derive_private_key(KeyIndex::hardened_from_normalize_index(44).unwrap()).unwrap()
|
|
||||||
.derive_private_key(KeyIndex::hardened_from_normalize_index(config.get_coin_type()).unwrap()).unwrap()
|
|
||||||
.derive_private_key(KeyIndex::hardened_from_normalize_index(0).unwrap()).unwrap()
|
|
||||||
.derive_private_key(KeyIndex::Normal(0)).unwrap()
|
|
||||||
.derive_private_key(KeyIndex::Normal(0)).unwrap()
|
|
||||||
.private_key;
|
|
||||||
|
|
||||||
// Derive only the first address
|
// Derive only the first address
|
||||||
|
let tpk = LightWallet::get_taddr_from_bip39seed(&config, &bip39_seed.as_bytes(), 0);
|
||||||
|
|
||||||
// TODO: We need to monitor addresses, and always keep 1 "free" address, so
|
// TODO: We need to monitor addresses, and always keep 1 "free" address, so
|
||||||
// users can import a seed phrase and automatically get all used addresses
|
// users can import a seed phrase and automatically get all used addresses
|
||||||
let (extsk, extfvk, address) = LightWallet::get_pk_from_bip39seed(config.clone(), &bip39_seed.as_bytes());
|
let (extsk, extfvk, address)
|
||||||
|
= LightWallet::get_zaddr_from_bip39seed(&config, &bip39_seed.as_bytes(), 0);
|
||||||
|
|
||||||
Ok(LightWallet {
|
Ok(LightWallet {
|
||||||
seed: seed_bytes,
|
seed: seed_bytes,
|
||||||
extsks: vec![extsk],
|
extsks: Arc::new(RwLock::new(vec![extsk])),
|
||||||
extfvks: vec![extfvk],
|
extfvks: Arc::new(RwLock::new(vec![extfvk])),
|
||||||
address: vec![address],
|
address: Arc::new(RwLock::new(vec![address])),
|
||||||
tkeys: vec![tpk],
|
tkeys: Arc::new(RwLock::new(vec![tpk])),
|
||||||
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())),
|
||||||
config: config.clone(),
|
config: config.clone(),
|
||||||
@ -245,10 +251,10 @@ impl LightWallet {
|
|||||||
|
|
||||||
Ok(LightWallet{
|
Ok(LightWallet{
|
||||||
seed: seed_bytes,
|
seed: seed_bytes,
|
||||||
extsks,
|
extsks: Arc::new(RwLock::new(extsks)),
|
||||||
extfvks,
|
extfvks: Arc::new(RwLock::new(extfvks)),
|
||||||
address: addresses,
|
address: Arc::new(RwLock::new(addresses)),
|
||||||
tkeys: tkeys,
|
tkeys: Arc::new(RwLock::new(tkeys)),
|
||||||
blocks: Arc::new(RwLock::new(blocks)),
|
blocks: Arc::new(RwLock::new(blocks)),
|
||||||
txs: Arc::new(RwLock::new(txs)),
|
txs: Arc::new(RwLock::new(txs)),
|
||||||
config: config.clone(),
|
config: config.clone(),
|
||||||
@ -264,12 +270,12 @@ impl LightWallet {
|
|||||||
writer.write_all(&self.seed)?;
|
writer.write_all(&self.seed)?;
|
||||||
|
|
||||||
// Write all the spending keys
|
// Write all the spending keys
|
||||||
Vector::write(&mut writer, &self.extsks,
|
Vector::write(&mut writer, &self.extsks.read().unwrap(),
|
||||||
|w, sk| sk.write(w)
|
|w, sk| sk.write(w)
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Write the transparent private key
|
// Write the transparent private key
|
||||||
Vector::write(&mut writer, &self.tkeys,
|
Vector::write(&mut writer, &self.tkeys.read().unwrap(),
|
||||||
|w, pk| w.write_all(&pk[..])
|
|w, pk| w.write_all(&pk[..])
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@ -317,7 +323,7 @@ impl LightWallet {
|
|||||||
|
|
||||||
// Get all z-address private keys. Returns a Vector of (address, privatekey)
|
// Get all z-address private keys. Returns a Vector of (address, privatekey)
|
||||||
pub fn get_z_private_keys(&self) -> Vec<(String, String)> {
|
pub fn get_z_private_keys(&self) -> Vec<(String, String)> {
|
||||||
self.extsks.iter().map(|sk| {
|
self.extsks.read().unwrap().iter().map(|sk| {
|
||||||
(encode_payment_address(self.config.hrp_sapling_address(),
|
(encode_payment_address(self.config.hrp_sapling_address(),
|
||||||
&ExtendedFullViewingKey::from(sk).default_address().unwrap().1),
|
&ExtendedFullViewingKey::from(sk).default_address().unwrap().1),
|
||||||
encode_extended_spending_key(self.config.hrp_sapling_private_key(), &sk)
|
encode_extended_spending_key(self.config.hrp_sapling_private_key(), &sk)
|
||||||
@ -325,16 +331,44 @@ impl LightWallet {
|
|||||||
}).collect::<Vec<(String, String)>>()
|
}).collect::<Vec<(String, String)>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.iter().map(|sk| {
|
self.tkeys.read().unwrap().iter().map(|sk| {
|
||||||
(self.address_from_sk(sk), sk[..].to_base58check(&self.config.base58_secretkey_prefix(), &[0x01]))
|
(self.address_from_sk(sk), sk[..].to_base58check(&self.config.base58_secretkey_prefix(), &[0x01]))
|
||||||
}).collect::<Vec<(String, String)>>()
|
}).collect::<Vec<(String, String)>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clears all the downloaded blocks and resets the state back to the initial block.
|
/// Adds a new z address to the wallet. This will derive a new address from the seed
|
||||||
// After this, the wallet's initial state will need to be set
|
/// at the next position and add it to the wallet.
|
||||||
// and the wallet will need to be rescanned
|
/// NOTE: This does NOT rescan
|
||||||
|
pub fn add_zaddr(&self) -> String {
|
||||||
|
let pos = self.extsks.read().unwrap().len() as u32;
|
||||||
|
let (extsk, extfvk, address) =
|
||||||
|
LightWallet::get_zaddr_from_bip39seed(&self.config, &self.seed, pos);
|
||||||
|
|
||||||
|
let zaddr = encode_payment_address(self.config.hrp_sapling_address(), &address);
|
||||||
|
self.extsks.write().unwrap().push(extsk);
|
||||||
|
self.extfvks.write().unwrap().push(extfvk);
|
||||||
|
self.address.write().unwrap().push(address);
|
||||||
|
|
||||||
|
zaddr
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a new t address to the wallet. This will derive a new address from the seed
|
||||||
|
/// at the next position.
|
||||||
|
/// NOTE: This is not rescan the wallet
|
||||||
|
pub fn add_taddr(&self) -> String {
|
||||||
|
let pos = self.tkeys.read().unwrap().len() as u32;
|
||||||
|
let sk = LightWallet::get_taddr_from_bip39seed(&self.config, &self.seed, pos);
|
||||||
|
|
||||||
|
self.tkeys.write().unwrap().push(sk);
|
||||||
|
|
||||||
|
self.address_from_sk(&sk)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clears all the downloaded blocks and resets the state back to the initial block.
|
||||||
|
/// After this, the wallet's initial state will need to be set
|
||||||
|
/// and the wallet will need to be rescanned
|
||||||
pub fn clear_blocks(&self) {
|
pub fn clear_blocks(&self) {
|
||||||
self.blocks.write().unwrap().clear();
|
self.blocks.write().unwrap().clear();
|
||||||
self.txs.write().unwrap().clear();
|
self.txs.write().unwrap().clear();
|
||||||
@ -590,7 +624,7 @@ impl LightWallet {
|
|||||||
|
|
||||||
// TODO: Iterate over all transparent addresses. This is currently looking only at
|
// TODO: Iterate over all transparent addresses. This is currently looking only at
|
||||||
// the first one.
|
// the first one.
|
||||||
let pubkey = secp256k1::PublicKey::from_secret_key(&secp, &self.tkeys[0]).serialize();
|
let pubkey = secp256k1::PublicKey::from_secret_key(&secp, &self.tkeys.read().unwrap()[0]).serialize();
|
||||||
|
|
||||||
let mut total_transparent_spend: u64 = 0;
|
let mut total_transparent_spend: u64 = 0;
|
||||||
|
|
||||||
@ -653,7 +687,7 @@ impl LightWallet {
|
|||||||
// outgoing metadata
|
// outgoing metadata
|
||||||
|
|
||||||
// Collect our t-addresses
|
// Collect our t-addresses
|
||||||
let wallet_taddrs = self.tkeys.iter()
|
let wallet_taddrs = self.tkeys.read().unwrap().iter()
|
||||||
.map(|sk| self.address_from_sk(sk))
|
.map(|sk| self.address_from_sk(sk))
|
||||||
.collect::<HashSet<String>>();
|
.collect::<HashSet<String>>();
|
||||||
|
|
||||||
@ -688,7 +722,9 @@ impl LightWallet {
|
|||||||
|
|
||||||
// Scan shielded sapling outputs to see if anyone of them is us, and if it is, extract the memo
|
// Scan shielded sapling outputs to see if anyone of them is us, and if it is, extract the memo
|
||||||
for output in tx.shielded_outputs.iter() {
|
for output in tx.shielded_outputs.iter() {
|
||||||
let ivks: Vec<_> = self.extfvks.iter().map(|extfvk| extfvk.fvk.vk.ivk()).collect();
|
let ivks: Vec<_> = self.extfvks.read().unwrap().iter().map(
|
||||||
|
|extfvk| extfvk.fvk.vk.ivk().clone()
|
||||||
|
).collect();
|
||||||
|
|
||||||
let cmu = output.cmu;
|
let cmu = output.cmu;
|
||||||
let ct = output.enc_ciphertext;
|
let ct = output.enc_ciphertext;
|
||||||
@ -720,12 +756,15 @@ impl LightWallet {
|
|||||||
|
|
||||||
// First, collect all our z addresses, to check for change
|
// First, collect all our z addresses, to check for change
|
||||||
// Collect z addresses
|
// Collect z addresses
|
||||||
let z_addresses = self.address.iter().map( |ad| {
|
let z_addresses = self.address.read().unwrap().iter().map( |ad| {
|
||||||
encode_payment_address(self.config.hrp_sapling_address(), &ad)
|
encode_payment_address(self.config.hrp_sapling_address(), &ad)
|
||||||
}).collect::<HashSet<String>>();
|
}).collect::<HashSet<String>>();
|
||||||
|
|
||||||
// Search all ovks that we have
|
// Search all ovks that we have
|
||||||
let ovks: Vec<_> = self.extfvks.iter().map(|extfvk| extfvk.fvk.ovk).collect();
|
let ovks: Vec<_> = self.extfvks.read().unwrap().iter().map(
|
||||||
|
|extfvk| extfvk.fvk.ovk.clone()
|
||||||
|
).collect();
|
||||||
|
|
||||||
for (_account, ovk) in ovks.iter().enumerate() {
|
for (_account, ovk) in ovks.iter().enumerate() {
|
||||||
match try_sapling_output_recovery(ovk,
|
match try_sapling_output_recovery(ovk,
|
||||||
&output.cv,
|
&output.cv,
|
||||||
@ -921,7 +960,7 @@ impl LightWallet {
|
|||||||
|
|
||||||
scan_block(
|
scan_block(
|
||||||
block,
|
block,
|
||||||
&self.extfvks,
|
&self.extfvks.read().unwrap(),
|
||||||
&nf_refs[..],
|
&nf_refs[..],
|
||||||
&mut block_data.tree,
|
&mut block_data.tree,
|
||||||
&mut witness_refs[..],
|
&mut witness_refs[..],
|
||||||
@ -971,7 +1010,7 @@ impl LightWallet {
|
|||||||
{
|
{
|
||||||
info!("Received sapling output");
|
info!("Received sapling output");
|
||||||
|
|
||||||
let new_note = SaplingNoteData::new(&self.extfvks[output.account], output);
|
let new_note = SaplingNoteData::new(&self.extfvks.read().unwrap()[output.account], output);
|
||||||
match tx_entry.notes.iter().find(|nd| nd.nullifier == new_note.nullifier) {
|
match tx_entry.notes.iter().find(|nd| nd.nullifier == new_note.nullifier) {
|
||||||
None => tx_entry.notes.push(new_note),
|
None => tx_entry.notes.push(new_note),
|
||||||
Some(_) => warn!("Tried to insert duplicate note for Tx {}", tx.txid)
|
Some(_) => warn!("Tried to insert duplicate note for Tx {}", tx.txid)
|
||||||
@ -1021,8 +1060,8 @@ impl LightWallet {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// TODO: This only spends from the first address right now.
|
// TODO: This only spends from the first address right now.
|
||||||
let extsk = &self.extsks[0];
|
let extsk = &self.extsks.read().unwrap()[0];
|
||||||
let extfvk = &self.extfvks[0];
|
let extfvk = &self.extfvks.read().unwrap()[0];
|
||||||
let ovk = extfvk.fvk.ovk;
|
let ovk = extfvk.fvk.ovk;
|
||||||
|
|
||||||
let to = match address::RecipientAddress::from_str(to,
|
let to = match address::RecipientAddress::from_str(to,
|
||||||
@ -1082,7 +1121,7 @@ impl LightWallet {
|
|||||||
address::RecipientAddress::Shielded(_) => {
|
address::RecipientAddress::Shielded(_) => {
|
||||||
// The destination is a sapling address, so add all transparent inputs
|
// The destination is a sapling address, so add all transparent inputs
|
||||||
// TODO: This only spends from the first address right now.
|
// TODO: This only spends from the first address right now.
|
||||||
let sk = self.tkeys[0];
|
let sk = self.tkeys.read().unwrap()[0];
|
||||||
|
|
||||||
// Add all tinputs
|
// Add all tinputs
|
||||||
tinputs.iter()
|
tinputs.iter()
|
||||||
@ -1136,8 +1175,8 @@ impl LightWallet {
|
|||||||
// the builder will automatically send change to that address
|
// the builder will automatically send change to that address
|
||||||
if notes.len() == 0 {
|
if notes.len() == 0 {
|
||||||
builder.send_change_to(
|
builder.send_change_to(
|
||||||
ExtendedFullViewingKey::from(&self.extsks[0]).fvk.ovk,
|
ExtendedFullViewingKey::from(&self.extsks.read().unwrap()[0]).fvk.ovk,
|
||||||
self.extsks[0].default_address().unwrap().1);
|
self.extsks.read().unwrap()[0].default_address().unwrap().1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute memo if it exists
|
// Compute memo if it exists
|
||||||
@ -1497,10 +1536,10 @@ pub mod tests {
|
|||||||
const AMOUNT1:u64 = 5;
|
const AMOUNT1:u64 = 5;
|
||||||
// Address is encoded in bech32
|
// Address is encoded in bech32
|
||||||
let address = Some(encode_payment_address(wallet.config.hrp_sapling_address(),
|
let address = Some(encode_payment_address(wallet.config.hrp_sapling_address(),
|
||||||
&wallet.extfvks[0].default_address().unwrap().1));
|
&wallet.extfvks.read().unwrap()[0].default_address().unwrap().1));
|
||||||
|
|
||||||
let mut cb1 = FakeCompactBlock::new(0, BlockHash([0; 32]));
|
let mut cb1 = FakeCompactBlock::new(0, BlockHash([0; 32]));
|
||||||
cb1.add_tx_paying(wallet.extfvks[0].clone(), AMOUNT1);
|
cb1.add_tx_paying(wallet.extfvks.read().unwrap()[0].clone(), AMOUNT1);
|
||||||
|
|
||||||
// Make sure that the intial state is empty
|
// Make sure that the intial state is empty
|
||||||
assert_eq!(wallet.txs.read().unwrap().len(), 0);
|
assert_eq!(wallet.txs.read().unwrap().len(), 0);
|
||||||
@ -1519,7 +1558,7 @@ pub mod tests {
|
|||||||
|
|
||||||
// Add a second block
|
// Add a second block
|
||||||
let mut cb2 = FakeCompactBlock::new(1, cb1.hash());
|
let mut cb2 = FakeCompactBlock::new(1, cb1.hash());
|
||||||
cb2.add_tx_paying(wallet.extfvks[0].clone(), AMOUNT2);
|
cb2.add_tx_paying(wallet.extfvks.read().unwrap()[0].clone(), AMOUNT2);
|
||||||
|
|
||||||
wallet.scan_block(&cb2.as_bytes()).unwrap();
|
wallet.scan_block(&cb2.as_bytes()).unwrap();
|
||||||
|
|
||||||
@ -1537,7 +1576,7 @@ pub mod tests {
|
|||||||
const AMOUNT1:u64 = 5;
|
const AMOUNT1:u64 = 5;
|
||||||
|
|
||||||
let mut cb1 = FakeCompactBlock::new(0, BlockHash([0; 32]));
|
let mut cb1 = FakeCompactBlock::new(0, BlockHash([0; 32]));
|
||||||
let (nf1, txid1) = cb1.add_tx_paying(wallet.extfvks[0].clone(), AMOUNT1);
|
let (nf1, txid1) = cb1.add_tx_paying(wallet.extfvks.read().unwrap()[0].clone(), AMOUNT1);
|
||||||
|
|
||||||
wallet.scan_block(&cb1.as_bytes()).unwrap();
|
wallet.scan_block(&cb1.as_bytes()).unwrap();
|
||||||
|
|
||||||
@ -1551,7 +1590,7 @@ pub mod tests {
|
|||||||
let addr2 = ExtendedFullViewingKey::from(&ExtendedSpendingKey::master(&[0u8; 32]))
|
let addr2 = ExtendedFullViewingKey::from(&ExtendedSpendingKey::master(&[0u8; 32]))
|
||||||
.default_address().unwrap().1;
|
.default_address().unwrap().1;
|
||||||
let mut cb2 = FakeCompactBlock::new(1, cb1.hash());
|
let mut cb2 = FakeCompactBlock::new(1, cb1.hash());
|
||||||
let txid2 = cb2.add_tx_spending((nf1, AMOUNT1), wallet.extfvks[0].clone(), addr2, AMOUNT2);
|
let txid2 = cb2.add_tx_spending((nf1, AMOUNT1), wallet.extfvks.read().unwrap()[0].clone(), addr2, AMOUNT2);
|
||||||
wallet.scan_block(&cb2.as_bytes()).unwrap();
|
wallet.scan_block(&cb2.as_bytes()).unwrap();
|
||||||
|
|
||||||
// Now, the original note should be spent and there should be a change
|
// Now, the original note should be spent and there should be a change
|
||||||
@ -1582,8 +1621,8 @@ pub mod tests {
|
|||||||
|
|
||||||
let wallet = LightWallet::new(None, &get_test_config(), 0).unwrap();
|
let wallet = LightWallet::new(None, &get_test_config(), 0).unwrap();
|
||||||
|
|
||||||
let pk = PublicKey::from_secret_key(&secp, &wallet.tkeys[0]);
|
let pk = PublicKey::from_secret_key(&secp, &wallet.tkeys.read().unwrap()[0]);
|
||||||
let taddr = wallet.address_from_sk(&wallet.tkeys[0]);
|
let taddr = wallet.address_from_sk(&wallet.tkeys.read().unwrap()[0]);
|
||||||
|
|
||||||
const AMOUNT1: u64 = 20;
|
const AMOUNT1: u64 = 20;
|
||||||
|
|
||||||
@ -1648,8 +1687,8 @@ pub mod tests {
|
|||||||
|
|
||||||
let wallet = LightWallet::new(None, &get_test_config(), 0).unwrap();
|
let wallet = LightWallet::new(None, &get_test_config(), 0).unwrap();
|
||||||
|
|
||||||
let pk = PublicKey::from_secret_key(&secp, &wallet.tkeys[0]);
|
let pk = PublicKey::from_secret_key(&secp, &wallet.tkeys.read().unwrap()[0]);
|
||||||
let taddr = wallet.address_from_sk(&wallet.tkeys[0]);
|
let taddr = wallet.address_from_sk(&wallet.tkeys.read().unwrap()[0]);
|
||||||
|
|
||||||
let non_wallet_sk = &SecretKey::from_slice(&[1u8; 32]).unwrap();
|
let non_wallet_sk = &SecretKey::from_slice(&[1u8; 32]).unwrap();
|
||||||
let non_wallet_pk = PublicKey::from_secret_key(&secp, &non_wallet_sk);
|
let non_wallet_pk = PublicKey::from_secret_key(&secp, &non_wallet_sk);
|
||||||
@ -1721,7 +1760,7 @@ pub mod tests {
|
|||||||
const AMOUNT1:u64 = 5;
|
const AMOUNT1:u64 = 5;
|
||||||
|
|
||||||
let mut cb1 = FakeCompactBlock::new(0, BlockHash([0; 32]));
|
let mut cb1 = FakeCompactBlock::new(0, BlockHash([0; 32]));
|
||||||
let (nf1, txid1) = cb1.add_tx_paying(wallet.extfvks[0].clone(), AMOUNT1);
|
let (nf1, txid1) = cb1.add_tx_paying(wallet.extfvks.read().unwrap()[0].clone(), AMOUNT1);
|
||||||
|
|
||||||
wallet.scan_block(&cb1.as_bytes()).unwrap();
|
wallet.scan_block(&cb1.as_bytes()).unwrap();
|
||||||
|
|
||||||
@ -1730,8 +1769,8 @@ pub mod tests {
|
|||||||
assert_eq!(wallet.zbalance(None), AMOUNT1);
|
assert_eq!(wallet.zbalance(None), AMOUNT1);
|
||||||
|
|
||||||
// Add a t input at the Tx
|
// Add a t input at the Tx
|
||||||
let pk = PublicKey::from_secret_key(&secp, &wallet.tkeys[0]);
|
let pk = PublicKey::from_secret_key(&secp, &wallet.tkeys.read().unwrap()[0]);
|
||||||
let taddr = wallet.address_from_sk(&wallet.tkeys[0]);
|
let taddr = wallet.address_from_sk(&wallet.tkeys.read().unwrap()[0]);
|
||||||
|
|
||||||
const TAMOUNT1: u64 = 20;
|
const TAMOUNT1: u64 = 20;
|
||||||
|
|
||||||
@ -1745,7 +1784,7 @@ pub mod tests {
|
|||||||
let addr2 = ExtendedFullViewingKey::from(&ExtendedSpendingKey::master(&[0u8; 32]))
|
let addr2 = ExtendedFullViewingKey::from(&ExtendedSpendingKey::master(&[0u8; 32]))
|
||||||
.default_address().unwrap().1;
|
.default_address().unwrap().1;
|
||||||
let mut cb2 = FakeCompactBlock::new(1, cb1.hash());
|
let mut cb2 = FakeCompactBlock::new(1, cb1.hash());
|
||||||
let txid2 = cb2.add_tx_spending((nf1, AMOUNT1), wallet.extfvks[0].clone(), addr2, AMOUNT2);
|
let txid2 = cb2.add_tx_spending((nf1, AMOUNT1), wallet.extfvks.read().unwrap()[0].clone(), addr2, AMOUNT2);
|
||||||
wallet.scan_block(&cb2.as_bytes()).unwrap();
|
wallet.scan_block(&cb2.as_bytes()).unwrap();
|
||||||
|
|
||||||
let mut tx = FakeTransaction::new_with_txid(txid2);
|
let mut tx = FakeTransaction::new_with_txid(txid2);
|
||||||
@ -1766,13 +1805,13 @@ pub mod tests {
|
|||||||
{
|
{
|
||||||
assert_eq!(wallet.seed, wallet2.seed);
|
assert_eq!(wallet.seed, wallet2.seed);
|
||||||
|
|
||||||
assert_eq!(wallet.extsks.len(), wallet2.extsks.len());
|
assert_eq!(wallet.extsks.read().unwrap().len(), wallet2.extsks.read().unwrap().len());
|
||||||
assert_eq!(wallet.extsks[0], wallet2.extsks[0]);
|
assert_eq!(wallet.extsks.read().unwrap()[0], wallet2.extsks.read().unwrap()[0]);
|
||||||
assert_eq!(wallet.extfvks[0], wallet2.extfvks[0]);
|
assert_eq!(wallet.extfvks.read().unwrap()[0], wallet2.extfvks.read().unwrap()[0]);
|
||||||
assert_eq!(wallet.address[0], wallet2.address[0]);
|
assert_eq!(wallet.address.read().unwrap()[0], wallet2.address.read().unwrap()[0]);
|
||||||
|
|
||||||
assert_eq!(wallet.tkeys.len(), wallet2.tkeys.len());
|
assert_eq!(wallet.tkeys.read().unwrap().len(), wallet2.tkeys.read().unwrap().len());
|
||||||
assert_eq!(wallet.tkeys[0], wallet2.tkeys[0]);
|
assert_eq!(wallet.tkeys.read().unwrap()[0], wallet2.tkeys.read().unwrap()[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test blocks were serialized properly
|
// Test blocks were serialized properly
|
||||||
@ -1836,7 +1875,7 @@ pub mod tests {
|
|||||||
let wallet = LightWallet::new(None, &config, 0).unwrap();
|
let wallet = LightWallet::new(None, &config, 0).unwrap();
|
||||||
|
|
||||||
let mut cb1 = FakeCompactBlock::new(0, BlockHash([0; 32]));
|
let mut cb1 = FakeCompactBlock::new(0, BlockHash([0; 32]));
|
||||||
let (_, txid1) = cb1.add_tx_paying(wallet.extfvks[0].clone(), amount);
|
let (_, txid1) = cb1.add_tx_paying(wallet.extfvks.read().unwrap()[0].clone(), amount);
|
||||||
wallet.scan_block(&cb1.as_bytes()).unwrap();
|
wallet.scan_block(&cb1.as_bytes()).unwrap();
|
||||||
|
|
||||||
// We have one note
|
// We have one note
|
||||||
@ -1997,8 +2036,8 @@ pub mod tests {
|
|||||||
const AMOUNT_T: u64 = 40000;
|
const AMOUNT_T: u64 = 40000;
|
||||||
let (wallet, txid1, block_hash) = get_test_wallet(AMOUNT_Z);
|
let (wallet, txid1, block_hash) = get_test_wallet(AMOUNT_Z);
|
||||||
|
|
||||||
let pk = PublicKey::from_secret_key(&secp, &wallet.tkeys[0]);
|
let pk = PublicKey::from_secret_key(&secp, &wallet.tkeys.read().unwrap()[0]);
|
||||||
let taddr = wallet.address_from_sk(&wallet.tkeys[0]);
|
let taddr = wallet.address_from_sk(&wallet.tkeys.read().unwrap()[0]);
|
||||||
|
|
||||||
let mut tx = FakeTransaction::new(&mut rng);
|
let mut tx = FakeTransaction::new(&mut rng);
|
||||||
tx.add_t_output(&pk, AMOUNT_T);
|
tx.add_t_output(&pk, AMOUNT_T);
|
||||||
@ -2096,7 +2135,7 @@ pub mod tests {
|
|||||||
let (wallet, _txid1, block_hash) = get_test_wallet(AMOUNT1);
|
let (wallet, _txid1, block_hash) = get_test_wallet(AMOUNT1);
|
||||||
|
|
||||||
let my_address = encode_payment_address(wallet.config.hrp_sapling_address(),
|
let my_address = encode_payment_address(wallet.config.hrp_sapling_address(),
|
||||||
&wallet.extfvks[0].default_address().unwrap().1);
|
&wallet.extfvks.read().unwrap()[0].default_address().unwrap().1);
|
||||||
|
|
||||||
let memo = "Incoming Memo".to_string();
|
let memo = "Incoming Memo".to_string();
|
||||||
let fee: u64 = DEFAULT_FEE.try_into().unwrap();
|
let fee: u64 = DEFAULT_FEE.try_into().unwrap();
|
||||||
@ -2123,7 +2162,7 @@ pub mod tests {
|
|||||||
|
|
||||||
assert_eq!(txs[&sent_txid].notes.len(), 1);
|
assert_eq!(txs[&sent_txid].notes.len(), 1);
|
||||||
|
|
||||||
assert_eq!(txs[&sent_txid].notes[0].extfvk, wallet.extfvks[0]);
|
assert_eq!(txs[&sent_txid].notes[0].extfvk, wallet.extfvks.read().unwrap()[0]);
|
||||||
assert_eq!(txs[&sent_txid].notes[0].note.value, AMOUNT1 - fee);
|
assert_eq!(txs[&sent_txid].notes[0].note.value, AMOUNT1 - fee);
|
||||||
assert_eq!(wallet.note_address(&txs[&sent_txid].notes[0]), Some(my_address));
|
assert_eq!(wallet.note_address(&txs[&sent_txid].notes[0]), Some(my_address));
|
||||||
assert_eq!(LightWallet::memo_str(&txs[&sent_txid].notes[0].memo), Some(memo));
|
assert_eq!(LightWallet::memo_str(&txs[&sent_txid].notes[0].memo), Some(memo));
|
||||||
@ -2136,7 +2175,7 @@ pub mod tests {
|
|||||||
const AMOUNT_SENT: u64 = 20000;
|
const AMOUNT_SENT: u64 = 20000;
|
||||||
let (wallet, txid1, block_hash) = get_test_wallet(AMOUNT);
|
let (wallet, txid1, block_hash) = get_test_wallet(AMOUNT);
|
||||||
|
|
||||||
let taddr = wallet.address_from_sk(&wallet.tkeys[0]);
|
let taddr = wallet.address_from_sk(&wallet.tkeys.read().unwrap()[0]);
|
||||||
|
|
||||||
let fee: u64 = DEFAULT_FEE.try_into().unwrap();
|
let fee: u64 = DEFAULT_FEE.try_into().unwrap();
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ mod lightwallet;
|
|||||||
mod commands;
|
mod commands;
|
||||||
|
|
||||||
use std::io::{Result, Error, ErrorKind};
|
use std::io::{Result, Error, ErrorKind};
|
||||||
use std::sync::Arc;
|
use std::sync::{Arc};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use lightclient::{LightClient, LightClientConfig};
|
use lightclient::{LightClient, LightClientConfig};
|
||||||
@ -136,7 +136,6 @@ pub fn main() {
|
|||||||
Err(e) => { eprintln!("Failed to start wallet. Error was:\n{}", e); return; }
|
Err(e) => { eprintln!("Failed to start wallet. Error was:\n{}", e); return; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// At startup, run a sync
|
// At startup, run a sync
|
||||||
let sync_update = lightclient.do_sync(true);
|
let sync_update = lightclient.do_sync(true);
|
||||||
println!("{}", sync_update);
|
println!("{}", sync_update);
|
||||||
@ -150,7 +149,8 @@ pub fn main() {
|
|||||||
match command_rx.recv_timeout(Duration::from_secs(5 * 60)) {
|
match command_rx.recv_timeout(Duration::from_secs(5 * 60)) {
|
||||||
Ok((cmd, args)) => {
|
Ok((cmd, args)) => {
|
||||||
let args = args.iter().map(|s| s.as_ref()).collect();
|
let args = args.iter().map(|s| s.as_ref()).collect();
|
||||||
let cmd_response = commands::do_user_command(&cmd, &args, &lc);
|
|
||||||
|
let cmd_response = commands::do_user_command(&cmd, &args, lc.as_ref());
|
||||||
resp_tx.send(cmd_response).unwrap();
|
resp_tx.send(cmd_response).unwrap();
|
||||||
|
|
||||||
if cmd == "quit" {
|
if cmd == "quit" {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user