|
58 | 58 | } |
59 | 59 |
|
60 | 60 | const DEFAULT_BOLT12_INVOICE_EXPIRY_SECS: u32 = 2 * 60 * 60; |
| 61 | +const LEASE_NEGOTIATION_MAX_ATTEMPTS: usize = 3; |
| 62 | + |
| 63 | +fn should_retry_lease_negotiation(error: Error, attempt: usize) -> bool { |
| 64 | + error == Error::LiquidityRequestFailed && attempt < LEASE_NEGOTIATION_MAX_ATTEMPTS |
| 65 | +} |
61 | 66 |
|
62 | 67 | pub(crate) struct JitInvoiceResponse { |
63 | 68 | pub(crate) payment_metadata: BTreeMap<u64, Vec<u8>>, |
@@ -323,6 +328,34 @@ where |
323 | 328 |
|
324 | 329 | async fn negotiate_fixed_lease( |
325 | 330 | self: &Arc<Self>, amount_msat: u64, connection_manager: &Arc<ConnectionManager<L>>, |
| 331 | + ) -> Result<(PaymentLease, u64, LspConfig), Error> { |
| 332 | + let mut attempt = 1; |
| 333 | + loop { |
| 334 | + let result = self |
| 335 | + .negotiate_fixed_lease_once( |
| 336 | + amount_msat, |
| 337 | + max_total_lsp_fee_limit_msat, |
| 338 | + connection_manager, |
| 339 | + ) |
| 340 | + .await; |
| 341 | + match result { |
| 342 | + Err(error) if should_retry_lease_negotiation(error, attempt) => { |
| 343 | + log_warn!( |
| 344 | + self.logger, |
| 345 | + "LSPS2 lease negotiation attempt {} failed, retrying: {}", |
| 346 | + attempt, |
| 347 | + error |
| 348 | + ); |
| 349 | + attempt += 1; |
| 350 | + }, |
| 351 | + result => return result, |
| 352 | + } |
| 353 | + } |
| 354 | + } |
| 355 | + |
| 356 | + async fn negotiate_fixed_lease_once( |
| 357 | + self: &Arc<Self>, amount_msat: u64, max_total_lsp_fee_limit_msat: Option<u64>, |
| 358 | + connection_manager: &Arc<ConnectionManager<L>>, |
326 | 359 | ) -> Result<(PaymentLease, u64, LspConfig), Error> { |
327 | 360 | let all_offers = self.gather_lsps2_offers(connection_manager).await?; |
328 | 361 | let (cheapest_lsp, min_total_fee_msat, min_opening_params) = all_offers |
@@ -409,6 +442,33 @@ where |
409 | 442 |
|
410 | 443 | async fn negotiate_variable_lease( |
411 | 444 | self: &Arc<Self>, connection_manager: &Arc<ConnectionManager<L>>, |
| 445 | + ) -> Result<(PaymentLease, u64, LspConfig), Error> { |
| 446 | + let mut attempt = 1; |
| 447 | + loop { |
| 448 | + let result = self |
| 449 | + .negotiate_variable_lease_once( |
| 450 | + max_proportional_lsp_fee_limit_ppm_msat, |
| 451 | + connection_manager, |
| 452 | + ) |
| 453 | + .await; |
| 454 | + match result { |
| 455 | + Err(error) if should_retry_lease_negotiation(error, attempt) => { |
| 456 | + log_warn!( |
| 457 | + self.logger, |
| 458 | + "LSPS2 lease negotiation attempt {} failed, retrying: {}", |
| 459 | + attempt, |
| 460 | + error |
| 461 | + ); |
| 462 | + attempt += 1; |
| 463 | + }, |
| 464 | + result => return result, |
| 465 | + } |
| 466 | + } |
| 467 | + } |
| 468 | + |
| 469 | + async fn negotiate_variable_lease_once( |
| 470 | + self: &Arc<Self>, max_proportional_lsp_fee_limit_ppm_msat: Option<u64>, |
| 471 | + connection_manager: &Arc<ConnectionManager<L>>, |
412 | 472 | ) -> Result<(PaymentLease, u64, LspConfig), Error> { |
413 | 473 | let all_offers = self.gather_lsps2_offers(connection_manager).await?; |
414 | 474 | let mut rejected_for_fee = false; |
@@ -981,6 +1041,15 @@ mod tests { |
981 | 1041 | assert_eq!(result, Err(())); |
982 | 1042 | assert!(!*consumed.lock().unwrap()); |
983 | 1043 | } |
| 1044 | + |
| 1045 | + #[test] |
| 1046 | + fn lease_negotiation_retries_only_transient_failures() { |
| 1047 | + assert!(should_retry_lease_negotiation(Error::LiquidityRequestFailed, 1)); |
| 1048 | + assert!(should_retry_lease_negotiation(Error::LiquidityRequestFailed, 2)); |
| 1049 | + assert!(!should_retry_lease_negotiation(Error::LiquidityRequestFailed, 3)); |
| 1050 | + assert!(!should_retry_lease_negotiation(Error::LiquidityFeeTooHigh, 1)); |
| 1051 | + assert!(!should_retry_lease_negotiation(Error::LiquiditySourceUnavailable, 1)); |
| 1052 | + } |
984 | 1053 | } |
985 | 1054 |
|
986 | 1055 | pub(crate) mod router; |
|
0 commit comments