@@ -218,18 +218,17 @@ where
218218 } ,
219219 )
220220 } ,
221- JitInvoiceRequest :: Variable { .. } => {
222- let ( lease, proportional_fee, _, _) =
223- self . acquire_variable_lease ( & connection_manager) . await ?;
221+ JitInvoiceRequest :: Variable { amount_msat, .. } => {
222+ // A BOLT12 invoice request has already resolved the payment amount. Use it to avoid
223+ // consuming a cached variable lease outside its advertised range, and record the exact
224+ // fee for that amount so payment validation need not accept the broader node-wide cap.
225+ let ( lease, total_fee_msat, _, _) =
226+ self . acquire_variable_lease ( Some ( amount_msat) , & connection_manager) . await ?;
224227 (
225228 lease,
226229 LSPS2Parameters {
227- max_total_opening_fee_msat : self . config . lsps2_max_total_lsp_fee_limit_msat ,
228- max_proportional_opening_fee_ppm_msat : self
229- . config
230- . lsps2_max_total_lsp_fee_limit_msat
231- . is_none ( )
232- . then_some ( proportional_fee) ,
230+ max_total_opening_fee_msat : Some ( total_fee_msat) ,
231+ max_proportional_opening_fee_ppm_msat : None ,
233232 } ,
234233 )
235234 } ,
@@ -293,7 +292,7 @@ where
293292 payment_hash : Option < PaymentHash > , connection_manager : Arc < ConnectionManager < L > > ,
294293 ) -> Result < ( Bolt11Invoice , LspConfig ) , Error > {
295294 let ( lease, proportional_fee, lsp, was_negotiated) =
296- self . acquire_variable_lease ( & connection_manager) . await ?;
295+ self . acquire_variable_lease ( None , & connection_manager) . await ?;
297296 let lsps2_parameters = LSPS2Parameters {
298297 max_total_opening_fee_msat : self . config . lsps2_max_total_lsp_fee_limit_msat ,
299298 max_proportional_opening_fee_ppm_msat : self
@@ -452,36 +451,36 @@ where
452451 }
453452
454453 async fn acquire_variable_lease (
455- self : & Arc < Self > , connection_manager : & Arc < ConnectionManager < L > > ,
454+ self : & Arc < Self > , amount_msat : Option < u64 > , connection_manager : & Arc < ConnectionManager < L > > ,
456455 ) -> Result < ( PaymentLease , u64 , LspConfig , bool ) , Error > {
457- if let Some ( ( lease, proportional_fee , lsp) ) = self . take_cached_variable_lease ( ) . await ? {
456+ if let Some ( ( lease, fee , lsp) ) = self . take_cached_variable_lease ( amount_msat ) . await ? {
458457 self . schedule_variable_lease_refill ( connection_manager) ;
459- return Ok ( ( lease, proportional_fee , lsp, false ) ) ;
458+ return Ok ( ( lease, fee , lsp, false ) ) ;
460459 }
461460 let request_lock = self
462461 . pending_lease_request_state
463462 . lock ( )
464463 . expect ( "lock" )
465464 . request_lock ( LeaseRequestKey :: Variable ) ;
466465 let _request_guard = request_lock. lock ( ) . await ;
467- if let Some ( ( lease, proportional_fee , lsp) ) = self . take_cached_variable_lease ( ) . await ? {
466+ if let Some ( ( lease, fee , lsp) ) = self . take_cached_variable_lease ( amount_msat ) . await ? {
468467 self . schedule_variable_lease_refill ( connection_manager) ;
469- return Ok ( ( lease, proportional_fee , lsp, false ) ) ;
468+ return Ok ( ( lease, fee , lsp, false ) ) ;
470469 }
471470
472- let ( negotiated_lease, min_prop_fee_ppm_msat , cheapest_lsp) =
473- self . negotiate_variable_lease ( connection_manager) . await ?;
471+ let ( negotiated_lease, fee , cheapest_lsp) =
472+ self . negotiate_variable_lease ( amount_msat , connection_manager) . await ?;
474473 let lease = self . consume_lease ( & negotiated_lease. id ) . await ?;
475474 self . schedule_variable_lease_refill ( connection_manager) ;
476- Ok ( ( lease, min_prop_fee_ppm_msat , cheapest_lsp, true ) )
475+ Ok ( ( lease, fee , cheapest_lsp, true ) )
477476 }
478477
479478 async fn negotiate_variable_lease (
480- self : & Arc < Self > , connection_manager : & Arc < ConnectionManager < L > > ,
479+ self : & Arc < Self > , amount_msat : Option < u64 > , connection_manager : & Arc < ConnectionManager < L > > ,
481480 ) -> Result < ( PaymentLease , u64 , LspConfig ) , Error > {
482481 let mut attempt = 1 ;
483482 loop {
484- let result = self . negotiate_variable_lease_once ( connection_manager) . await ;
483+ let result = self . negotiate_variable_lease_once ( amount_msat , connection_manager) . await ;
485484 match result {
486485 Err ( error) if should_retry_lease_negotiation ( error, attempt) => {
487486 log_warn ! (
@@ -498,7 +497,7 @@ where
498497 }
499498
500499 async fn negotiate_variable_lease_once (
501- self : & Arc < Self > , connection_manager : & Arc < ConnectionManager < L > > ,
500+ self : & Arc < Self > , amount_msat : Option < u64 > , connection_manager : & Arc < ConnectionManager < L > > ,
502501 ) -> Result < ( PaymentLease , u64 , LspConfig ) , Error > {
503502 let all_offers = self . gather_lsps2_offers ( connection_manager) . await ?;
504503 let mut rejected_for_fee = false ;
@@ -507,20 +506,34 @@ where
507506 . flat_map ( |( lsp, resp) | {
508507 resp. opening_fee_params_menu . into_iter ( ) . map ( move |params| ( lsp. clone ( ) , params) )
509508 } )
510- . map ( |( lsp, params) | {
511- let ppm = params. proportional as u64 ;
512- ( lsp, ppm, params)
513- } )
514- . filter ( |( _, _, params) | {
515- let allowed = self
509+ . filter_map ( |( lsp, params) | {
510+ // BOLT12 supplies a resolved amount here, while a BOLT11 zero-amount invoice does not.
511+ // In the former case, only negotiate parameters that can carry that exact payment and
512+ // compare providers by the total fee the payment would actually incur.
513+ let selection_fee = if let Some ( amount_msat) = amount_msat {
514+ if amount_msat < params. min_payment_size_msat
515+ || amount_msat > params. max_payment_size_msat
516+ {
517+ return None ;
518+ }
519+ compute_opening_fee (
520+ amount_msat,
521+ params. min_fee_msat ,
522+ params. proportional as u64 ,
523+ ) ?
524+ } else {
525+ params. proportional as u64
526+ } ;
527+ let fee_for_limit = amount_msat. map_or ( params. min_fee_msat , |_| selection_fee) ;
528+ let fee_allowed = self
516529 . config
517530 . lsps2_max_total_lsp_fee_limit_msat
518- . map_or ( true , |limit| params . min_fee_msat <= limit) ;
519- rejected_for_fee |= !allowed ;
520- allowed
531+ . map_or ( true , |limit| fee_for_limit <= limit) ;
532+ rejected_for_fee |= !fee_allowed ;
533+ fee_allowed . then_some ( ( lsp , selection_fee , params ) )
521534 } )
522535 . collect :: < Vec < _ > > ( ) ;
523- candidates. sort_unstable_by_key ( |( _, ppm , _) | * ppm ) ;
536+ candidates. sort_unstable_by_key ( |( _, fee , _) | * fee ) ;
524537 if candidates. is_empty ( ) {
525538 return Err ( if rejected_for_fee {
526539 log_error ! (
@@ -537,20 +550,29 @@ where
537550 try_lease_candidates (
538551 candidates,
539552 |( lsp, _, _) | lsp. node_id ,
540- |( lsp, proportional_fee_ppm_msat , opening_params) | {
553+ |( lsp, fee , opening_params) | {
541554 let client = Arc :: clone ( self ) ;
542555 async move {
543- log_debug ! (
544- client. logger,
545- "Choosing liquidity offer from LSP {}, will pay {}ppm msat in proportional LSP fees" ,
546- lsp. node_id,
547- proportional_fee_ppm_msat
548- ) ;
556+ if amount_msat. is_some ( ) {
557+ log_debug ! (
558+ client. logger,
559+ "Choosing liquidity offer from LSP {}, will pay {}msat in total LSP fees" ,
560+ lsp. node_id,
561+ fee
562+ ) ;
563+ } else {
564+ log_debug ! (
565+ client. logger,
566+ "Choosing liquidity offer from LSP {}, will pay {}ppm msat in proportional LSP fees" ,
567+ lsp. node_id,
568+ fee
569+ ) ;
570+ }
549571 match client
550572 . lsps2_send_buy_request ( None , opening_params, Some ( & lsp. node_id ) )
551573 . await
552574 {
553- Ok ( lease) => Ok ( ( lease, proportional_fee_ppm_msat , lsp) ) ,
575+ Ok ( lease) => Ok ( ( lease, fee , lsp) ) ,
554576 Err ( error) => {
555577 log_warn ! (
556578 client. logger,
@@ -625,11 +647,11 @@ where
625647 . lease_state
626648 . lock ( )
627649 . expect ( "lock" )
628- . has_variable_amount ( self . config . lsps2_max_total_lsp_fee_limit_msat )
650+ . has_variable_amount ( None , self . config . lsps2_max_total_lsp_fee_limit_msat )
629651 {
630652 return Ok ( ( ) ) ;
631653 }
632- self . negotiate_variable_lease ( connection_manager) . await ?;
654+ self . negotiate_variable_lease ( None , connection_manager) . await ?;
633655 Ok ( ( ) )
634656 }
635657
@@ -829,22 +851,22 @@ where
829851 }
830852
831853 async fn take_cached_variable_lease (
832- & self ,
854+ & self , amount_msat : Option < u64 > ,
833855 ) -> Result < Option < ( PaymentLease , u64 , LspConfig ) > , Error > {
834856 loop {
835- let Some ( ( lease, proportional_fee ) ) = self
857+ let Some ( ( lease, fee ) ) = self
836858 . lease_state
837859 . lock ( )
838860 . expect ( "lock" )
839- . variable_amount ( self . config . lsps2_max_total_lsp_fee_limit_msat )
861+ . variable_amount ( amount_msat , self . config . lsps2_max_total_lsp_fee_limit_msat )
840862 else {
841863 return Ok ( None ) ;
842864 } ;
843865 let lease = self . consume_selected_lease ( lease) . await ?;
844866 if let Some ( lsp) =
845867 select_lsps_for_protocol ( & self . lsp_nodes , 2 , Some ( & lease. id . lsp_node_id ) )
846868 {
847- return Ok ( Some ( ( lease, proportional_fee , lsp) ) ) ;
869+ return Ok ( Some ( ( lease, fee , lsp) ) ) ;
848870 }
849871 }
850872 }
0 commit comments