Skip to content

Commit 0f9d105

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

File tree

2 files changed

+148
-5
lines changed

2 files changed

+148
-5
lines changed

src/lib.rs

Lines changed: 31 additions & 2 deletions
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
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 vss_client;
104105

105106
pub use balance::{BalanceDetails, LightningBalance, PendingSweepBalance};
@@ -129,7 +130,7 @@ use event::{EventHandler, EventQueue};
129130
use gossip::GossipSource;
130131
use graph::NetworkGraph;
131132
use io::utils::write_node_metrics;
132-
use liquidity::LiquiditySource;
133+
use liquidity::{LiquiditySource, Lsps1Liquidity};
133134
use payment::store::PaymentStore;
134135
use payment::{
135136
Bolt11Payment, Bolt12Payment, OnchainPayment, PaymentDetails, SpontaneousPayment,
@@ -960,6 +961,34 @@ impl Node {
960961
))
961962
}
962963

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

src/liquidity.rs

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

0 commit comments

Comments
 (0)