gui(transactions): check for direct descendants before rbf
This commit is contained in:
parent
881d9a74a2
commit
dc817dee3b
@ -43,4 +43,5 @@ pub enum Message {
|
||||
PendingTransactions(Result<Vec<HistoryTransaction>, Error>),
|
||||
LabelsUpdated(Result<HashMap<String, Option<String>>, Error>),
|
||||
BroadcastModal(Result<HashSet<Txid>, Error>),
|
||||
RbfModal(HistoryTransaction, bool, u64, Result<HashSet<Txid>, Error>),
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
collections::{HashMap, HashSet},
|
||||
convert::TryInto,
|
||||
sync::Arc,
|
||||
time::{SystemTime, UNIX_EPOCH},
|
||||
@ -112,6 +112,16 @@ impl State for TransactionsPanel {
|
||||
}
|
||||
}
|
||||
},
|
||||
Message::RbfModal(tx, is_cancel, prev_feerate_vb, res) => match res {
|
||||
Ok(descendant_txids) => {
|
||||
let modal =
|
||||
CreateRbfModal::new(tx, is_cancel, prev_feerate_vb, descendant_txids);
|
||||
self.create_rbf_modal = Some(modal);
|
||||
}
|
||||
Err(e) => {
|
||||
self.warning = e.into();
|
||||
}
|
||||
},
|
||||
Message::View(view::Message::Close) => {
|
||||
self.selected_tx = None;
|
||||
}
|
||||
@ -129,8 +139,30 @@ impl State for TransactionsPanel {
|
||||
.to_sat()
|
||||
.checked_div(tx.tx.vsize().try_into().unwrap())
|
||||
.unwrap();
|
||||
let modal = CreateRbfModal::new(tx.clone(), is_cancel, prev_feerate_vb);
|
||||
self.create_rbf_modal = Some(modal);
|
||||
let tx = tx.clone();
|
||||
let txid = tx.tx.txid();
|
||||
return Command::perform(
|
||||
async move {
|
||||
daemon
|
||||
// TODO: filter for spending coins when this is possible:
|
||||
// https://github.com/wizardsardine/liana/issues/677
|
||||
.list_coins()
|
||||
.map(|res| {
|
||||
res.coins
|
||||
.iter()
|
||||
.filter_map(|c| {
|
||||
if c.outpoint.txid == txid {
|
||||
c.spend_info.map(|info| info.txid)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.map_err(|e| e.into())
|
||||
},
|
||||
move |res| Message::RbfModal(tx, is_cancel, prev_feerate_vb, res),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -247,6 +279,9 @@ pub struct CreateRbfModal {
|
||||
is_cancel: bool,
|
||||
/// Min feerate required for RBF.
|
||||
min_feerate_vb: u64,
|
||||
/// IDs of any transactions from this wallet that are direct descendants of
|
||||
/// the transaction to be replaced.
|
||||
descendant_txids: HashSet<Txid>,
|
||||
/// Feerate form value.
|
||||
feerate_val: form::Value<String>,
|
||||
/// Parsed feerate.
|
||||
@ -259,12 +294,18 @@ pub struct CreateRbfModal {
|
||||
}
|
||||
|
||||
impl CreateRbfModal {
|
||||
fn new(tx: model::HistoryTransaction, is_cancel: bool, prev_feerate_vb: u64) -> Self {
|
||||
fn new(
|
||||
tx: model::HistoryTransaction,
|
||||
is_cancel: bool,
|
||||
prev_feerate_vb: u64,
|
||||
descendant_txids: HashSet<Txid>,
|
||||
) -> Self {
|
||||
let min_feerate_vb = prev_feerate_vb.checked_add(1).unwrap();
|
||||
Self {
|
||||
tx,
|
||||
is_cancel,
|
||||
min_feerate_vb,
|
||||
descendant_txids,
|
||||
feerate_val: form::Value {
|
||||
valid: true,
|
||||
value: min_feerate_vb.to_string(),
|
||||
@ -329,6 +370,7 @@ impl CreateRbfModal {
|
||||
content,
|
||||
view::transactions::create_rbf_modal(
|
||||
self.is_cancel,
|
||||
&self.descendant_txids,
|
||||
&self.feerate_val,
|
||||
self.replacement_txid,
|
||||
self.warning.as_ref(),
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
use chrono::NaiveDateTime;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use iced::{alignment, widget::tooltip, Alignment, Length};
|
||||
|
||||
@ -157,8 +157,13 @@ fn tx_list_view(i: usize, tx: &HistoryTransaction) -> Element<'_, Message> {
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Return the modal view for a new RBF transaction.
|
||||
///
|
||||
/// `descendant_txids` contains the IDs of any transactions from this wallet that are
|
||||
/// direct descendants of the transaction to be replaced.
|
||||
pub fn create_rbf_modal<'a>(
|
||||
is_cancel: bool,
|
||||
descendant_txids: &HashSet<Txid>,
|
||||
feerate: &form::Value<String>,
|
||||
replacement_txid: Option<Txid>,
|
||||
warning: Option<&'a Error>,
|
||||
@ -183,6 +188,56 @@ pub fn create_rbf_modal<'a>(
|
||||
.spacing(10)
|
||||
.push(Container::new(h4_bold("Transaction replacement")).width(Length::Fill))
|
||||
.push(Row::new().push(text(help_text)))
|
||||
.push_maybe(if descendant_txids.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(
|
||||
descendant_txids.iter().fold(
|
||||
Column::new()
|
||||
.spacing(5)
|
||||
.push(Row::new().spacing(10).push(icon::warning_icon()).push(text(
|
||||
if descendant_txids.len() > 1 {
|
||||
"WARNING: Replacing this transaction \
|
||||
will invalidate some later payments."
|
||||
} else {
|
||||
"WARNING: Replacing this transaction \
|
||||
will invalidate a later payment."
|
||||
},
|
||||
)))
|
||||
.push(Row::new().padding([0, 30]).push(text(
|
||||
if descendant_txids.len() > 1 {
|
||||
"The following transactions are \
|
||||
spending one or more outputs \
|
||||
from the transaction to be replaced \
|
||||
and will be dropped when the replacement \
|
||||
is broadcast, along with any other \
|
||||
transactions that depend on them:"
|
||||
} else {
|
||||
"The following transaction is \
|
||||
spending one or more outputs \
|
||||
from the transaction to be replaced \
|
||||
and will be dropped when the replacement \
|
||||
is broadcast, along with any other \
|
||||
transactions that depend on it:"
|
||||
},
|
||||
))),
|
||||
|col, txid| {
|
||||
col.push(
|
||||
Row::new()
|
||||
.padding([0, 30])
|
||||
.spacing(5)
|
||||
.align_items(Alignment::Center)
|
||||
.push(text(txid.to_string()))
|
||||
.push(
|
||||
Button::new(icon::clipboard_icon().style(color::GREY_3))
|
||||
.on_press(Message::Clipboard(txid.to_string()))
|
||||
.style(theme::Button::TransparentBorder),
|
||||
),
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
})
|
||||
.push_maybe(if !is_cancel {
|
||||
Some(
|
||||
Row::new()
|
||||
@ -225,7 +280,7 @@ pub fn create_rbf_modal<'a>(
|
||||
)
|
||||
})),
|
||||
)
|
||||
.width(Length::Fixed(600.0))
|
||||
.width(Length::Fixed(800.0))
|
||||
.into()
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user