Skip to content

Commit 8423917

Browse files
committed
Add LSPS1 API
We add an `Lsps1Liquidity` API object, mirroring the approach we took with the `payment` APIs.
1 parent e01d435 commit 8423917

File tree

2 files changed

+147
-5
lines changed

2 files changed

+147
-5
lines changed

src/lib.rs

+31-2
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ mod gossip;
8484
pub mod graph;
8585
mod hex_utils;
8686
pub mod io;
87-
mod liquidity;
87+
pub mod liquidity;
8888
pub mod logger;
8989
mod message_handler;
9090
pub mod payment;
@@ -100,6 +100,7 @@ pub use bip39;
100100
pub use bitcoin;
101101
pub use lightning;
102102
pub use lightning_invoice;
103+
pub use lightning_liquidity;
103104
pub use lightning_types;
104105
pub use vss_client;
105106

@@ -130,7 +131,7 @@ use event::{EventHandler, EventQueue};
130131
use gossip::GossipSource;
131132
use graph::NetworkGraph;
132133
use io::utils::write_node_metrics;
133-
use liquidity::LiquiditySource;
134+
use liquidity::{LiquiditySource, Lsps1Liquidity};
134135
use payment::store::PaymentStore;
135136
use payment::{
136137
Bolt11Payment, Bolt12Payment, OnchainPayment, PaymentDetails, SpontaneousPayment,
@@ -958,6 +959,34 @@ impl Node {
958959
))
959960
}
960961

962+
/// Returns a liquidity handler allowing to request channels via the [LSPS1] protocol.
963+
///
964+
/// [LSPS1]: https://github.com/BitcoinAndLightningLayerSpecs/lsp/tree/main/LSPS1
965+
#[cfg(not(feature = "uniffi"))]
966+
pub fn lsps1_liquidity(&self) -> Lsps1Liquidity {
967+
Lsps1Liquidity::new(
968+
Arc::clone(&self.runtime),
969+
Arc::clone(&self.wallet),
970+
Arc::clone(&self.connection_manager),
971+
self.liquidity_source.clone(),
972+
Arc::clone(&self.logger),
973+
)
974+
}
975+
976+
/// Returns a liquidity handler allowing to request channels via the [LSPS1] protocol.
977+
///
978+
/// [LSPS1]: https://github.com/BitcoinAndLightningLayerSpecs/lsp/tree/main/LSPS1
979+
#[cfg(feature = "uniffi")]
980+
pub fn lsps1_liquidity(&self) -> Arc<Lsps1Liquidity> {
981+
Arc::new(Lsps1Liquidity::new(
982+
Arc::clone(&self.runtime),
983+
Arc::clone(&self.wallet),
984+
Arc::clone(&self.connection_manager),
985+
self.liquidity_source.clone(),
986+
Arc::clone(&self.logger),
987+
))
988+
}
989+
961990
/// Retrieve a list of known channels.
962991
pub fn list_channels(&self) -> Vec<ChannelDetails> {
963992
self.channel_manager.list_channels().into_iter().map(|c| c.into()).collect()

src/liquidity.rs

+116-3
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@
55
// http://opensource.org/licenses/MIT>, at your option. You may not use this file except in
66
// accordance with one or both of these licenses.
77

8+
//! Objects related to liquidity management.
9+
810
use crate::chain::ChainSource;
9-
use crate::logger::{log_debug, log_error, log_info, LdkLogger};
10-
use crate::types::{ChannelManager, KeysManager, LiquidityManager, PeerManager};
11+
use crate::connection::ConnectionManager;
12+
use crate::logger::{log_debug, log_error, log_info, LdkLogger, Logger};
13+
use crate::types::{ChannelManager, KeysManager, LiquidityManager, PeerManager, Wallet};
1114
use crate::{Config, Error};
1215

1316
use lightning::ln::channelmanager::MIN_FINAL_CLTV_EXPIRY_DELTA;
@@ -34,7 +37,7 @@ use tokio::sync::oneshot;
3437

3538
use std::collections::HashMap;
3639
use std::ops::Deref;
37-
use std::sync::{Arc, Mutex};
40+
use std::sync::{Arc, Mutex, RwLock};
3841
use std::time::Duration;
3942

4043
const LIQUIDITY_REQUEST_TIMEOUT_SECS: u64 = 5;
@@ -892,3 +895,113 @@ pub(crate) struct LSPS2BuyResponse {
892895
intercept_scid: u64,
893896
cltv_expiry_delta: u32,
894897
}
898+
899+
/// A liquidity handler allowing to request channels via the [LSPS1] protocol.
900+
///
901+
/// Should be retrieved by calling [`Node::lsps1_liquidity`].
902+
///
903+
/// [LSPS1]: https://github.com/BitcoinAndLightningLayerSpecs/lsp/tree/main/LSPS1
904+
/// [`Node::lsps1_liquidity`]: crate::Node::lsps1_liquidity
905+
#[derive(Clone)]
906+
pub struct Lsps1Liquidity {
907+
runtime: Arc<RwLock<Option<Arc<tokio::runtime::Runtime>>>>,
908+
wallet: Arc<Wallet>,
909+
connection_manager: Arc<ConnectionManager<Arc<Logger>>>,
910+
liquidity_source: Option<Arc<LiquiditySource<Arc<Logger>>>>,
911+
logger: Arc<Logger>,
912+
}
913+
914+
impl Lsps1Liquidity {
915+
pub(crate) fn new(
916+
runtime: Arc<RwLock<Option<Arc<tokio::runtime::Runtime>>>>, wallet: Arc<Wallet>,
917+
connection_manager: Arc<ConnectionManager<Arc<Logger>>>,
918+
liquidity_source: Option<Arc<LiquiditySource<Arc<Logger>>>>, logger: Arc<Logger>,
919+
) -> Self {
920+
Self { runtime, wallet, connection_manager, liquidity_source, logger }
921+
}
922+
923+
/// Connects to the configured LSP and places an order for an inbound channel.
924+
///
925+
/// The channel will be opened after one of the returned payment options has successfully been
926+
/// paid.
927+
pub fn request_channel(
928+
&self, lsp_balance_sat: u64, client_balance_sat: u64, channel_expiry_blocks: u32,
929+
announce_channel: bool,
930+
) -> Result<LSPS1OrderStatus, Error> {
931+
let liquidity_source =
932+
self.liquidity_source.as_ref().ok_or(Error::LiquiditySourceUnavailable)?;
933+
934+
let (lsp_node_id, lsp_address) = liquidity_source
935+
.get_lsps1_service_details()
936+
.ok_or(Error::LiquiditySourceUnavailable)?;
937+
938+
let rt_lock = self.runtime.read().unwrap();
939+
let runtime = rt_lock.as_ref().unwrap();
940+
941+
let con_node_id = lsp_node_id;
942+
let con_addr = lsp_address.clone();
943+
let con_cm = Arc::clone(&self.connection_manager);
944+
945+
// We need to use our main runtime here as a local runtime might not be around to poll
946+
// connection futures going forward.
947+
tokio::task::block_in_place(move || {
948+
runtime.block_on(async move {
949+
con_cm.connect_peer_if_necessary(con_node_id, con_addr).await
950+
})
951+
})?;
952+
953+
log_info!(self.logger, "Connected to LSP {}@{}. ", lsp_node_id, lsp_address);
954+
955+
let refund_address = self.wallet.get_new_address()?;
956+
957+
let liquidity_source = Arc::clone(&liquidity_source);
958+
let response = tokio::task::block_in_place(move || {
959+
runtime.block_on(async move {
960+
liquidity_source
961+
.lsps1_request_channel(
962+
lsp_balance_sat,
963+
client_balance_sat,
964+
channel_expiry_blocks,
965+
announce_channel,
966+
refund_address,
967+
)
968+
.await
969+
})
970+
})?;
971+
972+
Ok(response)
973+
}
974+
975+
/// Connects to the configured LSP and checks for the status of a previously-placed order.
976+
pub fn check_order_status(&self, order_id: OrderId) -> Result<LSPS1OrderStatus, Error> {
977+
let liquidity_source =
978+
self.liquidity_source.as_ref().ok_or(Error::LiquiditySourceUnavailable)?;
979+
980+
let (lsp_node_id, lsp_address) = liquidity_source
981+
.get_lsps1_service_details()
982+
.ok_or(Error::LiquiditySourceUnavailable)?;
983+
984+
let rt_lock = self.runtime.read().unwrap();
985+
let runtime = rt_lock.as_ref().unwrap();
986+
987+
let con_node_id = lsp_node_id;
988+
let con_addr = lsp_address.clone();
989+
let con_cm = Arc::clone(&self.connection_manager);
990+
991+
// We need to use our main runtime here as a local runtime might not be around to poll
992+
// connection futures going forward.
993+
tokio::task::block_in_place(move || {
994+
runtime.block_on(async move {
995+
con_cm.connect_peer_if_necessary(con_node_id, con_addr).await
996+
})
997+
})?;
998+
999+
let liquidity_source = Arc::clone(&liquidity_source);
1000+
let response = tokio::task::block_in_place(move || {
1001+
runtime
1002+
.block_on(async move { liquidity_source.lsps1_check_order_status(order_id).await })
1003+
})?;
1004+
1005+
Ok(response)
1006+
}
1007+
}

0 commit comments

Comments
 (0)