From 6c7ca2ccaebd8acd6629c7faf8e418cb50128677 Mon Sep 17 00:00:00 2001 From: jp1ac4 <121959000+jp1ac4@users.noreply.github.com> Date: Mon, 5 Feb 2024 17:23:22 +0000 Subject: [PATCH 01/14] gui(daemon): add optional filter to `list_coins` --- gui/src/app/mod.rs | 2 +- gui/src/app/state/coins.rs | 4 ++-- gui/src/app/state/mod.rs | 2 +- gui/src/app/state/psbt.rs | 2 +- gui/src/app/state/recovery.rs | 4 ++-- gui/src/app/state/spend/mod.rs | 4 ++-- gui/src/app/state/transactions.rs | 4 ++-- gui/src/daemon/client/mod.rs | 16 +++++++++++++--- gui/src/daemon/embedded.rs | 10 +++++++--- gui/src/daemon/mod.rs | 16 ++++++++++------ gui/src/loader.rs | 2 +- 11 files changed, 42 insertions(+), 24 deletions(-) diff --git a/gui/src/app/mod.rs b/gui/src/app/mod.rs index 097b077fa..66b800668 100644 --- a/gui/src/app/mod.rs +++ b/gui/src/app/mod.rs @@ -240,7 +240,7 @@ impl App { let info = daemon.get_info()?; // todo: filter coins to only have current coins. - let coins = daemon.list_coins()?; + let coins = daemon.list_coins(&[], &[])?; Ok(Cache { datadir_path, coins: coins.coins, diff --git a/gui/src/app/state/coins.rs b/gui/src/app/state/coins.rs index e5f2c1b8d..9783d2ba4 100644 --- a/gui/src/app/state/coins.rs +++ b/gui/src/app/state/coins.rs @@ -162,7 +162,7 @@ impl State for CoinsPanel { Command::perform( async move { daemon1 - .list_coins() + .list_coins(&[], &[]) .map(|res| res.coins) .map_err(|e| e.into()) }, @@ -171,7 +171,7 @@ impl State for CoinsPanel { Command::perform( async move { let coins = daemon2 - .list_coins() + .list_coins(&[], &[]) .map(|res| res.coins) .map_err(Error::from)?; let mut targets = HashSet::::new(); diff --git a/gui/src/app/state/mod.rs b/gui/src/app/state/mod.rs index 2daba35e2..1ef261a4e 100644 --- a/gui/src/app/state/mod.rs +++ b/gui/src/app/state/mod.rs @@ -295,7 +295,7 @@ impl State for Home { Command::perform( async move { daemon2 - .list_coins() + .list_coins(&[], &[]) .map(|res| res.coins) .map_err(|e| e.into()) }, diff --git a/gui/src/app/state/psbt.rs b/gui/src/app/state/psbt.rs index bfbf0b894..175a1892b 100644 --- a/gui/src/app/state/psbt.rs +++ b/gui/src/app/state/psbt.rs @@ -173,7 +173,7 @@ impl PsbtState { daemon // TODO: filter for the outpoints in `tx.coins` when this is possible: // https://github.com/wizardsardine/liana/issues/677 - .list_coins() + .list_coins(&[], &[]) .map(|res| { res.coins .iter() diff --git a/gui/src/app/state/recovery.rs b/gui/src/app/state/recovery.rs index bd8325cc6..8b20f661b 100644 --- a/gui/src/app/state/recovery.rs +++ b/gui/src/app/state/recovery.rs @@ -155,7 +155,7 @@ impl State for RecoveryPanel { return Command::perform( async move { let psbt = daemon.create_recovery(address, feerate_vb, sequence)?; - let coins = daemon.list_coins().map(|res| res.coins)?; + let coins = daemon.list_coins(&[], &[]).map(|res| res.coins)?; let coins = coins .into_iter() .filter(|coin| { @@ -207,7 +207,7 @@ impl State for RecoveryPanel { Command::perform( async move { daemon - .list_coins() + .list_coins(&[], &[]) .map(|res| res.coins) .map_err(|e| e.into()) }, diff --git a/gui/src/app/state/spend/mod.rs b/gui/src/app/state/spend/mod.rs index 953d684bb..1cd82ea36 100644 --- a/gui/src/app/state/spend/mod.rs +++ b/gui/src/app/state/spend/mod.rs @@ -123,7 +123,7 @@ impl State for CreateSpendPanel { Command::perform( async move { daemon1 - .list_coins() + .list_coins(&[], &[]) .map(|res| res.coins) .map_err(|e| e.into()) }, @@ -132,7 +132,7 @@ impl State for CreateSpendPanel { Command::perform( async move { let coins = daemon - .list_coins() + .list_coins(&[], &[]) .map(|res| res.coins) .map_err(Error::from)?; let mut targets = HashSet::::new(); diff --git a/gui/src/app/state/transactions.rs b/gui/src/app/state/transactions.rs index a5198a1da..6737471ce 100644 --- a/gui/src/app/state/transactions.rs +++ b/gui/src/app/state/transactions.rs @@ -152,7 +152,7 @@ impl State for TransactionsPanel { daemon // TODO: filter for spending coins when this is possible: // https://github.com/wizardsardine/liana/issues/677 - .list_coins() + .list_coins(&[], &[]) .map(|res| { res.coins .iter() @@ -271,7 +271,7 @@ impl State for TransactionsPanel { Command::perform( async move { daemon2 - .list_coins() + .list_coins(&[], &[]) .map(|res| res.coins) .map_err(|e| e.into()) }, diff --git a/gui/src/daemon/client/mod.rs b/gui/src/daemon/client/mod.rs index eed290714..7f354a286 100644 --- a/gui/src/daemon/client/mod.rs +++ b/gui/src/daemon/client/mod.rs @@ -2,7 +2,7 @@ use std::collections::{HashMap, HashSet}; use std::fmt::Debug; use std::iter::FromIterator; -use liana::commands::CreateRecoveryResult; +use liana::commands::{CoinStatus, CreateRecoveryResult}; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use serde_json::json; @@ -77,8 +77,18 @@ impl Daemon for Lianad { self.call("getnewaddress", Option::::None) } - fn list_coins(&self) -> Result { - self.call("listcoins", Option::::None) + fn list_coins( + &self, + statuses: &[CoinStatus], + outpoints: &[OutPoint], + ) -> Result { + self.call( + "listcoins", + Some(vec![ + json!(statuses.iter().map(|s| s.to_arg()).collect::>()), + json!(outpoints), + ]), + ) } fn list_spend_txs(&self) -> Result { diff --git a/gui/src/daemon/embedded.rs b/gui/src/daemon/embedded.rs index 0ccf16b10..ec4942f7e 100644 --- a/gui/src/daemon/embedded.rs +++ b/gui/src/daemon/embedded.rs @@ -3,7 +3,7 @@ use std::sync::Mutex; use super::{model::*, Daemon, DaemonError}; use liana::{ - commands::LabelItem, + commands::{CoinStatus, LabelItem}, config::Config, miniscript::bitcoin::{address, psbt::Psbt, Address, OutPoint, Txid}, DaemonControl, DaemonHandle, @@ -87,8 +87,12 @@ impl Daemon for EmbeddedDaemon { self.command(|daemon| Ok(daemon.get_new_address())) } - fn list_coins(&self) -> Result { - self.command(|daemon| Ok(daemon.list_coins(&[], &[]))) + fn list_coins( + &self, + statuses: &[CoinStatus], + outpoints: &[OutPoint], + ) -> Result { + self.command(|daemon| Ok(daemon.list_coins(statuses, outpoints))) } fn list_spend_txs(&self) -> Result { diff --git a/gui/src/daemon/mod.rs b/gui/src/daemon/mod.rs index 5edbc9b09..f33ceb562 100644 --- a/gui/src/daemon/mod.rs +++ b/gui/src/daemon/mod.rs @@ -8,7 +8,7 @@ use std::io::ErrorKind; use std::iter::FromIterator; use liana::{ - commands::LabelItem, + commands::{CoinStatus, LabelItem}, config::Config, miniscript::bitcoin::{address, psbt::Psbt, secp256k1, Address, OutPoint, Txid}, StartupError, @@ -56,7 +56,11 @@ pub trait Daemon: Debug { fn stop(&self) -> Result<(), DaemonError>; fn get_info(&self) -> Result; fn get_new_address(&self) -> Result; - fn list_coins(&self) -> Result; + fn list_coins( + &self, + statuses: &[CoinStatus], + outpoints: &[OutPoint], + ) -> Result; fn list_spend_txs(&self) -> Result; fn create_spend_tx( &self, @@ -102,7 +106,7 @@ pub trait Daemon: Debug { txids: Option<&[Txid]>, ) -> Result, DaemonError> { let info = self.get_info()?; - let coins = self.list_coins()?.coins; + let coins = self.list_coins(&[], &[])?.coins; let mut spend_txs = Vec::new(); let curve = secp256k1::Secp256k1::verification_only(); for tx in self.list_spend_txs()?.spend_txs { @@ -152,7 +156,7 @@ pub trait Daemon: Debug { limit: u64, ) -> Result, DaemonError> { let info = self.get_info()?; - let coins = self.list_coins()?.coins; + let coins = self.list_coins(&[], &[])?.coins; let txs = self.list_confirmed_txs(start, end, limit)?.transactions; let mut txs = txs .into_iter() @@ -190,7 +194,7 @@ pub trait Daemon: Debug { txids: &[Txid], ) -> Result, DaemonError> { let info = self.get_info()?; - let coins = self.list_coins()?.coins; + let coins = self.list_coins(&[], &[])?.coins; let txs = self.list_txs(txids)?.transactions; let mut txs = txs .into_iter() @@ -225,7 +229,7 @@ pub trait Daemon: Debug { fn list_pending_txs(&self) -> Result, DaemonError> { let info = self.get_info()?; - let coins = self.list_coins()?.coins; + let coins = self.list_coins(&[], &[])?.coins; let mut txids: Vec = Vec::new(); for coin in &coins { if coin.block_height.is_none() && !txids.contains(&coin.outpoint.txid) { diff --git a/gui/src/loader.rs b/gui/src/loader.rs index 0716485f8..af175b6dd 100644 --- a/gui/src/loader.rs +++ b/gui/src/loader.rs @@ -370,7 +370,7 @@ pub async fn load_application( let wallet = Wallet::new(info.descriptors.main).load_settings(&gui_config, &datadir_path, network)?; - let coins = daemon.list_coins().map(|res| res.coins)?; + let coins = daemon.list_coins(&[], &[]).map(|res| res.coins)?; let cache = Cache { datadir_path, From f3fdb968275db3987d7afaee3b1ba84e0020f8a9 Mon Sep 17 00:00:00 2001 From: jp1ac4 <121959000+jp1ac4@users.noreply.github.com> Date: Mon, 25 Mar 2024 17:06:31 +0000 Subject: [PATCH 02/14] gui(daemon): extract common function for historytxs --- gui/src/daemon/mod.rs | 51 ++++++++++++------------------------------- 1 file changed, 14 insertions(+), 37 deletions(-) diff --git a/gui/src/daemon/mod.rs b/gui/src/daemon/mod.rs index f33ceb562..ea846baa3 100644 --- a/gui/src/daemon/mod.rs +++ b/gui/src/daemon/mod.rs @@ -8,7 +8,7 @@ use std::io::ErrorKind; use std::iter::FromIterator; use liana::{ - commands::{CoinStatus, LabelItem}, + commands::{CoinStatus, LabelItem, TransactionInfo}, config::Config, miniscript::bitcoin::{address, psbt::Psbt, secp256k1, Address, OutPoint, Txid}, StartupError, @@ -149,15 +149,12 @@ pub trait Daemon: Debug { Ok(spend_txs) } - fn list_history_txs( + fn txs_to_historytxs( &self, - start: u32, - end: u32, - limit: u64, + txs: Vec, ) -> Result, DaemonError> { let info = self.get_info()?; let coins = self.list_coins(&[], &[])?.coins; - let txs = self.list_confirmed_txs(start, end, limit)?.transactions; let mut txs = txs .into_iter() .map(|tx| { @@ -189,42 +186,22 @@ pub trait Daemon: Debug { Ok(txs) } + fn list_history_txs( + &self, + start: u32, + end: u32, + limit: u64, + ) -> Result, DaemonError> { + let txs = self.list_confirmed_txs(start, end, limit)?.transactions; + self.txs_to_historytxs(txs) + } + fn get_history_txs( &self, txids: &[Txid], ) -> Result, DaemonError> { - let info = self.get_info()?; - let coins = self.list_coins(&[], &[])?.coins; let txs = self.list_txs(txids)?.transactions; - let mut txs = txs - .into_iter() - .map(|tx| { - let mut tx_coins = Vec::new(); - let mut change_indexes = Vec::new(); - for coin in &coins { - if coin.outpoint.txid == tx.tx.txid() { - change_indexes.push(coin.outpoint.vout as usize) - } else if tx - .tx - .input - .iter() - .any(|input| input.previous_output == coin.outpoint) - { - tx_coins.push(coin.clone()); - } - } - model::HistoryTransaction::new( - tx.tx, - tx.height, - tx.time, - tx_coins, - change_indexes, - info.network, - ) - }) - .collect(); - load_labels(self, &mut txs)?; - Ok(txs) + self.txs_to_historytxs(txs) } fn list_pending_txs(&self) -> Result, DaemonError> { From 46121590c36947d8eac2be4e9989e0b4e8615235 Mon Sep 17 00:00:00 2001 From: jp1ac4 <121959000+jp1ac4@users.noreply.github.com> Date: Mon, 25 Mar 2024 17:14:55 +0000 Subject: [PATCH 03/14] gui(daemon): filter outpoints from txs inputs & outputs --- gui/src/daemon/mod.rs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/gui/src/daemon/mod.rs b/gui/src/daemon/mod.rs index ea846baa3..4fdd2340f 100644 --- a/gui/src/daemon/mod.rs +++ b/gui/src/daemon/mod.rs @@ -3,6 +3,7 @@ pub mod embedded; pub mod model; use std::collections::{HashMap, HashSet}; +use std::convert::TryInto; use std::fmt::Debug; use std::io::ErrorKind; use std::iter::FromIterator; @@ -154,7 +155,25 @@ pub trait Daemon: Debug { txs: Vec, ) -> Result, DaemonError> { let info = self.get_info()?; - let coins = self.list_coins(&[], &[])?.coins; + let outpoints: Vec<_> = txs + .iter() + .flat_map(|tx| { + (0..tx.tx.output.len()) + .map(|vout| { + OutPoint::new( + tx.tx.txid(), + vout.try_into() + .expect("number of transaction outputs must fit in u32"), + ) + }) + .chain(tx.tx.input.iter().map(|txin| txin.previous_output)) + .collect::>() + }) + .collect::>() // remove duplicates + .iter() + .cloned() + .collect(); + let coins = self.list_coins(&[], &outpoints)?.coins; let mut txs = txs .into_iter() .map(|tx| { From aa578ba1fd846e28ed4566be9069e35a5c22dc43 Mon Sep 17 00:00:00 2001 From: jp1ac4 <121959000+jp1ac4@users.noreply.github.com> Date: Tue, 6 Feb 2024 14:09:14 +0000 Subject: [PATCH 04/14] gui(daemon): filter coins for pending txs --- gui/src/daemon/mod.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/gui/src/daemon/mod.rs b/gui/src/daemon/mod.rs index 4fdd2340f..b5fb00f9a 100644 --- a/gui/src/daemon/mod.rs +++ b/gui/src/daemon/mod.rs @@ -225,7 +225,11 @@ pub trait Daemon: Debug { fn list_pending_txs(&self) -> Result, DaemonError> { let info = self.get_info()?; - let coins = self.list_coins(&[], &[])?.coins; + // We want coins that are inputs to and/or outputs of a pending tx, + // which can only be unconfirmed and spending coins. + let coins = self + .list_coins(&[CoinStatus::Unconfirmed, CoinStatus::Spending], &[])? + .coins; let mut txids: Vec = Vec::new(); for coin in &coins { if coin.block_height.is_none() && !txids.contains(&coin.outpoint.txid) { @@ -233,7 +237,7 @@ pub trait Daemon: Debug { } if let Some(spend) = coin.spend_info { - if spend.height.is_none() && !txids.contains(&spend.txid) { + if !txids.contains(&spend.txid) { txids.push(spend.txid); } } From 4641d9eb7bd5fd2f8b8ed053601ff296965ea552 Mon Sep 17 00:00:00 2001 From: jp1ac4 <121959000+jp1ac4@users.noreply.github.com> Date: Tue, 19 Mar 2024 13:01:34 +0000 Subject: [PATCH 05/14] gui(daemon): filter coins using spend txs prev outpoints --- gui/src/daemon/mod.rs | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/gui/src/daemon/mod.rs b/gui/src/daemon/mod.rs index b5fb00f9a..477e68b3d 100644 --- a/gui/src/daemon/mod.rs +++ b/gui/src/daemon/mod.rs @@ -107,15 +107,26 @@ pub trait Daemon: Debug { txids: Option<&[Txid]>, ) -> Result, DaemonError> { let info = self.get_info()?; - let coins = self.list_coins(&[], &[])?.coins; let mut spend_txs = Vec::new(); let curve = secp256k1::Secp256k1::verification_only(); - for tx in self.list_spend_txs()?.spend_txs { - if let Some(txids) = txids { - if !txids.contains(&tx.psbt.unsigned_tx.txid()) { - continue; - } - } + // TODO: Use filters in `list_spend_txs` command. + let mut txs = self.list_spend_txs()?.spend_txs; + if let Some(txids) = txids { + txs.retain(|tx| txids.contains(&tx.psbt.unsigned_tx.txid())); + } + let outpoints: Vec<_> = txs + .iter() + .flat_map(|tx| { + tx.psbt + .unsigned_tx + .input + .iter() + .map(|txin| txin.previous_output) + .collect::>() + }) + .collect(); + let coins = self.list_coins(&[], &outpoints)?.coins; + for tx in txs { let coins = coins .iter() .filter(|coin| { From ddd1e84700642abbe43d2086de66bcf6d43741fa Mon Sep 17 00:00:00 2001 From: jp1ac4 <121959000+jp1ac4@users.noreply.github.com> Date: Tue, 6 Feb 2024 08:32:56 +0000 Subject: [PATCH 06/14] gui(psbt): get conflicting txs from filtered coins --- gui/src/app/state/psbt.rs | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/gui/src/app/state/psbt.rs b/gui/src/app/state/psbt.rs index 175a1892b..4bc53d48b 100644 --- a/gui/src/app/state/psbt.rs +++ b/gui/src/app/state/psbt.rs @@ -7,6 +7,7 @@ use iced::Subscription; use iced::Command; use liana::{ + commands::CoinStatus, descriptors::LianaPolicy, miniscript::bitcoin::{bip32::Fingerprint, psbt::Psbt, Network, Txid}, }; @@ -167,23 +168,15 @@ impl PsbtState { return cmd; } Message::View(view::Message::Spend(view::SpendTxMessage::Broadcast)) => { - let outpoints: HashSet<_> = self.tx.coins.keys().cloned().collect(); + let outpoints: Vec<_> = self.tx.coins.keys().cloned().collect(); return Command::perform( async move { daemon - // TODO: filter for the outpoints in `tx.coins` when this is possible: - // https://github.com/wizardsardine/liana/issues/677 - .list_coins(&[], &[]) + .list_coins(&[CoinStatus::Spending], &outpoints) .map(|res| { res.coins .iter() - .filter_map(|c| { - if outpoints.contains(&c.outpoint) { - c.spend_info.map(|info| info.txid) - } else { - None - } - }) + .filter_map(|c| c.spend_info.map(|info| info.txid)) .collect() }) .map_err(|e| e.into()) From 21f87047ac013c22bd405de584d655ad9cdb76f8 Mon Sep 17 00:00:00 2001 From: jp1ac4 <121959000+jp1ac4@users.noreply.github.com> Date: Tue, 6 Feb 2024 09:32:26 +0000 Subject: [PATCH 07/14] gui(transactions): filter coins by tx outpoints --- gui/src/app/state/transactions.rs | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/gui/src/app/state/transactions.rs b/gui/src/app/state/transactions.rs index 6737471ce..3f2f84cad 100644 --- a/gui/src/app/state/transactions.rs +++ b/gui/src/app/state/transactions.rs @@ -7,6 +7,7 @@ use std::{ use iced::Command; use liana::{ + commands::CoinStatus, miniscript::bitcoin::{OutPoint, Txid}, spend::{SpendCreationError, MAX_FEERATE}, }; @@ -146,23 +147,23 @@ impl State for TransactionsPanel { if let Some(tx) = &self.selected_tx { if tx.fee_amount.is_some() { let tx = tx.clone(); - let txid = tx.tx.txid(); + let outpoints: Vec<_> = (0..tx.tx.output.len()) + .map(|vout| { + OutPoint::new( + tx.tx.txid(), + vout.try_into() + .expect("number of transaction outputs must fit in u32"), + ) + }) + .collect(); return Command::perform( async move { daemon - // TODO: filter for spending coins when this is possible: - // https://github.com/wizardsardine/liana/issues/677 - .list_coins(&[], &[]) + .list_coins(&[CoinStatus::Spending], &outpoints) .map(|res| { res.coins .iter() - .filter_map(|c| { - if c.outpoint.txid == txid { - c.spend_info.map(|info| info.txid) - } else { - None - } - }) + .filter_map(|c| c.spend_info.map(|info| info.txid)) .collect() }) .map_err(|e| e.into()) From 685f83bd0eef46d1448cdc2caf4eef2a7fa09b71 Mon Sep 17 00:00:00 2001 From: jp1ac4 <121959000+jp1ac4@users.noreply.github.com> Date: Tue, 19 Mar 2024 10:11:10 +0000 Subject: [PATCH 08/14] gui(transactions): remove unused list_coins --- gui/src/app/state/transactions.rs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/gui/src/app/state/transactions.rs b/gui/src/app/state/transactions.rs index 3f2f84cad..d88d53382 100644 --- a/gui/src/app/state/transactions.rs +++ b/gui/src/app/state/transactions.rs @@ -249,7 +249,6 @@ impl State for TransactionsPanel { self.selected_tx = None; let daemon1 = daemon.clone(); let daemon2 = daemon.clone(); - let daemon3 = daemon.clone(); let now: u32 = SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap() @@ -258,7 +257,7 @@ impl State for TransactionsPanel { .unwrap(); Command::batch(vec![ Command::perform( - async move { daemon3.list_pending_txs().map_err(|e| e.into()) }, + async move { daemon2.list_pending_txs().map_err(|e| e.into()) }, Message::PendingTransactions, ), Command::perform( @@ -269,15 +268,6 @@ impl State for TransactionsPanel { }, Message::HistoryTransactions, ), - Command::perform( - async move { - daemon2 - .list_coins(&[], &[]) - .map(|res| res.coins) - .map_err(|e| e.into()) - }, - Message::Coins, - ), ]) } } From 4509bd6baf380c59363a2042fad28796bd0b09ab Mon Sep 17 00:00:00 2001 From: jp1ac4 <121959000+jp1ac4@users.noreply.github.com> Date: Tue, 6 Feb 2024 10:03:26 +0000 Subject: [PATCH 09/14] gui(coins): filter for confirmed & unconfirmed --- gui/src/app/state/coins.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gui/src/app/state/coins.rs b/gui/src/app/state/coins.rs index 9783d2ba4..999e667e1 100644 --- a/gui/src/app/state/coins.rs +++ b/gui/src/app/state/coins.rs @@ -4,6 +4,7 @@ use std::{cmp::Ordering, collections::HashSet}; use iced::Command; +use liana::commands::CoinStatus; use liana_ui::widget::Element; use crate::{ @@ -162,7 +163,7 @@ impl State for CoinsPanel { Command::perform( async move { daemon1 - .list_coins(&[], &[]) + .list_coins(&[CoinStatus::Unconfirmed, CoinStatus::Confirmed], &[]) .map(|res| res.coins) .map_err(|e| e.into()) }, @@ -171,7 +172,7 @@ impl State for CoinsPanel { Command::perform( async move { let coins = daemon2 - .list_coins(&[], &[]) + .list_coins(&[CoinStatus::Unconfirmed, CoinStatus::Confirmed], &[]) .map(|res| res.coins) .map_err(Error::from)?; let mut targets = HashSet::::new(); From c9fcfae9d46b81a56b731d95a51fa1e3ea812287 Mon Sep 17 00:00:00 2001 From: jp1ac4 <121959000+jp1ac4@users.noreply.github.com> Date: Tue, 6 Feb 2024 10:06:11 +0000 Subject: [PATCH 10/14] gui(recovery): filter coins by psbt outpoints --- gui/src/app/state/recovery.rs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/gui/src/app/state/recovery.rs b/gui/src/app/state/recovery.rs index 8b20f661b..80ecb369c 100644 --- a/gui/src/app/state/recovery.rs +++ b/gui/src/app/state/recovery.rs @@ -155,16 +155,13 @@ impl State for RecoveryPanel { return Command::perform( async move { let psbt = daemon.create_recovery(address, feerate_vb, sequence)?; - let coins = daemon.list_coins(&[], &[]).map(|res| res.coins)?; - let coins = coins - .into_iter() - .filter(|coin| { - psbt.unsigned_tx - .input - .iter() - .any(|input| input.previous_output == coin.outpoint) - }) + let outpoints: Vec<_> = psbt + .unsigned_tx + .input + .iter() + .map(|txin| txin.previous_output) .collect(); + let coins = daemon.list_coins(&[], &outpoints).map(|res| res.coins)?; Ok(SpendTx::new( None, psbt, From 85b053ff90d7eb679cd2c1e72a59249b3796400f Mon Sep 17 00:00:00 2001 From: jp1ac4 <121959000+jp1ac4@users.noreply.github.com> Date: Tue, 6 Feb 2024 10:07:37 +0000 Subject: [PATCH 11/14] gui(recovery): filter for unconfirmed & confirmed coins --- gui/src/app/state/recovery.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/gui/src/app/state/recovery.rs b/gui/src/app/state/recovery.rs index 80ecb369c..392ac180e 100644 --- a/gui/src/app/state/recovery.rs +++ b/gui/src/app/state/recovery.rs @@ -4,9 +4,12 @@ use std::sync::Arc; use iced::Command; -use liana::miniscript::bitcoin::{ - bip32::{DerivationPath, Fingerprint}, - secp256k1, +use liana::{ + commands::CoinStatus, + miniscript::bitcoin::{ + bip32::{DerivationPath, Fingerprint}, + secp256k1, + }, }; use liana_ui::{component::form, widget::Element}; @@ -204,7 +207,7 @@ impl State for RecoveryPanel { Command::perform( async move { daemon - .list_coins(&[], &[]) + .list_coins(&[CoinStatus::Unconfirmed, CoinStatus::Confirmed], &[]) .map(|res| res.coins) .map_err(|e| e.into()) }, From 982220da030d7f4f001374b54345c4703891a3ba Mon Sep 17 00:00:00 2001 From: jp1ac4 <121959000+jp1ac4@users.noreply.github.com> Date: Tue, 6 Feb 2024 10:41:42 +0000 Subject: [PATCH 12/14] gui(home): filter for unconfirmed & confirmed coins --- gui/src/app/state/mod.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/gui/src/app/state/mod.rs b/gui/src/app/state/mod.rs index 1ef261a4e..2a32f9d9f 100644 --- a/gui/src/app/state/mod.rs +++ b/gui/src/app/state/mod.rs @@ -13,7 +13,10 @@ use std::sync::Arc; use std::time::{SystemTime, UNIX_EPOCH}; use iced::{Command, Subscription}; -use liana::miniscript::bitcoin::{Amount, OutPoint}; +use liana::{ + commands::CoinStatus, + miniscript::bitcoin::{Amount, OutPoint}, +}; use liana_ui::widget::*; use super::{cache::Cache, error::Error, menu::Menu, message::Message, view, wallet::Wallet}; @@ -295,7 +298,7 @@ impl State for Home { Command::perform( async move { daemon2 - .list_coins(&[], &[]) + .list_coins(&[CoinStatus::Unconfirmed, CoinStatus::Confirmed], &[]) .map(|res| res.coins) .map_err(|e| e.into()) }, From daf9f85cfd3ef77698773f9c86763f4bb5122b6a Mon Sep 17 00:00:00 2001 From: jp1ac4 <121959000+jp1ac4@users.noreply.github.com> Date: Tue, 6 Feb 2024 10:42:53 +0000 Subject: [PATCH 13/14] gui(spend): filter for unconfirmed & confirmed coins --- gui/src/app/state/spend/mod.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/gui/src/app/state/spend/mod.rs b/gui/src/app/state/spend/mod.rs index 1cd82ea36..60e8d933f 100644 --- a/gui/src/app/state/spend/mod.rs +++ b/gui/src/app/state/spend/mod.rs @@ -5,7 +5,10 @@ use std::sync::Arc; use iced::Command; -use liana::miniscript::bitcoin::{Network, OutPoint}; +use liana::{ + commands::CoinStatus, + miniscript::bitcoin::{Network, OutPoint}, +}; use liana_ui::widget::Element; use super::{redirect, State}; @@ -123,7 +126,7 @@ impl State for CreateSpendPanel { Command::perform( async move { daemon1 - .list_coins(&[], &[]) + .list_coins(&[CoinStatus::Unconfirmed, CoinStatus::Confirmed], &[]) .map(|res| res.coins) .map_err(|e| e.into()) }, @@ -132,7 +135,7 @@ impl State for CreateSpendPanel { Command::perform( async move { let coins = daemon - .list_coins(&[], &[]) + .list_coins(&[CoinStatus::Unconfirmed, CoinStatus::Confirmed], &[]) .map(|res| res.coins) .map_err(Error::from)?; let mut targets = HashSet::::new(); From 1c99376860458f350c3289cd995716aca96c4d87 Mon Sep 17 00:00:00 2001 From: jp1ac4 <121959000+jp1ac4@users.noreply.github.com> Date: Mon, 18 Mar 2024 17:13:22 +0000 Subject: [PATCH 14/14] gui(app): cache unconfirmed & confirmed coins only --- gui/src/app/mod.rs | 6 +++--- gui/src/loader.rs | 5 ++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/gui/src/app/mod.rs b/gui/src/app/mod.rs index 66b800668..a2aa1717b 100644 --- a/gui/src/app/mod.rs +++ b/gui/src/app/mod.rs @@ -18,7 +18,7 @@ use std::time::Duration; use iced::{clipboard, time, Command, Subscription}; use tracing::{error, info, warn}; -pub use liana::{config::Config as DaemonConfig, miniscript::bitcoin}; +pub use liana::{commands::CoinStatus, config::Config as DaemonConfig, miniscript::bitcoin}; use liana_ui::widget::Element; pub use config::Config; @@ -239,8 +239,8 @@ impl App { daemon.is_alive()?; let info = daemon.get_info()?; - // todo: filter coins to only have current coins. - let coins = daemon.list_coins(&[], &[])?; + let coins = daemon + .list_coins(&[CoinStatus::Unconfirmed, CoinStatus::Confirmed], &[])?; Ok(Cache { datadir_path, coins: coins.coins, diff --git a/gui/src/loader.rs b/gui/src/loader.rs index af175b6dd..84cd19eec 100644 --- a/gui/src/loader.rs +++ b/gui/src/loader.rs @@ -9,6 +9,7 @@ use iced::{Alignment, Command, Length, Subscription}; use tracing::{debug, info, warn}; use liana::{ + commands::CoinStatus, config::{Config, ConfigError}, miniscript::bitcoin, StartupError, @@ -370,7 +371,9 @@ pub async fn load_application( let wallet = Wallet::new(info.descriptors.main).load_settings(&gui_config, &datadir_path, network)?; - let coins = daemon.list_coins(&[], &[]).map(|res| res.coins)?; + let coins = daemon + .list_coins(&[CoinStatus::Unconfirmed, CoinStatus::Confirmed], &[]) + .map(|res| res.coins)?; let cache = Cache { datadir_path,