From 8b8b48142f60ede2b8ce702c89a61f3fd32a1842 Mon Sep 17 00:00:00 2001 From: Michael Mallan Date: Mon, 5 May 2025 16:21:13 +0100 Subject: [PATCH] feat: add list addresses method to gui daemon interface --- liana-gui/src/daemon/client/mod.rs | 22 +++++++- liana-gui/src/daemon/embedded.rs | 19 ++++++- liana-gui/src/daemon/mod.rs | 12 +++- liana-gui/src/daemon/model.rs | 3 +- .../services/connect/client/backend/api.rs | 15 +++++ .../services/connect/client/backend/mod.rs | 55 ++++++++++++++++++- lianad/src/commands/mod.rs | 5 +- 7 files changed, 124 insertions(+), 7 deletions(-) diff --git a/liana-gui/src/daemon/client/mod.rs b/liana-gui/src/daemon/client/mod.rs index 376aa3fa..6341477d 100644 --- a/liana-gui/src/daemon/client/mod.rs +++ b/liana-gui/src/daemon/client/mod.rs @@ -13,7 +13,9 @@ use tracing::{error, info}; pub mod error; pub mod jsonrpc; -use liana::miniscript::bitcoin::{address, psbt::Psbt, Address, Network, OutPoint, Txid}; +use liana::miniscript::bitcoin::{ + address, bip32::ChildNumber, psbt::Psbt, Address, Network, OutPoint, Txid, +}; use lianad::{ commands::{CoinStatus, CreateRecoveryResult, LabelItem}, config::Config, @@ -87,6 +89,24 @@ impl Daemon for Lianad { self.call("getnewaddress", Option::::None) } + async fn list_revealed_addresses( + &self, + is_change: bool, + exclude_used: bool, + limit: usize, + start_index: Option, + ) -> Result { + self.call( + "listrevealedaddresses", + Some(vec![ + json!(is_change), + json!(exclude_used), + json!(limit), + json!(start_index), // a `null` argument is parsed as `None` by the command + ]), + ) + } + async fn update_deriv_indexes( &self, receive: Option, diff --git a/liana-gui/src/daemon/embedded.rs b/liana-gui/src/daemon/embedded.rs index c86483f4..59213b2e 100644 --- a/liana-gui/src/daemon/embedded.rs +++ b/liana-gui/src/daemon/embedded.rs @@ -6,7 +6,9 @@ use tokio::sync::Mutex; use super::{model::*, node, Daemon, DaemonBackend, DaemonError}; use crate::dir::LianaDirectory; use async_trait::async_trait; -use liana::miniscript::bitcoin::{address, psbt::Psbt, Address, Network, OutPoint, Txid}; +use liana::miniscript::bitcoin::{ + address, bip32::ChildNumber, psbt::Psbt, Address, Network, OutPoint, Txid, +}; use lianad::{ commands::{CoinStatus, LabelItem}, config::Config, @@ -103,6 +105,21 @@ impl Daemon for EmbeddedDaemon { self.command(|daemon| Ok(daemon.get_new_address())).await } + async fn list_revealed_addresses( + &self, + is_change: bool, + exclude_used: bool, + limit: usize, + start_index: Option, + ) -> Result { + self.command(|daemon| { + daemon + .list_revealed_addresses(is_change, exclude_used, limit, start_index) + .map_err(|e| DaemonError::Unexpected(e.to_string())) + }) + .await + } + async fn update_deriv_indexes( &self, receive: Option, diff --git a/liana-gui/src/daemon/mod.rs b/liana-gui/src/daemon/mod.rs index 4afea243..98a3d410 100644 --- a/liana-gui/src/daemon/mod.rs +++ b/liana-gui/src/daemon/mod.rs @@ -11,7 +11,10 @@ use std::iter::FromIterator; use async_trait::async_trait; use liana::miniscript::bitcoin::{ - address, bip32::Fingerprint, psbt::Psbt, secp256k1, Address, Network, OutPoint, Txid, + address, + bip32::{ChildNumber, Fingerprint}, + psbt::Psbt, + secp256k1, Address, Network, OutPoint, Txid, }; use lianad::bip329::Labels; use lianad::commands::UpdateDerivIndexesResult; @@ -104,6 +107,13 @@ pub trait Daemon: Debug { async fn stop(&self) -> Result<(), DaemonError>; async fn get_info(&self) -> Result; async fn get_new_address(&self) -> Result; + async fn list_revealed_addresses( + &self, + is_change: bool, + exclude_used: bool, + limit: usize, + start_index: Option, + ) -> Result; async fn update_deriv_indexes( &self, receive: Option, diff --git a/liana-gui/src/daemon/model.rs b/liana-gui/src/daemon/model.rs index 9bbb0b94..53606e89 100644 --- a/liana-gui/src/daemon/model.rs +++ b/liana-gui/src/daemon/model.rs @@ -12,7 +12,8 @@ pub use liana::{ }; pub use lianad::commands::{ CreateSpendResult, GetAddressResult, GetInfoResult, GetLabelsResult, LabelItem, ListCoinsEntry, - ListCoinsResult, ListSpendEntry, ListSpendResult, ListTransactionsResult, TransactionInfo, + ListCoinsResult, ListRevealedAddressesEntry, ListRevealedAddressesResult, ListSpendEntry, + ListSpendResult, ListTransactionsResult, TransactionInfo, }; pub type Coin = ListCoinsEntry; diff --git a/liana-gui/src/services/connect/client/backend/api.rs b/liana-gui/src/services/connect/client/backend/api.rs index f795bd0e..e4dee4e3 100644 --- a/liana-gui/src/services/connect/client/backend/api.rs +++ b/liana-gui/src/services/connect/client/backend/api.rs @@ -347,6 +347,21 @@ pub struct Address { pub derivation_index: bip32::ChildNumber, } +#[derive(Deserialize)] +pub struct RevealedAddress { + #[serde(deserialize_with = "deser_addr_assume_checked")] + pub address: bitcoin::Address, + pub derivation_index: bip32::ChildNumber, + pub label: Option, + pub used_count: u32, +} + +#[derive(Deserialize)] +pub struct ListRevealedAddresses { + pub addresses: Vec, + pub continue_from: Option, +} + pub mod payload { use liana::{descriptors::LianaDescriptor, miniscript::bitcoin}; use serde::{Serialize, Serializer}; diff --git a/liana-gui/src/services/connect/client/backend/mod.rs b/liana-gui/src/services/connect/client/backend/mod.rs index ba58f713..a6426205 100644 --- a/liana-gui/src/services/connect/client/backend/mod.rs +++ b/liana-gui/src/services/connect/client/backend/mod.rs @@ -9,7 +9,9 @@ use async_trait::async_trait; use chrono::Utc; use liana::{ descriptors::LianaDescriptor, - miniscript::bitcoin::{address, psbt::Psbt, Address, Network, OutPoint, Txid}, + miniscript::bitcoin::{ + address, bip32::ChildNumber, psbt::Psbt, Address, Network, OutPoint, Txid, + }, }; use lianad::{ bip329::Labels, @@ -610,6 +612,57 @@ impl Daemon for BackendWalletClient { }) } + async fn list_revealed_addresses( + &self, + is_change: bool, + exclude_used: bool, + limit: usize, + start_index: Option, + ) -> Result { + let mut query = Vec::<(&str, String)>::new(); + query.push(("is_change_address", is_change.to_string())); + query.push(("exclude_used", exclude_used.to_string())); + query.push(("limit", limit.to_string())); + if let Some(start) = start_index { + query.push(("start_derivation_index", start.to_string())); + } + let response: Response = self + .inner + .request( + Method::GET, + &format!( + "{}/v1/wallets/{}/addresses", + self.inner.url, self.wallet_uuid + ), + ) + .await + .query(&query) + .send() + .await?; + + if !response.status().is_success() { + return Err(DaemonError::Http( + Some(response.status().into()), + response.text().await?, + )); + } + + let res: api::ListRevealedAddresses = response.json().await?; + Ok(ListRevealedAddressesResult { + addresses: res + .addresses + .into_iter() + .map(|addr| ListRevealedAddressesEntry { + index: addr.derivation_index, + address: addr.address, + label: addr.label, + used_count: addr.used_count, + }) + .collect(), + continue_from: res.continue_from, + }) + } + async fn update_deriv_indexes( &self, _receive: Option, diff --git a/lianad/src/commands/mod.rs b/lianad/src/commands/mod.rs index 77ffcf21..1ff60886 100644 --- a/lianad/src/commands/mod.rs +++ b/lianad/src/commands/mod.rs @@ -1412,11 +1412,12 @@ impl ListAddressesResult { } /// A revealed address entry in the list returned by [`DaemonControl::list_revealed_addresses`]. -#[derive(Debug, Clone, Serialize, PartialEq, Eq)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct ListRevealedAddressesEntry { /// The address's derivation index. pub index: ChildNumber, /// The address. + #[serde(deserialize_with = "deser_addr_assume_checked")] pub address: bitcoin::Address, /// Label assigned to the address, if any. pub label: Option, @@ -1428,7 +1429,7 @@ pub struct ListRevealedAddressesEntry { } /// Result of a [`DaemonControl::list_revealed_addresses`] request. -#[derive(Debug, Clone, Serialize, PartialEq, Eq)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct ListRevealedAddressesResult { /// Revealed addresses in order of descending derivation index. pub addresses: Vec,