|
57 | 57 | } |
58 | 58 |
|
59 | 59 | const DEFAULT_BOLT12_INVOICE_EXPIRY_SECS: u32 = 2 * 60 * 60; |
| 60 | +const LEASE_NEGOTIATION_MAX_ATTEMPTS: usize = 3; |
| 61 | + |
| 62 | +fn should_retry_lease_negotiation(error: Error, attempt: usize) -> bool { |
| 63 | + error == Error::LiquidityRequestFailed && attempt < LEASE_NEGOTIATION_MAX_ATTEMPTS |
| 64 | +} |
60 | 65 |
|
61 | 66 | pub(crate) struct JitInvoiceResponse { |
62 | 67 | pub(crate) payment_metadata: BTreeMap<u64, Vec<u8>>, |
@@ -312,6 +317,34 @@ where |
312 | 317 |
|
313 | 318 | async fn negotiate_fixed_lease( |
314 | 319 | self: &Arc<Self>, amount_msat: u64, connection_manager: &Arc<ConnectionManager<L>>, |
| 320 | + ) -> Result<(PaymentLease, u64, LspConfig), Error> { |
| 321 | + let mut attempt = 1; |
| 322 | + loop { |
| 323 | + let result = self |
| 324 | + .negotiate_fixed_lease_once( |
| 325 | + amount_msat, |
| 326 | + max_total_lsp_fee_limit_msat, |
| 327 | + connection_manager, |
| 328 | + ) |
| 329 | + .await; |
| 330 | + match result { |
| 331 | + Err(error) if should_retry_lease_negotiation(error, attempt) => { |
| 332 | + log_warn!( |
| 333 | + self.logger, |
| 334 | + "LSPS2 lease negotiation attempt {} failed, retrying: {}", |
| 335 | + attempt, |
| 336 | + error |
| 337 | + ); |
| 338 | + attempt += 1; |
| 339 | + }, |
| 340 | + result => return result, |
| 341 | + } |
| 342 | + } |
| 343 | + } |
| 344 | + |
| 345 | + async fn negotiate_fixed_lease_once( |
| 346 | + self: &Arc<Self>, amount_msat: u64, max_total_lsp_fee_limit_msat: Option<u64>, |
| 347 | + connection_manager: &Arc<ConnectionManager<L>>, |
315 | 348 | ) -> Result<(PaymentLease, u64, LspConfig), Error> { |
316 | 349 | let all_offers = self.gather_lsps2_offers(connection_manager).await?; |
317 | 350 | let (cheapest_lsp, min_total_fee_msat, min_opening_params) = all_offers |
@@ -398,6 +431,33 @@ where |
398 | 431 |
|
399 | 432 | async fn negotiate_variable_lease( |
400 | 433 | self: &Arc<Self>, connection_manager: &Arc<ConnectionManager<L>>, |
| 434 | + ) -> Result<(PaymentLease, u64, LspConfig), Error> { |
| 435 | + let mut attempt = 1; |
| 436 | + loop { |
| 437 | + let result = self |
| 438 | + .negotiate_variable_lease_once( |
| 439 | + max_proportional_lsp_fee_limit_ppm_msat, |
| 440 | + connection_manager, |
| 441 | + ) |
| 442 | + .await; |
| 443 | + match result { |
| 444 | + Err(error) if should_retry_lease_negotiation(error, attempt) => { |
| 445 | + log_warn!( |
| 446 | + self.logger, |
| 447 | + "LSPS2 lease negotiation attempt {} failed, retrying: {}", |
| 448 | + attempt, |
| 449 | + error |
| 450 | + ); |
| 451 | + attempt += 1; |
| 452 | + }, |
| 453 | + result => return result, |
| 454 | + } |
| 455 | + } |
| 456 | + } |
| 457 | + |
| 458 | + async fn negotiate_variable_lease_once( |
| 459 | + self: &Arc<Self>, max_proportional_lsp_fee_limit_ppm_msat: Option<u64>, |
| 460 | + connection_manager: &Arc<ConnectionManager<L>>, |
401 | 461 | ) -> Result<(PaymentLease, u64, LspConfig), Error> { |
402 | 462 | let all_offers = self.gather_lsps2_offers(connection_manager).await?; |
403 | 463 | let mut rejected_for_fee = false; |
@@ -970,6 +1030,15 @@ mod tests { |
970 | 1030 | assert_eq!(result, Err(())); |
971 | 1031 | assert!(!*consumed.lock().unwrap()); |
972 | 1032 | } |
| 1033 | + |
| 1034 | + #[test] |
| 1035 | + fn lease_negotiation_retries_only_transient_failures() { |
| 1036 | + assert!(should_retry_lease_negotiation(Error::LiquidityRequestFailed, 1)); |
| 1037 | + assert!(should_retry_lease_negotiation(Error::LiquidityRequestFailed, 2)); |
| 1038 | + assert!(!should_retry_lease_negotiation(Error::LiquidityRequestFailed, 3)); |
| 1039 | + assert!(!should_retry_lease_negotiation(Error::LiquidityFeeTooHigh, 1)); |
| 1040 | + assert!(!should_retry_lease_negotiation(Error::LiquiditySourceUnavailable, 1)); |
| 1041 | + } |
973 | 1042 | } |
974 | 1043 |
|
975 | 1044 | pub(crate) mod router; |
|
0 commit comments