Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions bindings/ldk_node.udl
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
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);
};

dictionary Config {
Expand Down Expand Up @@ -145,6 +147,8 @@ 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);
[Throws=NodeError]
Expand Down Expand Up @@ -293,6 +297,28 @@ interface LSPS1Liquidity {
LSPS1OrderStatus check_order_status(LSPS1OrderId order_id);
};

interface LSPS2Liquidity {
[Throws=NodeError]
// Alby: fetch the LSP's LSPS2 opening fee menu.
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 {
// Alby: return the raw LSPS2 fee menu so clients can read min_payment_size_msat.
sequence<LSPS2OpeningFeeParams> opening_fee_params_menu;
};

[Error]
enum NodeError {
"AlreadyRunning",
Expand Down
5 changes: 4 additions & 1 deletion src/ffi/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ 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;
pub use vss_client::headers::{VssHeaderProvider, VssHeaderProviderError};
Expand All @@ -49,7 +51,8 @@ pub use crate::config::{
};
use crate::error::Error;
pub use crate::graph::{ChannelInfo, ChannelUpdateInfo, NodeAnnouncementInfo, NodeInfo};
pub use crate::liquidity::{LSPS1OrderStatus, LSPS2ServiceConfig};
// 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::{
ConfirmationStatus, LSPFeeLimits, PaymentDirection, PaymentKind, PaymentStatus,
Expand Down
32 changes: 31 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ 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;
use lightning::events::bump_transaction::{Input, Wallet as LdkWallet};
Expand All @@ -144,7 +146,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;
Expand Down Expand Up @@ -1045,6 +1047,34 @@ 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 {
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.
///
/// 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<LSPS2Liquidity> {
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<ChannelDetails> {
self.channel_manager.list_channels().into_iter().map(|c| c.into()).collect()
Expand Down
104 changes: 96 additions & 8 deletions src/liquidity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ struct LSPS2Client {
lsp_address: SocketAddress,
token: Option<String>,
ldk_client_config: LdkLSPS2ClientConfig,
pending_fee_requests: Mutex<HashMap<LSPSRequestId, oneshot::Sender<LSPS2FeeResponse>>>,
pending_fee_requests: Mutex<HashMap<LSPSRequestId, oneshot::Sender<LSPS2GetInfoResponse>>>,
pending_buy_requests: Mutex<HashMap<LSPSRequestId, oneshot::Sender<LSPS2BuyResponse>>>,
}

Expand Down Expand Up @@ -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(()) => (),
Expand Down Expand Up @@ -1189,7 +1189,7 @@ where
Ok((invoice, min_prop_fee_ppm_msat))
}

async fn lsps2_request_opening_fee_params(&self) -> Result<LSPS2FeeResponse, Error> {
async fn lsps2_request_opening_fee_params(&self) -> Result<LSPS2GetInfoResponse, Error> {
let lsps2_client = self.lsps2_client.as_ref().ok_or(Error::LiquiditySourceUnavailable)?;

let client_handler = self.liquidity_manager.lsps2_client_handler().ok_or_else(|| {
Expand Down Expand Up @@ -1431,9 +1431,18 @@ type LSPS1PaymentInfo = lightning_liquidity::lsps1::msgs::LSPS1PaymentInfo;
#[cfg(feature = "uniffi")]
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(crate) struct LSPS2FeeResponse {
opening_fee_params_menu: Vec<LSPS2OpeningFeeParams>,
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<LSPS2OpeningFeeParams>,
}

#[derive(Debug, Clone)]
Expand All @@ -1446,13 +1455,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<Runtime>,
Expand Down Expand Up @@ -1540,3 +1549,82 @@ impl LSPS1Liquidity {
Ok(response)
}
}

/// A liquidity handler allowing to query [bLIP-52 / LSPS2] JIT channel parameters.
///
/// 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`].
///
/// [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<Runtime>,
connection_manager: Arc<ConnectionManager<Arc<Logger>>>,
liquidity_source: Option<Arc<LiquiditySource<Arc<Logger>>>>,
logger: Arc<Logger>,
}

impl LSPS2Liquidity {
pub(crate) fn new(
runtime: Arc<Runtime>,
connection_manager: Arc<ConnectionManager<Arc<Logger>>>,
liquidity_source: Option<Arc<LiquiditySource<Arc<Logger>>>>,
logger: Arc<Logger>,
) -> Self {
Self { runtime, connection_manager, liquidity_source, logger }
}

/// 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<LSPS2GetInfoResponse, Error> {
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.
///
/// 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
pub fn lsps2_compute_opening_fee_msat(
payment_size_msat: u64, opening_fee_params: LSPS2OpeningFeeParams,
) -> Option<u64> {
compute_opening_fee(
payment_size_msat,
opening_fee_params.min_fee_msat,
opening_fee_params.proportional as u64,
)
}
Loading