diff --git a/gui/src/app/state/transactions.rs b/gui/src/app/state/transactions.rs index 3e03bb0d9..17faa458b 100644 --- a/gui/src/app/state/transactions.rs +++ b/gui/src/app/state/transactions.rs @@ -1,4 +1,5 @@ use std::{ + collections::HashMap, convert::TryInto, sync::Arc, time::{SystemTime, UNIX_EPOCH}, @@ -6,7 +7,7 @@ use std::{ use iced::Command; use liana::{ - miniscript::bitcoin::Txid, + miniscript::bitcoin::{OutPoint, Txid}, spend::{SpendCreationError, MAX_FEERATE}, }; use liana_ui::{ @@ -14,16 +15,19 @@ use liana_ui::{ widget::*, }; -use crate::app::{ - cache::Cache, - error::Error, - message::Message, - state::{label::LabelsEdited, State}, - view, +use crate::{ + app::{ + cache::Cache, + error::Error, + message::Message, + state::{label::LabelsEdited, State}, + view, + }, + daemon::model, }; use crate::daemon::{ - model::{CreateSpendResult, HistoryTransaction, Labelled}, + model::{CreateSpendResult, HistoryTransaction, LabelItem, Labelled}, Daemon, }; @@ -125,8 +129,7 @@ impl State for TransactionsPanel { .to_sat() .checked_div(tx.tx.vsize().try_into().unwrap()) .unwrap(); - let modal = - CreateRbfModal::new(tx.tx.txid(), is_cancel, prev_feerate_vb); + let modal = CreateRbfModal::new(tx.clone(), is_cancel, prev_feerate_vb); self.create_rbf_modal = Some(modal); } } @@ -239,7 +242,7 @@ impl From for Box { pub struct CreateRbfModal { /// Transaction to replace. - txid: Txid, + tx: model::HistoryTransaction, /// Whether to cancel or bump fee. is_cancel: bool, /// Min feerate required for RBF. @@ -256,10 +259,10 @@ pub struct CreateRbfModal { } impl CreateRbfModal { - fn new(txid: Txid, is_cancel: bool, prev_feerate_vb: u64) -> Self { + fn new(tx: model::HistoryTransaction, is_cancel: bool, prev_feerate_vb: u64) -> Self { let min_feerate_vb = prev_feerate_vb.checked_add(1).unwrap(); Self { - txid, + tx, is_cancel, min_feerate_vb, feerate_val: form::Value { @@ -313,7 +316,7 @@ impl CreateRbfModal { self.warning = None; self.processing = true; return Command::perform( - rbf(daemon, self.txid, self.is_cancel, self.feerate_vb), + rbf(daemon, self.tx.clone(), self.is_cancel, self.feerate_vb), Message::RbfPsbt, ); } @@ -344,12 +347,12 @@ impl CreateRbfModal { async fn rbf( daemon: Arc, - txid: Txid, + previous_tx: model::HistoryTransaction, is_cancel: bool, feerate_vb: Option, ) -> Result { - let res = daemon.rbf_psbt(&txid, is_cancel, feerate_vb)?; - let psbt = match res { + let previous_txid = previous_tx.tx.txid(); + let psbt = match daemon.rbf_psbt(&previous_txid, is_cancel, feerate_vb)? { CreateSpendResult::Success { psbt, .. } => psbt, CreateSpendResult::InsufficientFunds { missing } => { return Err( @@ -358,6 +361,40 @@ async fn rbf( ); } }; + + if !is_cancel { + let mut labels = HashMap::>::new(); + let new_txid = psbt.unsigned_tx.txid(); + for item in previous_tx.labelled() { + if let Some(label) = previous_tx.labels.get(&item.to_string()) { + match item { + LabelItem::Txid(_) => { + labels.insert(new_txid.into(), Some(label.to_string())); + } + LabelItem::OutPoint(o) => { + if let Some(previous_output) = previous_tx.tx.output.get(o.vout as usize) { + for (vout, output) in psbt.unsigned_tx.output.iter().enumerate() { + if output.script_pubkey == previous_output.script_pubkey { + labels.insert( + LabelItem::OutPoint(OutPoint { + txid: new_txid, + vout: vout as u32, + }), + Some(label.to_string()), + ); + } + } + } + } + // Address label is already in database + LabelItem::Address(_) => {} + } + } + } + + daemon.update_labels(&labels)?; + } + daemon.update_spend_tx(&psbt)?; Ok(psbt.unsigned_tx.txid()) }