@@ -34,6 +34,8 @@ static LIMITER: OnceCell<RateLimiter<NotKeyed, InMemoryState, MonotonicClock>> =
3434static RPC_CLIENT : OnceCell < Arc < Mutex < cln_rpc:: ClnRpc > > > = OnceCell :: const_new ( ) ;
3535static RPC_POLL_INTERVAL : Duration = Duration :: from_millis ( 500 ) ;
3636
37+ const OPT_SUPPORTS_LSPS : usize = 729 ;
38+
3739pub async fn get_rpc < P : AsRef < Path > > ( path : P ) -> Arc < Mutex < cln_rpc:: ClnRpc > > {
3840 RPC_CLIENT
3941 . get_or_init ( || async {
@@ -186,15 +188,31 @@ impl Node for PluginNodeServer {
186188 req : Request < pb:: LspInvoiceRequest > ,
187189 ) -> Result < Response < pb:: LspInvoiceResponse > , Status > {
188190 let req: pb:: LspInvoiceRequest = req. into_inner ( ) ;
189- let invreq: crate :: requests:: InvoiceRequest = req. into ( ) ;
191+ let mut invreq: crate :: requests:: LspInvoiceRequest = req. into ( ) ;
190192 let rpc_arc = get_rpc ( & self . rpc_path ) . await ;
191193
192194 let mut rpc = rpc_arc. lock ( ) . await ;
195+
196+ // In case the client did not specify an LSP to work with,
197+ // let's enumerate them, and select the best option ourselves.
198+ let lsps = self . get_lsps_offers ( & mut rpc) . await . map_err ( |_e| {
199+ Status :: not_found ( "Could not retrieve LSPS peers for invoice negotiation." )
200+ } ) ?;
201+
202+ if lsps. len ( ) < 1 {
203+ return Err ( Status :: not_found (
204+ "Could not find an LSP peer to negotiate the LSPS2 channel for this invoice." ,
205+ ) ) ;
206+ }
207+
208+ let lsp = & lsps[ 0 ] ;
209+ log:: info!( "Selecting {:?} for invoice negotiation" , lsp) ;
210+ invreq. lsp_id = lsp. node_id . to_owned ( ) ;
211+
193212 let res = rpc
194213 . call_typed ( & invreq)
195214 . await
196215 . map_err ( |e| Status :: new ( Code :: Internal , e. to_string ( ) ) ) ?;
197-
198216 Ok ( Response :: new ( res. into ( ) ) )
199217 }
200218
@@ -595,6 +613,12 @@ impl Node for PluginNodeServer {
595613
596614use cln_grpc:: pb:: node_server:: NodeServer ;
597615
616+ #[ derive( Clone , Debug ) ]
617+ struct Lsps2Offer {
618+ node_id : String ,
619+ params : Vec < crate :: responses:: OpeningFeeParams > ,
620+ }
621+
598622impl PluginNodeServer {
599623 pub async fn run ( self ) -> Result < ( ) > {
600624 let addr = self . grpc_binding . parse ( ) . unwrap ( ) ;
@@ -655,17 +679,99 @@ impl PluginNodeServer {
655679 return Ok ( ( ) ) ;
656680 }
657681
682+ async fn list_peers (
683+ & self ,
684+ rpc : & mut cln_rpc:: ClnRpc ,
685+ ) -> Result < cln_rpc:: model:: responses:: ListpeersResponse , Error > {
686+ rpc. call_typed ( & cln_rpc:: model:: requests:: ListpeersRequest {
687+ id : None ,
688+ level : None ,
689+ } )
690+ . await
691+ . map_err ( |e| e. into ( ) )
692+ }
693+
694+ async fn get_lsps_offers ( & self , rpc : & mut cln_rpc:: ClnRpc ) -> Result < Vec < Lsps2Offer > , Error > {
695+ // Collect peers offering LSP functionality
696+ let lpeers = self . list_peers ( rpc) . await ?;
697+
698+ // Filter out the ones that do not announce the LSPs features.
699+ // TODO: Re-enable the filtering once the cln-lsps-service plugin announces the features.
700+ let _lsps: Vec < cln_rpc:: model:: responses:: ListpeersPeers > = lpeers
701+ . peers
702+ . into_iter ( )
703+ //.filter(|p| has_feature(
704+ // hex::decode(p.features.clone().unwrap_or_default()).expect("featurebits are hex"),
705+ // OPT_SUPPORTS_LSPS
706+ //))
707+ . collect ( ) ;
708+
709+ // Query all peers for their LSPS offers, but with a brief
710+ // timeout so the invoice creation isn't help up too long.
711+ let futs: Vec <
712+ tokio:: task:: JoinHandle < (
713+ String ,
714+ Result <
715+ Result < crate :: responses:: LspGetinfoResponse , cln_rpc:: RpcError > ,
716+ tokio:: time:: error:: Elapsed ,
717+ > ,
718+ ) > ,
719+ > = _lsps
720+ . into_iter ( )
721+ . map ( |peer| {
722+ let rpc_path = self . rpc_path . clone ( ) ;
723+ tokio:: spawn ( async move {
724+ let peer_id = format ! ( "{:x}" , peer. id) ;
725+ let mut rpc = cln_rpc:: ClnRpc :: new ( rpc_path. clone ( ) ) . await . unwrap ( ) ;
726+ let req = crate :: requests:: LspGetinfoRequest {
727+ lsp_id : peer_id. clone ( ) ,
728+ token : None ,
729+ } ;
730+
731+ (
732+ peer_id,
733+ tokio:: time:: timeout (
734+ tokio:: time:: Duration :: from_secs ( 2 ) ,
735+ rpc. call_typed ( & req) ,
736+ )
737+ . await ,
738+ )
739+ } )
740+ } )
741+ . collect ( ) ;
742+
743+ let mut res = vec ! [ ] ;
744+ for f in futs {
745+ match f. await {
746+ //TODO We need to drag the node_id along.
747+ Ok ( ( node_id, Ok ( Ok ( r) ) ) ) => res. push ( Lsps2Offer {
748+ node_id : node_id,
749+ params : r. opening_fee_params_menu ,
750+ } ) ,
751+ Ok ( ( node_id, Err ( e) ) ) => warn ! (
752+ "Error fetching LSPS menu items from peer_id={}: {:?}" ,
753+ node_id, e
754+ ) ,
755+ Ok ( ( node_id, Ok ( Err ( e) ) ) ) => warn ! (
756+ "Error fetching LSPS menu items from peer_id={}: {:?}" ,
757+ node_id, e
758+ ) ,
759+ Err ( _) => warn ! ( "Timeout fetching LSPS menu items" ) ,
760+ }
761+ }
762+
763+ log:: info!( "Gathered {} LSP menus" , res. len( ) ) ;
764+ log:: trace!( "LSP menus: {:?}" , & res) ;
765+
766+ Ok ( res)
767+ }
768+
658769 async fn get_reconnect_peers (
659770 & self ,
660771 ) -> Result < Vec < cln_rpc:: model:: requests:: ConnectRequest > , Error > {
661772 let rpc_arc = get_rpc ( & self . rpc_path ) . await ;
662773 let mut rpc = rpc_arc. lock ( ) . await ;
663- let peers = rpc
664- . call_typed ( & cln_rpc:: model:: requests:: ListpeersRequest {
665- id : None ,
666- level : None ,
667- } )
668- . await ?;
774+ let peers = self . list_peers ( & mut rpc) . await ?;
669775
670776 let mut requests: Vec < cln_rpc:: model:: requests:: ConnectRequest > = peers
671777 . peers
0 commit comments