lianad: add feature to dump labels in BIP-0329 format

This commit is contained in:
pythcoiner 2025-01-22 10:22:53 +01:00
parent 158651ebe7
commit 38eed1b881
No known key found for this signature in database
GPG Key ID: C1048AEEDF303B88
13 changed files with 518 additions and 401 deletions

809
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -4,6 +4,7 @@ use std::iter::FromIterator;
use std::path::Path;
use async_trait::async_trait;
use lianad::bip329::Labels;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use serde_json::json;
@ -205,6 +206,10 @@ impl<C: Client + Send + Sync + Debug> Daemon for Lianad<C> {
let _res: serde_json::value::Value = self.call("updatelabels", Some(vec![labels]))?;
Ok(())
}
async fn get_labels_bip329(&self, _offset: u32, _limit: u32) -> Result<Labels, DaemonError> {
todo!()
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]

View File

@ -1,3 +1,4 @@
use lianad::bip329::Labels;
use std::collections::{HashMap, HashSet};
use std::path::Path;
use tokio::sync::Mutex;
@ -227,4 +228,9 @@ impl Daemon for EmbeddedDaemon {
})
.await
}
async fn get_labels_bip329(&self, offset: u32, limit: u32) -> Result<Labels, DaemonError> {
self.command(|daemon| Ok(daemon.get_labels_bip329(offset, limit).labels))
.await
}
}

View File

@ -14,6 +14,7 @@ use async_trait::async_trait;
use liana::miniscript::bitcoin::{
address, bip32::Fingerprint, psbt::Psbt, secp256k1, Address, Network, OutPoint, Txid,
};
use lianad::bip329::Labels;
use lianad::{
commands::{CoinStatus, LabelItem, TransactionInfo},
config::Config,
@ -125,6 +126,7 @@ pub trait Daemon: Debug {
&self,
labels: &HashMap<LabelItem, Option<String>>,
) -> Result<(), DaemonError>;
async fn get_labels_bip329(&self, offset: u32, limit: u32) -> Result<Labels, DaemonError>;
async fn send_wallet_invitation(&self, _email: &str) -> Result<(), DaemonError> {
Ok(())
}

View File

@ -13,6 +13,7 @@ use liana::{
miniscript::bitcoin::{address, psbt::Psbt, Address, Network, OutPoint, Txid},
};
use lianad::{
bip329::Labels,
commands::{CoinStatus, GetInfoDescriptors, LCSpendInfo, LabelItem},
config::Config,
};
@ -1115,6 +1116,11 @@ impl Daemon for BackendWalletClient {
Ok(())
}
async fn get_labels_bip329(&self, _offset: u32, _limit: u32) -> Result<Labels, DaemonError> {
// TODO: add an endpoint on backend
todo!()
}
}
fn history_tx_from_api(value: api::Transaction, network: Network) -> HistoryTransaction {

View File

@ -55,3 +55,4 @@ rusqlite = { version = "0.30", features = ["bundled", "unlock_notify"] }
# To talk to bitcoind
jsonrpc = { version = "0.17", features = ["minreq_http"], default-features = false }
bip329 = {version = "0.1.1", default-features = false }

View File

@ -687,6 +687,13 @@ impl DaemonControl {
}
}
pub fn get_labels_bip329(&self, offset: u32, limit: u32) -> GetLabelsBip329Result {
let mut db_conn = self.db.connection();
GetLabelsBip329Result {
labels: db_conn.get_labels_bip329(offset, limit),
}
}
pub fn list_spend(
&self,
txids: Option<Vec<bitcoin::Txid>>,
@ -1184,6 +1191,11 @@ pub struct GetLabelsResult {
pub labels: HashMap<String, String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GetLabelsBip329Result {
pub labels: crate::bip329::Labels,
}
#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
pub struct AddressInfo {
index: u32,

View File

@ -20,6 +20,7 @@ use std::{
sync,
};
use bip329::Labels;
use miniscript::bitcoin::{self, bip32, psbt::Psbt, secp256k1};
/// Information about the wallet.
@ -190,6 +191,9 @@ pub trait DatabaseConnection {
&mut self,
txids: &[bitcoin::Txid],
) -> Vec<(bitcoin::Transaction, Option<i32>, Option<u32>)>;
/// Dump all labels
fn get_labels_bip329(&mut self, offset: u32, limit: u32) -> Labels;
}
impl DatabaseConnection for SqliteConn {
@ -367,6 +371,15 @@ impl DatabaseConnection for SqliteConn {
HashMap::from_iter(labels.into_iter().map(|label| (label.item, label.value)))
}
fn get_labels_bip329(&mut self, offset: u32, limit: u32) -> Labels {
let labels = self
.labels_bip329(offset, limit)
.into_iter()
.map(|l| l.into())
.collect();
Labels::new(labels)
}
fn rollback_tip(&mut self, new_tip: &BlockChainTip) {
self.rollback_tip(new_tip)
}

View File

@ -678,6 +678,18 @@ impl SqliteConn {
.expect("Db must not fail")
}
pub fn labels_bip329(&mut self, offset: u32, limit: u32) -> Vec<DbLabel> {
db_query(
&mut self.conn,
"SELECT * FROM labels \
ORDER BY id \
LIMIT ?1 OFFSET ?2",
rusqlite::params![limit, offset],
|row| row.try_into(),
)
.expect("Db must not fail")
}
/// Retrieves a limited and ordered list of transactions ids that happened during the given
/// range.
pub fn db_list_txids(&mut self, start: u32, end: u32, limit: u64) -> Vec<bitcoin::Txid> {

View File

@ -1,3 +1,4 @@
use bip329::Label;
use liana::descriptors::LianaDescriptor;
use std::{convert::TryFrom, str::FromStr};
@ -366,6 +367,31 @@ impl From<i64> for DbLabelledKind {
}
}
impl From<DbLabel> for Label {
fn from(value: DbLabel) -> Self {
let ref_ = value.item;
let label = if value.value.is_empty() {
None
} else {
Some(value.value)
};
match value.item_kind {
DbLabelledKind::Address => Label::Address(bip329::AddressRecord { ref_, label }),
DbLabelledKind::OutPoint => Label::Output(bip329::OutputRecord {
ref_,
label,
spendable: None,
}),
DbLabelledKind::Txid => Label::Transaction(bip329::TransactionRecord {
ref_,
label,
// FIXME: "Optional key origin information referencing the wallet associated with the label"
origin: None,
}),
}
}
}
impl TryFrom<&rusqlite::Row<'_>> for DbLabel {
type Error = rusqlite::Error;

View File

@ -364,6 +364,22 @@ fn get_labels(control: &DaemonControl, params: Params) -> Result<serde_json::Val
Ok(serde_json::json!(control.get_labels(&items)))
}
fn get_labels_bip329(control: &DaemonControl, params: Params) -> Result<serde_json::Value, Error> {
let offset: u32 = params
.get(0, "offset")
.ok_or_else(|| Error::invalid_params("Missing 'offset' parameter."))?
.as_u64()
.and_then(|t| t.try_into().ok())
.ok_or_else(|| Error::invalid_params("Invalid 'offset' parameter."))?;
let limit: u32 = params
.get(1, "limit")
.ok_or_else(|| Error::invalid_params("Missing 'limit' parameter."))?
.as_u64()
.and_then(|t| t.try_into().ok())
.ok_or_else(|| Error::invalid_params("Invalid 'limit' parameter."))?;
Ok(serde_json::json!(control.get_labels_bip329(offset, limit)))
}
/// Handle an incoming JSONRPC2 request.
pub fn handle_request(control: &mut DaemonControl, req: Request) -> Result<Response, Error> {
let result = match req.method.as_str() {
@ -451,6 +467,12 @@ pub fn handle_request(control: &mut DaemonControl, req: Request) -> Result<Respo
.ok_or_else(|| Error::invalid_params("Missing 'items' parameter."))?;
get_labels(control, params)?
}
"getlabelsbip329" => {
let params = req
.params
.ok_or_else(|| Error::invalid_params("Missing 'offset' and 'limit' parameters."))?;
get_labels_bip329(control, params)?
}
_ => {
return Err(Error::method_not_found());
}

View File

@ -7,6 +7,7 @@ mod jsonrpc;
mod testutils;
pub use bdk_electrum::electrum_client;
pub use bip329;
use bitcoin::electrum;
pub use miniscript;

View File

@ -523,6 +523,10 @@ impl DatabaseConnection for DummyDatabase {
}
wallet_txs
}
fn get_labels_bip329(&mut self, _offset: u32, _limit: u32) -> bip329::Labels {
todo!()
}
}
pub struct DummyLiana {