From 6b65662363b5d0ebf4f96c44aaffabbcb084a437 Mon Sep 17 00:00:00 2001 From: Chuks Agbakuru Date: Sat, 2 Aug 2025 17:08:31 +0100 Subject: [PATCH 01/13] Rename unified_qr to unified This rename reflects that this module is a unified payment interface for both QR code payments and HRN payments passed in as a string without scanning a QR code --- src/payment/mod.rs | 4 ++-- src/payment/{unified_qr.rs => unified.rs} | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename src/payment/{unified_qr.rs => unified.rs} (99%) diff --git a/src/payment/mod.rs b/src/payment/mod.rs index f629960e1..8642ec1ee 100644 --- a/src/payment/mod.rs +++ b/src/payment/mod.rs @@ -13,7 +13,7 @@ mod bolt12; mod onchain; mod spontaneous; pub(crate) mod store; -mod unified_qr; +mod unified; pub use bolt11::Bolt11Payment; pub use bolt12::Bolt12Payment; @@ -22,4 +22,4 @@ pub use spontaneous::SpontaneousPayment; pub use store::{ ConfirmationStatus, LSPFeeLimits, PaymentDetails, PaymentDirection, PaymentKind, PaymentStatus, }; -pub use unified_qr::{QrPaymentResult, UnifiedQrPayment}; +pub use unified::{QrPaymentResult, UnifiedQrPayment}; diff --git a/src/payment/unified_qr.rs b/src/payment/unified.rs similarity index 99% rename from src/payment/unified_qr.rs rename to src/payment/unified.rs index af5ee1c7b..01454beb2 100644 --- a/src/payment/unified_qr.rs +++ b/src/payment/unified.rs @@ -304,7 +304,7 @@ impl DeserializationError for Extras { #[cfg(test)] mod tests { use super::*; - use crate::payment::unified_qr::Extras; + use crate::payment::unified::Extras; use bitcoin::{Address, Network}; use std::str::FromStr; From eaf1a1a05de4476608c506c4e895d0844a700b3e Mon Sep 17 00:00:00 2001 From: Chuks Agbakuru Date: Sat, 2 Aug 2025 17:19:21 +0100 Subject: [PATCH 02/13] rename UnifiedQRPayment to UnifiedPayment, rename QRPaymentResult to UnifiedPaymentResult These renamings are necessary to reflect the expanded responsibilities for this module. --- bindings/ldk_node.udl | 8 ++--- src/ffi/types.rs | 2 +- src/lib.rs | 16 ++++++--- src/payment/mod.rs | 2 +- src/payment/unified.rs | 14 ++++---- tests/integration_tests_rust.rs | 60 ++++++++++++++++----------------- 6 files changed, 54 insertions(+), 48 deletions(-) diff --git a/bindings/ldk_node.udl b/bindings/ldk_node.udl index 9f0ef697e..5363b92ea 100644 --- a/bindings/ldk_node.udl +++ b/bindings/ldk_node.udl @@ -128,7 +128,7 @@ interface Node { Bolt12Payment bolt12_payment(); SpontaneousPayment spontaneous_payment(); OnchainPayment onchain_payment(); - UnifiedQrPayment unified_qr_payment(); + UnifiedPayment unified_payment(); LSPS1Liquidity lsps1_liquidity(); [Throws=NodeError] void connect(PublicKey node_id, SocketAddress address, boolean persist); @@ -250,11 +250,11 @@ interface FeeRate { u64 to_sat_per_vb_ceil(); }; -interface UnifiedQrPayment { +interface UnifiedPayment { [Throws=NodeError] string receive(u64 amount_sats, [ByRef]string message, u32 expiry_sec); [Throws=NodeError] - QrPaymentResult send([ByRef]string uri_str); + UnifiedPaymentResult send([ByRef]string uri_str); }; interface LSPS1Liquidity { @@ -428,7 +428,7 @@ interface PaymentKind { }; [Enum] -interface QrPaymentResult { +interface UnifiedPaymentResult { Onchain(Txid txid); Bolt11(PaymentId payment_id); Bolt12(PaymentId payment_id); diff --git a/src/ffi/types.rs b/src/ffi/types.rs index 02d321787..185539a1f 100644 --- a/src/ffi/types.rs +++ b/src/ffi/types.rs @@ -20,7 +20,7 @@ pub use crate::logger::{LogLevel, LogRecord, LogWriter}; pub use crate::payment::store::{ ConfirmationStatus, LSPFeeLimits, PaymentDirection, PaymentKind, PaymentStatus, }; -pub use crate::payment::QrPaymentResult; +pub use crate::payment::UnifiedPaymentResult; pub use lightning::chain::channelmonitor::BalanceSource; pub use lightning::events::{ClosureReason, PaymentFailureReason}; diff --git a/src/lib.rs b/src/lib.rs index e7e27273b..3242ad062 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -139,7 +139,7 @@ use liquidity::{LSPS1Liquidity, LiquiditySource}; use payment::asynchronous::static_invoice_store::StaticInvoiceStore; use payment::{ Bolt11Payment, Bolt12Payment, OnchainPayment, PaymentDetails, SpontaneousPayment, - UnifiedQrPayment, + UnifiedPayment, }; use peer_store::{PeerInfo, PeerStore}; use runtime::Runtime; @@ -899,12 +899,15 @@ impl Node { /// Returns a payment handler allowing to create [BIP 21] URIs with an on-chain, [BOLT 11], /// and [BOLT 12] payment options. /// + /// This handler allows you to send payments to these URIs as well as [BIP 353] HRNs. + /// /// [BOLT 11]: https://github.com/lightning/bolts/blob/master/11-payment-encoding.md /// [BOLT 12]: https://github.com/lightning/bolts/blob/master/12-offer-encoding.md /// [BIP 21]: https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki + /// [BIP 353]: https://github.com/bitcoin/bips/blob/master/bip-0353.mediawiki #[cfg(not(feature = "uniffi"))] - pub fn unified_qr_payment(&self) -> UnifiedQrPayment { - UnifiedQrPayment::new( + pub fn unified_payment(&self) -> UnifiedPayment { + UnifiedPayment::new( self.onchain_payment().into(), self.bolt11_payment().into(), self.bolt12_payment().into(), @@ -916,12 +919,15 @@ impl Node { /// Returns a payment handler allowing to create [BIP 21] URIs with an on-chain, [BOLT 11], /// and [BOLT 12] payment options. /// + /// This handler allows you to send payments to these URIs as well as [BIP 353] HRNs. + /// /// [BOLT 11]: https://github.com/lightning/bolts/blob/master/11-payment-encoding.md /// [BOLT 12]: https://github.com/lightning/bolts/blob/master/12-offer-encoding.md /// [BIP 21]: https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki + /// [BIP 353]: https://github.com/bitcoin/bips/blob/master/bip-0353.mediawiki #[cfg(feature = "uniffi")] - pub fn unified_qr_payment(&self) -> Arc { - Arc::new(UnifiedQrPayment::new( + pub fn unified_payment(&self) -> Arc { + Arc::new(UnifiedPayment::new( self.onchain_payment(), self.bolt11_payment(), self.bolt12_payment(), diff --git a/src/payment/mod.rs b/src/payment/mod.rs index 8642ec1ee..3d21a0bae 100644 --- a/src/payment/mod.rs +++ b/src/payment/mod.rs @@ -22,4 +22,4 @@ pub use spontaneous::SpontaneousPayment; pub use store::{ ConfirmationStatus, LSPFeeLimits, PaymentDetails, PaymentDirection, PaymentKind, PaymentStatus, }; -pub use unified::{QrPaymentResult, UnifiedQrPayment}; +pub use unified::{UnifiedPayment, UnifiedPaymentResult}; \ No newline at end of file diff --git a/src/payment/unified.rs b/src/payment/unified.rs index 01454beb2..024373415 100644 --- a/src/payment/unified.rs +++ b/src/payment/unified.rs @@ -46,7 +46,7 @@ struct Extras { /// [BOLT 11]: https://github.com/lightning/bolts/blob/master/11-payment-encoding.md /// [BOLT 12]: https://github.com/lightning/bolts/blob/master/12-offer-encoding.md /// [`Node::unified_qr_payment`]: crate::Node::unified_qr_payment -pub struct UnifiedQrPayment { +pub struct UnifiedPayment { onchain_payment: Arc, bolt11_invoice: Arc, bolt12_payment: Arc, @@ -54,7 +54,7 @@ pub struct UnifiedQrPayment { logger: Arc, } -impl UnifiedQrPayment { +impl UnifiedPayment { pub(crate) fn new( onchain_payment: Arc, bolt11_invoice: Arc, bolt12_payment: Arc, config: Arc, logger: Arc, @@ -139,7 +139,7 @@ impl UnifiedQrPayment { /// occurs, an `Error` is returned detailing the issue encountered. /// /// [BIP 21]: https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki - pub fn send(&self, uri_str: &str) -> Result { + pub fn send(&self, uri_str: &str) -> Result { let uri: bip21::Uri = uri_str.parse().map_err(|_| Error::InvalidUri)?; @@ -149,7 +149,7 @@ impl UnifiedQrPayment { if let Some(offer) = uri_network_checked.extras.bolt12_offer { let offer = maybe_wrap(offer); match self.bolt12_payment.send(&offer, None, None) { - Ok(payment_id) => return Ok(QrPaymentResult::Bolt12 { payment_id }), + Ok(payment_id) => return Ok(UnifiedPaymentResult::Bolt12 { payment_id }), Err(e) => log_error!(self.logger, "Failed to send BOLT12 offer: {:?}. This is part of a unified QR code payment. Falling back to the BOLT11 invoice.", e), } } @@ -157,7 +157,7 @@ impl UnifiedQrPayment { if let Some(invoice) = uri_network_checked.extras.bolt11_invoice { let invoice = maybe_wrap(invoice); match self.bolt11_invoice.send(&invoice, None) { - Ok(payment_id) => return Ok(QrPaymentResult::Bolt11 { payment_id }), + Ok(payment_id) => return Ok(UnifiedPaymentResult::Bolt11 { payment_id }), Err(e) => log_error!(self.logger, "Failed to send BOLT11 invoice: {:?}. This is part of a unified QR code payment. Falling back to the on-chain transaction.", e), } } @@ -176,7 +176,7 @@ impl UnifiedQrPayment { None, )?; - Ok(QrPaymentResult::Onchain { txid }) + Ok(UnifiedPaymentResult::Onchain { txid }) } } @@ -189,7 +189,7 @@ impl UnifiedQrPayment { /// [`PaymentId`]: lightning::ln::channelmanager::PaymentId /// [`Txid`]: bitcoin::hash_types::Txid #[derive(Debug)] -pub enum QrPaymentResult { +pub enum UnifiedPaymentResult { /// An on-chain payment. Onchain { /// The transaction ID (txid) of the on-chain payment. diff --git a/tests/integration_tests_rust.rs b/tests/integration_tests_rust.rs index f2e8407cd..9f8ff58b9 100644 --- a/tests/integration_tests_rust.rs +++ b/tests/integration_tests_rust.rs @@ -23,7 +23,7 @@ use ldk_node::config::EsploraSyncConfig; use ldk_node::liquidity::LSPS2ServiceConfig; use ldk_node::payment::{ ConfirmationStatus, PaymentDetails, PaymentDirection, PaymentKind, PaymentStatus, - QrPaymentResult, + UnifiedPaymentResult, }; use ldk_node::{Builder, Event, NodeError}; @@ -1353,15 +1353,15 @@ fn generate_bip21_uri() { // Test 1: Verify URI generation (on-chain + BOLT11) works // even before any channels are opened. This checks the graceful fallback behavior. - let initial_uqr_payment = node_b - .unified_qr_payment() + let initial_uni_payment = node_b + .unified_payment() .receive(expected_amount_sats, "asdf", expiry_sec) .expect("Failed to generate URI"); - println!("Initial URI (no channels): {}", initial_uqr_payment); + println!("Initial URI (no channels): {}", initial_uni_payment); - assert!(initial_uqr_payment.contains("bitcoin:")); - assert!(initial_uqr_payment.contains("lightning=")); - assert!(!initial_uqr_payment.contains("lno=")); // BOLT12 requires channels + assert!(initial_uni_payment.contains("bitcoin:")); + assert!(initial_uni_payment.contains("lightning=")); + assert!(!initial_uni_payment.contains("lno=")); // BOLT12 requires channels premine_and_distribute_funds( &bitcoind.client, @@ -1381,15 +1381,15 @@ fn generate_bip21_uri() { expect_channel_ready_event!(node_b, node_a.node_id()); // Test 2: Verify URI generation (on-chain + BOLT11 + BOLT12) works after channels are established. - let uqr_payment = node_b - .unified_qr_payment() + let uni_payment = node_b + .unified_payment() .receive(expected_amount_sats, "asdf", expiry_sec) .expect("Failed to generate URI"); - println!("Generated URI: {}", uqr_payment); - assert!(uqr_payment.contains("bitcoin:")); - assert!(uqr_payment.contains("lightning=")); - assert!(uqr_payment.contains("lno=")); + println!("Generated URI: {}", uni_payment); + assert!(uni_payment.contains("bitcoin:")); + assert!(uni_payment.contains("lightning=")); + assert!(uni_payment.contains("lno=")); } #[test] @@ -1430,17 +1430,17 @@ fn unified_qr_send_receive() { let expected_amount_sats = 100_000; let expiry_sec = 4_000; - let uqr_payment = node_b.unified_qr_payment().receive(expected_amount_sats, "asdf", expiry_sec); - let uri_str = uqr_payment.clone().unwrap(); - let offer_payment_id: PaymentId = match node_a.unified_qr_payment().send(&uri_str) { - Ok(QrPaymentResult::Bolt12 { payment_id }) => { + let uni_payment = node_b.unified_payment().receive(expected_amount_sats, "asdf", expiry_sec); + let uri_str = uni_payment.clone().unwrap(); + let offer_payment_id: PaymentId = match node_a.unified_payment().send(&uri_str) { + Ok(UnifiedPaymentResult::Bolt12 { payment_id }) => { println!("\nBolt12 payment sent successfully with PaymentID: {:?}", payment_id); payment_id }, - Ok(QrPaymentResult::Bolt11 { payment_id: _ }) => { + Ok(UnifiedPaymentResult::Bolt11 { payment_id: _ }) => { panic!("Expected Bolt12 payment but got Bolt11"); }, - Ok(QrPaymentResult::Onchain { txid: _ }) => { + Ok(UnifiedPaymentResult::Onchain { txid: _ }) => { panic!("Expected Bolt12 payment but get On-chain transaction"); }, Err(e) => { @@ -1453,15 +1453,15 @@ fn unified_qr_send_receive() { // Cut off the BOLT12 part to fallback to BOLT11. let uri_str_without_offer = uri_str.split("&lno=").next().unwrap(); let invoice_payment_id: PaymentId = - match node_a.unified_qr_payment().send(uri_str_without_offer) { - Ok(QrPaymentResult::Bolt12 { payment_id: _ }) => { + match node_a.unified_payment().send(uri_str_without_offer) { + Ok(UnifiedPaymentResult::Bolt12 { payment_id: _ }) => { panic!("Expected Bolt11 payment but got Bolt12"); }, - Ok(QrPaymentResult::Bolt11 { payment_id }) => { + Ok(UnifiedPaymentResult::Bolt11 { payment_id }) => { println!("\nBolt11 payment sent successfully with PaymentID: {:?}", payment_id); payment_id }, - Ok(QrPaymentResult::Onchain { txid: _ }) => { + Ok(UnifiedPaymentResult::Onchain { txid: _ }) => { panic!("Expected Bolt11 payment but got on-chain transaction"); }, Err(e) => { @@ -1471,19 +1471,19 @@ fn unified_qr_send_receive() { expect_payment_successful_event!(node_a, Some(invoice_payment_id), None); let expect_onchain_amount_sats = 800_000; - let onchain_uqr_payment = - node_b.unified_qr_payment().receive(expect_onchain_amount_sats, "asdf", 4_000).unwrap(); + let onchain_uni_payment = + node_b.unified_payment().receive(expect_onchain_amount_sats, "asdf", 4_000).unwrap(); // Cut off any lightning part to fallback to on-chain only. - let uri_str_without_lightning = onchain_uqr_payment.split("&lightning=").next().unwrap(); - let txid = match node_a.unified_qr_payment().send(&uri_str_without_lightning) { - Ok(QrPaymentResult::Bolt12 { payment_id: _ }) => { + let uri_str_without_lightning = onchain_uni_payment.split("&lightning=").next().unwrap(); + let txid = match node_a.unified_payment().send(&uri_str_without_lightning) { + Ok(UnifiedPaymentResult::Bolt12 { payment_id: _ }) => { panic!("Expected on-chain payment but got Bolt12") }, - Ok(QrPaymentResult::Bolt11 { payment_id: _ }) => { + Ok(UnifiedPaymentResult::Bolt11 { payment_id: _ }) => { panic!("Expected on-chain payment but got Bolt11"); }, - Ok(QrPaymentResult::Onchain { txid }) => { + Ok(UnifiedPaymentResult::Onchain { txid }) => { println!("\nOn-chain transaction successful with Txid: {}", txid); txid }, From 23f28462d6ad0c348bd862f552c14c9786110f33 Mon Sep 17 00:00:00 2001 From: Chuks Agbakuru Date: Sat, 2 Aug 2025 17:42:22 +0100 Subject: [PATCH 03/13] add bitcoin-payment-instructions to cargo.toml --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 1d3f45bfa..18f42ad06 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -102,6 +102,7 @@ log = { version = "0.4.22", default-features = false, features = ["std"]} vss-client = "0.3" prost = { version = "0.11.6", default-features = false} +bitcoin-payment-instructions = { version = "0.5" } [target.'cfg(windows)'.dependencies] winapi = { version = "0.3", features = ["winbase"] } From 29ac03fcbe58361729bcda351cf12a49ac960845 Mon Sep 17 00:00:00 2001 From: Chuks Agbakuru Date: Mon, 11 Aug 2025 16:47:39 +0100 Subject: [PATCH 04/13] Add hrn_resolver to Node and pass into UnifiedPayment This commit adds a HRN Resolver to the Node struct which will be useful for resolving HRNs when making BIP 353 payments. It also passes the HRN Resolver into UnifiedPayment. --- src/builder.rs | 13 ++++++++++++- src/lib.rs | 6 +++++- src/payment/unified.rs | 19 +++++++++++++++---- src/types.rs | 6 +++++- 4 files changed, 37 insertions(+), 7 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index b99c44cec..becdd527d 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -80,6 +80,8 @@ use std::sync::{Arc, Mutex, Once, RwLock}; use std::time::SystemTime; use vss_client::headers::{FixedHeaders, LnurlAuthToJwtProvider, VssHeaderProvider}; +use bitcoin_payment_instructions::onion_message_resolver::LDKOnionMessageDNSSECHrnResolver; + const VSS_HARDENED_CHILD_INDEX: u32 = 877; const VSS_LNURL_AUTH_HARDENED_CHILD_INDEX: u32 = 138; const LSPS_HARDENED_CHILD_INDEX: u32 = 577; @@ -1451,6 +1453,8 @@ fn build_with_store_internal( })?; } + let hrn_resolver = Arc::new(LDKOnionMessageDNSSECHrnResolver::new(Arc::clone(&network_graph))); + // Initialize the PeerManager let onion_messenger: Arc = Arc::new(OnionMessenger::new( Arc::clone(&keys_manager), @@ -1460,7 +1464,7 @@ fn build_with_store_internal( message_router, Arc::clone(&channel_manager), Arc::clone(&channel_manager), - IgnoringMessageHandler {}, + Arc::clone(&hrn_resolver), IgnoringMessageHandler {}, )); let ephemeral_bytes: [u8; 32] = keys_manager.get_secure_random_bytes(); @@ -1588,6 +1592,12 @@ fn build_with_store_internal( Arc::clone(&keys_manager), )); + let peer_manager_clone = Arc::clone(&peer_manager); + + hrn_resolver.register_post_queue_action(Box::new(move || { + peer_manager_clone.process_events(); + })); + liquidity_source.as_ref().map(|l| l.set_peer_manager(Arc::clone(&peer_manager))); gossip_source.set_gossip_verifier( @@ -1681,6 +1691,7 @@ fn build_with_store_internal( is_running, is_listening, node_metrics, + hrn_resolver, }) } diff --git a/src/lib.rs b/src/lib.rs index 3242ad062..56e0a631f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -145,7 +145,8 @@ use peer_store::{PeerInfo, PeerStore}; use runtime::Runtime; use types::{ Broadcaster, BumpTransactionEventHandler, ChainMonitor, ChannelManager, DynStore, Graph, - KeysManager, OnionMessenger, PaymentStore, PeerManager, Router, Scorer, Sweeper, Wallet, + HRNResolver, KeysManager, OnionMessenger, PaymentStore, PeerManager, Router, Scorer, Sweeper, + Wallet, }; pub use types::{ChannelDetails, CustomTlvRecord, PeerDetails, UserChannelId}; @@ -205,6 +206,7 @@ pub struct Node { is_running: Arc>, is_listening: Arc, node_metrics: Arc>, + hrn_resolver: Arc, } impl Node { @@ -913,6 +915,7 @@ impl Node { self.bolt12_payment().into(), Arc::clone(&self.config), Arc::clone(&self.logger), + Arc::clone(&self.hrn_resolver), ) } @@ -933,6 +936,7 @@ impl Node { self.bolt12_payment(), Arc::clone(&self.config), Arc::clone(&self.logger), + Arc::clone(&self.hrn_resolver), )) } diff --git a/src/payment/unified.rs b/src/payment/unified.rs index 024373415..88ed6286f 100644 --- a/src/payment/unified.rs +++ b/src/payment/unified.rs @@ -5,16 +5,20 @@ // http://opensource.org/licenses/MIT>, at your option. You may not use this file except in // accordance with one or both of these licenses. -//! Holds a payment handler allowing to create [BIP 21] URIs with an on-chain, [BOLT 11], and [BOLT 12] payment +//! Holds a payment handler allowing to create [BIP 21] URIs with on-chain, [BOLT 11], and [BOLT 12] payment //! options. //! +//! Also allows to send payments using these URIs as well as [BIP 353] HRNs. +//! //! [BIP 21]: https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki +//! [BIP 353]: https://github.com/bitcoin/bips/blob/master/bip-0353.mediawiki //! [BOLT 11]: https://github.com/lightning/bolts/blob/master/11-payment-encoding.md //! [BOLT 12]: https://github.com/lightning/bolts/blob/master/12-offer-encoding.md use crate::error::Error; use crate::ffi::maybe_wrap; use crate::logger::{log_error, LdkLogger, Logger}; use crate::payment::{Bolt11Payment, Bolt12Payment, OnchainPayment}; +use crate::types::HRNResolver; use crate::Config; use lightning::ln::channelmanager::PaymentId; @@ -40,26 +44,31 @@ struct Extras { /// A payment handler allowing to create [BIP 21] URIs with an on-chain, [BOLT 11], and [BOLT 12] payment /// option. /// -/// Should be retrieved by calling [`Node::unified_qr_payment`] +/// Should be retrieved by calling [`Node::unified_payment`] +/// +/// This handler allows you to send payments to these URIs as well as [BIP 353] HRNs. /// /// [BIP 21]: https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki +/// [BIP 353]: https://github.com/bitcoin/bips/blob/master/bip-0353.mediawiki /// [BOLT 11]: https://github.com/lightning/bolts/blob/master/11-payment-encoding.md /// [BOLT 12]: https://github.com/lightning/bolts/blob/master/12-offer-encoding.md -/// [`Node::unified_qr_payment`]: crate::Node::unified_qr_payment +/// [`Node::unified_payment`]: crate::Node::unified_payment pub struct UnifiedPayment { onchain_payment: Arc, bolt11_invoice: Arc, bolt12_payment: Arc, config: Arc, logger: Arc, + hrn_resolver: Arc, } impl UnifiedPayment { pub(crate) fn new( onchain_payment: Arc, bolt11_invoice: Arc, bolt12_payment: Arc, config: Arc, logger: Arc, + hrn_resolver: Arc, ) -> Self { - Self { onchain_payment, bolt11_invoice, bolt12_payment, config, logger } + Self { onchain_payment, bolt11_invoice, bolt12_payment, config, logger, hrn_resolver } } /// Generates a URI with an on-chain address, [BOLT 11] invoice and [BOLT 12] offer. @@ -143,6 +152,8 @@ impl UnifiedPayment { let uri: bip21::Uri = uri_str.parse().map_err(|_| Error::InvalidUri)?; + let _resolver = &self.hrn_resolver; + let uri_network_checked = uri.clone().require_network(self.config.network).map_err(|_| Error::InvalidNetwork)?; diff --git a/src/types.rs b/src/types.rs index 3635badff..ff612e30d 100644 --- a/src/types.rs +++ b/src/types.rs @@ -39,6 +39,8 @@ use lightning_liquidity::utils::time::DefaultTimeProvider; use bitcoin::secp256k1::PublicKey; use bitcoin::OutPoint; +use bitcoin_payment_instructions::onion_message_resolver::LDKOnionMessageDNSSECHrnResolver; + use std::sync::{Arc, Mutex}; pub(crate) type DynStore = dyn KVStoreSync + Sync + Send; @@ -124,10 +126,12 @@ pub(crate) type OnionMessenger = lightning::onion_message::messenger::OnionMesse Arc, Arc, Arc, - IgnoringMessageHandler, + Arc, IgnoringMessageHandler, >; +pub(crate) type HRNResolver = LDKOnionMessageDNSSECHrnResolver, Arc>; + pub(crate) type MessageRouter = lightning::onion_message::messenger::DefaultMessageRouter< Arc, Arc, From 6546d48f81b58f98afca23e0945983d671ee42b8 Mon Sep 17 00:00:00 2001 From: Chuks Agbakuru Date: Mon, 11 Aug 2025 17:10:20 +0100 Subject: [PATCH 05/13] Refactor unified.rs to support sending to BIP 21 URIs as well as BIP 353 HRNs --- bindings/ldk_node.udl | 4 +- src/payment/unified.rs | 131 +++++++++++++++++++++++--------- tests/integration_tests_rust.rs | 10 +-- 3 files changed, 102 insertions(+), 43 deletions(-) diff --git a/bindings/ldk_node.udl b/bindings/ldk_node.udl index 5363b92ea..016ca249a 100644 --- a/bindings/ldk_node.udl +++ b/bindings/ldk_node.udl @@ -253,8 +253,8 @@ interface FeeRate { interface UnifiedPayment { [Throws=NodeError] string receive(u64 amount_sats, [ByRef]string message, u32 expiry_sec); - [Throws=NodeError] - UnifiedPaymentResult send([ByRef]string uri_str); + [Throws=NodeError, Async] + UnifiedPaymentResult send([ByRef]string uri_str, u64? amount_msat); }; interface LSPS1Liquidity { diff --git a/src/payment/unified.rs b/src/payment/unified.rs index 88ed6286f..05814e53c 100644 --- a/src/payment/unified.rs +++ b/src/payment/unified.rs @@ -27,8 +27,11 @@ use lightning_invoice::{Bolt11Invoice, Bolt11InvoiceDescription, Description}; use bip21::de::ParamKind; use bip21::{DeserializationError, DeserializeParams, Param, SerializeParams}; -use bitcoin::address::{NetworkChecked, NetworkUnchecked}; +use bitcoin::address::NetworkChecked; use bitcoin::{Amount, Txid}; +use bitcoin_payment_instructions::{ + amount::Amount as BPIAmount, PaymentInstructions, PaymentMethod, +}; use std::sync::Arc; use std::vec::IntoIter; @@ -138,56 +141,112 @@ impl UnifiedPayment { Ok(format_uri(uri)) } - /// Sends a payment given a [BIP 21] URI. + /// Sends a payment given a [BIP 21] URI or [BIP 353] HRN. /// /// This method parses the provided URI string and attempts to send the payment. If the URI /// has an offer and or invoice, it will try to pay the offer first followed by the invoice. /// If they both fail, the on-chain payment will be paid. /// - /// Returns a `QrPaymentResult` indicating the outcome of the payment. If an error + /// Returns a `UnifiedPaymentResult` indicating the outcome of the payment. If an error /// occurs, an `Error` is returned detailing the issue encountered. /// /// [BIP 21]: https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki - pub fn send(&self, uri_str: &str) -> Result { - let uri: bip21::Uri = - uri_str.parse().map_err(|_| Error::InvalidUri)?; - - let _resolver = &self.hrn_resolver; + /// [BIP 353]: https://github.com/bitcoin/bips/blob/master/bip-0353.mediawiki + pub async fn send( + &self, uri_str: &str, amount_msat: Option, + ) -> Result { + let instructions = PaymentInstructions::parse( + uri_str, + self.config.network, + self.hrn_resolver.as_ref(), + false, + ) + .await + .map_err(|e| { + log_error!(self.logger, "Failed to parse payment instructions: {:?}", e); + Error::UriParameterParsingFailed + })?; + + let resolved = match instructions { + PaymentInstructions::ConfigurableAmount(instr) => { + let amount = amount_msat.ok_or_else(|| { + log_error!(self.logger, "No amount specified. Aborting the payment."); + Error::InvalidAmount + })?; + + let amt = BPIAmount::from_milli_sats(amount).map_err(|e| { + log_error!(self.logger, "Error while converting amount : {:?}", e); + Error::InvalidAmount + })?; + + instr.set_amount(amt, self.hrn_resolver.as_ref()).await.map_err(|e| { + log_error!(self.logger, "Failed to set amount: {:?}", e); + Error::InvalidAmount + })? + }, + PaymentInstructions::FixedAmount(instr) => { + if let Some(user_amount) = amount_msat { + if instr.max_amount().map_or(false, |amt| user_amount < amt.milli_sats()) { + log_error!(self.logger, "Amount specified is less than the amount in the parsed URI. Aborting the payment."); + return Err(Error::InvalidAmount); + } + } + instr + }, + }; - let uri_network_checked = - uri.clone().require_network(self.config.network).map_err(|_| Error::InvalidNetwork)?; + if let Some(PaymentMethod::LightningBolt12(offer)) = + resolved.methods().iter().find(|m| matches!(m, PaymentMethod::LightningBolt12(_))) + { + let offer = maybe_wrap(offer.clone()); + let payment_result = if let Some(amount_msat) = amount_msat { + self.bolt12_payment.send_using_amount(&offer, amount_msat, None, None) + } else { + self.bolt12_payment.send(&offer, None, None) + } + .map_err(|e| { + log_error!(self.logger, "Failed to send BOLT12 offer: {:?}. This is part of a unified payment. Falling back to the BOLT11 invoice.", e); + e + }); - if let Some(offer) = uri_network_checked.extras.bolt12_offer { - let offer = maybe_wrap(offer); - match self.bolt12_payment.send(&offer, None, None) { - Ok(payment_id) => return Ok(UnifiedPaymentResult::Bolt12 { payment_id }), - Err(e) => log_error!(self.logger, "Failed to send BOLT12 offer: {:?}. This is part of a unified QR code payment. Falling back to the BOLT11 invoice.", e), + if let Ok(payment_id) = payment_result { + return Ok(UnifiedPaymentResult::Bolt12 { payment_id }); } } - if let Some(invoice) = uri_network_checked.extras.bolt11_invoice { - let invoice = maybe_wrap(invoice); - match self.bolt11_invoice.send(&invoice, None) { - Ok(payment_id) => return Ok(UnifiedPaymentResult::Bolt11 { payment_id }), - Err(e) => log_error!(self.logger, "Failed to send BOLT11 invoice: {:?}. This is part of a unified QR code payment. Falling back to the on-chain transaction.", e), + if let Some(PaymentMethod::LightningBolt11(invoice)) = + resolved.methods().iter().find(|m| matches!(m, PaymentMethod::LightningBolt11(_))) + { + let invoice = maybe_wrap(invoice.clone()); + let payment_result = self.bolt11_invoice.send(&invoice, None) + .map_err(|e| { + log_error!(self.logger, "Failed to send BOLT11 invoice: {:?}. This is part of a unified payment. Falling back to the on-chain transaction.", e); + e + }); + + if let Ok(payment_id) = payment_result { + return Ok(UnifiedPaymentResult::Bolt11 { payment_id }); } } - let amount = match uri_network_checked.amount { - Some(amount) => amount, - None => { - log_error!(self.logger, "No amount specified in the URI. Aborting the payment."); - return Err(Error::InvalidAmount); - }, - }; - - let txid = self.onchain_payment.send_to_address( - &uri_network_checked.address, - amount.to_sat(), - None, - )?; - - Ok(UnifiedPaymentResult::Onchain { txid }) + if let Some(PaymentMethod::OnChain(address)) = + resolved.methods().iter().find(|m| matches!(m, PaymentMethod::OnChain(_))) + { + let amount = resolved.onchain_payment_amount().ok_or_else(|| { + log_error!(self.logger, "No amount specified. Aborting the payment."); + Error::InvalidAmount + })?; + + let amt_sats = amount.sats().map_err(|_| { + log_error!(self.logger, "Amount in sats returned an error. Aborting the payment."); + Error::InvalidAmount + })?; + + let txid = self.onchain_payment.send_to_address(&address, amt_sats, None)?; + return Ok(UnifiedPaymentResult::Onchain { txid }); + } + log_error!(self.logger, "Payable methods not found in URI"); + Err(Error::PaymentSendingFailed) } } @@ -316,7 +375,7 @@ impl DeserializationError for Extras { mod tests { use super::*; use crate::payment::unified::Extras; - use bitcoin::{Address, Network}; + use bitcoin::{address::NetworkUnchecked, Address, Network}; use std::str::FromStr; #[test] diff --git a/tests/integration_tests_rust.rs b/tests/integration_tests_rust.rs index 9f8ff58b9..88329b23d 100644 --- a/tests/integration_tests_rust.rs +++ b/tests/integration_tests_rust.rs @@ -1392,8 +1392,8 @@ fn generate_bip21_uri() { assert!(uni_payment.contains("lno=")); } -#[test] -fn unified_qr_send_receive() { +#[tokio::test(flavor = "multi_thread")] +async fn unified_qr_send_receive() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = TestChainSource::Esplora(&electrsd); @@ -1432,7 +1432,7 @@ fn unified_qr_send_receive() { let uni_payment = node_b.unified_payment().receive(expected_amount_sats, "asdf", expiry_sec); let uri_str = uni_payment.clone().unwrap(); - let offer_payment_id: PaymentId = match node_a.unified_payment().send(&uri_str) { + let offer_payment_id: PaymentId = match node_a.unified_payment().send(&uri_str, None).await { Ok(UnifiedPaymentResult::Bolt12 { payment_id }) => { println!("\nBolt12 payment sent successfully with PaymentID: {:?}", payment_id); payment_id @@ -1453,7 +1453,7 @@ fn unified_qr_send_receive() { // Cut off the BOLT12 part to fallback to BOLT11. let uri_str_without_offer = uri_str.split("&lno=").next().unwrap(); let invoice_payment_id: PaymentId = - match node_a.unified_payment().send(uri_str_without_offer) { + match node_a.unified_payment().send(uri_str_without_offer, None).await { Ok(UnifiedPaymentResult::Bolt12 { payment_id: _ }) => { panic!("Expected Bolt11 payment but got Bolt12"); }, @@ -1476,7 +1476,7 @@ fn unified_qr_send_receive() { // Cut off any lightning part to fallback to on-chain only. let uri_str_without_lightning = onchain_uni_payment.split("&lightning=").next().unwrap(); - let txid = match node_a.unified_payment().send(&uri_str_without_lightning) { + let txid = match node_a.unified_payment().send(&uri_str_without_lightning, None).await { Ok(UnifiedPaymentResult::Bolt12 { payment_id: _ }) => { panic!("Expected on-chain payment but got Bolt12") }, From 2362714c5f070d783a0a04af477338ee1cd76a6f Mon Sep 17 00:00:00 2001 From: Chuks Agbakuru Date: Mon, 11 Aug 2025 17:19:05 +0100 Subject: [PATCH 06/13] Fix typo in unified_payment send test and improve test name. --- tests/integration_tests_rust.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/integration_tests_rust.rs b/tests/integration_tests_rust.rs index 88329b23d..fe5860aab 100644 --- a/tests/integration_tests_rust.rs +++ b/tests/integration_tests_rust.rs @@ -1393,7 +1393,7 @@ fn generate_bip21_uri() { } #[tokio::test(flavor = "multi_thread")] -async fn unified_qr_send_receive() { +async fn unified_send_receive_qr_uri() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let chain_source = TestChainSource::Esplora(&electrsd); @@ -1441,7 +1441,7 @@ async fn unified_qr_send_receive() { panic!("Expected Bolt12 payment but got Bolt11"); }, Ok(UnifiedPaymentResult::Onchain { txid: _ }) => { - panic!("Expected Bolt12 payment but get On-chain transaction"); + panic!("Expected Bolt12 payment but got On-chain transaction"); }, Err(e) => { panic!("Expected Bolt12 payment but got error: {:?}", e); From cc94f39ca9f4749af7f5a55ebdc596197a4d2c1e Mon Sep 17 00:00:00 2001 From: Chuks Agbakuru Date: Thu, 21 Aug 2025 12:47:46 +0100 Subject: [PATCH 07/13] Switch to explicit imports in unified.rs mod tests --- src/payment/unified.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/payment/unified.rs b/src/payment/unified.rs index 05814e53c..6bf39a6ca 100644 --- a/src/payment/unified.rs +++ b/src/payment/unified.rs @@ -373,8 +373,7 @@ impl DeserializationError for Extras { #[cfg(test)] mod tests { - use super::*; - use crate::payment::unified::Extras; + use super::{Amount, Bolt11Invoice, Extras, Offer}; use bitcoin::{address::NetworkUnchecked, Address, Network}; use std::str::FromStr; From 71624c53145f94400a8dabcd1988fca3870b746a Mon Sep 17 00:00:00 2001 From: Chuks Agbakuru Date: Tue, 23 Sep 2025 10:17:18 +0100 Subject: [PATCH 08/13] Use the pay_for_offer_from_hrn method from LDK upstream This commit ensures that when using the unified API to send to a HRN, we use pay_for_offer_from_hrn --- bindings/ldk_node.udl | 10 +++++++- src/error.rs | 5 ++++ src/ffi/types.rs | 53 ++++++++++++++++++++++++++++++++++++++++++ src/payment/bolt12.rs | 14 +++++++++-- src/payment/unified.rs | 9 +++++-- 5 files changed, 86 insertions(+), 5 deletions(-) diff --git a/bindings/ldk_node.udl b/bindings/ldk_node.udl index 016ca249a..d62d84276 100644 --- a/bindings/ldk_node.udl +++ b/bindings/ldk_node.udl @@ -201,7 +201,7 @@ interface Bolt12Payment { [Throws=NodeError] PaymentId send([ByRef]Offer offer, u64? quantity, string? payer_note); [Throws=NodeError] - PaymentId send_using_amount([ByRef]Offer offer, u64 amount_msat, u64? quantity, string? payer_note); + PaymentId send_using_amount([ByRef]Offer offer, u64 amount_msat, u64? quantity, string? payer_note, HumanReadableName? hrn); [Throws=NodeError] Offer receive(u64 amount_msat, [ByRef]string description, u32? expiry_secs, u64? quantity); [Throws=NodeError] @@ -320,6 +320,7 @@ enum NodeError { "LiquidityFeeTooHigh", "InvalidBlindedPaths", "AsyncPaymentServicesDisabled", + "HrnParsingFailed", }; dictionary NodeStatus { @@ -775,6 +776,13 @@ interface Offer { PublicKey? issuer_signing_pubkey(); }; +interface HumanReadableName { + [Throws=NodeError, Name=from_encoded] + constructor([ByRef] string encoded); + string user(); + string domain(); +}; + [Traits=(Debug, Display, Eq)] interface Refund { [Throws=NodeError, Name=from_str] diff --git a/src/error.rs b/src/error.rs index eaa022e56..b2f3cd7d0 100644 --- a/src/error.rs +++ b/src/error.rs @@ -124,6 +124,8 @@ pub enum Error { InvalidBlindedPaths, /// Asynchronous payment services are disabled. AsyncPaymentServicesDisabled, + /// Parsing a Human-Readable Name has failed. + HrnParsingFailed, } impl fmt::Display for Error { @@ -201,6 +203,9 @@ impl fmt::Display for Error { Self::AsyncPaymentServicesDisabled => { write!(f, "Asynchronous payment services are disabled.") }, + Self::HrnParsingFailed => { + write!(f, "Failed to parse a human-readable name.") + }, } } } diff --git a/src/ffi/types.rs b/src/ffi/types.rs index 185539a1f..478f4e583 100644 --- a/src/ffi/types.rs +++ b/src/ffi/types.rs @@ -28,6 +28,7 @@ pub use lightning::ln::types::ChannelId; pub use lightning::offers::offer::OfferId; pub use lightning::routing::gossip::{NodeAlias, NodeId, RoutingFees}; pub use lightning::routing::router::RouteParametersConfig; +pub use lightning::onion_message::dns_resolution::HumanReadableName as LdkHumanReadableName; pub use lightning_types::string::UntrustedString; pub use lightning_types::payment::{PaymentHash, PaymentPreimage, PaymentSecret}; @@ -278,6 +279,58 @@ impl std::fmt::Display for Offer { } } +pub struct HumanReadableName { + pub(crate) inner: LdkHumanReadableName, +} + +impl HumanReadableName { + pub fn into_inner(&self) -> LdkHumanReadableName { + self.inner.clone() + } + + pub fn from_encoded(encoded: &str) -> Result { + let hrn = match LdkHumanReadableName::from_encoded(encoded) { + Ok(hrn) => Ok(hrn), + Err(_) => Err(Error::HrnParsingFailed), + }?; + + Ok(Self { inner: hrn }) + } + + pub fn user(&self) -> String { + self.inner.user().to_string() + } + + pub fn domain(&self) -> String { + self.inner.domain().to_string() + } +} + +impl From for HumanReadableName { + fn from(ldk_hrn: LdkHumanReadableName) -> Self { + HumanReadableName { inner: ldk_hrn } + } +} + +impl From for LdkHumanReadableName { + fn from(wrapper: HumanReadableName) -> Self { + wrapper.into_inner() + } +} + +impl Deref for HumanReadableName { + type Target = LdkHumanReadableName; + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl AsRef for HumanReadableName { + fn as_ref(&self) -> &LdkHumanReadableName { + self.deref() + } +} + /// A `Refund` is a request to send an [`Bolt12Invoice`] without a preceding [`Offer`]. /// /// Typically, after an invoice is paid, the recipient may publish a refund allowing the sender to diff --git a/src/payment/bolt12.rs b/src/payment/bolt12.rs index 601c03d7d..4be2ab3cd 100644 --- a/src/payment/bolt12.rs +++ b/src/payment/bolt12.rs @@ -18,7 +18,7 @@ use crate::types::{ChannelManager, PaymentStore}; use lightning::blinded_path::message::BlindedMessagePath; use lightning::ln::channelmanager::{OptionalOfferPaymentParams, PaymentId, Retry}; -use lightning::offers::offer::{Amount, Offer as LdkOffer, Quantity}; +use lightning::offers::offer::{Amount, Offer as LdkOffer, OfferFromHrn, Quantity}; use lightning::offers::parse::Bolt12SemanticError; use lightning::routing::router::RouteParametersConfig; @@ -47,6 +47,11 @@ type Refund = lightning::offers::refund::Refund; #[cfg(feature = "uniffi")] type Refund = Arc; +#[cfg(not(feature = "uniffi"))] +type HumanReadableName = lightning::onion_message::dns_resolution::HumanReadableName; +#[cfg(feature = "uniffi")] +type HumanReadableName = Arc; + /// A payment handler allowing to create and pay [BOLT 12] offers and refunds. /// /// Should be retrieved by calling [`Node::bolt12_payment`]. @@ -184,6 +189,7 @@ impl Bolt12Payment { /// response. pub fn send_using_amount( &self, offer: &Offer, amount_msat: u64, quantity: Option, payer_note: Option, + hrn: Option, ) -> Result { if !*self.is_running.read().unwrap() { return Err(Error::NotRunning); @@ -218,7 +224,11 @@ impl Bolt12Payment { retry_strategy, route_params_config, }; - let res = if let Some(quantity) = quantity { + let res = if let Some(hrn) = hrn { + let hrn = maybe_deref(&hrn); + let offer = OfferFromHrn { offer: offer.clone(), hrn: *hrn }; + self.channel_manager.pay_for_offer_from_hrn(&offer, amount_msat, payment_id, params) + } else if let Some(quantity) = quantity { self.channel_manager.pay_for_offer_with_quantity( &offer, Some(amount_msat), diff --git a/src/payment/unified.rs b/src/payment/unified.rs index 6bf39a6ca..06c4ec083 100644 --- a/src/payment/unified.rs +++ b/src/payment/unified.rs @@ -23,6 +23,7 @@ use crate::Config; use lightning::ln::channelmanager::PaymentId; use lightning::offers::offer::Offer; +use lightning::onion_message::dns_resolution::HumanReadableName; use lightning_invoice::{Bolt11Invoice, Bolt11InvoiceDescription, Description}; use bip21::de::ParamKind; @@ -199,8 +200,12 @@ impl UnifiedPayment { resolved.methods().iter().find(|m| matches!(m, PaymentMethod::LightningBolt12(_))) { let offer = maybe_wrap(offer.clone()); - let payment_result = if let Some(amount_msat) = amount_msat { - self.bolt12_payment.send_using_amount(&offer, amount_msat, None, None) + + let payment_result = if let Ok(hrn) = HumanReadableName::from_encoded(uri_str) { + let hrn = maybe_wrap(hrn.clone()); + self.bolt12_payment.send_using_amount(&offer, amount_msat.unwrap_or(0), None, None, Some(hrn)) + } else if let Some(amount_msat) = amount_msat { + self.bolt12_payment.send_using_amount(&offer, amount_msat, None, None, None) } else { self.bolt12_payment.send(&offer, None, None) } From 88df49324c786c0b5e1ecd097d6b0fff49388896 Mon Sep 17 00:00:00 2001 From: Chuks Agbakuru Date: Tue, 23 Sep 2025 10:24:37 +0100 Subject: [PATCH 09/13] fixup! add bitcoin-payment-instructions to cargo.toml This fixup points bitcoin-payment-instructions to my fork allowing me to bump some lightning dependencies in order to resolve dep conflicts here. --- Cargo.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 18f42ad06..cb8746d68 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -102,7 +102,8 @@ log = { version = "0.4.22", default-features = false, features = ["std"]} vss-client = "0.3" prost = { version = "0.11.6", default-features = false} -bitcoin-payment-instructions = { version = "0.5" } +#bitcoin-payment-instructions = { version = "0.5" } +bitcoin-payment-instructions = { git = "https://github.com/chuksys/bitcoin-payment-instructions", branch = "bump-lightning" } [target.'cfg(windows)'.dependencies] winapi = { version = "0.3", features = ["winbase"] } From f45de4a30d153f59c84126c2116847c10480191b Mon Sep 17 00:00:00 2001 From: Chuks Agbakuru Date: Tue, 23 Sep 2025 10:31:54 +0100 Subject: [PATCH 10/13] fixup! rename UnifiedQRPayment to UnifiedPayment, rename QRPaymentResult to UnifiedPaymentResult --- src/payment/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/payment/mod.rs b/src/payment/mod.rs index 3d21a0bae..c82f35c8f 100644 --- a/src/payment/mod.rs +++ b/src/payment/mod.rs @@ -22,4 +22,4 @@ pub use spontaneous::SpontaneousPayment; pub use store::{ ConfirmationStatus, LSPFeeLimits, PaymentDetails, PaymentDirection, PaymentKind, PaymentStatus, }; -pub use unified::{UnifiedPayment, UnifiedPaymentResult}; \ No newline at end of file +pub use unified::{UnifiedPayment, UnifiedPaymentResult}; From 68c33b764185db56182ea5cbba4f0f986385ed91 Mon Sep 17 00:00:00 2001 From: Chuks Agbakuru Date: Tue, 23 Sep 2025 10:42:27 +0100 Subject: [PATCH 11/13] fixup! Use the pay_for_offer_from_hrn method from LDK upstream Just fixing some formatting issues. --- src/ffi/types.rs | 2 +- src/payment/unified.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ffi/types.rs b/src/ffi/types.rs index 478f4e583..8d3a4c338 100644 --- a/src/ffi/types.rs +++ b/src/ffi/types.rs @@ -26,9 +26,9 @@ pub use lightning::chain::channelmonitor::BalanceSource; pub use lightning::events::{ClosureReason, PaymentFailureReason}; pub use lightning::ln::types::ChannelId; pub use lightning::offers::offer::OfferId; +pub use lightning::onion_message::dns_resolution::HumanReadableName as LdkHumanReadableName; pub use lightning::routing::gossip::{NodeAlias, NodeId, RoutingFees}; pub use lightning::routing::router::RouteParametersConfig; -pub use lightning::onion_message::dns_resolution::HumanReadableName as LdkHumanReadableName; pub use lightning_types::string::UntrustedString; pub use lightning_types::payment::{PaymentHash, PaymentPreimage, PaymentSecret}; diff --git a/src/payment/unified.rs b/src/payment/unified.rs index 06c4ec083..ded8cb18e 100644 --- a/src/payment/unified.rs +++ b/src/payment/unified.rs @@ -200,7 +200,7 @@ impl UnifiedPayment { resolved.methods().iter().find(|m| matches!(m, PaymentMethod::LightningBolt12(_))) { let offer = maybe_wrap(offer.clone()); - + let payment_result = if let Ok(hrn) = HumanReadableName::from_encoded(uri_str) { let hrn = maybe_wrap(hrn.clone()); self.bolt12_payment.send_using_amount(&offer, amount_msat.unwrap_or(0), None, None, Some(hrn)) From d32e01241ae6a719ec40a344a338f3d705623d27 Mon Sep 17 00:00:00 2001 From: Chuks Agbakuru Date: Tue, 23 Sep 2025 11:29:35 +0100 Subject: [PATCH 12/13] fixup! Use the pay_for_offer_from_hrn method from LDK upstream This fixup commit simply fixes issues with the bolt12 send_using_amount method signature in the tests. --- tests/integration_tests_rust.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/integration_tests_rust.rs b/tests/integration_tests_rust.rs index fe5860aab..111134d37 100644 --- a/tests/integration_tests_rust.rs +++ b/tests/integration_tests_rust.rs @@ -1007,7 +1007,7 @@ fn simple_bolt12_send_receive() { let expected_payer_note = Some("Test".to_string()); assert!(node_a .bolt12_payment() - .send_using_amount(&offer, less_than_offer_amount, None, None) + .send_using_amount(&offer, less_than_offer_amount, None, None, None) .is_err()); let payment_id = node_a .bolt12_payment() @@ -1016,6 +1016,7 @@ fn simple_bolt12_send_receive() { expected_amount_msat, expected_quantity, expected_payer_note.clone(), + None ) .unwrap(); @@ -1242,7 +1243,7 @@ fn static_invoice_server() { }; let payment_id = - node_sender.bolt12_payment().send_using_amount(&offer, 5_000, None, None).unwrap(); + node_sender.bolt12_payment().send_using_amount(&offer, 5_000, None, None, None).unwrap(); expect_payment_successful_event!(node_sender, Some(payment_id), None); } From 5c170c1f376f80b903984d795e7b5fc52bff82f4 Mon Sep 17 00:00:00 2001 From: Chuks Agbakuru Date: Tue, 23 Sep 2025 11:32:45 +0100 Subject: [PATCH 13/13] fixup! Use the pay_for_offer_from_hrn method from LDK upstream Just fixing a simple formatting issue. --- tests/integration_tests_rust.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration_tests_rust.rs b/tests/integration_tests_rust.rs index 111134d37..581decedd 100644 --- a/tests/integration_tests_rust.rs +++ b/tests/integration_tests_rust.rs @@ -1016,7 +1016,7 @@ fn simple_bolt12_send_receive() { expected_amount_msat, expected_quantity, expected_payer_note.clone(), - None + None, ) .unwrap();