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+
810use 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 } ;
1114use crate :: { Config , Error } ;
1215
1316use lightning:: ln:: channelmanager:: MIN_FINAL_CLTV_EXPIRY_DELTA ;
@@ -34,7 +37,7 @@ use tokio::sync::oneshot;
3437
3538use std:: collections:: HashMap ;
3639use std:: ops:: Deref ;
37- use std:: sync:: { Arc , Mutex } ;
40+ use std:: sync:: { Arc , Mutex , RwLock } ;
3841use std:: time:: Duration ;
3942
4043const 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