|
61 | 61 | } |
62 | 62 |
|
63 | 63 | const DEFAULT_BOLT12_INVOICE_EXPIRY_SECS: u32 = 2 * 60 * 60; |
| 64 | +const LEASE_NEGOTIATION_MAX_ATTEMPTS: usize = 3; |
| 65 | + |
| 66 | +fn should_retry_lease_negotiation(error: Error, attempt: usize) -> bool { |
| 67 | + error == Error::LiquidityRequestFailed && attempt < LEASE_NEGOTIATION_MAX_ATTEMPTS |
| 68 | +} |
64 | 69 |
|
65 | 70 | pub(crate) struct JitInvoiceResponse { |
66 | 71 | pub(crate) payment_metadata: BTreeMap<u64, Vec<u8>>, |
@@ -316,6 +321,34 @@ where |
316 | 321 |
|
317 | 322 | async fn negotiate_fixed_lease( |
318 | 323 | self: &Arc<Self>, amount_msat: u64, connection_manager: &Arc<ConnectionManager<L>>, |
| 324 | + ) -> Result<(PaymentLease, u64, LspConfig), Error> { |
| 325 | + let mut attempt = 1; |
| 326 | + loop { |
| 327 | + let result = self |
| 328 | + .negotiate_fixed_lease_once( |
| 329 | + amount_msat, |
| 330 | + max_total_lsp_fee_limit_msat, |
| 331 | + connection_manager, |
| 332 | + ) |
| 333 | + .await; |
| 334 | + match result { |
| 335 | + Err(error) if should_retry_lease_negotiation(error, attempt) => { |
| 336 | + log_warn!( |
| 337 | + self.logger, |
| 338 | + "LSPS2 lease negotiation attempt {} failed, retrying: {}", |
| 339 | + attempt, |
| 340 | + error |
| 341 | + ); |
| 342 | + attempt += 1; |
| 343 | + }, |
| 344 | + result => return result, |
| 345 | + } |
| 346 | + } |
| 347 | + } |
| 348 | + |
| 349 | + async fn negotiate_fixed_lease_once( |
| 350 | + self: &Arc<Self>, amount_msat: u64, max_total_lsp_fee_limit_msat: Option<u64>, |
| 351 | + connection_manager: &Arc<ConnectionManager<L>>, |
319 | 352 | ) -> Result<(PaymentLease, u64, LspConfig), Error> { |
320 | 353 | let all_offers = self.gather_lsps2_offers(connection_manager).await?; |
321 | 354 | let (cheapest_lsp, min_total_fee_msat, min_opening_params) = all_offers |
@@ -402,6 +435,33 @@ where |
402 | 435 |
|
403 | 436 | async fn negotiate_variable_lease( |
404 | 437 | self: &Arc<Self>, connection_manager: &Arc<ConnectionManager<L>>, |
| 438 | + ) -> Result<(PaymentLease, u64, LspConfig), Error> { |
| 439 | + let mut attempt = 1; |
| 440 | + loop { |
| 441 | + let result = self |
| 442 | + .negotiate_variable_lease_once( |
| 443 | + max_proportional_lsp_fee_limit_ppm_msat, |
| 444 | + connection_manager, |
| 445 | + ) |
| 446 | + .await; |
| 447 | + match result { |
| 448 | + Err(error) if should_retry_lease_negotiation(error, attempt) => { |
| 449 | + log_warn!( |
| 450 | + self.logger, |
| 451 | + "LSPS2 lease negotiation attempt {} failed, retrying: {}", |
| 452 | + attempt, |
| 453 | + error |
| 454 | + ); |
| 455 | + attempt += 1; |
| 456 | + }, |
| 457 | + result => return result, |
| 458 | + } |
| 459 | + } |
| 460 | + } |
| 461 | + |
| 462 | + async fn negotiate_variable_lease_once( |
| 463 | + self: &Arc<Self>, max_proportional_lsp_fee_limit_ppm_msat: Option<u64>, |
| 464 | + connection_manager: &Arc<ConnectionManager<L>>, |
405 | 465 | ) -> Result<(PaymentLease, u64, LspConfig), Error> { |
406 | 466 | let all_offers = self.gather_lsps2_offers(connection_manager).await?; |
407 | 467 | let mut rejected_for_fee = false; |
@@ -977,6 +1037,15 @@ mod tests { |
977 | 1037 | assert_eq!(result, Err(())); |
978 | 1038 | assert_eq!(*restored.lock().unwrap(), Some(42)); |
979 | 1039 | } |
| 1040 | + |
| 1041 | + #[test] |
| 1042 | + fn lease_negotiation_retries_only_transient_failures() { |
| 1043 | + assert!(should_retry_lease_negotiation(Error::LiquidityRequestFailed, 1)); |
| 1044 | + assert!(should_retry_lease_negotiation(Error::LiquidityRequestFailed, 2)); |
| 1045 | + assert!(!should_retry_lease_negotiation(Error::LiquidityRequestFailed, 3)); |
| 1046 | + assert!(!should_retry_lease_negotiation(Error::LiquidityFeeTooHigh, 1)); |
| 1047 | + assert!(!should_retry_lease_negotiation(Error::LiquiditySourceUnavailable, 1)); |
| 1048 | + } |
980 | 1049 | } |
981 | 1050 |
|
982 | 1051 | pub(crate) mod router; |
|
0 commit comments