|
|
@ -39,7 +39,7 @@ use zcash_primitives::{ |
|
|
|
serialize::{Vector}, |
|
|
|
transaction::{ |
|
|
|
builder::{Builder}, |
|
|
|
components::{Amount, OutPoint, TxOut}, components::amount::DEFAULT_FEE, |
|
|
|
components::{Amount, OutPoint, TxOut}, |
|
|
|
TxId, Transaction,
|
|
|
|
}, |
|
|
|
sapling::Node, |
|
|
@ -1470,26 +1470,43 @@ pub fn scan_full_tx(&self, tx: &Transaction, height: i32, datetime: u64) { |
|
|
|
// Do it in a short scope because of the write lock.
|
|
|
|
{ |
|
|
|
info!("A sapling output was sent in {}", tx.txid()); |
|
|
|
let mut txs = self.txs.write().unwrap(); |
|
|
|
if txs.get(&tx.txid()).unwrap().outgoing_metadata.iter() |
|
|
|
.find(|om| om.address == address && om.value == note.value && om.memo == memo) |
|
|
|
.is_some() { |
|
|
|
warn!("Duplicate outgoing metadata"); |
|
|
|
continue; |
|
|
|
} |
|
|
|
match self.txs.write() { |
|
|
|
Ok(mut txs) => { |
|
|
|
match txs.get(&tx.txid()) { |
|
|
|
Some(wtx) => { |
|
|
|
if wtx.outgoing_metadata.iter() |
|
|
|
.any(|om| om.address == address && om.value == note.value && om.memo == memo) |
|
|
|
{ |
|
|
|
warn!("Duplicate outgoing metadata"); |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
// Write the outgoing metadata
|
|
|
|
txs.get_mut(&tx.txid()).unwrap() |
|
|
|
.outgoing_metadata |
|
|
|
.push(OutgoingTxMetadata{ |
|
|
|
address, value: note.value, memo, |
|
|
|
}); |
|
|
|
// Write the outgoing metadata
|
|
|
|
txs.get_mut(&tx.txid()).unwrap() |
|
|
|
.outgoing_metadata |
|
|
|
.push(OutgoingTxMetadata { |
|
|
|
address, |
|
|
|
value: note.value, |
|
|
|
memo, |
|
|
|
}); |
|
|
|
}, |
|
|
|
None => { |
|
|
|
error!("Can not find any entry for txid : {}", tx.txid()); |
|
|
|
continue;
|
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
Err(poisoned) => { |
|
|
|
error!("Lock is poisoned: {}", poisoned); |
|
|
|
return;
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
None => {} |
|
|
|
}; |
|
|
|
} |
|
|
|
} |
|
|
|
}
|
|
|
|
// Mark this Tx as scanned
|
|
|
|
{ |
|
|
|
let mut txs = self.txs.write().unwrap(); |
|
|
@ -1545,20 +1562,11 @@ pub fn scan_full_mempool_tx(&self, tx: &Transaction, height: i32, _datetime: u64 |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
let position = if formatted_memo.as_ref().map_or(false, |m| m.starts_with('{')) { |
|
|
|
1 |
|
|
|
} else { |
|
|
|
existing_txs.iter() |
|
|
|
.filter(|tx| !LightWallet::memo_str(&Some(tx.incoming_metadata.iter().last().unwrap().memo.clone())).as_ref().map_or(false, |m| m.starts_with('{'))) |
|
|
|
.count() as u64 + 2 |
|
|
|
}; |
|
|
|
|
|
|
|
let incoming_metadata = IncomingTxMetadata { |
|
|
|
address: addr.clone(), |
|
|
|
value: amt, |
|
|
|
memo: memo.clone(),
|
|
|
|
incoming_mempool: true, |
|
|
|
position: position, |
|
|
|
}; |
|
|
|
|
|
|
|
wtx.incoming_metadata.push(incoming_metadata); |
|
|
@ -1578,7 +1586,6 @@ pub fn scan_full_mempool_tx(&self, tx: &Transaction, height: i32, _datetime: u64 |
|
|
|
value: amt, |
|
|
|
memo: memo.clone(), |
|
|
|
incoming_mempool: true, |
|
|
|
position: position, |
|
|
|
}); |
|
|
|
} else { |
|
|
|
let mut new_wtx = WalletTx::new(height, now() as u64, &tx.txid()); |
|
|
@ -1587,14 +1594,12 @@ pub fn scan_full_mempool_tx(&self, tx: &Transaction, height: i32, _datetime: u64 |
|
|
|
value: amt, |
|
|
|
memo: memo.clone(), |
|
|
|
incoming_mempool: true, |
|
|
|
position: position, |
|
|
|
}); |
|
|
|
txs.insert(tx.txid(), new_wtx); |
|
|
|
} |
|
|
|
|
|
|
|
info!("Successfully added txid with memo"); |
|
|
|
} else { |
|
|
|
let position = 0; |
|
|
|
|
|
|
|
// Check if txid already exists in the hashmap
|
|
|
|
let txid_exists = match self.txs.read() { |
|
|
@ -1616,7 +1621,6 @@ pub fn scan_full_mempool_tx(&self, tx: &Transaction, height: i32, _datetime: u64 |
|
|
|
value: amt, |
|
|
|
memo: memo.clone(),
|
|
|
|
incoming_mempool: true, |
|
|
|
position: position, |
|
|
|
}; |
|
|
|
|
|
|
|
wtx.incoming_metadata.push(incoming_metadata); |
|
|
@ -1636,7 +1640,6 @@ pub fn scan_full_mempool_tx(&self, tx: &Transaction, height: i32, _datetime: u64 |
|
|
|
value: amt, |
|
|
|
memo: memo.clone(), |
|
|
|
incoming_mempool: true, |
|
|
|
position: position, |
|
|
|
}); |
|
|
|
} else { |
|
|
|
let mut new_wtx = WalletTx::new(height, now() as u64, &tx.txid()); |
|
|
@ -1645,7 +1648,6 @@ pub fn scan_full_mempool_tx(&self, tx: &Transaction, height: i32, _datetime: u64 |
|
|
|
value: amt, |
|
|
|
memo: memo.clone(), |
|
|
|
incoming_mempool: true, |
|
|
|
position: position, |
|
|
|
}); |
|
|
|
txs.insert(tx.txid(), new_wtx); |
|
|
|
} |
|
|
@ -1668,7 +1670,6 @@ pub fn scan_full_mempool_tx(&self, tx: &Transaction, height: i32, _datetime: u64 |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Invalidate all blocks including and after "at_height".
|
|
|
|
// Returns the number of blocks invalidated
|
|
|
|
pub fn invalidate_block(&self, at_height: i32) -> u64 { |
|
|
@ -2177,6 +2178,7 @@ pub fn scan_full_mempool_tx(&self, tx: &Transaction, height: i32, _datetime: u64 |
|
|
|
output_params: &[u8], |
|
|
|
_transparent_only: bool, |
|
|
|
tos: Vec<(&str, u64, Option<String>)>, |
|
|
|
fee: &u64, |
|
|
|
broadcast_fn: F |
|
|
|
) -> Result<(String, Vec<u8>), String>
|
|
|
|
where F: Fn(Box<[u8]>) -> Result<String, String> |
|
|
@ -2207,24 +2209,31 @@ pub fn scan_full_mempool_tx(&self, tx: &Transaction, height: i32, _datetime: u64 |
|
|
|
total_value, tos.len() |
|
|
|
); |
|
|
|
|
|
|
|
// 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(),
|
|
|
|
self.config.base58_script_address()) { |
|
|
|
Some(to) => to, |
|
|
|
None => { |
|
|
|
let e = format!("Invalid recipient address: '{}'", to.0); |
|
|
|
error!("{}", e); |
|
|
|
return Err(e); |
|
|
|
} |
|
|
|
}; |
|
|
|
// Convert address (str) to RecipientAddress and value to Amount
|
|
|
|
|
|
|
|
let recepients: Result<Vec<(address::RecipientAddress, Amount, Option<String>)>, String> = tos.iter().map(|to| { |
|
|
|
// Convert string to RecipientAddress
|
|
|
|
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(addr) => addr, |
|
|
|
None => { |
|
|
|
let e = format!("Invalid recipient address: '{}'", to.0); |
|
|
|
error!("{}", e); |
|
|
|
return Err(e); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
let value = Amount::from_u64(to.1).unwrap(); |
|
|
|
// Convert the second tuple element to Amount
|
|
|
|
let value = Amount::from_u64(to.1).expect("Invalid amount value"); |
|
|
|
|
|
|
|
Ok((ra, value, to.2.clone())) |
|
|
|
}).collect::<Result<Vec<(address::RecipientAddress, Amount, Option<String>)>, String>>()?; |
|
|
|
Ok((ra, value, to.2.clone())) |
|
|
|
}).collect(); |
|
|
|
|
|
|
|
let recepients = recepients?; |
|
|
|
|
|
|
|
// Target the next block, assuming we are up-to-date.
|
|
|
|
let (height, anchor_offset) = match self.get_target_height_and_anchor_offset() { |
|
|
@ -2237,8 +2246,9 @@ pub fn scan_full_mempool_tx(&self, tx: &Transaction, height: i32, _datetime: u64 |
|
|
|
}; |
|
|
|
|
|
|
|
// Select notes to cover the target value
|
|
|
|
println!("{}: Selecting notes", now() - start_time); |
|
|
|
let target_value = Amount::from_u64(total_value).unwrap() + DEFAULT_FEE ; |
|
|
|
// Select notes to cover the target value
|
|
|
|
println!("{}: Selecting notes", now() - start_time); |
|
|
|
let target_value = Amount::from_u64(total_value).unwrap() + Amount::from_u64(*fee).unwrap(); |
|
|
|
// Select the candidate notes that are eligible to be spent
|
|
|
|
let notes: Vec<_> = self.txs.read().unwrap().iter() |
|
|
|
.map(|(txid, tx)| tx.notes.iter().map(move |note| (*txid, note))) |
|
|
@ -2323,8 +2333,11 @@ pub fn scan_full_mempool_tx(&self, tx: &Transaction, height: i32, _datetime: u64 |
|
|
|
return Err(e); |
|
|
|
} |
|
|
|
|
|
|
|
let fee_amount = Amount::from_u64(*fee).expect("Invalid fee amount"); |
|
|
|
builder.set_fee(fee_amount); |
|
|
|
|
|
|
|
// Create the transaction
|
|
|
|
println!("{}: Adding {} notes and {} utxos", now() - start_time, notes.len(), tinputs.len()); |
|
|
|
println!("{}: Adding {} notes and {} utxos and fee {:?}", now() - start_time, notes.len(), tinputs.len(), fee_amount); |
|
|
|
|
|
|
|
for selected in notes.iter() { |
|
|
|
if let Err(e) = builder.add_sapling_spend( |
|
|
|