mirror of
https://github.com/Qortal/piratewallet-light-cli.git
synced 2025-07-30 03:41:28 +00:00
lazy consolidation of transparent inputs
This commit is contained in:
@@ -95,8 +95,8 @@ impl LightClient {
|
|||||||
}).collect::<Vec<JsonValue>>();
|
}).collect::<Vec<JsonValue>>();
|
||||||
|
|
||||||
// Collect t addresses
|
// Collect t addresses
|
||||||
let t_addresses = self.wallet.tkeys.iter().map( |pk| {
|
let t_addresses = self.wallet.tkeys.iter().map( |sk| {
|
||||||
let address = LightWallet::address_from_pk(&pk);
|
let address = LightWallet::address_from_sk(&sk);
|
||||||
|
|
||||||
// Get the balance for this address
|
// Get the balance for this address
|
||||||
let balance = self.wallet.tbalance(Some(address.clone()));
|
let balance = self.wallet.tbalance(Some(address.clone()));
|
||||||
@@ -190,6 +190,7 @@ impl LightClient {
|
|||||||
"created_in_block" => wtx.block,
|
"created_in_block" => wtx.block,
|
||||||
"created_in_txid" => format!("{}", utxo.txid),
|
"created_in_txid" => format!("{}", utxo.txid),
|
||||||
"value" => utxo.value,
|
"value" => utxo.value,
|
||||||
|
"scriptkey" => hex::encode(utxo.script.clone()),
|
||||||
"is_change" => false, // TODO: Identify notes as change
|
"is_change" => false, // TODO: Identify notes as change
|
||||||
"address" => utxo.address.clone(),
|
"address" => utxo.address.clone(),
|
||||||
"spent" => utxo.spent.map(|spent_txid| format!("{}", spent_txid)),
|
"spent" => utxo.spent.map(|spent_txid| format!("{}", spent_txid)),
|
||||||
@@ -317,7 +318,7 @@ impl LightClient {
|
|||||||
|
|
||||||
// Fetch UTXOs
|
// Fetch UTXOs
|
||||||
self.wallet.tkeys.iter()
|
self.wallet.tkeys.iter()
|
||||||
.map( |pk| LightWallet::address_from_pk(&pk))
|
.map( |sk| LightWallet::address_from_sk(&sk))
|
||||||
.for_each( |taddr| {
|
.for_each( |taddr| {
|
||||||
let wallet = self.wallet.clone();
|
let wallet = self.wallet.clone();
|
||||||
self.fetch_utxos(taddr, move |utxo| {
|
self.fetch_utxos(taddr, move |utxo| {
|
||||||
|
@@ -25,10 +25,10 @@ use zcash_primitives::{
|
|||||||
serialize::{Vector, Optional},
|
serialize::{Vector, Optional},
|
||||||
transaction::{
|
transaction::{
|
||||||
builder::{Builder},
|
builder::{Builder},
|
||||||
components::{Amount, OutPoint}, components::amount::DEFAULT_FEE,
|
components::{Amount, OutPoint, TxOut}, components::amount::DEFAULT_FEE,
|
||||||
TxId, Transaction,
|
TxId, Transaction,
|
||||||
},
|
},
|
||||||
legacy::{TransparentAddress::PublicKey},
|
legacy::{Script, TransparentAddress::PublicKey},
|
||||||
note_encryption::{Memo, try_sapling_note_decryption},
|
note_encryption::{Memo, try_sapling_note_decryption},
|
||||||
zip32::{ExtendedFullViewingKey, ExtendedSpendingKey, ChildIndex},
|
zip32::{ExtendedFullViewingKey, ExtendedSpendingKey, ChildIndex},
|
||||||
JUBJUB,
|
JUBJUB,
|
||||||
@@ -338,8 +338,8 @@ pub struct Utxo {
|
|||||||
pub unconfirmed_spent: Option<TxId>, // If this utxo was spent in a send, but has not yet been confirmed.
|
pub unconfirmed_spent: Option<TxId>, // If this utxo was spent in a send, but has not yet been confirmed.
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<OutPoint> for Utxo {
|
impl Utxo {
|
||||||
fn into(self) -> OutPoint {
|
fn to_outpoint(&self) -> OutPoint {
|
||||||
OutPoint { hash: self.txid.0, n: self.output_index as u32 }
|
OutPoint { hash: self.txid.0, n: self.output_index as u32 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -644,10 +644,13 @@ impl LightWallet {
|
|||||||
&extfvk.fvk.vk.into_payment_address(diversifier, &JUBJUB).unwrap())
|
&extfvk.fvk.vk.into_payment_address(diversifier, &JUBJUB).unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn address_from_pk(pk: &secp256k1::SecretKey) -> String {
|
pub fn address_from_sk(sk: &secp256k1::SecretKey) -> String {
|
||||||
|
let secp = secp256k1::Secp256k1::new();
|
||||||
|
let pk = secp256k1::PublicKey::from_secret_key(&secp, &sk);
|
||||||
|
|
||||||
// Encode into t address
|
// Encode into t address
|
||||||
let mut hash160 = ripemd160::Ripemd160::new();
|
let mut hash160 = ripemd160::Ripemd160::new();
|
||||||
hash160.input(Sha256::digest(&pk[..].to_vec()));
|
hash160.input(Sha256::digest(&pk.serialize()[..].to_vec()));
|
||||||
|
|
||||||
// TODO: The taddr version prefix needs to be different for testnet and mainnet
|
// TODO: The taddr version prefix needs to be different for testnet and mainnet
|
||||||
hash160.result().to_base58check(&B58_PUBKEY_ADDRESS_PREFIX, &[])
|
hash160.result().to_base58check(&B58_PUBKEY_ADDRESS_PREFIX, &[])
|
||||||
@@ -949,6 +952,7 @@ impl LightWallet {
|
|||||||
to
|
to
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// TODO: This only spends from the first address right now.
|
||||||
let extsk = &self.extsks[0];
|
let extsk = &self.extsks[0];
|
||||||
let extfvk = &self.extfvks[0];
|
let extfvk = &self.extfvks[0];
|
||||||
let ovk = extfvk.fvk.ovk;
|
let ovk = extfvk.fvk.ovk;
|
||||||
@@ -974,11 +978,7 @@ impl LightWallet {
|
|||||||
// Select notes to cover the target value
|
// Select notes to cover the target value
|
||||||
println!("{}: Selecting notes", now() - start_time);
|
println!("{}: Selecting notes", now() - start_time);
|
||||||
let target_value = value + DEFAULT_FEE ;
|
let target_value = value + DEFAULT_FEE ;
|
||||||
let notes: Vec<_> = self
|
let notes: Vec<_> = self.txs.read().unwrap().iter()
|
||||||
.txs
|
|
||||||
.read()
|
|
||||||
.unwrap()
|
|
||||||
.iter()
|
|
||||||
.map(|(txid, tx)| tx.notes.iter().map(move |note| (*txid, note)))
|
.map(|(txid, tx)| tx.notes.iter().map(move |note| (*txid, note)))
|
||||||
.flatten()
|
.flatten()
|
||||||
.filter_map(|(txid, note)| SpendableNote::from(txid, note, anchor_offset))
|
.filter_map(|(txid, note)| SpendableNote::from(txid, note, anchor_offset))
|
||||||
@@ -994,11 +994,50 @@ impl LightWallet {
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
let mut builder = Builder::new(height);
|
||||||
|
|
||||||
|
// A note on t addresses
|
||||||
|
// Funds recieved 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.
|
||||||
|
let tinputs = self.txs.read().unwrap().iter()
|
||||||
|
.flat_map(|(_, wtx)| {
|
||||||
|
wtx.utxos.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() },
|
||||||
|
};
|
||||||
|
|
||||||
|
(outpoint, coin)
|
||||||
|
})
|
||||||
|
}).collect::<Vec<(OutPoint, TxOut)>>();
|
||||||
|
|
||||||
|
if let Err(e) = match to {
|
||||||
|
address::RecipientAddress::Shielded(_) => {
|
||||||
|
// The destination is a sapling address, so add all transparent inputs
|
||||||
|
// TODO: This only spends from the first address right now.
|
||||||
|
let sk = self.tkeys[0];
|
||||||
|
|
||||||
|
// Add all tinputs
|
||||||
|
tinputs.iter().map( |(outpoint, coin)| {
|
||||||
|
builder.add_transparent_input(sk, outpoint.clone(), coin.clone())
|
||||||
|
}).collect::<Result<Vec<_>, _>>()
|
||||||
|
}
|
||||||
|
_ => {Ok(vec![])}
|
||||||
|
} {
|
||||||
|
eprintln!("Error adding transparent inputs: {:?}", e);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
// Confirm we were able to select sufficient value
|
// Confirm we were able to select sufficient value
|
||||||
let selected_value = notes
|
// TODO: If we're sending to a t-address, we could also use t-address inputs
|
||||||
.iter()
|
let selected_value = notes.iter().map(|selected| selected.note.value).sum::<u64>()
|
||||||
.map(|selected| selected.note.value)
|
+ tinputs.iter().map::<u64, _>(|(_, coin)| coin.value.into()).sum::<u64>();
|
||||||
.sum::<u64>();
|
|
||||||
if selected_value < u64::from(target_value) {
|
if selected_value < u64::from(target_value) {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"Insufficient funds (have {}, need {:?})",
|
"Insufficient funds (have {}, need {:?})",
|
||||||
@@ -1008,8 +1047,8 @@ impl LightWallet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create the transaction
|
// Create the transaction
|
||||||
println!("{}: Adding {} inputs", now() - start_time, notes.len());
|
println!("{}: Adding {} notes and {} utxos", now() - start_time, notes.len(), tinputs.len());
|
||||||
let mut builder = Builder::new(height);
|
|
||||||
for selected in notes.iter() {
|
for selected in notes.iter() {
|
||||||
if let Err(e) = builder.add_sapling_spend(
|
if let Err(e) = builder.add_sapling_spend(
|
||||||
extsk.clone(),
|
extsk.clone(),
|
||||||
@@ -1021,7 +1060,7 @@ impl LightWallet {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO: Temp - Add a transparent input manually for testing
|
// TODO: Temp - Add a transparent input manually for testing
|
||||||
// use zcash_primitives::transaction::components::{TxOut, OutPoint};
|
// use zcash_primitives::transaction::components::{TxOut, OutPoint};
|
||||||
|
Reference in New Issue
Block a user