mirror of
https://github.com/Qortal/piratewallet-light-cli.git
synced 2025-01-30 02:22:15 +00:00
update wallet and transaction list json format
This commit is contained in:
parent
041dc38b67
commit
546196e756
@ -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))
|
||||
|
@ -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
|
||||
for vin in tx.vin.iter() {
|
||||
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);
|
||||
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user