From 67a784041ef5139c64c120996c114aa7127e7258 Mon Sep 17 00:00:00 2001 From: fmar Date: Mon, 8 Jun 2026 13:53:11 +0200 Subject: [PATCH 1/2] feat: expose lsps2 liquidity opening params --- bindings/ldk_node.udl | 22 ++++++++++ src/ffi/types.rs | 3 +- src/lib.rs | 29 ++++++++++++- src/liquidity.rs | 94 +++++++++++++++++++++++++++++++++++++++---- 4 files changed, 138 insertions(+), 10 deletions(-) diff --git a/bindings/ldk_node.udl b/bindings/ldk_node.udl index 33c9d98c83..348a3f179a 100644 --- a/bindings/ldk_node.udl +++ b/bindings/ldk_node.udl @@ -1,6 +1,7 @@ namespace ldk_node { Mnemonic generate_entropy_mnemonic(WordCount? word_count); Config default_config(); + u64? lsps2_compute_opening_fee_msat(u64 payment_size_msat, LSPS2OpeningFeeParams opening_fee_params); }; dictionary Config { @@ -145,6 +146,7 @@ interface Node { OnchainPayment onchain_payment(); UnifiedQrPayment unified_qr_payment(); LSPS1Liquidity lsps1_liquidity(); + LSPS2Liquidity lsps2_liquidity(); [Throws=NodeError] void connect(PublicKey node_id, SocketAddress address, boolean persist); [Throws=NodeError] @@ -293,6 +295,26 @@ interface LSPS1Liquidity { LSPS1OrderStatus check_order_status(LSPS1OrderId order_id); }; +interface LSPS2Liquidity { + [Throws=NodeError] + LSPS2GetInfoResponse request_opening_fee_params(); +}; + +dictionary LSPS2OpeningFeeParams { + u64 min_fee_msat; + u32 proportional; + LSPSDateTime valid_until; + u32 min_lifetime; + u32 max_client_to_self_delay; + u64 min_payment_size_msat; + u64 max_payment_size_msat; + string promise; +}; + +dictionary LSPS2GetInfoResponse { + sequence opening_fee_params_menu; +}; + [Error] enum NodeError { "AlreadyRunning", diff --git a/src/ffi/types.rs b/src/ffi/types.rs index 3c88a665fe..54ac1a6dc4 100644 --- a/src/ffi/types.rs +++ b/src/ffi/types.rs @@ -38,6 +38,7 @@ pub use lightning_liquidity::lsps0::ser::LSPSDateTime; pub use lightning_liquidity::lsps1::msgs::{ LSPS1ChannelInfo, LSPS1OrderId, LSPS1OrderParams, LSPS1PaymentState, }; +pub use lightning_liquidity::lsps2::msgs::LSPS2OpeningFeeParams; pub use lightning_types::payment::{PaymentHash, PaymentPreimage, PaymentSecret}; pub use lightning_types::string::UntrustedString; pub use vss_client::headers::{VssHeaderProvider, VssHeaderProviderError}; @@ -49,7 +50,7 @@ pub use crate::config::{ }; use crate::error::Error; pub use crate::graph::{ChannelInfo, ChannelUpdateInfo, NodeAnnouncementInfo, NodeInfo}; -pub use crate::liquidity::{LSPS1OrderStatus, LSPS2ServiceConfig}; +pub use crate::liquidity::{LSPS1OrderStatus, LSPS2GetInfoResponse, LSPS2ServiceConfig}; pub use crate::logger::{LogLevel, LogRecord, LogWriter}; pub use crate::payment::store::{ ConfirmationStatus, LSPFeeLimits, PaymentDirection, PaymentKind, PaymentStatus, diff --git a/src/lib.rs b/src/lib.rs index 254788aa8e..9f40deeabb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -132,6 +132,7 @@ use ffi::*; use gossip::GossipSource; use graph::NetworkGraph; pub use io::utils::generate_entropy_mnemonic; +pub use liquidity::lsps2_compute_opening_fee_msat; use io::utils::write_node_metrics; use lightning::chain::BestBlock; use lightning::events::bump_transaction::{Input, Wallet as LdkWallet}; @@ -144,7 +145,7 @@ use lightning::ln::msgs::SocketAddress; use lightning::routing::gossip::NodeAlias; use lightning::util::persist::KVStoreSync; use lightning_background_processor::process_events_async; -use liquidity::{LSPS1Liquidity, LiquiditySource}; +use liquidity::{LSPS1Liquidity, LSPS2Liquidity, LiquiditySource}; use logger::{log_debug, log_error, log_info, log_trace, LdkLogger, Logger}; use payment::asynchronous::om_mailbox::OnionMessageMailbox; use payment::asynchronous::static_invoice_store::StaticInvoiceStore; @@ -1045,6 +1046,32 @@ impl Node { )) } + /// Returns a liquidity handler allowing to query [bLIP-52 / LSPS2] JIT channel parameters. + /// + /// [bLIP-52 / LSPS2]: https://github.com/lightning/blips/blob/master/blip-0052.md + #[cfg(not(feature = "uniffi"))] + pub fn lsps2_liquidity(&self) -> LSPS2Liquidity { + LSPS2Liquidity::new( + Arc::clone(&self.runtime), + Arc::clone(&self.connection_manager), + self.liquidity_source.clone(), + Arc::clone(&self.logger), + ) + } + + /// Returns a liquidity handler allowing to query [bLIP-52 / LSPS2] JIT channel parameters. + /// + /// [bLIP-52 / LSPS2]: https://github.com/lightning/blips/blob/master/blip-0052.md + #[cfg(feature = "uniffi")] + pub fn lsps2_liquidity(&self) -> Arc { + Arc::new(LSPS2Liquidity::new( + Arc::clone(&self.runtime), + Arc::clone(&self.connection_manager), + self.liquidity_source.clone(), + Arc::clone(&self.logger), + )) + } + /// Retrieve a list of known channels. pub fn list_channels(&self) -> Vec { self.channel_manager.list_channels().into_iter().map(|c| c.into()).collect() diff --git a/src/liquidity.rs b/src/liquidity.rs index 74e6098ddc..1143491e1f 100644 --- a/src/liquidity.rs +++ b/src/liquidity.rs @@ -78,7 +78,7 @@ struct LSPS2Client { lsp_address: SocketAddress, token: Option, ldk_client_config: LdkLSPS2ClientConfig, - pending_fee_requests: Mutex>>, + pending_fee_requests: Mutex>>, pending_buy_requests: Mutex>>, } @@ -829,7 +829,7 @@ where if let Some(sender) = lsps2_client.pending_fee_requests.lock().unwrap().remove(&request_id) { - let response = LSPS2FeeResponse { opening_fee_params_menu }; + let response = LSPS2GetInfoResponse { opening_fee_params_menu }; match sender.send(response) { Ok(()) => (), @@ -1189,7 +1189,7 @@ where Ok((invoice, min_prop_fee_ppm_msat)) } - async fn lsps2_request_opening_fee_params(&self) -> Result { + async fn lsps2_request_opening_fee_params(&self) -> Result { let lsps2_client = self.lsps2_client.as_ref().ok_or(Error::LiquiditySourceUnavailable)?; let client_handler = self.liquidity_manager.lsps2_client_handler().ok_or_else(|| { @@ -1431,9 +1431,15 @@ type LSPS1PaymentInfo = lightning_liquidity::lsps1::msgs::LSPS1PaymentInfo; #[cfg(feature = "uniffi")] type LSPS1PaymentInfo = crate::ffi::LSPS1PaymentInfo; +/// Response to an LSPS2 `get_info` request. +/// +/// See [bLIP-52 / LSPS2] for more information. +/// +/// [bLIP-52 / LSPS2]: https://github.com/lightning/blips/blob/master/blip-0052.md #[derive(Debug, Clone)] -pub(crate) struct LSPS2FeeResponse { - opening_fee_params_menu: Vec, +pub struct LSPS2GetInfoResponse { + /// The set of opening fee parameters offered by the LSP. + pub opening_fee_params_menu: Vec, } #[derive(Debug, Clone)] @@ -1446,13 +1452,13 @@ pub(crate) struct LSPS2BuyResponse { /// /// Should be retrieved by calling [`Node::lsps1_liquidity`]. /// -/// To open [bLIP-52 / LSPS2] JIT channels, please refer to -/// [`Bolt11Payment::receive_via_jit_channel`]. +/// To query [bLIP-52 / LSPS2] JIT channel fees and limits, please refer to +/// [`Node::lsps2_liquidity`]. /// /// [bLIP-51 / LSPS1]: https://github.com/lightning/blips/blob/master/blip-0051.md /// [bLIP-52 / LSPS2]: https://github.com/lightning/blips/blob/master/blip-0052.md /// [`Node::lsps1_liquidity`]: crate::Node::lsps1_liquidity -/// [`Bolt11Payment::receive_via_jit_channel`]: crate::payment::Bolt11Payment::receive_via_jit_channel +/// [`Node::lsps2_liquidity`]: crate::Node::lsps2_liquidity #[derive(Clone)] pub struct LSPS1Liquidity { runtime: Arc, @@ -1540,3 +1546,75 @@ impl LSPS1Liquidity { Ok(response) } } + +/// A liquidity handler allowing to query [bLIP-52 / LSPS2] JIT channel parameters. +/// +/// Should be retrieved by calling [`Node::lsps2_liquidity`]. +/// +/// To open [bLIP-52 / LSPS2] JIT channels, please refer to +/// [`Bolt11Payment::receive_via_jit_channel`]. +/// +/// [bLIP-52 / LSPS2]: https://github.com/lightning/blips/blob/master/blip-0052.md +/// [`Node::lsps2_liquidity`]: crate::Node::lsps2_liquidity +/// [`Bolt11Payment::receive_via_jit_channel`]: crate::payment::Bolt11Payment::receive_via_jit_channel +#[derive(Clone)] +pub struct LSPS2Liquidity { + runtime: Arc, + connection_manager: Arc>>, + liquidity_source: Option>>>, + logger: Arc, +} + +impl LSPS2Liquidity { + pub(crate) fn new( + runtime: Arc, + connection_manager: Arc>>, + liquidity_source: Option>>>, + logger: Arc, + ) -> Self { + Self { runtime, connection_manager, liquidity_source, logger } + } + + /// Connects to the configured LSP and requests its current JIT channel opening fee parameters. + /// + /// This issues an `lsps2.get_info` request and returns the LSP's `opening_fee_params_menu`. + pub fn request_opening_fee_params(&self) -> Result { + let liquidity_source = + self.liquidity_source.as_ref().ok_or(Error::LiquiditySourceUnavailable)?; + + let (lsp_node_id, lsp_address) = + liquidity_source.get_lsps2_lsp_details().ok_or(Error::LiquiditySourceUnavailable)?; + + let con_node_id = lsp_node_id; + let con_addr = lsp_address.clone(); + let con_cm = Arc::clone(&self.connection_manager); + + // We need to use our main runtime here as a local runtime might not be around to poll + // connection futures going forward. + self.runtime.block_on(async move { + con_cm.connect_peer_if_necessary(con_node_id, con_addr).await + })?; + + log_info!(self.logger, "Connected to LSPS2 LSP {}@{}. ", lsp_node_id, lsp_address); + + let liquidity_source = Arc::clone(&liquidity_source); + self.runtime.block_on(async move { + liquidity_source.lsps2_request_opening_fee_params().await + }) + } +} + +/// Computes the LSPS2 opening fee for a given payment size and fee parameters. +/// +/// See [bLIP-52 / LSPS2] for more information. +/// +/// [bLIP-52 / LSPS2]: https://github.com/lightning/blips/blob/master/blip-0052.md#computing-the-opening_fee +pub fn lsps2_compute_opening_fee_msat( + payment_size_msat: u64, opening_fee_params: LSPS2OpeningFeeParams, +) -> Option { + compute_opening_fee( + payment_size_msat, + opening_fee_params.min_fee_msat, + opening_fee_params.proportional as u64, + ) +} From 00e602fd648e6365bc3ce69262f210ebf6415a50 Mon Sep 17 00:00:00 2001 From: fmar Date: Mon, 8 Jun 2026 14:42:59 +0200 Subject: [PATCH 2/2] feat: add Alby comment descriptions --- bindings/ldk_node.udl | 4 ++++ src/ffi/types.rs | 2 ++ src/lib.rs | 3 +++ src/liquidity.rs | 10 ++++++++++ 4 files changed, 19 insertions(+) diff --git a/bindings/ldk_node.udl b/bindings/ldk_node.udl index 348a3f179a..75f67365b9 100644 --- a/bindings/ldk_node.udl +++ b/bindings/ldk_node.udl @@ -1,6 +1,7 @@ namespace ldk_node { Mnemonic generate_entropy_mnemonic(WordCount? word_count); Config default_config(); + // Alby: compute LSPS2 opening fees for an LSP-provided offer. u64? lsps2_compute_opening_fee_msat(u64 payment_size_msat, LSPS2OpeningFeeParams opening_fee_params); }; @@ -146,6 +147,7 @@ interface Node { OnchainPayment onchain_payment(); UnifiedQrPayment unified_qr_payment(); LSPS1Liquidity lsps1_liquidity(); + // Alby: expose the LSPS2 opening fee menu so Hub can surface JIT limits to users. LSPS2Liquidity lsps2_liquidity(); [Throws=NodeError] void connect(PublicKey node_id, SocketAddress address, boolean persist); @@ -297,6 +299,7 @@ interface LSPS1Liquidity { interface LSPS2Liquidity { [Throws=NodeError] + // Alby: fetch the LSP's LSPS2 opening fee menu. LSPS2GetInfoResponse request_opening_fee_params(); }; @@ -312,6 +315,7 @@ dictionary LSPS2OpeningFeeParams { }; dictionary LSPS2GetInfoResponse { + // Alby: return the raw LSPS2 fee menu so clients can read min_payment_size_msat. sequence opening_fee_params_menu; }; diff --git a/src/ffi/types.rs b/src/ffi/types.rs index 54ac1a6dc4..bc11833a1b 100644 --- a/src/ffi/types.rs +++ b/src/ffi/types.rs @@ -38,6 +38,7 @@ pub use lightning_liquidity::lsps0::ser::LSPSDateTime; pub use lightning_liquidity::lsps1::msgs::{ LSPS1ChannelInfo, LSPS1OrderId, LSPS1OrderParams, LSPS1PaymentState, }; +// Alby: expose the LSPS2 opening fee menu item type over UniFFI. pub use lightning_liquidity::lsps2::msgs::LSPS2OpeningFeeParams; pub use lightning_types::payment::{PaymentHash, PaymentPreimage, PaymentSecret}; pub use lightning_types::string::UntrustedString; @@ -50,6 +51,7 @@ pub use crate::config::{ }; use crate::error::Error; pub use crate::graph::{ChannelInfo, ChannelUpdateInfo, NodeAnnouncementInfo, NodeInfo}; +// Alby: expose the LSPS2 get_info response over UniFFI. pub use crate::liquidity::{LSPS1OrderStatus, LSPS2GetInfoResponse, LSPS2ServiceConfig}; pub use crate::logger::{LogLevel, LogRecord, LogWriter}; pub use crate::payment::store::{ diff --git a/src/lib.rs b/src/lib.rs index 9f40deeabb..5f091ef27c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -132,6 +132,7 @@ use ffi::*; use gossip::GossipSource; use graph::NetworkGraph; pub use io::utils::generate_entropy_mnemonic; +// Alby: export LSPS2 opening fee computation for local fee estimation in Hub. pub use liquidity::lsps2_compute_opening_fee_msat; use io::utils::write_node_metrics; use lightning::chain::BestBlock; @@ -1048,6 +1049,7 @@ impl Node { /// Returns a liquidity handler allowing to query [bLIP-52 / LSPS2] JIT channel parameters. /// + /// Alby: expose the LSPS2 opening fee menu so Hub can surface min/max payment sizes. /// [bLIP-52 / LSPS2]: https://github.com/lightning/blips/blob/master/blip-0052.md #[cfg(not(feature = "uniffi"))] pub fn lsps2_liquidity(&self) -> LSPS2Liquidity { @@ -1061,6 +1063,7 @@ impl Node { /// Returns a liquidity handler allowing to query [bLIP-52 / LSPS2] JIT channel parameters. /// + /// Alby: expose the LSPS2 opening fee menu so Hub can surface min/max payment sizes. /// [bLIP-52 / LSPS2]: https://github.com/lightning/blips/blob/master/blip-0052.md #[cfg(feature = "uniffi")] pub fn lsps2_liquidity(&self) -> Arc { diff --git a/src/liquidity.rs b/src/liquidity.rs index 1143491e1f..b8992c5076 100644 --- a/src/liquidity.rs +++ b/src/liquidity.rs @@ -1433,11 +1433,14 @@ type LSPS1PaymentInfo = crate::ffi::LSPS1PaymentInfo; /// Response to an LSPS2 `get_info` request. /// +/// Alby: surfaced so Hub can read the LSP's min_payment_size_msat and other JIT limits. +/// /// See [bLIP-52 / LSPS2] for more information. /// /// [bLIP-52 / LSPS2]: https://github.com/lightning/blips/blob/master/blip-0052.md #[derive(Debug, Clone)] pub struct LSPS2GetInfoResponse { + /// Alby: the raw LSPS2 fee menu returned by the LSP. /// The set of opening fee parameters offered by the LSP. pub opening_fee_params_menu: Vec, } @@ -1551,6 +1554,9 @@ impl LSPS1Liquidity { /// /// Should be retrieved by calling [`Node::lsps2_liquidity`]. /// +/// Alby: this provides direct access to the LSP's opening fee menu without forcing invoice +/// creation first. +/// /// To open [bLIP-52 / LSPS2] JIT channels, please refer to /// [`Bolt11Payment::receive_via_jit_channel`]. /// @@ -1577,6 +1583,8 @@ impl LSPS2Liquidity { /// Connects to the configured LSP and requests its current JIT channel opening fee parameters. /// + /// Alby: Hub uses this to display the effective LSPS2 payment-size limits to users. + /// /// This issues an `lsps2.get_info` request and returns the LSP's `opening_fee_params_menu`. pub fn request_opening_fee_params(&self) -> Result { let liquidity_source = @@ -1606,6 +1614,8 @@ impl LSPS2Liquidity { /// Computes the LSPS2 opening fee for a given payment size and fee parameters. /// +/// Alby: exported for clients that want to estimate the fee for a selected LSPS2 offer. +/// /// See [bLIP-52 / LSPS2] for more information. /// /// [bLIP-52 / LSPS2]: https://github.com/lightning/blips/blob/master/blip-0052.md#computing-the-opening_fee