@@ -225,18 +225,17 @@ where
225225 } ,
226226 )
227227 } ,
228- JitInvoiceRequest :: Variable { .. } => {
229- let ( lease, proportional_fee, _, _) =
230- self . acquire_variable_lease ( & connection_manager) . await ?;
228+ JitInvoiceRequest :: Variable { amount_msat, .. } => {
229+ // A BOLT12 invoice request has already resolved the payment amount. Use it to avoid
230+ // consuming a cached variable lease outside its advertised range, and record the exact
231+ // fee for that amount so payment validation need not accept the broader node-wide cap.
232+ let ( lease, total_fee_msat, _, _) =
233+ self . acquire_variable_lease ( Some ( amount_msat) , & connection_manager) . await ?;
231234 (
232235 lease,
233236 LSPS2Parameters {
234- max_total_opening_fee_msat : self . config . lsps2_max_total_lsp_fee_limit_msat ,
235- max_proportional_opening_fee_ppm_msat : self
236- . config
237- . lsps2_max_total_lsp_fee_limit_msat
238- . is_none ( )
239- . then_some ( proportional_fee) ,
237+ max_total_opening_fee_msat : Some ( total_fee_msat) ,
238+ max_proportional_opening_fee_ppm_msat : None ,
240239 } ,
241240 )
242241 } ,
@@ -300,7 +299,7 @@ where
300299 payment_hash : Option < PaymentHash > , connection_manager : Arc < ConnectionManager < L > > ,
301300 ) -> Result < ( Bolt11Invoice , LspConfig ) , Error > {
302301 let ( lease, proportional_fee, lsp, was_negotiated) =
303- self . acquire_variable_lease ( & connection_manager) . await ?;
302+ self . acquire_variable_lease ( None , & connection_manager) . await ?;
304303 let lsps2_parameters = LSPS2Parameters {
305304 max_total_opening_fee_msat : self . config . lsps2_max_total_lsp_fee_limit_msat ,
306305 max_proportional_opening_fee_ppm_msat : self
@@ -459,36 +458,36 @@ where
459458 }
460459
461460 async fn acquire_variable_lease (
462- self : & Arc < Self > , connection_manager : & Arc < ConnectionManager < L > > ,
461+ self : & Arc < Self > , amount_msat : Option < u64 > , connection_manager : & Arc < ConnectionManager < L > > ,
463462 ) -> Result < ( PaymentLease , u64 , LspConfig , bool ) , Error > {
464- if let Some ( ( lease, proportional_fee , lsp) ) = self . take_cached_variable_lease ( ) . await ? {
463+ if let Some ( ( lease, fee , lsp) ) = self . take_cached_variable_lease ( amount_msat ) . await ? {
465464 self . schedule_variable_lease_refill ( connection_manager) ;
466- return Ok ( ( lease, proportional_fee , lsp, false ) ) ;
465+ return Ok ( ( lease, fee , lsp, false ) ) ;
467466 }
468467 let request_lock = self
469468 . pending_lease_request_state
470469 . lock ( )
471470 . expect ( "lock" )
472471 . request_lock ( LeaseRequestKey :: Variable ) ;
473472 let _request_guard = request_lock. lock ( ) . await ;
474- if let Some ( ( lease, proportional_fee , lsp) ) = self . take_cached_variable_lease ( ) . await ? {
473+ if let Some ( ( lease, fee , lsp) ) = self . take_cached_variable_lease ( amount_msat ) . await ? {
475474 self . schedule_variable_lease_refill ( connection_manager) ;
476- return Ok ( ( lease, proportional_fee , lsp, false ) ) ;
475+ return Ok ( ( lease, fee , lsp, false ) ) ;
477476 }
478477
479- let ( negotiated_lease, min_prop_fee_ppm_msat , cheapest_lsp) =
480- self . negotiate_variable_lease ( connection_manager) . await ?;
478+ let ( negotiated_lease, fee , cheapest_lsp) =
479+ self . negotiate_variable_lease ( amount_msat , connection_manager) . await ?;
481480 let lease = self . consume_lease ( & negotiated_lease. id ) . await ?;
482481 self . schedule_variable_lease_refill ( connection_manager) ;
483- Ok ( ( lease, min_prop_fee_ppm_msat , cheapest_lsp, true ) )
482+ Ok ( ( lease, fee , cheapest_lsp, true ) )
484483 }
485484
486485 async fn negotiate_variable_lease (
487- self : & Arc < Self > , connection_manager : & Arc < ConnectionManager < L > > ,
486+ self : & Arc < Self > , amount_msat : Option < u64 > , connection_manager : & Arc < ConnectionManager < L > > ,
488487 ) -> Result < ( PaymentLease , u64 , LspConfig ) , Error > {
489488 let mut attempt = 1 ;
490489 loop {
491- let result = self . negotiate_variable_lease_once ( connection_manager) . await ;
490+ let result = self . negotiate_variable_lease_once ( amount_msat , connection_manager) . await ;
492491 match result {
493492 Err ( error) if should_retry_lease_negotiation ( error, attempt) => {
494493 log_warn ! (
@@ -505,7 +504,7 @@ where
505504 }
506505
507506 async fn negotiate_variable_lease_once (
508- self : & Arc < Self > , connection_manager : & Arc < ConnectionManager < L > > ,
507+ self : & Arc < Self > , amount_msat : Option < u64 > , connection_manager : & Arc < ConnectionManager < L > > ,
509508 ) -> Result < ( PaymentLease , u64 , LspConfig ) , Error > {
510509 let all_offers = self . gather_lsps2_offers ( connection_manager) . await ?;
511510 let mut rejected_for_fee = false ;
@@ -514,20 +513,34 @@ where
514513 . flat_map ( |( lsp, resp) | {
515514 resp. opening_fee_params_menu . into_iter ( ) . map ( move |params| ( lsp. clone ( ) , params) )
516515 } )
517- . map ( |( lsp, params) | {
518- let ppm = params. proportional as u64 ;
519- ( lsp, ppm, params)
520- } )
521- . filter ( |( _, _, params) | {
522- let allowed = self
516+ . filter_map ( |( lsp, params) | {
517+ // BOLT12 supplies a resolved amount here, while a BOLT11 zero-amount invoice does not.
518+ // In the former case, only negotiate parameters that can carry that exact payment and
519+ // compare providers by the total fee the payment would actually incur.
520+ let selection_fee = if let Some ( amount_msat) = amount_msat {
521+ if amount_msat < params. min_payment_size_msat
522+ || amount_msat > params. max_payment_size_msat
523+ {
524+ return None ;
525+ }
526+ compute_opening_fee (
527+ amount_msat,
528+ params. min_fee_msat ,
529+ params. proportional as u64 ,
530+ ) ?
531+ } else {
532+ params. proportional as u64
533+ } ;
534+ let fee_for_limit = amount_msat. map_or ( params. min_fee_msat , |_| selection_fee) ;
535+ let fee_allowed = self
523536 . config
524537 . lsps2_max_total_lsp_fee_limit_msat
525- . map_or ( true , |limit| params . min_fee_msat <= limit) ;
526- rejected_for_fee |= !allowed ;
527- allowed
538+ . map_or ( true , |limit| fee_for_limit <= limit) ;
539+ rejected_for_fee |= !fee_allowed ;
540+ fee_allowed . then_some ( ( lsp , selection_fee , params ) )
528541 } )
529542 . collect :: < Vec < _ > > ( ) ;
530- candidates. sort_unstable_by_key ( |( _, ppm , _) | * ppm ) ;
543+ candidates. sort_unstable_by_key ( |( _, fee , _) | * fee ) ;
531544 if candidates. is_empty ( ) {
532545 return Err ( if rejected_for_fee {
533546 log_error ! (
@@ -544,20 +557,29 @@ where
544557 try_lease_candidates (
545558 candidates,
546559 |( lsp, _, _) | lsp. node_id ,
547- |( lsp, proportional_fee_ppm_msat , opening_params) | {
560+ |( lsp, fee , opening_params) | {
548561 let client = Arc :: clone ( self ) ;
549562 async move {
550- log_debug ! (
551- client. logger,
552- "Choosing liquidity offer from LSP {}, will pay {}ppm msat in proportional LSP fees" ,
553- lsp. node_id,
554- proportional_fee_ppm_msat
555- ) ;
563+ if amount_msat. is_some ( ) {
564+ log_debug ! (
565+ client. logger,
566+ "Choosing liquidity offer from LSP {}, will pay {}msat in total LSP fees" ,
567+ lsp. node_id,
568+ fee
569+ ) ;
570+ } else {
571+ log_debug ! (
572+ client. logger,
573+ "Choosing liquidity offer from LSP {}, will pay {}ppm msat in proportional LSP fees" ,
574+ lsp. node_id,
575+ fee
576+ ) ;
577+ }
556578 match client
557579 . lsps2_send_buy_request ( None , opening_params, Some ( & lsp. node_id ) )
558580 . await
559581 {
560- Ok ( lease) => Ok ( ( lease, proportional_fee_ppm_msat , lsp) ) ,
582+ Ok ( lease) => Ok ( ( lease, fee , lsp) ) ,
561583 Err ( error) => {
562584 log_warn ! (
563585 client. logger,
@@ -632,11 +654,11 @@ where
632654 . lease_state
633655 . lock ( )
634656 . expect ( "lock" )
635- . has_variable_amount ( self . config . lsps2_max_total_lsp_fee_limit_msat )
657+ . has_variable_amount ( None , self . config . lsps2_max_total_lsp_fee_limit_msat )
636658 {
637659 return Ok ( ( ) ) ;
638660 }
639- self . negotiate_variable_lease ( connection_manager) . await ?;
661+ self . negotiate_variable_lease ( None , connection_manager) . await ?;
640662 Ok ( ( ) )
641663 }
642664
@@ -833,22 +855,22 @@ where
833855 }
834856
835857 async fn take_cached_variable_lease (
836- & self ,
858+ & self , amount_msat : Option < u64 > ,
837859 ) -> Result < Option < ( PaymentLease , u64 , LspConfig ) > , Error > {
838860 loop {
839- let Some ( ( lease, proportional_fee ) ) = self
861+ let Some ( ( lease, fee ) ) = self
840862 . lease_state
841863 . lock ( )
842864 . expect ( "lock" )
843- . variable_amount ( self . config . lsps2_max_total_lsp_fee_limit_msat )
865+ . variable_amount ( amount_msat , self . config . lsps2_max_total_lsp_fee_limit_msat )
844866 else {
845867 return Ok ( None ) ;
846868 } ;
847869 let lease = self . consume_selected_lease ( lease) . await ?;
848870 if let Some ( lsp) =
849871 select_lsps_for_protocol ( & self . lsp_nodes , 2 , Some ( & lease. id . lsp_node_id ) )
850872 {
851- return Ok ( Some ( ( lease, proportional_fee , lsp) ) ) ;
873+ return Ok ( Some ( ( lease, fee , lsp) ) ) ;
852874 }
853875 }
854876 }
0 commit comments