gui(spend): use create_spend_tx for missing amount

This commit is contained in:
jp1ac4 2024-01-29 17:06:14 +00:00
parent fae32df086
commit cb5073c83a
No known key found for this signature in database
GPG Key ID: A7ACD32423568D7B

View File

@ -6,12 +6,9 @@ use iced::{Command, Subscription};
use liana::{
descriptors::LianaDescriptor,
miniscript::bitcoin::{
self, address, psbt::Psbt, secp256k1, Address, Amount, Denomination, Network, OutPoint,
},
spend::{
create_spend, CandidateCoin, SpendCreationError, SpendOutputAddress, SpendTxFees, TxGetter,
MAX_FEERATE,
address, psbt::Psbt, secp256k1, Address, Amount, Denomination, Network, OutPoint,
},
spend::{SpendCreationError, MAX_FEERATE},
};
use liana_ui::{component::form, widget::Element};
@ -192,86 +189,60 @@ impl DefineSpend {
return;
}
let destinations: Vec<(SpendOutputAddress, Amount)> = self
let destinations: HashMap<Address<address::NetworkUnchecked>, u64> = self
.recipients
.iter()
.map(|recipient| {
(
SpendOutputAddress {
addr: Address::from_str(&recipient.address.value)
.expect("Checked before")
.assume_checked(),
info: None,
},
Amount::from_sat(recipient.amount().expect("Checked before")),
Address::from_str(&recipient.address.value).expect("Checked before"),
recipient.amount().expect("Checked before"),
)
})
.collect();
let coins: Vec<CandidateCoin> = if self.is_user_coin_selection {
self.coins
let outpoints = if self.is_user_coin_selection {
let outpoints: Vec<_> = self
.coins
.iter()
.filter_map(|(c, selected)| {
if *selected {
Some(CandidateCoin {
amount: c.amount,
outpoint: c.outpoint,
deriv_index: c.derivation_index,
is_change: c.is_change,
sequence: None,
must_select: *selected,
})
} else {
None
}
})
.collect()
.filter_map(
|(c, selected)| {
if *selected {
Some(c.outpoint)
} else {
None
}
},
)
.collect();
if outpoints.is_empty() {
// If the user has deselected all coins, simply set the amount left to select as the
// total destination value. Note this doesn't take account of the fee, but passing
// an empty list to `create_spend_tx` would use auto-selection and so we settle for
// this approximation.
self.amount_left_to_select = Some(Amount::from_sat(destinations.values().sum()));
return;
}
outpoints
} else {
// For automated coin selection, only confirmed coins are considered
self.coins
.iter()
.filter_map(|(c, _)| {
if c.block_height.is_some() {
Some(CandidateCoin {
amount: c.amount,
outpoint: c.outpoint,
deriv_index: c.derivation_index,
is_change: c.is_change,
sequence: None,
must_select: false,
})
} else {
None
}
})
.collect()
Vec::new() // pass empty list for auto-selection
};
// Use a fixed change address so that we don't increment the change index.
let dummy_address = self
.descriptor
.change_descriptor()
.derive(0.into(), &self.curve)
.address(self.network);
.address(self.network)
.as_unchecked()
.clone();
let feerate_vb = self.feerate.value.parse::<u64>().expect("Checked before");
// Create a spend with empty inputs in order to use auto-selection.
match create_spend(
&self.descriptor,
&self.curve,
&mut DaemonTxGetter(&daemon),
&destinations,
&coins,
SpendTxFees::Regular(feerate_vb),
SpendOutputAddress {
addr: dummy_address,
info: None,
},
) {
Ok(spend) => {
match daemon.create_spend_tx(&outpoints, &destinations, feerate_vb, Some(dummy_address)) {
Ok(CreateSpendResult::Success { psbt, .. }) => {
self.warning = None;
if !self.is_user_coin_selection {
let selected_coins: Vec<OutPoint> = spend
.psbt
let selected_coins: Vec<OutPoint> = psbt
.unsigned_tx
.input
.iter()
@ -290,8 +261,8 @@ impl DefineSpend {
// User can then either:
// - modify recipient amounts and/or feerate and let coin selection run again, or
// - select coins manually.
Err(SpendCreationError::CoinSelection(amount)) => {
self.amount_left_to_select = Some(Amount::from_sat(amount.missing));
Ok(CreateSpendResult::InsufficientFunds { missing }) => {
self.amount_left_to_select = Some(Amount::from_sat(missing));
}
Err(e) => {
self.warning = Some(e.into());
@ -300,16 +271,6 @@ impl DefineSpend {
}
}
pub struct DaemonTxGetter<'a>(&'a Arc<dyn Daemon + Sync + Send>);
impl<'a> TxGetter for DaemonTxGetter<'a> {
fn get_tx(&mut self, txid: &bitcoin::Txid) -> Option<bitcoin::Transaction> {
self.0
.list_txs(&[*txid])
.ok()
.and_then(|mut txs| txs.transactions.pop().map(|tx| tx.tx))
}
}
impl Step for DefineSpend {
fn update(
&mut self,