mirror of
https://github.com/Qortal/piratewallet-light-cli.git
synced 2025-01-29 02:02:14 +00:00
newline cleanup
This commit is contained in:
parent
6aa383daa3
commit
e000ff3bc9
12
README.md
12
README.md
@ -1,14 +1,14 @@
|
||||
## Zecwallet CLI - A command line ZecWallet light client.
|
||||
## Zecwallet CLI - A command line ZecWallet light client.
|
||||
|
||||
`zecwallet-cli` is a command line ZecWallet light client. To use it, download the latest binary from the releases page and run `./zecwallet-cli`
|
||||
|
||||
This will launch the interactive prompt. Type `help` to get a list of commands
|
||||
|
||||
## Running in non-interactive mode:
|
||||
You can also run `zecwallet-cli` in non-interactive mode by passing the command you want to run as an argument. For example, `zecwallet-cli addresses` will list all wallet addresses and exit.
|
||||
Run `zecwallet-cli help` to see a list of all commands.
|
||||
You can also run `zecwallet-cli` in non-interactive mode by passing the command you want to run as an argument. For example, `zecwallet-cli addresses` will list all wallet addresses and exit.
|
||||
Run `zecwallet-cli help` to see a list of all commands.
|
||||
|
||||
## Privacy
|
||||
## Privacy
|
||||
* While all the keys and transaction detection happens on the client, the server can learn what blocks contain your shielded transactions.
|
||||
* The server also learns other metadata about you like your ip address etc...
|
||||
* Also remember that t-addresses don't provide any privacy protection.
|
||||
@ -42,9 +42,9 @@ cargo build --release
|
||||
```
|
||||
|
||||
## Options
|
||||
Here are some CLI arguments you can pass to `zecwallet-cli`. Please run `zecwallet-cli --help` for the full list.
|
||||
Here are some CLI arguments you can pass to `zecwallet-cli`. Please run `zecwallet-cli --help` for the full list.
|
||||
|
||||
* `--server`: Connect to a custom zecwallet lightwalletd server.
|
||||
* `--server`: Connect to a custom zecwallet lightwalletd server.
|
||||
* Example: `./zecwallet-cli --server 127.0.0.1:9067`
|
||||
* `--seed`: Restore a wallet from a seed phrase. Note that this will fail if there is an existing wallet. Delete (or move) any existing wallet to restore from the 24-word seed phrase
|
||||
* Example: `./zecwallet-cli --seed "twenty four words seed phrase"`
|
||||
|
@ -79,7 +79,7 @@ impl Command for SyncStatusCommand {
|
||||
false => object!{ "syncing" => "false" },
|
||||
true => object!{ "syncing" => "true",
|
||||
"synced_blocks" => status.synced_blocks,
|
||||
"total_blocks" => status.total_blocks }
|
||||
"total_blocks" => status.total_blocks }
|
||||
}.pretty(2)
|
||||
}
|
||||
}
|
||||
@ -120,7 +120,7 @@ impl Command for ClearCommand {
|
||||
h.push("clear");
|
||||
h.push("");
|
||||
h.push("This command will clear all notes, utxos and transactions from the wallet, setting up the wallet to be synced from scratch.");
|
||||
|
||||
|
||||
h.join("\n")
|
||||
}
|
||||
|
||||
@ -130,9 +130,9 @@ impl Command for ClearCommand {
|
||||
|
||||
fn exec(&self, _args: &[&str], lightclient: &LightClient) -> String {
|
||||
lightclient.clear_state();
|
||||
|
||||
|
||||
let result = object!{ "result" => "success" };
|
||||
|
||||
|
||||
result.pretty(2)
|
||||
}
|
||||
}
|
||||
@ -197,7 +197,7 @@ impl Command for InfoCommand {
|
||||
"Get the lightwalletd server's info".to_string()
|
||||
}
|
||||
|
||||
fn exec(&self, _args: &[&str], lightclient: &LightClient) -> String {
|
||||
fn exec(&self, _args: &[&str], lightclient: &LightClient) -> String {
|
||||
lightclient.do_info()
|
||||
}
|
||||
}
|
||||
@ -436,7 +436,7 @@ impl Command for LockCommand {
|
||||
let mut h = vec![];
|
||||
h.push("Extra arguments to lock. Did you mean 'encrypt'?");
|
||||
h.push("");
|
||||
|
||||
|
||||
return format!("{}\n{}", h.join("\n"), self.help());
|
||||
}
|
||||
|
||||
@ -584,9 +584,9 @@ impl Command for SaveCommand {
|
||||
r.pretty(2)
|
||||
},
|
||||
Err(e) => {
|
||||
let r = object!{
|
||||
let r = object!{
|
||||
"result" => "error",
|
||||
"error" => e
|
||||
"error" => e
|
||||
};
|
||||
r.pretty(2)
|
||||
}
|
||||
@ -715,7 +715,7 @@ impl Command for NotesCommand {
|
||||
}
|
||||
|
||||
fn exec(&self, args: &[&str], lightclient: &LightClient) -> String {
|
||||
// Parse the args.
|
||||
// Parse the args.
|
||||
if args.len() > 1 {
|
||||
return self.short_help();
|
||||
}
|
||||
@ -843,6 +843,6 @@ pub mod tests {
|
||||
|
||||
#[test]
|
||||
pub fn test_nosync_commands() {
|
||||
// The following commands should run
|
||||
// The following commands should run
|
||||
}
|
||||
}
|
||||
|
@ -140,7 +140,7 @@ impl LightClientConfig {
|
||||
}
|
||||
|
||||
pub fn get_zcash_data_path(&self) -> Box<Path> {
|
||||
let mut zcash_data_location;
|
||||
let mut zcash_data_location;
|
||||
if self.data_dir.is_some() {
|
||||
zcash_data_location = PathBuf::from(&self.data_dir.as_ref().unwrap());
|
||||
} else {
|
||||
@ -178,7 +178,7 @@ impl LightClientConfig {
|
||||
pub fn get_wallet_path(&self) -> Box<Path> {
|
||||
let mut wallet_location = self.get_zcash_data_path().into_path_buf();
|
||||
wallet_location.push(WALLET_NAME);
|
||||
|
||||
|
||||
wallet_location.into_boxed_path()
|
||||
}
|
||||
|
||||
@ -296,7 +296,7 @@ pub struct LightClient {
|
||||
}
|
||||
|
||||
impl LightClient {
|
||||
|
||||
|
||||
pub fn set_wallet_initial_state(&self, height: u64) {
|
||||
use std::convert::TryInto;
|
||||
|
||||
@ -349,14 +349,14 @@ impl LightClient {
|
||||
let mut l = LightClient {
|
||||
wallet : Arc::new(RwLock::new(LightWallet::new(Some(seed_phrase), &config, 0)?)),
|
||||
config : config.clone(),
|
||||
sapling_output : vec![],
|
||||
sapling_output : vec![],
|
||||
sapling_spend : vec![],
|
||||
sync_lock : Mutex::new(()),
|
||||
sync_status : Arc::new(RwLock::new(WalletStatus::new())),
|
||||
};
|
||||
|
||||
l.set_wallet_initial_state(0);
|
||||
|
||||
|
||||
#[cfg(feature = "embed_params")]
|
||||
l.read_sapling_params();
|
||||
|
||||
@ -366,11 +366,11 @@ impl LightClient {
|
||||
Ok(l)
|
||||
}
|
||||
|
||||
/// Create a brand new wallet with a new seed phrase. Will fail if a wallet file
|
||||
/// Create a brand new wallet with a new seed phrase. Will fail if a wallet file
|
||||
/// already exists on disk
|
||||
pub fn new(config: &LightClientConfig, latest_block: u64) -> io::Result<Self> {
|
||||
#[cfg(all(not(target_os="ios"), not(target_os="android")))]
|
||||
{
|
||||
{
|
||||
if config.wallet_exists() {
|
||||
return Err(Error::new(ErrorKind::AlreadyExists,
|
||||
"Cannot create a new wallet from seed, because a wallet already exists"));
|
||||
@ -380,14 +380,14 @@ impl LightClient {
|
||||
let mut l = LightClient {
|
||||
wallet : Arc::new(RwLock::new(LightWallet::new(None, config, latest_block)?)),
|
||||
config : config.clone(),
|
||||
sapling_output : vec![],
|
||||
sapling_output : vec![],
|
||||
sapling_spend : vec![],
|
||||
sync_lock : Mutex::new(()),
|
||||
sync_status : Arc::new(RwLock::new(WalletStatus::new())),
|
||||
};
|
||||
|
||||
l.set_wallet_initial_state(latest_block);
|
||||
|
||||
|
||||
#[cfg(feature = "embed_params")]
|
||||
l.read_sapling_params();
|
||||
|
||||
@ -412,7 +412,7 @@ impl LightClient {
|
||||
let mut l = LightClient {
|
||||
wallet : Arc::new(RwLock::new(LightWallet::new(Some(seed_phrase), config, birthday)?)),
|
||||
config : config.clone(),
|
||||
sapling_output : vec![],
|
||||
sapling_output : vec![],
|
||||
sapling_spend : vec![],
|
||||
sync_lock : Mutex::new(()),
|
||||
sync_status : Arc::new(RwLock::new(WalletStatus::new())),
|
||||
@ -420,7 +420,7 @@ impl LightClient {
|
||||
|
||||
println!("Setting birthday to {}", birthday);
|
||||
l.set_wallet_initial_state(birthday);
|
||||
|
||||
|
||||
#[cfg(feature = "embed_params")]
|
||||
l.read_sapling_params();
|
||||
|
||||
@ -438,7 +438,7 @@ impl LightClient {
|
||||
let mut lc = LightClient {
|
||||
wallet : Arc::new(RwLock::new(wallet)),
|
||||
config : config.clone(),
|
||||
sapling_output : vec![],
|
||||
sapling_output : vec![],
|
||||
sapling_spend : vec![],
|
||||
sync_lock : Mutex::new(()),
|
||||
sync_status : Arc::new(RwLock::new(WalletStatus::new())),
|
||||
@ -460,12 +460,12 @@ impl LightClient {
|
||||
}
|
||||
|
||||
let mut file_buffer = BufReader::new(File::open(config.get_wallet_path())?);
|
||||
|
||||
|
||||
let wallet = LightWallet::read(&mut file_buffer, config)?;
|
||||
let mut lc = LightClient {
|
||||
wallet : Arc::new(RwLock::new(wallet)),
|
||||
config : config.clone(),
|
||||
sapling_output : vec![],
|
||||
sapling_output : vec![],
|
||||
sapling_spend : vec![],
|
||||
sync_lock : Mutex::new(()),
|
||||
sync_status : Arc::new(RwLock::new(WalletStatus::new())),
|
||||
@ -547,16 +547,16 @@ impl LightClient {
|
||||
Ok(s) => s,
|
||||
Err(_) => return Err("Decryption failed. Is your password correct?".to_string())
|
||||
};
|
||||
|
||||
|
||||
Mnemonic::from_entropy(&seed, Language::English)
|
||||
} else {
|
||||
// Seed
|
||||
let mut seed_bytes = [0u8; 32];
|
||||
reader.read_exact(&mut seed_bytes).unwrap();
|
||||
|
||||
Mnemonic::from_entropy(&seed_bytes, Language::English)
|
||||
Mnemonic::from_entropy(&seed_bytes, Language::English)
|
||||
}.map_err(|e| format!("Failed to read seed. {:?}", e));
|
||||
|
||||
|
||||
phrase.map(|m| m.phrase().to_string())
|
||||
}
|
||||
|
||||
@ -640,7 +640,7 @@ impl LightClient {
|
||||
let t_addresses = wallet.taddresses.read().unwrap().iter().map( |address| {
|
||||
// Get the balance for this address
|
||||
let balance = wallet.tbalance(Some(address.clone()));
|
||||
|
||||
|
||||
object!{
|
||||
"address" => address.clone(),
|
||||
"balance" => balance,
|
||||
@ -656,9 +656,9 @@ impl LightClient {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn do_save(&self) -> Result<(), String> {
|
||||
pub fn do_save(&self) -> Result<(), String> {
|
||||
// On mobile platforms, disable the save, because the saves will be handled by the native layer, and not in rust
|
||||
if cfg!(all(not(target_os="ios"), not(target_os="android"))) {
|
||||
if cfg!(all(not(target_os="ios"), not(target_os="android"))) {
|
||||
// If the wallet is encrypted but unlocked, lock it again.
|
||||
{
|
||||
let mut wallet = self.wallet.write().unwrap();
|
||||
@ -672,7 +672,7 @@ impl LightClient {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Prevent any overlapping syncs during save, and don't save in the middle of a sync
|
||||
@ -686,7 +686,7 @@ impl LightClient {
|
||||
let mut file = File::create(self.config.get_wallet_path()).unwrap();
|
||||
file.write_all(&wallet_bytes).map_err(|e| format!("{}", e))?;
|
||||
Ok(())
|
||||
},
|
||||
},
|
||||
Err(e) => {
|
||||
let err = format!("ERR: {}", e);
|
||||
error!("{}", err);
|
||||
@ -715,7 +715,7 @@ impl LightClient {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut buffer: Vec<u8> = vec![];
|
||||
match self.wallet.write().unwrap().write(&mut buffer) {
|
||||
@ -774,7 +774,7 @@ impl LightClient {
|
||||
let wallet = self.wallet.read().unwrap();
|
||||
wallet.txs.read().unwrap().iter()
|
||||
.flat_map( |(txid, wtx)| {
|
||||
wtx.notes.iter().filter_map(move |nd|
|
||||
wtx.notes.iter().filter_map(move |nd|
|
||||
if !all_notes && nd.spent.is_some() {
|
||||
None
|
||||
} else {
|
||||
@ -801,16 +801,16 @@ impl LightClient {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
let mut unspent_utxos: Vec<JsonValue> = vec![];
|
||||
let mut spent_utxos : Vec<JsonValue> = vec![];
|
||||
let mut pending_utxos: Vec<JsonValue> = vec![];
|
||||
|
||||
|
||||
{
|
||||
let wallet = self.wallet.read().unwrap();
|
||||
wallet.txs.read().unwrap().iter()
|
||||
.flat_map( |(txid, wtx)| {
|
||||
wtx.utxos.iter().filter_map(move |utxo|
|
||||
wtx.utxos.iter().filter_map(move |utxo|
|
||||
if !all_notes && utxo.spent.is_some() {
|
||||
None
|
||||
} else {
|
||||
@ -945,12 +945,12 @@ impl LightClient {
|
||||
|
||||
// Collect outgoing metadata
|
||||
let outgoing_json = wtx.outgoing_metadata.iter()
|
||||
.map(|om|
|
||||
.map(|om|
|
||||
object!{
|
||||
"address" => om.address.clone(),
|
||||
"value" => om.value,
|
||||
"memo" => LightWallet::memo_str(&Some(om.memo.clone())),
|
||||
}).collect::<Vec<JsonValue>>();
|
||||
}).collect::<Vec<JsonValue>>();
|
||||
|
||||
object! {
|
||||
"block_height" => wtx.block,
|
||||
@ -1004,16 +1004,16 @@ impl LightClient {
|
||||
|
||||
// Then set the initial block
|
||||
self.set_wallet_initial_state(self.wallet.read().unwrap().get_birthday());
|
||||
info!("Cleared wallet state");
|
||||
info!("Cleared wallet state");
|
||||
}
|
||||
|
||||
pub fn do_rescan(&self) -> Result<JsonValue, String> {
|
||||
if !self.wallet.read().unwrap().is_unlocked_for_spending() {
|
||||
warn!("Wallet is locked, new HD addresses won't be added!");
|
||||
}
|
||||
|
||||
|
||||
info!("Rescan starting");
|
||||
|
||||
|
||||
self.clear_state();
|
||||
|
||||
// Then, do a sync, which will force a full rescan from the initial state
|
||||
@ -1056,13 +1056,13 @@ impl LightClient {
|
||||
// Sync is 3 parts
|
||||
// 1. Get the latest block
|
||||
// 2. Get all the blocks that we don't have
|
||||
// 3. Find all new Txns that don't have the full Tx, and get them as full transactions
|
||||
// 3. Find all new Txns that don't have the full Tx, and get them as full transactions
|
||||
// and scan them, mainly to get the memos
|
||||
let mut last_scanned_height = self.wallet.read().unwrap().last_scanned_height() as u64;
|
||||
|
||||
// This will hold the latest block fetched from the RPC
|
||||
let latest_block = fetch_latest_block(&self.get_server_uri())?.height;
|
||||
|
||||
|
||||
if latest_block < last_scanned_height {
|
||||
let w = format!("Server's latest block({}) is behind ours({})", latest_block, last_scanned_height);
|
||||
warn!("{}", w);
|
||||
@ -1145,7 +1145,7 @@ impl LightClient {
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse the block and save it's time. We'll use this timestamp for
|
||||
// Parse the block and save it's time. We'll use this timestamp for
|
||||
// transactions in this block that might belong to us.
|
||||
let block: Result<zcash_client_backend::proto::compact_formats::CompactBlock, _>
|
||||
= parse_from_bytes(encoded_block);
|
||||
@ -1172,13 +1172,13 @@ impl LightClient {
|
||||
|
||||
{
|
||||
// println!("Total scan duration: {:?}", self.wallet.read().unwrap().total_scan_duration.read().unwrap().get(0).unwrap().as_millis());
|
||||
|
||||
|
||||
let t = self.wallet.read().unwrap();
|
||||
let mut d = t.total_scan_duration.write().unwrap();
|
||||
d.clear();
|
||||
d.push(std::time::Duration::new(0, 0));
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Check if there was any invalid block, which means we might have to do a reorg
|
||||
let invalid_height = last_invalid_height.load(Ordering::SeqCst);
|
||||
@ -1192,8 +1192,8 @@ impl LightClient {
|
||||
if total_reorg > (crate::lightwallet::MAX_REORG - 1) as u64 {
|
||||
error!("Reorg has now exceeded {} blocks!", crate::lightwallet::MAX_REORG);
|
||||
return Err(format!("Reorg has exceeded {} blocks. Aborting.", crate::lightwallet::MAX_REORG));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if invalid_height > 0 {
|
||||
// Reset the scanning heights
|
||||
last_scanned_height = (invalid_height - 1) as u64;
|
||||
@ -1204,17 +1204,17 @@ impl LightClient {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If it got here, that means the blocks are scanning properly now.
|
||||
// If it got here, that means the blocks are scanning properly now.
|
||||
// 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.
|
||||
// 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>>();
|
||||
|
||||
|
||||
// Create a channel so the fetch_transparent_txids can send the results back
|
||||
let (ctx, crx) = channel();
|
||||
let num_addresses = addresses.len();
|
||||
@ -1236,7 +1236,7 @@ impl LightClient {
|
||||
let ctx = ctx.clone();
|
||||
|
||||
pool.execute(move || {
|
||||
// Fetch the transparent transactions for this address, and send the results
|
||||
// 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,
|
||||
move |tx_bytes: &[u8], height: u64| {
|
||||
@ -1244,17 +1244,17 @@ impl LightClient {
|
||||
|
||||
// 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);
|
||||
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.
|
||||
// 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
|
||||
last_scanned_height = end_height;
|
||||
end_height = last_scanned_height + 1000;
|
||||
@ -1269,7 +1269,7 @@ impl LightClient {
|
||||
if print_updates{
|
||||
println!(""); // New line to finish up the updates
|
||||
}
|
||||
|
||||
|
||||
info!("Synced to {}, Downloaded {} kB", latest_block, bytes_downloaded.load(Ordering::SeqCst) / 1024);
|
||||
{
|
||||
let mut status = self.sync_status.write().unwrap();
|
||||
@ -1292,13 +1292,13 @@ impl LightClient {
|
||||
txids_to_fetch.sort();
|
||||
txids_to_fetch.dedup();
|
||||
|
||||
let mut rng = OsRng;
|
||||
let mut rng = OsRng;
|
||||
txids_to_fetch.shuffle(&mut rng);
|
||||
|
||||
let num_fetches = txids_to_fetch.len();
|
||||
let (ctx, crx) = channel();
|
||||
|
||||
// And go and fetch the txids, getting the full transaction, so we can
|
||||
// And go and fetch the txids, getting the full transaction, so we can
|
||||
// read the memos
|
||||
for (txid, height) in txids_to_fetch {
|
||||
let light_wallet_clone = self.wallet.clone();
|
||||
@ -1306,19 +1306,19 @@ impl LightClient {
|
||||
let pool = pool.clone();
|
||||
let server_uri = self.get_server_uri();
|
||||
let ctx = ctx.clone();
|
||||
|
||||
|
||||
pool.execute(move || {
|
||||
info!("Fetching full Tx: {}", txid);
|
||||
|
||||
match fetch_full_tx(&server_uri, txid) {
|
||||
Ok(tx_bytes) => {
|
||||
let tx = Transaction::read(&tx_bytes[..]).unwrap();
|
||||
|
||||
|
||||
light_wallet_clone.read().unwrap().scan_full_tx(&tx, height, 0);
|
||||
ctx.send(Ok(())).unwrap();
|
||||
},
|
||||
Err(e) => ctx.send(Err(e)).unwrap()
|
||||
};
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
@ -1331,7 +1331,7 @@ impl LightClient {
|
||||
"downloaded_bytes" => bytes_downloaded.load(Ordering::SeqCst)
|
||||
}),
|
||||
Err(e) => Err(format!("Error fetching all txns for memos: {}", e))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn do_send(&self, from: &str, addrs: Vec<(&str, u64, Option<String>)>, fee: &u64) -> Result<String, String> {
|
||||
@ -1350,7 +1350,7 @@ impl LightClient {
|
||||
|
||||
info!("Transaction Complete");
|
||||
|
||||
|
||||
|
||||
match rawtx {
|
||||
Ok(txbytes) => broadcast_raw_tx(&self.get_server_uri(), txbytes),
|
||||
Err(e) => Err(format!("Error: No Tx to broadcast. Error was: {}", e))
|
||||
@ -1393,27 +1393,27 @@ pub mod tests {
|
||||
// This will lock the wallet again, so after this, we'll need to unlock again
|
||||
assert!(!lc.do_new_address("t").is_err());
|
||||
lc.wallet.write().unwrap().unlock("password".to_string()).unwrap();
|
||||
|
||||
|
||||
assert!(!lc.do_new_address("z").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_addresses() {
|
||||
let lc = super::LightClient::unconnected(TEST_SEED.to_string(), None).unwrap();
|
||||
|
||||
|
||||
{
|
||||
let addresses = lc.do_address();
|
||||
// When restoring from seed, there should be 5+1 addresses
|
||||
assert_eq!(addresses["z_addresses"].len(), 6);
|
||||
assert_eq!(addresses["t_addresses"].len(), 6);
|
||||
}
|
||||
|
||||
|
||||
// Add new z and t addresses
|
||||
let taddr1 = lc.do_new_address("t").unwrap()[0].as_str().unwrap().to_string();
|
||||
let taddr2 = lc.do_new_address("t").unwrap()[0].as_str().unwrap().to_string();
|
||||
let taddr2 = lc.do_new_address("t").unwrap()[0].as_str().unwrap().to_string();
|
||||
let zaddr1 = lc.do_new_address("z").unwrap()[0].as_str().unwrap().to_string();
|
||||
let zaddr2 = lc.do_new_address("z").unwrap()[0].as_str().unwrap().to_string();
|
||||
|
||||
|
||||
let addresses = lc.do_address();
|
||||
assert_eq!(addresses["z_addresses"].len(), 8);
|
||||
assert_eq!(addresses["z_addresses"][6], zaddr1);
|
||||
@ -1431,7 +1431,7 @@ pub mod tests {
|
||||
let lc = LightClient {
|
||||
wallet : Arc::new(RwLock::new(LightWallet::new(None, &config, 0).unwrap())),
|
||||
config : config,
|
||||
sapling_output : vec![],
|
||||
sapling_output : vec![],
|
||||
sapling_spend : vec![],
|
||||
sync_lock : Mutex::new(()),
|
||||
sync_status : Arc::new(RwLock::new(WalletStatus::new())),
|
||||
@ -1523,8 +1523,8 @@ pub mod tests {
|
||||
|
||||
use crate::SaplingParams;
|
||||
assert!(lc.set_sapling_params(
|
||||
SaplingParams::get("sapling-output.params").unwrap().as_ref(),
|
||||
SaplingParams::get("sapling-output.params").unwrap().as_ref(),
|
||||
SaplingParams::get("sapling-spend.params").unwrap().as_ref()).is_ok());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -99,16 +99,16 @@ impl ToBase58Check for [u8] {
|
||||
}
|
||||
|
||||
pub struct LightWallet {
|
||||
// Is the wallet encrypted? If it is, then when writing to disk, the seed is always encrypted
|
||||
// and the individual spending keys are not written
|
||||
encrypted: bool,
|
||||
// Is the wallet encrypted? If it is, then when writing to disk, the seed is always encrypted
|
||||
// and the individual spending keys are not written
|
||||
encrypted: bool,
|
||||
|
||||
// In memory only (i.e, this field is not written to disk). Is the wallet unlocked and are
|
||||
// the spending keys present to allow spending from this wallet?
|
||||
unlocked: bool,
|
||||
|
||||
enc_seed: [u8; 48], // If locked, this contains the encrypted seed
|
||||
nonce: Vec<u8>, // Nonce used to encrypt the wallet.
|
||||
nonce: Vec<u8>, // Nonce used to encrypt the wallet.
|
||||
|
||||
seed: [u8; 32], // Seed phrase for this wallet. If wallet is locked, this is 0
|
||||
|
||||
@ -118,17 +118,17 @@ pub struct LightWallet {
|
||||
extfvks: Arc<RwLock<Vec<ExtendedFullViewingKey>>>,
|
||||
|
||||
pub zaddress: Arc<RwLock<Vec<PaymentAddress<Bls12>>>>,
|
||||
|
||||
|
||||
// Transparent keys. If the wallet is locked, then the secret keys will be encrypted,
|
||||
// but the addresses will be present.
|
||||
// but the addresses will be present.
|
||||
tkeys: Arc<RwLock<Vec<secp256k1::SecretKey>>>,
|
||||
pub taddresses: Arc<RwLock<Vec<String>>>,
|
||||
|
||||
blocks: Arc<RwLock<Vec<BlockData>>>,
|
||||
pub txs: Arc<RwLock<HashMap<TxId, WalletTx>>>,
|
||||
|
||||
// Transactions that are only in the mempool, but haven't been confirmed yet.
|
||||
// This is not stored to disk.
|
||||
// Transactions that are only in the mempool, but haven't been confirmed yet.
|
||||
// This is not stored to disk.
|
||||
pub mempool_txs: Arc<RwLock<HashMap<TxId, WalletTx>>>,
|
||||
|
||||
// The block at which this wallet was born. Rescans
|
||||
@ -163,7 +163,7 @@ impl LightWallet {
|
||||
fn get_zaddr_from_bip39seed(config: &LightClientConfig, bip39_seed: &[u8], pos: u32) ->
|
||||
(ExtendedSpendingKey, ExtendedFullViewingKey, PaymentAddress<Bls12>) {
|
||||
assert_eq!(bip39_seed.len(), 64);
|
||||
|
||||
|
||||
let extsk: ExtendedSpendingKey = ExtendedSpendingKey::from_path(
|
||||
&ExtendedSpendingKey::master(bip39_seed),
|
||||
&[
|
||||
@ -180,12 +180,12 @@ impl LightWallet {
|
||||
|
||||
pub fn is_shielded_address(addr: &String, config: &LightClientConfig) -> bool {
|
||||
match address::RecipientAddress::from_str(addr,
|
||||
config.hrp_sapling_address(),
|
||||
config.base58_pubkey_address(),
|
||||
config.hrp_sapling_address(),
|
||||
config.base58_pubkey_address(),
|
||||
config.base58_script_address()) {
|
||||
Some(address::RecipientAddress::Shielded(_)) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(seed_phrase: Option<String>, config: &LightClientConfig, latest_block: u64) -> io::Result<Self> {
|
||||
@ -193,7 +193,7 @@ impl LightWallet {
|
||||
let mut seed_bytes = [0u8; 32];
|
||||
|
||||
if seed_phrase.is_none() {
|
||||
// Create a random seed.
|
||||
// Create a random seed.
|
||||
let mut system_rng = OsRng;
|
||||
system_rng.fill(&mut seed_bytes);
|
||||
} else {
|
||||
@ -205,11 +205,11 @@ impl LightWallet {
|
||||
return Err(io::Error::new(ErrorKind::InvalidData, e));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
seed_bytes.copy_from_slice(&phrase.entropy());
|
||||
}
|
||||
|
||||
// The seed bytes is the raw entropy. To pass it to HD wallet generation,
|
||||
// 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(&seed_bytes, Language::English).unwrap(), "");
|
||||
|
||||
@ -217,7 +217,7 @@ impl LightWallet {
|
||||
let tpk = LightWallet::get_taddr_from_bip39seed(&config, &bip39_seed.as_bytes(), 0);
|
||||
let taddr = LightWallet::address_from_prefix_sk(&config.base58_pubkey_address(), &tpk);
|
||||
|
||||
// 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
|
||||
let (extsk, extfvk, address)
|
||||
= LightWallet::get_zaddr_from_bip39seed(&config, &bip39_seed.as_bytes(), 0);
|
||||
@ -262,7 +262,7 @@ impl LightWallet {
|
||||
|
||||
println!("Reading wallet version {}", version);
|
||||
info!("Reading wallet version {}", version);
|
||||
|
||||
|
||||
// At version 5, we're writing the rest of the file as a compressed stream (gzip)
|
||||
let mut reader: Box<dyn Read> = if version != 5 {
|
||||
info!("Reading direct");
|
||||
@ -278,12 +278,12 @@ impl LightWallet {
|
||||
false
|
||||
};
|
||||
info!("Wallet Encryption {:?}", encrypted);
|
||||
|
||||
|
||||
let mut enc_seed = [0u8; 48];
|
||||
if version >= 4 {
|
||||
reader.read_exact(&mut enc_seed)?;
|
||||
}
|
||||
|
||||
|
||||
let nonce = if version >= 4 {
|
||||
Vector::read(&mut reader, |r| r.read_u8())?
|
||||
} else {
|
||||
@ -293,10 +293,10 @@ impl LightWallet {
|
||||
// Seed
|
||||
let mut seed_bytes = [0u8; 32];
|
||||
reader.read_exact(&mut seed_bytes)?;
|
||||
|
||||
|
||||
// Read the spending keys
|
||||
let extsks = Vector::read(&mut reader, |r| ExtendedSpendingKey::read(r))?;
|
||||
|
||||
|
||||
let extfvks = if version >= 4 {
|
||||
// Read the viewing keys
|
||||
Vector::read(&mut reader, |r| ExtendedFullViewingKey::read(r))?
|
||||
@ -305,7 +305,7 @@ impl LightWallet {
|
||||
extsks.iter().map(|sk| ExtendedFullViewingKey::from(sk))
|
||||
.collect::<Vec<ExtendedFullViewingKey>>()
|
||||
};
|
||||
|
||||
|
||||
// Calculate the addresses
|
||||
let addresses = extfvks.iter().map( |fvk| fvk.default_address().unwrap().1 )
|
||||
.collect::<Vec<PaymentAddress<Bls12>>>();
|
||||
@ -314,8 +314,8 @@ impl LightWallet {
|
||||
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))?
|
||||
@ -323,9 +323,9 @@ impl LightWallet {
|
||||
// Calculate the addresses
|
||||
tkeys.iter().map(|sk| LightWallet::address_from_prefix_sk(&config.base58_pubkey_address(), sk)).collect()
|
||||
};
|
||||
|
||||
|
||||
let blocks = Vector::read(&mut reader, |r| BlockData::read(r))?;
|
||||
|
||||
|
||||
let txs_tuples = Vector::read(&mut reader, |r| {
|
||||
let mut txid_bytes = [0u8; 32];
|
||||
r.read_exact(&mut txid_bytes)?;
|
||||
@ -345,7 +345,7 @@ impl LightWallet {
|
||||
|
||||
Ok(LightWallet{
|
||||
encrypted: encrypted,
|
||||
unlocked: !encrypted, // When reading from disk, if wallet is encrypted, it starts off locked.
|
||||
unlocked: !encrypted, // When reading from disk, if wallet is encrypted, it starts off locked.
|
||||
enc_seed: enc_seed,
|
||||
nonce: nonce,
|
||||
seed: seed_bytes,
|
||||
@ -365,7 +365,7 @@ impl LightWallet {
|
||||
|
||||
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||
if self.encrypted && self.unlocked {
|
||||
return Err(Error::new(ErrorKind::InvalidInput,
|
||||
return Err(Error::new(ErrorKind::InvalidInput,
|
||||
format!("Cannot write while wallet is unlocked while encrypted.")));
|
||||
}
|
||||
|
||||
@ -408,7 +408,7 @@ impl LightWallet {
|
||||
)?;
|
||||
|
||||
Vector::write(&mut writer, &self.blocks.read().unwrap(), |w, b| b.write(w))?;
|
||||
|
||||
|
||||
// The hashmap, write as a set of tuples. Store them sorted so that wallets are
|
||||
// deterministically saved
|
||||
{
|
||||
@ -471,7 +471,7 @@ impl LightWallet {
|
||||
/// Get all t-address private keys. Returns a Vector of (address, secretkey)
|
||||
pub fn get_t_secret_keys(&self) -> Vec<(String, String)> {
|
||||
self.tkeys.read().unwrap().iter().map(|sk| {
|
||||
(self.address_from_sk(sk),
|
||||
(self.address_from_sk(sk),
|
||||
sk[..].to_base58check(&self.config.base58_secretkey_prefix(), &[0x01]))
|
||||
}).collect::<Vec<(String, String)>>()
|
||||
}
|
||||
@ -508,7 +508,7 @@ impl LightWallet {
|
||||
|
||||
let pos = self.tkeys.read().unwrap().len() as u32;
|
||||
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 address = self.address_from_sk(&sk);
|
||||
|
||||
@ -637,7 +637,7 @@ impl LightWallet {
|
||||
pub fn address_from_sk(&self, sk: &secp256k1::SecretKey) -> String {
|
||||
LightWallet::address_from_prefix_sk(&self.config.base58_pubkey_address(), sk)
|
||||
}
|
||||
|
||||
|
||||
pub fn address_from_pubkeyhash(&self, ta: Option<TransparentAddress>) -> Option<String> {
|
||||
match ta {
|
||||
Some(TransparentAddress::PublicKey(hash)) => {
|
||||
@ -655,7 +655,7 @@ impl LightWallet {
|
||||
return "".to_string();
|
||||
}
|
||||
|
||||
Mnemonic::from_entropy(&self.seed,
|
||||
Mnemonic::from_entropy(&self.seed,
|
||||
Language::English,
|
||||
).unwrap().phrase().to_string()
|
||||
}
|
||||
@ -672,7 +672,7 @@ impl LightWallet {
|
||||
let nonce = secretbox::gen_nonce();
|
||||
|
||||
let cipher = secretbox::seal(&self.seed, &nonce, &key);
|
||||
|
||||
|
||||
self.enc_seed.copy_from_slice(&cipher);
|
||||
self.nonce = vec![];
|
||||
self.nonce.extend_from_slice(nonce.as_ref());
|
||||
@ -725,7 +725,7 @@ impl LightWallet {
|
||||
// Now that we have the seed, we'll generate the extsks and tkeys, and verify the fvks and addresses
|
||||
// respectively match
|
||||
|
||||
// The seed bytes is the raw entropy. To pass it to HD wallet generation,
|
||||
// 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(&seed, Language::English).unwrap(), "");
|
||||
|
||||
@ -736,12 +736,12 @@ impl LightWallet {
|
||||
LightWallet::get_zaddr_from_bip39seed(&self.config, &bip39_seed.as_bytes(), pos as u32);
|
||||
|
||||
if address != self.zaddress.read().unwrap()[pos] {
|
||||
return Err(io::Error::new(ErrorKind::InvalidData,
|
||||
return Err(io::Error::new(ErrorKind::InvalidData,
|
||||
format!("zaddress mismatch at {}. {:?} vs {:?}", pos, address, self.zaddress.read().unwrap()[pos])));
|
||||
}
|
||||
|
||||
if extfvk != self.extfvks.read().unwrap()[pos] {
|
||||
return Err(io::Error::new(ErrorKind::InvalidData,
|
||||
return Err(io::Error::new(ErrorKind::InvalidData,
|
||||
format!("fvk mismatch at {}. {:?} vs {:?}", pos, extfvk, self.extfvks.read().unwrap()[pos])));
|
||||
}
|
||||
|
||||
@ -756,7 +756,7 @@ impl LightWallet {
|
||||
let address = self.address_from_sk(&sk);
|
||||
|
||||
if address != self.taddresses.read().unwrap()[pos] {
|
||||
return Err(io::Error::new(ErrorKind::InvalidData,
|
||||
return Err(io::Error::new(ErrorKind::InvalidData,
|
||||
format!("taddress mismatch at {}. {} vs {}", pos, address, self.taddresses.read().unwrap()[pos])));
|
||||
}
|
||||
|
||||
@ -767,7 +767,7 @@ impl LightWallet {
|
||||
self.extsks = Arc::new(RwLock::new(extsks));
|
||||
self.tkeys = Arc::new(RwLock::new(tkeys));
|
||||
self.seed.copy_from_slice(&seed);
|
||||
|
||||
|
||||
self.encrypted = true;
|
||||
self.unlocked = true;
|
||||
|
||||
@ -776,7 +776,7 @@ impl LightWallet {
|
||||
|
||||
// Removing encryption means unlocking it and setting the self.encrypted = false,
|
||||
// permanantly removing the encryption
|
||||
pub fn remove_encryption(&mut self, passwd: String) -> io::Result<()> {
|
||||
pub fn remove_encryption(&mut self, passwd: String) -> io::Result<()> {
|
||||
if !self.encrypted {
|
||||
return Err(Error::new(ErrorKind::AlreadyExists, "Wallet is not encrypted"));
|
||||
}
|
||||
@ -785,7 +785,7 @@ impl LightWallet {
|
||||
if !self.unlocked {
|
||||
self.unlock(passwd)?;
|
||||
}
|
||||
|
||||
|
||||
// Permanantly remove the encryption
|
||||
self.encrypted = false;
|
||||
self.nonce = vec![];
|
||||
@ -807,7 +807,7 @@ impl LightWallet {
|
||||
.values()
|
||||
.map(|tx| {
|
||||
tx.notes.iter()
|
||||
.filter(|nd| { // TODO, this whole section is shared with verified_balance. Refactor it.
|
||||
.filter(|nd| { // TODO, this whole section is shared with verified_balance. Refactor it.
|
||||
match addr.clone() {
|
||||
Some(a) => a == encode_payment_address(
|
||||
self.config.hrp_sapling_address(),
|
||||
@ -861,7 +861,7 @@ impl LightWallet {
|
||||
if tx.block as u32 <= anchor_height {
|
||||
tx.notes
|
||||
.iter()
|
||||
.filter(|nd| { // TODO, this whole section is shared with verified_balance. Refactor it.
|
||||
.filter(|nd| { // TODO, this whole section is shared with verified_balance. Refactor it.
|
||||
match addr.clone() {
|
||||
Some(a) => a == encode_payment_address(
|
||||
self.config.hrp_sapling_address(),
|
||||
@ -894,7 +894,7 @@ impl LightWallet {
|
||||
match tx_entry.utxos.iter().find(|utxo| {
|
||||
utxo.txid == *txid && utxo.output_index == n && Amount::from_u64(utxo.value).unwrap() == vout.value
|
||||
}) {
|
||||
Some(utxo) => {
|
||||
Some(utxo) => {
|
||||
info!("Already have {}:{}", utxo.txid, utxo.output_index);
|
||||
}
|
||||
None => {
|
||||
@ -919,14 +919,14 @@ impl LightWallet {
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
// 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) {
|
||||
let last_addresses = {
|
||||
self.taddresses.read().unwrap().iter().rev().take(GAP_RULE_UNUSED_ADDRESSES).map(|s| s.clone()).collect::<Vec<String>>()
|
||||
};
|
||||
|
||||
|
||||
match last_addresses.iter().position(|s| *s == *address) {
|
||||
None => {
|
||||
None => {
|
||||
return;
|
||||
},
|
||||
Some(pos) => {
|
||||
@ -935,7 +935,7 @@ impl LightWallet {
|
||||
for _ in 0..(GAP_RULE_UNUSED_ADDRESSES - pos) {
|
||||
// If the wallet is locked, this is a no-op. That is fine, since we really
|
||||
// need to only add new addresses when restoring a new wallet, when it will not be locked.
|
||||
// Also, if it is locked, the user can't create new addresses anyway.
|
||||
// Also, if it is locked, the user can't create new addresses anyway.
|
||||
self.add_taddr();
|
||||
}
|
||||
}
|
||||
@ -949,7 +949,7 @@ impl LightWallet {
|
||||
.map(|s| encode_payment_address(self.config.hrp_sapling_address(), s))
|
||||
.collect::<Vec<String>>()
|
||||
};
|
||||
|
||||
|
||||
match last_addresses.iter().position(|s| *s == *address) {
|
||||
None => {
|
||||
return;
|
||||
@ -960,7 +960,7 @@ impl LightWallet {
|
||||
for _ in 0..(GAP_RULE_UNUSED_ADDRESSES - pos) {
|
||||
// If the wallet is locked, this is a no-op. That is fine, since we really
|
||||
// need to only add new addresses when restoring a new wallet, when it will not be locked.
|
||||
// Also, if it is locked, the user can't create new addresses anyway.
|
||||
// Also, if it is locked, the user can't create new addresses anyway.
|
||||
self.add_zaddr();
|
||||
}
|
||||
}
|
||||
@ -1006,7 +1006,7 @@ impl LightWallet {
|
||||
let tx_entry = WalletTx::new(height, datetime, &tx.txid());
|
||||
txs.insert(tx.txid().clone(), tx_entry);
|
||||
}
|
||||
|
||||
|
||||
txs.get_mut(&tx.txid()).unwrap()
|
||||
.total_transparent_value_spent = total_transparent_spend;
|
||||
}
|
||||
@ -1093,11 +1093,11 @@ impl LightWallet {
|
||||
|
||||
{
|
||||
info!("A sapling note was sent in {}, getting memo", tx.txid());
|
||||
|
||||
// Do it in a short scope because of the write lock.
|
||||
|
||||
// Do it in a short scope because of the write lock.
|
||||
let mut txs = self.txs.write().unwrap();
|
||||
|
||||
// Update memo if we have this Tx.
|
||||
// Update memo if we have this Tx.
|
||||
match txs.get_mut(&tx.txid())
|
||||
.and_then(|t| {
|
||||
t.notes.iter_mut().find(|nd| nd.note == note)
|
||||
@ -1127,13 +1127,13 @@ impl LightWallet {
|
||||
|
||||
for (_account, ovk) in ovks.iter().enumerate() {
|
||||
match try_sapling_output_recovery(ovk,
|
||||
&output.cv,
|
||||
&output.cmu,
|
||||
&output.ephemeral_key.as_prime_order(&JUBJUB).unwrap(),
|
||||
&output.cv,
|
||||
&output.cmu,
|
||||
&output.ephemeral_key.as_prime_order(&JUBJUB).unwrap(),
|
||||
&output.enc_ciphertext,
|
||||
&output.out_ciphertext) {
|
||||
Some((note, payment_address, memo)) => {
|
||||
let address = encode_payment_address(self.config.hrp_sapling_address(),
|
||||
let address = encode_payment_address(self.config.hrp_sapling_address(),
|
||||
&payment_address);
|
||||
|
||||
// Check if this is a change address
|
||||
@ -1183,9 +1183,9 @@ impl LightWallet {
|
||||
let mut num_invalidated = 0;
|
||||
|
||||
// First remove the blocks
|
||||
{
|
||||
{
|
||||
let mut blks = self.blocks.write().unwrap();
|
||||
|
||||
|
||||
while blks.last().unwrap().height >= at_height {
|
||||
blks.pop();
|
||||
num_invalidated += 1;
|
||||
@ -1232,7 +1232,7 @@ impl LightWallet {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
num_invalidated as u64
|
||||
}
|
||||
|
||||
@ -1275,7 +1275,7 @@ impl LightWallet {
|
||||
tx.send(Some(None))
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
match r {
|
||||
Ok(_) => {},
|
||||
Err(e) => println!("Send error {:?}", e)
|
||||
@ -1301,7 +1301,7 @@ impl LightWallet {
|
||||
for _i in 0..ivks.len() {
|
||||
let n = rx.recv().unwrap();
|
||||
let epk = epk.clone();
|
||||
|
||||
|
||||
let wso = match n {
|
||||
None => panic!("Got a none!"),
|
||||
Some(None) => None,
|
||||
@ -1313,7 +1313,7 @@ impl LightWallet {
|
||||
// - Notes created by consolidation transactions.
|
||||
// - Notes sent from one account to itself.
|
||||
//let is_change = spent_from_accounts.contains(&account);
|
||||
|
||||
|
||||
Some(WalletShieldedOutput {
|
||||
index, cmu, epk, account, note, to, is_change: false,
|
||||
witness: IncrementalWitness::from_tree(tree),
|
||||
@ -1322,7 +1322,7 @@ impl LightWallet {
|
||||
};
|
||||
wsos.push(wso);
|
||||
}
|
||||
|
||||
|
||||
match wsos.into_iter().find(|wso| wso.is_some()) {
|
||||
Some(Some(wso)) => Some(wso),
|
||||
_ => None
|
||||
@ -1513,12 +1513,12 @@ impl LightWallet {
|
||||
.map(|block| block.tree.clone())
|
||||
.unwrap_or(CommitmentTree::new()),
|
||||
};
|
||||
|
||||
|
||||
// These are filled in inside the block
|
||||
let new_txs;
|
||||
let nfs: Vec<_>;
|
||||
{
|
||||
// Create a write lock
|
||||
// Create a write lock
|
||||
let mut txs = self.txs.write().unwrap();
|
||||
|
||||
// Create a Vec containing all unspent nullifiers.
|
||||
@ -1574,8 +1574,8 @@ impl LightWallet {
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
// If this block had any new Txs, return the list of ALL txids in this block,
|
||||
|
||||
// If this block had any new Txs, return the list of ALL txids in this block,
|
||||
// so the wallet can fetch them all as a decoy.
|
||||
let all_txs = if !new_txs.is_empty() {
|
||||
block.vtx.iter().map(|vtx| {
|
||||
@ -1588,7 +1588,7 @@ impl LightWallet {
|
||||
};
|
||||
|
||||
for tx in new_txs {
|
||||
// Create a write lock
|
||||
// Create a write lock
|
||||
let mut txs = self.txs.write().unwrap();
|
||||
|
||||
// Mark notes as spent.
|
||||
@ -1596,7 +1596,7 @@ impl LightWallet {
|
||||
|
||||
info!("Txid {} belongs to wallet", tx.txid);
|
||||
|
||||
for spend in &tx.shielded_spends {
|
||||
for spend in &tx.shielded_spends {
|
||||
let txid = nfs
|
||||
.iter()
|
||||
.find(|(nf, _, _)| &nf[..] == &spend.nf[..])
|
||||
@ -1609,7 +1609,7 @@ impl LightWallet {
|
||||
.iter_mut()
|
||||
.find(|nd| &nd.nullifier[..] == &spend.nf[..])
|
||||
.unwrap();
|
||||
|
||||
|
||||
// Mark the note as spent, and remove the unconfirmed part of it
|
||||
info!("Marked a note as spent");
|
||||
spent_note.spent = Some(tx.txid);
|
||||
@ -1641,13 +1641,13 @@ impl LightWallet {
|
||||
match tx_entry.notes.iter().find(|nd| nd.nullifier == new_note.nullifier) {
|
||||
None => tx_entry.notes.push(new_note),
|
||||
Some(_) => warn!("Tried to insert duplicate note for Tx {}", tx.txid)
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut blks = self.blocks.write().unwrap();
|
||||
|
||||
|
||||
// Store scanned data for this block.
|
||||
blks.push(block_data);
|
||||
|
||||
@ -1658,7 +1658,7 @@ impl LightWallet {
|
||||
blks.drain(..drain_first);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
// Cleanup mempool tx after adding a block, to remove all txns that got mined
|
||||
self.cleanup_mempool();
|
||||
@ -1701,9 +1701,9 @@ impl LightWallet {
|
||||
|
||||
// Convert address (str) to RecepientAddress and value to Amount
|
||||
let recepients = tos.iter().map(|to| {
|
||||
let ra = match address::RecipientAddress::from_str(to.0,
|
||||
self.config.hrp_sapling_address(),
|
||||
self.config.base58_pubkey_address(),
|
||||
let ra = match address::RecipientAddress::from_str(to.0,
|
||||
self.config.hrp_sapling_address(),
|
||||
self.config.base58_pubkey_address(),
|
||||
self.config.base58_script_address()) {
|
||||
Some(to) => to,
|
||||
None => {
|
||||
@ -1756,8 +1756,8 @@ impl LightWallet {
|
||||
builder.set_fee(Amount::from_u64(*fee).unwrap());
|
||||
|
||||
// A note on t addresses
|
||||
// Funds received by t-addresses can't be explicitly spent in ZecWallet.
|
||||
// ZecWallet will lazily consolidate all t address funds into your shielded addresses.
|
||||
// Funds received by t-addresses can't be explicitly spent in ZecWallet.
|
||||
// ZecWallet will lazily consolidate all t address funds into your shielded addresses.
|
||||
// Specifically, if you send an outgoing transaction that is sent to a shielded address,
|
||||
// ZecWallet will add all your t-address funds into that transaction, and send them to your shielded
|
||||
// address as change.
|
||||
@ -1766,8 +1766,8 @@ impl LightWallet {
|
||||
.filter(|utxo| utxo.unconfirmed_spent.is_none()) // Remove any unconfirmed spends
|
||||
.map(|utxo| utxo.clone())
|
||||
.collect();
|
||||
|
||||
// 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
|
||||
let address_to_sk = self.tkeys.read().unwrap().iter()
|
||||
.map(|sk| (self.address_from_sk(&sk), sk.clone()))
|
||||
@ -1777,7 +1777,7 @@ impl LightWallet {
|
||||
tinputs.iter()
|
||||
.map(|utxo| {
|
||||
let outpoint: OutPoint = utxo.to_outpoint();
|
||||
|
||||
|
||||
let coin = TxOut {
|
||||
value: Amount::from_u64(utxo.value).unwrap(),
|
||||
script_pubkey: Script { 0: utxo.script.clone() },
|
||||
@ -1793,14 +1793,14 @@ impl LightWallet {
|
||||
Err(zcash_primitives::transaction::builder::Error::InvalidAddress)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.map_err(|e| format!("{:?}", e))?;
|
||||
|
||||
|
||||
|
||||
// Confirm we were able to select sufficient value
|
||||
let selected_value = notes.iter().map(|selected| selected.note.value).sum::<u64>()
|
||||
let selected_value = notes.iter().map(|selected| selected.note.value).sum::<u64>()
|
||||
+ tinputs.iter().map::<u64, _>(|utxo| utxo.value.into()).sum::<u64>();
|
||||
|
||||
if selected_value < u64::from(target_value) {
|
||||
@ -1895,7 +1895,7 @@ impl LightWallet {
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
println!("{}: Building transaction", now() - start_time);
|
||||
let (tx, _) = match builder.build(
|
||||
@ -1952,7 +1952,7 @@ impl LightWallet {
|
||||
Memo::from_bytes(s.as_bytes()).unwrap()
|
||||
} else {
|
||||
Memo::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
@ -1962,7 +1962,7 @@ impl LightWallet {
|
||||
let mut wtx = WalletTx::new(height as i32, now() as u64, &tx.txid());
|
||||
wtx.outgoing_metadata = outgoing_metadata;
|
||||
|
||||
// Add it into the mempool
|
||||
// Add it into the mempool
|
||||
mempool_txs.insert(tx.txid(), wtx);
|
||||
},
|
||||
Some(_) => {
|
||||
@ -1989,7 +1989,7 @@ impl LightWallet {
|
||||
{
|
||||
// Remove all expired Txns
|
||||
self.mempool_txs.write().unwrap().retain( | _, wtx| {
|
||||
current_height < (wtx.block + DEFAULT_TX_EXPIRY_DELTA)
|
||||
current_height < (wtx.block + DEFAULT_TX_EXPIRY_DELTA)
|
||||
});
|
||||
}
|
||||
|
||||
@ -2003,4 +2003,4 @@ impl LightWallet {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests;
|
||||
pub mod tests;
|
||||
|
@ -1,10 +1,10 @@
|
||||
///
|
||||
/// 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
|
||||
/// 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
|
||||
@ -32,7 +32,7 @@ impl BugBip39Derivation {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The seed bytes is the raw entropy. To pass it to HD wallet generation,
|
||||
// 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(), "");
|
||||
|
||||
@ -70,13 +70,13 @@ impl BugBip39Derivation {
|
||||
};
|
||||
|
||||
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 amount: u64 = balance_json["zbalance"].as_u64().unwrap()
|
||||
let amount: u64 = balance_json["zbalance"].as_u64().unwrap()
|
||||
+ balance_json["tbalance"].as_u64().unwrap();
|
||||
|
||||
let txid = if amount > 0 {
|
||||
@ -127,4 +127,4 @@ impl BugBip39Derivation {
|
||||
|
||||
return r.pretty(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ use zcash_primitives::{
|
||||
sapling::Node,
|
||||
serialize::{Vector, Optional},
|
||||
transaction::{
|
||||
components::{OutPoint},
|
||||
components::{OutPoint},
|
||||
TxId,
|
||||
},
|
||||
note_encryption::{Memo,},
|
||||
|
Loading…
Reference in New Issue
Block a user