@@ -214,18 +214,17 @@ where
214214 } ,
215215 )
216216 } ,
217- JitInvoiceRequest :: Variable { .. } => {
218- let ( lease, proportional_fee, _, _) =
219- self . acquire_variable_lease ( & connection_manager) . await ?;
217+ JitInvoiceRequest :: Variable { amount_msat, .. } => {
218+ // A BOLT12 invoice request has already resolved the payment amount. Use it to avoid
219+ // consuming a cached variable lease outside its advertised range, and record the exact
220+ // fee for that amount so payment validation need not accept the broader node-wide cap.
221+ let ( lease, total_fee_msat, _, _) =
222+ self . acquire_variable_lease ( Some ( amount_msat) , & connection_manager) . await ?;
220223 (
221224 lease,
222225 LSPS2Parameters {
223- max_total_opening_fee_msat : self . config . lsps2_max_total_lsp_fee_limit_msat ,
224- max_proportional_opening_fee_ppm_msat : self
225- . config
226- . lsps2_max_total_lsp_fee_limit_msat
227- . is_none ( )
228- . then_some ( proportional_fee) ,
226+ max_total_opening_fee_msat : Some ( total_fee_msat) ,
227+ max_proportional_opening_fee_ppm_msat : None ,
229228 } ,
230229 )
231230 } ,
@@ -289,7 +288,7 @@ where
289288 payment_hash : Option < PaymentHash > , connection_manager : Arc < ConnectionManager < L > > ,
290289 ) -> Result < ( Bolt11Invoice , LspConfig ) , Error > {
291290 let ( lease, proportional_fee, lsp, was_negotiated) =
292- self . acquire_variable_lease ( & connection_manager) . await ?;
291+ self . acquire_variable_lease ( None , & connection_manager) . await ?;
293292 let lsps2_parameters = LSPS2Parameters {
294293 max_total_opening_fee_msat : self . config . lsps2_max_total_lsp_fee_limit_msat ,
295294 max_proportional_opening_fee_ppm_msat : self
@@ -448,36 +447,36 @@ where
448447 }
449448
450449 async fn acquire_variable_lease (
451- self : & Arc < Self > , connection_manager : & Arc < ConnectionManager < L > > ,
450+ self : & Arc < Self > , amount_msat : Option < u64 > , connection_manager : & Arc < ConnectionManager < L > > ,
452451 ) -> Result < ( PaymentLease , u64 , LspConfig , bool ) , Error > {
453- if let Some ( ( lease, proportional_fee , lsp) ) = self . take_cached_variable_lease ( ) . await ? {
452+ if let Some ( ( lease, fee , lsp) ) = self . take_cached_variable_lease ( amount_msat ) . await ? {
454453 self . schedule_variable_lease_refill ( connection_manager) ;
455- return Ok ( ( lease, proportional_fee , lsp, false ) ) ;
454+ return Ok ( ( lease, fee , lsp, false ) ) ;
456455 }
457456 let request_lock = self
458457 . pending_lease_request_state
459458 . lock ( )
460459 . expect ( "lock" )
461460 . request_lock ( LeaseRequestKey :: Variable ) ;
462461 let _request_guard = request_lock. lock ( ) . await ;
463- if let Some ( ( lease, proportional_fee , lsp) ) = self . take_cached_variable_lease ( ) . await ? {
462+ if let Some ( ( lease, fee , lsp) ) = self . take_cached_variable_lease ( amount_msat ) . await ? {
464463 self . schedule_variable_lease_refill ( connection_manager) ;
465- return Ok ( ( lease, proportional_fee , lsp, false ) ) ;
464+ return Ok ( ( lease, fee , lsp, false ) ) ;
466465 }
467466
468- let ( negotiated_lease, min_prop_fee_ppm_msat , cheapest_lsp) =
469- self . negotiate_variable_lease ( connection_manager) . await ?;
467+ let ( negotiated_lease, fee , cheapest_lsp) =
468+ self . negotiate_variable_lease ( amount_msat , connection_manager) . await ?;
470469 let lease = self . consume_lease ( & negotiated_lease. id ) . await ?;
471470 self . schedule_variable_lease_refill ( connection_manager) ;
472- Ok ( ( lease, min_prop_fee_ppm_msat , cheapest_lsp, true ) )
471+ Ok ( ( lease, fee , cheapest_lsp, true ) )
473472 }
474473
475474 async fn negotiate_variable_lease (
476- self : & Arc < Self > , connection_manager : & Arc < ConnectionManager < L > > ,
475+ self : & Arc < Self > , amount_msat : Option < u64 > , connection_manager : & Arc < ConnectionManager < L > > ,
477476 ) -> Result < ( PaymentLease , u64 , LspConfig ) , Error > {
478477 let mut attempt = 1 ;
479478 loop {
480- let result = self . negotiate_variable_lease_once ( connection_manager) . await ;
479+ let result = self . negotiate_variable_lease_once ( amount_msat , connection_manager) . await ;
481480 match result {
482481 Err ( error) if should_retry_lease_negotiation ( error, attempt) => {
483482 log_warn ! (
@@ -494,7 +493,7 @@ where
494493 }
495494
496495 async fn negotiate_variable_lease_once (
497- self : & Arc < Self > , connection_manager : & Arc < ConnectionManager < L > > ,
496+ self : & Arc < Self > , amount_msat : Option < u64 > , connection_manager : & Arc < ConnectionManager < L > > ,
498497 ) -> Result < ( PaymentLease , u64 , LspConfig ) , Error > {
499498 let all_offers = self . gather_lsps2_offers ( connection_manager) . await ?;
500499 let mut rejected_for_fee = false ;
@@ -503,20 +502,34 @@ where
503502 . flat_map ( |( lsp, resp) | {
504503 resp. opening_fee_params_menu . into_iter ( ) . map ( move |params| ( lsp. clone ( ) , params) )
505504 } )
506- . map ( |( lsp, params) | {
507- let ppm = params. proportional as u64 ;
508- ( lsp, ppm, params)
509- } )
510- . filter ( |( _, _, params) | {
511- let allowed = self
505+ . filter_map ( |( lsp, params) | {
506+ // BOLT12 supplies a resolved amount here, while a BOLT11 zero-amount invoice does not.
507+ // In the former case, only negotiate parameters that can carry that exact payment and
508+ // compare providers by the total fee the payment would actually incur.
509+ let selection_fee = if let Some ( amount_msat) = amount_msat {
510+ if amount_msat < params. min_payment_size_msat
511+ || amount_msat > params. max_payment_size_msat
512+ {
513+ return None ;
514+ }
515+ compute_opening_fee (
516+ amount_msat,
517+ params. min_fee_msat ,
518+ params. proportional as u64 ,
519+ ) ?
520+ } else {
521+ params. proportional as u64
522+ } ;
523+ let fee_for_limit = amount_msat. map_or ( params. min_fee_msat , |_| selection_fee) ;
524+ let fee_allowed = self
512525 . config
513526 . lsps2_max_total_lsp_fee_limit_msat
514- . map_or ( true , |limit| params . min_fee_msat <= limit) ;
515- rejected_for_fee |= !allowed ;
516- allowed
527+ . map_or ( true , |limit| fee_for_limit <= limit) ;
528+ rejected_for_fee |= !fee_allowed ;
529+ fee_allowed . then_some ( ( lsp , selection_fee , params ) )
517530 } )
518531 . collect :: < Vec < _ > > ( ) ;
519- candidates. sort_unstable_by_key ( |( _, ppm , _) | * ppm ) ;
532+ candidates. sort_unstable_by_key ( |( _, fee , _) | * fee ) ;
520533 if candidates. is_empty ( ) {
521534 return Err ( if rejected_for_fee {
522535 log_error ! (
@@ -533,20 +546,29 @@ where
533546 try_lease_candidates (
534547 candidates,
535548 |( lsp, _, _) | lsp. node_id ,
536- |( lsp, proportional_fee_ppm_msat , opening_params) | {
549+ |( lsp, fee , opening_params) | {
537550 let client = Arc :: clone ( self ) ;
538551 async move {
539- log_debug ! (
540- client. logger,
541- "Choosing liquidity offer from LSP {}, will pay {}ppm msat in proportional LSP fees" ,
542- lsp. node_id,
543- proportional_fee_ppm_msat
544- ) ;
552+ if amount_msat. is_some ( ) {
553+ log_debug ! (
554+ client. logger,
555+ "Choosing liquidity offer from LSP {}, will pay {}msat in total LSP fees" ,
556+ lsp. node_id,
557+ fee
558+ ) ;
559+ } else {
560+ log_debug ! (
561+ client. logger,
562+ "Choosing liquidity offer from LSP {}, will pay {}ppm msat in proportional LSP fees" ,
563+ lsp. node_id,
564+ fee
565+ ) ;
566+ }
545567 match client
546568 . lsps2_send_buy_request ( None , opening_params, Some ( & lsp. node_id ) )
547569 . await
548570 {
549- Ok ( lease) => Ok ( ( lease, proportional_fee_ppm_msat , lsp) ) ,
571+ Ok ( lease) => Ok ( ( lease, fee , lsp) ) ,
550572 Err ( error) => {
551573 log_warn ! (
552574 client. logger,
@@ -621,11 +643,11 @@ where
621643 . lease_state
622644 . lock ( )
623645 . expect ( "lock" )
624- . has_variable_amount ( self . config . lsps2_max_total_lsp_fee_limit_msat )
646+ . has_variable_amount ( None , self . config . lsps2_max_total_lsp_fee_limit_msat )
625647 {
626648 return Ok ( ( ) ) ;
627649 }
628- self . negotiate_variable_lease ( connection_manager) . await ?;
650+ self . negotiate_variable_lease ( None , connection_manager) . await ?;
629651 Ok ( ( ) )
630652 }
631653
@@ -822,22 +844,22 @@ where
822844 }
823845
824846 async fn take_cached_variable_lease (
825- & self ,
847+ & self , amount_msat : Option < u64 > ,
826848 ) -> Result < Option < ( PaymentLease , u64 , LspConfig ) > , Error > {
827849 loop {
828- let Some ( ( lease, proportional_fee ) ) = self
850+ let Some ( ( lease, fee ) ) = self
829851 . lease_state
830852 . lock ( )
831853 . expect ( "lock" )
832- . variable_amount ( self . config . lsps2_max_total_lsp_fee_limit_msat )
854+ . variable_amount ( amount_msat , self . config . lsps2_max_total_lsp_fee_limit_msat )
833855 else {
834856 return Ok ( None ) ;
835857 } ;
836858 let lease = self . consume_selected_lease ( lease) . await ?;
837859 if let Some ( lsp) =
838860 select_lsps_for_protocol ( & self . lsp_nodes , 2 , Some ( & lease. id . lsp_node_id ) )
839861 {
840- return Ok ( Some ( ( lease, proportional_fee , lsp) ) ) ;
862+ return Ok ( Some ( ( lease, fee , lsp) ) ) ;
841863 }
842864 }
843865 }
0 commit comments