mirror of
https://github.com/Qortal/piratewallet-light-cli.git
synced 2025-07-30 03:41:28 +00:00
Write outgoing metadata
This commit is contained in:
@@ -441,14 +441,23 @@ impl LightClient {
|
|||||||
|
|
||||||
// TODO: What happens if change is > than sent ?
|
// TODO: What happens if change is > than sent ?
|
||||||
|
|
||||||
|
// 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! {
|
txns.push(object! {
|
||||||
"block_height" => v.block,
|
"block_height" => v.block,
|
||||||
"txid" => format!("{}", v.txid),
|
"txid" => format!("{}", v.txid),
|
||||||
"amount" => total_change as i64
|
"amount" => total_change as i64
|
||||||
- v.total_shielded_value_spent as i64
|
- v.total_shielded_value_spent as i64
|
||||||
- v.total_transparent_value_spent as i64,
|
- v.total_transparent_value_spent as i64,
|
||||||
"address" => None::<String>, // TODO: For send, we don't have an address
|
"outgoing_metadata" => outgoing_json,
|
||||||
"memo" => None::<String>
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -461,15 +470,7 @@ impl LightClient {
|
|||||||
"txid" => format!("{}", v.txid),
|
"txid" => format!("{}", v.txid),
|
||||||
"amount" => nd.note.value as i64,
|
"amount" => nd.note.value as i64,
|
||||||
"address" => self.wallet.note_address(nd),
|
"address" => self.wallet.note_address(nd),
|
||||||
"memo" => match &nd.memo {
|
"memo" => LightWallet::memo_str(&nd.memo),
|
||||||
Some(memo) => {
|
|
||||||
match memo.to_utf8() {
|
|
||||||
Some(Ok(memo_str)) => Some(memo_str),
|
|
||||||
_ => None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => None
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@@ -324,6 +324,42 @@ impl Utxo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct OutgoingTxMetadata {
|
||||||
|
pub address: String,
|
||||||
|
pub value : u64,
|
||||||
|
pub memo : Memo,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OutgoingTxMetadata {
|
||||||
|
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
|
||||||
|
let address_len = reader.read_u64::<LittleEndian>()?;
|
||||||
|
let mut address_bytes = vec![0; address_len as usize];
|
||||||
|
reader.read_exact(&mut address_bytes)?;
|
||||||
|
let address = String::from_utf8(address_bytes).unwrap();
|
||||||
|
|
||||||
|
let value = reader.read_u64::<LittleEndian>()?;
|
||||||
|
|
||||||
|
let mut memo_bytes = [0u8; 512];
|
||||||
|
reader.read_exact(&mut memo_bytes)?;
|
||||||
|
let memo = Memo::from_bytes(&memo_bytes).unwrap();
|
||||||
|
|
||||||
|
Ok(OutgoingTxMetadata{
|
||||||
|
address,
|
||||||
|
value,
|
||||||
|
memo,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||||
|
// Strings are written as len + utf8
|
||||||
|
writer.write_u64::<LittleEndian>(self.address.as_bytes().len() as u64)?;
|
||||||
|
writer.write_all(self.address.as_bytes())?;
|
||||||
|
|
||||||
|
writer.write_u64::<LittleEndian>(self.value)?;
|
||||||
|
writer.write_all(self.memo.as_bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct WalletTx {
|
pub struct WalletTx {
|
||||||
pub block: i32,
|
pub block: i32,
|
||||||
|
|
||||||
@@ -345,11 +381,14 @@ pub struct WalletTx {
|
|||||||
|
|
||||||
// Total amount of transparent funds that belong to us that were spent in this Tx.
|
// Total amount of transparent funds that belong to us that were spent in this Tx.
|
||||||
pub total_transparent_value_spent : u64,
|
pub total_transparent_value_spent : u64,
|
||||||
|
|
||||||
|
// All outgoing sapling sends to addresses outside this wallet
|
||||||
|
pub outgoing_metadata: Vec<OutgoingTxMetadata>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WalletTx {
|
impl WalletTx {
|
||||||
pub fn serialized_version() -> u64 {
|
pub fn serialized_version() -> u64 {
|
||||||
return 1;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(height: i32, txid: &TxId) -> Self {
|
pub fn new(height: i32, txid: &TxId) -> Self {
|
||||||
@@ -359,13 +398,14 @@ impl WalletTx {
|
|||||||
notes: vec![],
|
notes: vec![],
|
||||||
utxos: vec![],
|
utxos: vec![],
|
||||||
total_shielded_value_spent: 0,
|
total_shielded_value_spent: 0,
|
||||||
total_transparent_value_spent: 0
|
total_transparent_value_spent: 0,
|
||||||
|
outgoing_metadata: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
|
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
|
||||||
let version = reader.read_u64::<LittleEndian>()?;
|
let version = reader.read_u64::<LittleEndian>()?;
|
||||||
assert_eq!(version, WalletTx::serialized_version());
|
assert!(version <= WalletTx::serialized_version());
|
||||||
|
|
||||||
let block = reader.read_i32::<LittleEndian>()?;
|
let block = reader.read_i32::<LittleEndian>()?;
|
||||||
|
|
||||||
@@ -380,13 +420,21 @@ impl WalletTx {
|
|||||||
let total_shielded_value_spent = reader.read_u64::<LittleEndian>()?;
|
let total_shielded_value_spent = reader.read_u64::<LittleEndian>()?;
|
||||||
let total_transparent_value_spent = reader.read_u64::<LittleEndian>()?;
|
let total_transparent_value_spent = reader.read_u64::<LittleEndian>()?;
|
||||||
|
|
||||||
|
|
||||||
|
// Outgoing metadata was only added in version 2
|
||||||
|
let outgoing_metadata = match version {
|
||||||
|
1 => vec![],
|
||||||
|
_ => Vector::read(&mut reader, |r| OutgoingTxMetadata::read(r))?
|
||||||
|
};
|
||||||
|
|
||||||
Ok(WalletTx{
|
Ok(WalletTx{
|
||||||
block,
|
block,
|
||||||
txid,
|
txid,
|
||||||
notes,
|
notes,
|
||||||
utxos,
|
utxos,
|
||||||
total_shielded_value_spent,
|
total_shielded_value_spent,
|
||||||
total_transparent_value_spent
|
total_transparent_value_spent,
|
||||||
|
outgoing_metadata,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -403,6 +451,9 @@ impl WalletTx {
|
|||||||
writer.write_u64::<LittleEndian>(self.total_shielded_value_spent)?;
|
writer.write_u64::<LittleEndian>(self.total_shielded_value_spent)?;
|
||||||
writer.write_u64::<LittleEndian>(self.total_transparent_value_spent)?;
|
writer.write_u64::<LittleEndian>(self.total_transparent_value_spent)?;
|
||||||
|
|
||||||
|
// Write the outgoing metadata
|
||||||
|
Vector::write(&mut writer, &self.outgoing_metadata, |w, om| om.write(w))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -34,7 +34,7 @@ use zcash_primitives::{
|
|||||||
primitives::{PaymentAddress},
|
primitives::{PaymentAddress},
|
||||||
};
|
};
|
||||||
|
|
||||||
use data::{BlockData, WalletTx, Utxo, SaplingNoteData, SpendableNote};
|
use data::{BlockData, WalletTx, Utxo, SaplingNoteData, SpendableNote, OutgoingTxMetadata};
|
||||||
|
|
||||||
use crate::address;
|
use crate::address;
|
||||||
use crate::prover;
|
use crate::prover;
|
||||||
@@ -354,6 +354,18 @@ impl LightWallet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn memo_str(memo: &Option<Memo>) -> Option<String> {
|
||||||
|
match memo {
|
||||||
|
Some(memo) => {
|
||||||
|
match memo.to_utf8() {
|
||||||
|
Some(Ok(memo_str)) => Some(memo_str),
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn address_from_sk(&self, sk: &secp256k1::SecretKey) -> String {
|
pub fn address_from_sk(&self, sk: &secp256k1::SecretKey) -> String {
|
||||||
let secp = secp256k1::Secp256k1::new();
|
let secp = secp256k1::Secp256k1::new();
|
||||||
let pk = secp256k1::PublicKey::from_secret_key(&secp, &sk);
|
let pk = secp256k1::PublicKey::from_secret_key(&secp, &sk);
|
||||||
@@ -593,6 +605,14 @@ impl LightWallet {
|
|||||||
// Also scan the output to see if it can be decoded with our OutgoingViewKey
|
// Also scan the output to see if it can be decoded with our OutgoingViewKey
|
||||||
// If it can, then we sent this transaction, so we should be able to get
|
// If it can, then we sent this transaction, so we should be able to get
|
||||||
// the memo and value for our records
|
// the memo and value for our records
|
||||||
|
|
||||||
|
// First, collect all our z addresses, to check for change
|
||||||
|
// Collect z addresses
|
||||||
|
let z_addresses = self.address.iter().map( |ad| {
|
||||||
|
encode_payment_address(self.config.hrp_sapling_address(), &ad)
|
||||||
|
}).collect::<HashSet<String>>();
|
||||||
|
|
||||||
|
// Search all ovks that we have
|
||||||
let ovks: Vec<_> = self.extfvks.iter().map(|extfvk| extfvk.fvk.ovk).collect();
|
let ovks: Vec<_> = self.extfvks.iter().map(|extfvk| extfvk.fvk.ovk).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,
|
||||||
@@ -601,12 +621,35 @@ impl LightWallet {
|
|||||||
&output.ephemeral_key.as_prime_order(&JUBJUB).unwrap(),
|
&output.ephemeral_key.as_prime_order(&JUBJUB).unwrap(),
|
||||||
&output.enc_ciphertext,
|
&output.enc_ciphertext,
|
||||||
&output.out_ciphertext) {
|
&output.out_ciphertext) {
|
||||||
Some((note, address, memo)) => {
|
Some((note, payment_address, memo)) => {
|
||||||
// This could be a chane or an outgoing transaction
|
let address = encode_payment_address(self.config.hrp_sapling_address(),
|
||||||
println!("Recovered outgoing for {} to {} :{:?}",
|
&payment_address);
|
||||||
note.value,
|
|
||||||
encode_payment_address(self.config.hrp_sapling_address(), &address),
|
// Check if this is a change address
|
||||||
memo.to_utf8())
|
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)
|
||||||
|
.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,
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
None => {}
|
None => {}
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user