Skip to content

Commit 3368f4e

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

File tree

2 files changed

+148
-5
lines changed

2 files changed

+148
-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
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,
@@ -959,6 +960,34 @@ impl Node {
959960
))
960961
}
961962

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

src/liquidity.rs

+117-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, 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;
@@ -892,3 +895,114 @@ 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<FilesystemLogger>>>,
910+
liquidity_source: Option<Arc<LiquiditySource<Arc<FilesystemLogger>>>>,
911+
logger: Arc<FilesystemLogger>,
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<FilesystemLogger>>>,
918+
liquidity_source: Option<Arc<LiquiditySource<Arc<FilesystemLogger>>>>,
919+
logger: Arc<FilesystemLogger>,
920+
) -> Self {
921+
Self { runtime, wallet, connection_manager, liquidity_source, logger }
922+
}
923+
924+
/// Connects to the configured LSP and places an order for an inbound channel.
925+
///
926+
/// The channel will be opened after one of the returned payment options has successfully been
927+
/// paid.
928+
pub fn request_channel(
929+
&self, lsp_balance_sat: u64, client_balance_sat: u64, channel_expiry_blocks: u32,
930+
announce_channel: bool,
931+
) -> Result<LSPS1OrderStatus, Error> {
932+
let liquidity_source =
933+
self.liquidity_source.as_ref().ok_or(Error::LiquiditySourceUnavailable)?;
934+
935+
let (lsp_node_id, lsp_address) = liquidity_source
936+
.get_lsps1_service_details()
937+
.ok_or(Error::LiquiditySourceUnavailable)?;
938+
939+
let rt_lock = self.runtime.read().unwrap();
940+
let runtime = rt_lock.as_ref().unwrap();
941+
942+
let con_node_id = lsp_node_id;
943+
let con_addr = lsp_address.clone();
944+
let con_cm = Arc::clone(&self.connection_manager);
945+
946+
// We need to use our main runtime here as a local runtime might not be around to poll
947+
// connection futures going forward.
948+
tokio::task::block_in_place(move || {
949+
runtime.block_on(async move {
950+
con_cm.connect_peer_if_necessary(con_node_id, con_addr).await
951+
})
952+
})?;
953+
954+
log_info!(self.logger, "Connected to LSP {}@{}. ", lsp_node_id, lsp_address);
955+
956+
let refund_address = self.wallet.get_new_address()?;
957+
958+
let liquidity_source = Arc::clone(&liquidity_source);
959+
let response = tokio::task::block_in_place(move || {
960+
runtime.block_on(async move {
961+
liquidity_source
962+
.lsps1_request_channel(
963+
lsp_balance_sat,
964+
client_balance_sat,
965+
channel_expiry_blocks,
966+
announce_channel,
967+
refund_address,
968+
)
969+
.await
970+
})
971+
})?;
972+
973+
Ok(response)
974+
}
975+
976+
/// Connects to the configured LSP and checks for the status of a previously-placed order.
977+
pub fn check_order_status(&self, order_id: OrderId) -> Result<LSPS1OrderStatus, Error> {
978+
let liquidity_source =
979+
self.liquidity_source.as_ref().ok_or(Error::LiquiditySourceUnavailable)?;
980+
981+
let (lsp_node_id, lsp_address) = liquidity_source
982+
.get_lsps1_service_details()
983+
.ok_or(Error::LiquiditySourceUnavailable)?;
984+
985+
let rt_lock = self.runtime.read().unwrap();
986+
let runtime = rt_lock.as_ref().unwrap();
987+
988+
let con_node_id = lsp_node_id;
989+
let con_addr = lsp_address.clone();
990+
let con_cm = Arc::clone(&self.connection_manager);
991+
992+
// We need to use our main runtime here as a local runtime might not be around to poll
993+
// connection futures going forward.
994+
tokio::task::block_in_place(move || {
995+
runtime.block_on(async move {
996+
con_cm.connect_peer_if_necessary(con_node_id, con_addr).await
997+
})
998+
})?;
999+
1000+
let liquidity_source = Arc::clone(&liquidity_source);
1001+
let response = tokio::task::block_in_place(move || {
1002+
runtime
1003+
.block_on(async move { liquidity_source.lsps1_check_order_status(order_id).await })
1004+
})?;
1005+
1006+
Ok(response)
1007+
}
1008+
}

0 commit comments

Comments
 (0)