update wallet and transaction list json format

This commit is contained in:
Cryptoforge 2020-08-05 22:08:22 -07:00
parent 041dc38b67
commit 546196e756
3 changed files with 236 additions and 112 deletions

View File

@ -870,78 +870,112 @@ impl LightClient {
.flat_map(| (_k, v) | {
let mut txns: Vec<JsonValue> = vec![];
if v.total_shielded_value_spent + v.total_transparent_value_spent > 0 {
// If money was spent, create a transaction. For this, we'll subtract
// all the change notes. TODO: Add transparent change here to subtract it also
let total_change: u64 = v.notes.iter()
.filter( |nd| nd.is_change )
.map( |nd| nd.note.value )
.sum();
//Get totals from outgoing metadata
let total_change: u64 = v.outgoing_metadata_change.iter().map(|u| u.value).sum::<u64>();
let total_send: u64 = v.outgoing_metadata.iter().map(|u| u.value).sum::<u64>();
// TODO: What happens if change is > than sent ?
//Get Change address from outgoing change metadata
let change_addresses = v.outgoing_metadata_change.iter()
.map(|om| om.address.clone())
.collect::<Vec<String>>();
// Collect outgoing metadata
let outgoing_json = v.outgoing_metadata.iter()
.map(|om|
object!{
"address" => om.address.clone(),
"value" => om.value,
"memo" => LightWallet::memo_str(&Some(om.memo.clone())),
})
.collect::<Vec<JsonValue>>();
txns.push(object! {
"block_height" => v.block,
"datetime" => v.datetime,
"txid" => format!("{}", v.txid),
"amount" => total_change as i64
- v.total_shielded_value_spent as i64
- v.total_transparent_value_spent as i64,
"outgoing_metadata" => outgoing_json,
});
}
// For each sapling note that is not a change, add a Tx.
txns.extend(v.notes.iter()
// Collect incoming metadata
let mut incoming_json = v.notes.iter()
.filter( |nd| !nd.is_change )
.enumerate()
.map ( |(i, nd)|
.map ( |(_i, nd)|
object! {
"block_height" => v.block,
"datetime" => v.datetime,
"position" => i,
"txid" => format!("{}", v.txid),
"amount" => nd.note.value as i64,
"address" => LightWallet::note_address(self.config.hrp_sapling_address(), nd),
"value" => nd.note.value as i64,
"memo" => LightWallet::memo_str(&nd.memo),
})
);
.collect::<Vec<JsonValue>>();
// Get the total transparent received
let total_transparent_received = v.utxos.iter().map(|u| u.value).sum::<u64>();
if total_transparent_received > v.total_transparent_value_spent {
// Create an input transaction for the transparent value as well.
txns.push(object!{
"block_height" => v.block,
"datetime" => v.datetime,
"txid" => format!("{}", v.txid),
"amount" => total_transparent_received as i64 - v.total_transparent_value_spent as i64,
"address" => v.utxos.iter().map(|u| u.address.clone()).collect::<Vec<String>>().join(","),
"memo" => None::<String>
})
let incoming_t_json = v.utxos.iter()
.filter(|u| !change_addresses.contains(&u.address))
.map( |uo|
object! {
"address" => uo.address.clone(),
"value" => uo.value.clone() as i64,
"memo" => None::<String>,
})
.collect::<Vec<JsonValue>>();
for json in incoming_t_json {
incoming_json.push(json.clone());
}
txns
})
.collect::<Vec<JsonValue>>();
// Collect incoming metadata change
let mut incoming_change_json = v.notes.iter()
.filter( |nd| nd.is_change )
.enumerate()
.map ( |(_i, nd)|
object! {
"address" => LightWallet::note_address(self.config.hrp_sapling_address(), nd),
"value" => nd.note.value as i64,
"memo" => LightWallet::memo_str(&nd.memo),
})
.collect::<Vec<JsonValue>>();
let incoming_t_change_json = v.utxos.iter()
.filter(|u| change_addresses.contains(&u.address))
.map( |uo|
object! {
"address" => uo.address.clone(),
"value" => uo.value.clone() as i64,
"memo" => None::<String>,
})
.collect::<Vec<JsonValue>>();
for json in incoming_t_change_json {
incoming_change_json.push(json.clone());
}
// Collect outgoing metadata
let outgoing_json = v.outgoing_metadata.iter()
.map(|om|
object!{
"address" => om.address.clone(),
"value" => om.value,
"memo" => LightWallet::memo_str(&Some(om.memo.clone())),
})
.collect::<Vec<JsonValue>>();
// Collect outgoing metadata change
let outgoing_change_json = v.outgoing_metadata_change.iter()
.map(|om|
object!{
"address" => om.address.clone(),
"value" => om.value,
"memo" => LightWallet::memo_str(&Some(om.memo.clone())),
})
.collect::<Vec<JsonValue>>();
txns.push(object! {
"block_height" => v.block,
"datetime" => v.datetime,
"txid" => format!("{}", v.txid),
"amount" => total_change as i64
- v.total_shielded_value_spent as i64
- v.total_transparent_value_spent as i64,
"fee" => v.total_shielded_value_spent as i64
+ v.total_transparent_value_spent as i64
- total_change as i64
- total_send as i64,
"incoming_metadata" => incoming_json,
"incoming_metadata_change" => incoming_change_json,
"outgoing_metadata" => outgoing_json,
"outgoing_metadata_change" => outgoing_change_json,
});
txns
})
.collect::<Vec<JsonValue>>();
// Add in all mempool txns
tx_list.extend(wallet.mempool_txs.read().unwrap().iter().map( |(_, wtx)| {
use zcash_primitives::transaction::components::amount::DEFAULT_FEE;
use std::convert::TryInto;
let amount: u64 = wtx.outgoing_metadata.iter().map(|om| om.value).sum::<u64>();
let fee: u64 = DEFAULT_FEE.try_into().unwrap();
let fee: u64 = wtx.total_shielded_value_spent - amount;
// Collect outgoing metadata
let outgoing_json = wtx.outgoing_metadata.iter()
@ -957,6 +991,7 @@ impl LightClient {
"datetime" => wtx.datetime,
"txid" => format!("{}", wtx.txid),
"amount" => -1 * (fee + amount) as i64,
"fee" => fee as i64,
"unconfirmed" => true,
"outgoing_metadata" => outgoing_json,
}
@ -1351,6 +1386,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))

View File

@ -971,13 +971,24 @@ impl LightWallet {
pub fn scan_full_tx(&self, tx: &Transaction, height: i32, datetime: u64) {
let mut total_transparent_spend: u64 = 0;
//Get Value Balance
{
let mut txs = self.txs.write().unwrap();
match txs.get_mut(&tx.txid()) {
Some(wtx) => wtx.value_balance = tx.value_balance.into(),
None => {},
};
}
// Scan all the inputs to see if we spent any transparent funds in this tx
let mut tinputs = Vec::new();
for vin in tx.vin.iter() {
// Find the txid in the list of utxos that we have.
let txid = TxId {0: vin.prevout.hash};
match self.txs.write().unwrap().get_mut(&txid) {
Some(wtx) => {
//println!("Looking for {}, {}", txid, vin.prevout.n);
// One of the tx outputs is a match
let spent_utxo = wtx.utxos.iter_mut()
@ -988,6 +999,7 @@ impl LightWallet {
info!("Spent utxo from {} was spent in {}", txid, tx.txid());
su.spent = Some(tx.txid().clone());
su.unconfirmed_spent = None;
tinputs.push(su.address.clone());
total_transparent_spend += su.value;
},
@ -1032,42 +1044,26 @@ impl LightWallet {
}
}
let mut outgoing = Vec::new();
let mut outgoing_change = Vec::new();
{
let total_shielded_value_spent = self.txs.read().unwrap().get(&tx.txid()).map_or(0, |wtx| wtx.total_shielded_value_spent);
if total_transparent_spend + total_shielded_value_spent > 0 {
// We spent money in this Tx, so grab all the transparent outputs (except ours) and add them to the
// outgoing metadata
// Collect our t-addresses
let wallet_taddrs = self.taddresses.read().unwrap().iter()
.map(|a| a.clone())
.collect::<HashSet<String>>();
for vout in tx.vout.iter() {
let taddr = self.address_from_pubkeyhash(vout.script_pubkey.address());
//if taddr.is_some() && !wallet_taddrs.contains(&taddr.clone().unwrap()) {
if taddr.is_some() {
let taddr = taddr.unwrap();
// Add it to outgoing metadata
let mut txs = self.txs.write().unwrap();
if txs.get(&tx.txid()).unwrap().outgoing_metadata.iter()
.find(|om|
om.address == taddr && Amount::from_u64(om.value).unwrap() == vout.value)
.is_some() {
warn!("Duplicate outgoing metadata");
continue;
if tinputs.contains(&taddr) {
outgoing_change.push((taddr, vout.value.into(), Memo::default()));
} else {
outgoing.push((taddr, vout.value.into(), Memo::default()));
}
// Write the outgoing metadata
txs.get_mut(&tx.txid()).unwrap()
.outgoing_metadata
.push(OutgoingTxMetadata{
address: taddr,
value: vout.value.into(),
memo: Memo::default(),
});
}
}
}
@ -1115,10 +1111,49 @@ impl LightWallet {
// the memo and value for our records
// First, collect all our z addresses, to check for change
// Collect z addresses
let z_addresses = self.zaddress.read().unwrap().iter().map( |ad| {
encode_payment_address(self.config.hrp_sapling_address(), &ad)
}).collect::<HashSet<String>>();
// Collect z addresses spent from
let mut zinputs = Vec::new();
{
let mut txs = self.txs.write().unwrap();
let nfs: Vec<_>;
nfs = txs
.iter()
.map(|(txid, tx)| {
let txid = *txid;
tx.notes.iter().filter_map(move |nd| {
Some((nd.nullifier, nd.account, txid))
})
})
.flatten()
.collect();
for spend in &tx.shielded_spends {
let txid = nfs
.iter()
.find(|(nf, _, _)| &nf[..] == &spend.nullifier[..])
.unwrap()
.2;
let spent_note = txs
.get_mut(&txid)
.unwrap()
.notes
.iter_mut()
.find(|nd| &nd.nullifier[..] == &spend.nullifier[..])
.unwrap();
zinputs.push(encode_payment_address(
self.config.hrp_sapling_address(),
&spent_note.extfvk.fvk.vk
.to_payment_address(spent_note.diversifier, &JUBJUB).unwrap()));
}
}
// Search all ovks that we have
let ovks: Vec<_> = self.extfvks.read().unwrap().iter().map(
@ -1137,34 +1172,57 @@ impl LightWallet {
&payment_address);
// Check if this is a change address
// if z_addresses.contains(&address) {
// continue;
// }
// Update the WalletTx
// 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;
}
// Write the outgoing metadata
txs.get_mut(&tx.txid()).unwrap()
.outgoing_metadata
.push(OutgoingTxMetadata{
address, value: note.value, memo,
});
if zinputs.contains(&address) {
outgoing_change.push((address, note.value, memo));
} else {
outgoing.push((address, note.value, memo));
}
},
None => {}
};
}
{
// Update the WalletTx
// Do it in a short scope because of the write lock.
// Write the outgoing metadata
for metadata in outgoing.iter().enumerate() {
let mut txs = self.txs.write().unwrap();
if txs.get(&tx.txid()).unwrap().outgoing_metadata.iter()
.find(|om| om.address == (metadata.1).0 && om.value == (metadata.1).1 && om.memo == (metadata.1).2)
.is_some() {
warn!("Duplicate outgoing metadata");
continue;
}
txs.get_mut(&tx.txid()).unwrap()
.outgoing_metadata
.push(OutgoingTxMetadata{
address: (metadata.1).0.clone(),
value: (metadata.1).1.clone(),
memo: (metadata.1).2.clone()});
}
for metadata in outgoing_change.iter().enumerate() {
let mut txs = self.txs.write().unwrap();
if txs.get(&tx.txid()).unwrap().outgoing_metadata_change.iter()
.find(|om| om.address == (metadata.1).0 && om.value == (metadata.1).1 && om.memo == (metadata.1).2)
.is_some() {
warn!("Duplicate outgoing metadata change");
continue;
}
txs.get_mut(&tx.txid()).unwrap()
.outgoing_metadata_change
.push(OutgoingTxMetadata{
address: (metadata.1).0.clone(),
value: (metadata.1).1.clone(),
memo: (metadata.1).2.clone()});
}
}
}
// Mark this Tx as scanned
@ -1912,6 +1970,8 @@ impl LightWallet {
println!("{}: Transaction created", now() - start_time);
println!("Transaction ID: {}", tx.txid());
// Mark notes as spent.
{
// Mark sapling notes as unconfirmed spent
@ -1961,6 +2021,7 @@ impl LightWallet {
// Create a new WalletTx
let mut wtx = WalletTx::new(height as i32, now() as u64, &tx.txid());
wtx.outgoing_metadata = outgoing_metadata;
wtx.total_shielded_value_spent = total_value + fee;
// Add it into the mempool
mempool_txs.insert(tx.txid(), wtx);

View File

@ -387,13 +387,19 @@ pub struct WalletTx {
// All outgoing sapling sends to addresses outside this wallet
pub outgoing_metadata: Vec<OutgoingTxMetadata>,
// All outgoing sapling sends to addresses outside this wallet
pub outgoing_metadata_change: Vec<OutgoingTxMetadata>,
// Whether this TxID was downloaded from the server and scanned for Memos
pub full_tx_scanned: bool,
// Value Balance of this Tx.
pub value_balance : u64,
}
impl WalletTx {
pub fn serialized_version() -> u64 {
return 4;
return 6;
}
pub fn new(height: i32, datetime: u64, txid: &TxId) -> Self {
@ -406,7 +412,9 @@ impl WalletTx {
total_shielded_value_spent: 0,
total_transparent_value_spent: 0,
outgoing_metadata: vec![],
outgoing_metadata_change: vec![],
full_tx_scanned: false,
value_balance: 0,
}
}
@ -436,8 +444,20 @@ impl WalletTx {
// Outgoing metadata was only added in version 2
let outgoing_metadata = Vector::read(&mut reader, |r| OutgoingTxMetadata::read(r))?;
let outgoing_metadata_change = if version >= 6 {
Vector::read(&mut reader, |r| OutgoingTxMetadata::read(r))?
} else {
vec![]
};
let full_tx_scanned = reader.read_u8()? > 0;
let value_balance = if version >= 5 {
reader.read_u64::<LittleEndian>()?
} else {
0
};
Ok(WalletTx{
block,
datetime,
@ -447,7 +467,9 @@ impl WalletTx {
total_shielded_value_spent,
total_transparent_value_spent,
outgoing_metadata,
full_tx_scanned
outgoing_metadata_change,
full_tx_scanned,
value_balance
})
}
@ -469,8 +491,13 @@ impl WalletTx {
// Write the outgoing metadata
Vector::write(&mut writer, &self.outgoing_metadata, |w, om| om.write(w))?;
// Write the outgoing metadata_change
Vector::write(&mut writer, &self.outgoing_metadata_change, |w, om| om.write(w))?;
writer.write_u8(if self.full_tx_scanned {1} else {0})?;
writer.write_u64::<LittleEndian>(self.value_balance)?;
Ok(())
}
}