Skip to content

Commit 233c5f7

Browse files
committed
lsps2: Refill consumed payment leases
Keep one usable lease ready after fixed or variable receive flows consume cached parameters. Foreground callers share the refill lock and reuse its result when it completes. Co-Authored-By: HAL 9000
1 parent 3e3722a commit 233c5f7

2 files changed

Lines changed: 129 additions & 4 deletions

File tree

src/liquidity/client/lsps2/mod.rs

Lines changed: 92 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@ where
270270
if let Some((lease, total_fee_msat, lsp)) =
271271
self.take_cached_fixed_lease(amount_msat).await?
272272
{
273+
self.schedule_fixed_lease_refill(amount_msat, connection_manager);
273274
return Ok((lease, total_fee_msat, lsp, false));
274275
}
275276
let request_lock = self
@@ -281,9 +282,20 @@ where
281282
if let Some((lease, total_fee_msat, lsp)) =
282283
self.take_cached_fixed_lease(amount_msat).await?
283284
{
285+
self.schedule_fixed_lease_refill(amount_msat, connection_manager);
284286
return Ok((lease, total_fee_msat, lsp, false));
285287
}
286288

289+
let (negotiated_lease, min_total_fee_msat, cheapest_lsp) =
290+
self.negotiate_fixed_lease(amount_msat, connection_manager).await?;
291+
let lease = self.consume_lease(&negotiated_lease.id).await?;
292+
self.schedule_fixed_lease_refill(amount_msat, connection_manager);
293+
Ok((lease, min_total_fee_msat, cheapest_lsp, true))
294+
}
295+
296+
async fn negotiate_fixed_lease(
297+
self: &Arc<Self>, amount_msat: u64, connection_manager: &Arc<ConnectionManager<L>>,
298+
) -> Result<(PaymentLease, u64, LspConfig), Error> {
287299
let all_offers = self.gather_lsps2_offers(connection_manager).await?;
288300
let (cheapest_lsp, min_total_fee_msat, min_opening_params) = all_offers
289301
.into_iter()
@@ -339,14 +351,14 @@ where
339351
Some(&cheapest_lsp.node_id),
340352
)
341353
.await?;
342-
let lease = self.consume_lease(&negotiated_lease.id).await?;
343-
Ok((lease, min_total_fee_msat, cheapest_lsp, true))
354+
Ok((negotiated_lease, min_total_fee_msat, cheapest_lsp))
344355
}
345356

346357
async fn acquire_variable_lease(
347358
self: &Arc<Self>, connection_manager: &Arc<ConnectionManager<L>>,
348359
) -> Result<(PaymentLease, u64, LspConfig, bool), Error> {
349360
if let Some((lease, proportional_fee, lsp)) = self.take_cached_variable_lease().await? {
361+
self.schedule_variable_lease_refill(connection_manager);
350362
return Ok((lease, proportional_fee, lsp, false));
351363
}
352364
let request_lock = self
@@ -356,9 +368,20 @@ where
356368
.request_lock(LeaseRequestKey::Variable);
357369
let _request_guard = request_lock.lock().await;
358370
if let Some((lease, proportional_fee, lsp)) = self.take_cached_variable_lease().await? {
371+
self.schedule_variable_lease_refill(connection_manager);
359372
return Ok((lease, proportional_fee, lsp, false));
360373
}
361374

375+
let (negotiated_lease, min_prop_fee_ppm_msat, cheapest_lsp) =
376+
self.negotiate_variable_lease(connection_manager).await?;
377+
let lease = self.consume_lease(&negotiated_lease.id).await?;
378+
self.schedule_variable_lease_refill(connection_manager);
379+
Ok((lease, min_prop_fee_ppm_msat, cheapest_lsp, true))
380+
}
381+
382+
async fn negotiate_variable_lease(
383+
self: &Arc<Self>, connection_manager: &Arc<ConnectionManager<L>>,
384+
) -> Result<(PaymentLease, u64, LspConfig), Error> {
362385
let all_offers = self.gather_lsps2_offers(connection_manager).await?;
363386
let mut rejected_for_fee = false;
364387
let (cheapest_lsp, min_prop_fee_ppm_msat, min_opening_params) = all_offers
@@ -400,8 +423,73 @@ where
400423
let negotiated_lease = self
401424
.lsps2_send_buy_request(None, min_opening_params, Some(&cheapest_lsp.node_id))
402425
.await?;
403-
let lease = self.consume_lease(&negotiated_lease.id).await?;
404-
Ok((lease, min_prop_fee_ppm_msat, cheapest_lsp, true))
426+
Ok((negotiated_lease, min_prop_fee_ppm_msat, cheapest_lsp))
427+
}
428+
429+
fn schedule_fixed_lease_refill(
430+
self: &Arc<Self>, amount_msat: u64, connection_manager: &Arc<ConnectionManager<L>>,
431+
) {
432+
let client = Arc::clone(self);
433+
let connection_manager = Arc::clone(connection_manager);
434+
tokio::spawn(async move {
435+
if let Err(error) = client.cache_fixed_lease(amount_msat, &connection_manager).await {
436+
log_warn!(client.logger, "Failed refilling LSPS2 payment lease: {}", error);
437+
}
438+
});
439+
}
440+
441+
fn schedule_variable_lease_refill(
442+
self: &Arc<Self>, connection_manager: &Arc<ConnectionManager<L>>,
443+
) {
444+
let client = Arc::clone(self);
445+
let connection_manager = Arc::clone(connection_manager);
446+
tokio::spawn(async move {
447+
if let Err(error) = client.cache_variable_lease(&connection_manager).await {
448+
log_warn!(client.logger, "Failed refilling LSPS2 payment lease: {}", error);
449+
}
450+
});
451+
}
452+
453+
async fn cache_fixed_lease(
454+
self: &Arc<Self>, amount_msat: u64, connection_manager: &Arc<ConnectionManager<L>>,
455+
) -> Result<(), Error> {
456+
let request_lock = self
457+
.pending_lease_request_state
458+
.lock()
459+
.expect("lock")
460+
.request_lock(LeaseRequestKey::Fixed(amount_msat));
461+
let _request_guard = request_lock.lock().await;
462+
if self
463+
.lease_state
464+
.lock()
465+
.expect("lock")
466+
.has_fixed_amount(amount_msat, self.config.lsps2_max_total_lsp_fee_limit_msat)
467+
{
468+
return Ok(());
469+
}
470+
self.negotiate_fixed_lease(amount_msat, connection_manager).await?;
471+
Ok(())
472+
}
473+
474+
async fn cache_variable_lease(
475+
self: &Arc<Self>, connection_manager: &Arc<ConnectionManager<L>>,
476+
) -> Result<(), Error> {
477+
let request_lock = self
478+
.pending_lease_request_state
479+
.lock()
480+
.expect("lock")
481+
.request_lock(LeaseRequestKey::Variable);
482+
let _request_guard = request_lock.lock().await;
483+
if self
484+
.lease_state
485+
.lock()
486+
.expect("lock")
487+
.has_variable_amount(self.config.lsps2_max_total_lsp_fee_limit_msat)
488+
{
489+
return Ok(());
490+
}
491+
self.negotiate_variable_lease(connection_manager).await?;
492+
Ok(())
405493
}
406494

407495
async fn gather_lsps2_offers(

src/liquidity/client/lsps2/state.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,21 @@ impl LSPS2LeaseState {
405405
self.leases.get(&id).cloned().map(|lease| (lease, fee_msat))
406406
}
407407

408+
pub(crate) fn has_fixed_amount(&self, amount_msat: u64, max_fee_msat: Option<u64>) -> bool {
409+
self.leases
410+
.values()
411+
.filter(|lease| lease.payment_size_msat == Some(amount_msat))
412+
.filter(|lease| is_lease_usable(lease))
413+
.filter_map(|lease| {
414+
compute_opening_fee(
415+
amount_msat,
416+
lease.params.min_fee_msat,
417+
lease.params.proportional as u64,
418+
)
419+
})
420+
.any(|fee_msat| max_fee_msat.map_or(true, |max| fee_msat <= max))
421+
}
422+
408423
pub(crate) fn variable_amount(
409424
&self, max_total_fee_msat: Option<u64>,
410425
) -> Option<(PaymentLease, u64)> {
@@ -421,6 +436,14 @@ impl LSPS2LeaseState {
421436
self.leases.get(&id).cloned().map(|lease| (lease, proportional_fee))
422437
}
423438

439+
pub(crate) fn has_variable_amount(&self, max_total_fee_msat: Option<u64>) -> bool {
440+
self.leases
441+
.values()
442+
.filter(|lease| lease.payment_size_msat.is_none())
443+
.filter(|lease| is_lease_usable(lease))
444+
.any(|lease| max_total_fee_msat.map_or(true, |max| lease.params.min_fee_msat <= max))
445+
}
446+
424447
pub(crate) fn prune(&mut self) {
425448
self.leases.retain(|_, lease| is_lease_usable(lease));
426449
}
@@ -563,6 +586,20 @@ mod tests {
563586
assert_eq!(state.variable_amount(Some(50)).unwrap().0.id, variable.id);
564587
}
565588

589+
#[test]
590+
fn detects_cached_leases_for_refill() {
591+
let valid_until = now_secs() + MIN_LEASE_REMAINING_SECS + 60;
592+
let fixed = lease(2, 46, 100, Some(1_000), valid_until);
593+
let variable = lease(3, 47, 50, None, valid_until);
594+
let state = LSPS2LeaseState::from_leases(vec![fixed, variable]);
595+
596+
assert!(state.has_fixed_amount(1_000, Some(100)));
597+
assert!(!state.has_fixed_amount(1_000, Some(99)));
598+
assert!(!state.has_fixed_amount(2_000, None));
599+
assert!(state.has_variable_amount(Some(50)));
600+
assert!(!state.has_variable_amount(Some(49)));
601+
}
602+
566603
fn cache_target(
567604
id: LeaseCacheTargetId, absolute_expiry: Option<u64>, last_accessed: u64,
568605
) -> LeaseCacheTarget {

0 commit comments

Comments
 (0)