Skip to content

Commit 211473a

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 fedc50e commit 211473a

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
@@ -281,6 +281,7 @@ where
281281
if let Some((lease, total_fee_msat, lsp)) =
282282
self.take_cached_fixed_lease(amount_msat).await?
283283
{
284+
self.schedule_fixed_lease_refill(amount_msat, connection_manager);
284285
return Ok((lease, total_fee_msat, lsp, false));
285286
}
286287
let request_lock = self
@@ -292,9 +293,20 @@ where
292293
if let Some((lease, total_fee_msat, lsp)) =
293294
self.take_cached_fixed_lease(amount_msat).await?
294295
{
296+
self.schedule_fixed_lease_refill(amount_msat, connection_manager);
295297
return Ok((lease, total_fee_msat, lsp, false));
296298
}
297299

300+
let (negotiated_lease, min_total_fee_msat, cheapest_lsp) =
301+
self.negotiate_fixed_lease(amount_msat, connection_manager).await?;
302+
let lease = self.consume_lease(&negotiated_lease.id).await?;
303+
self.schedule_fixed_lease_refill(amount_msat, connection_manager);
304+
Ok((lease, min_total_fee_msat, cheapest_lsp, true))
305+
}
306+
307+
async fn negotiate_fixed_lease(
308+
self: &Arc<Self>, amount_msat: u64, connection_manager: &Arc<ConnectionManager<L>>,
309+
) -> Result<(PaymentLease, u64, LspConfig), Error> {
298310
let all_offers = self.gather_lsps2_offers(connection_manager).await?;
299311
let (cheapest_lsp, min_total_fee_msat, min_opening_params) = all_offers
300312
.into_iter()
@@ -350,14 +362,14 @@ where
350362
Some(&cheapest_lsp.node_id),
351363
)
352364
.await?;
353-
let lease = self.consume_lease(&negotiated_lease.id).await?;
354-
Ok((lease, min_total_fee_msat, cheapest_lsp, true))
365+
Ok((negotiated_lease, min_total_fee_msat, cheapest_lsp))
355366
}
356367

357368
async fn acquire_variable_lease(
358369
self: &Arc<Self>, connection_manager: &Arc<ConnectionManager<L>>,
359370
) -> Result<(PaymentLease, u64, LspConfig, bool), Error> {
360371
if let Some((lease, proportional_fee, lsp)) = self.take_cached_variable_lease().await? {
372+
self.schedule_variable_lease_refill(connection_manager);
361373
return Ok((lease, proportional_fee, lsp, false));
362374
}
363375
let request_lock = self
@@ -367,9 +379,20 @@ where
367379
.request_lock(LeaseRequestKey::Variable);
368380
let _request_guard = request_lock.lock().await;
369381
if let Some((lease, proportional_fee, lsp)) = self.take_cached_variable_lease().await? {
382+
self.schedule_variable_lease_refill(connection_manager);
370383
return Ok((lease, proportional_fee, lsp, false));
371384
}
372385

386+
let (negotiated_lease, min_prop_fee_ppm_msat, cheapest_lsp) =
387+
self.negotiate_variable_lease(connection_manager).await?;
388+
let lease = self.consume_lease(&negotiated_lease.id).await?;
389+
self.schedule_variable_lease_refill(connection_manager);
390+
Ok((lease, min_prop_fee_ppm_msat, cheapest_lsp, true))
391+
}
392+
393+
async fn negotiate_variable_lease(
394+
self: &Arc<Self>, connection_manager: &Arc<ConnectionManager<L>>,
395+
) -> Result<(PaymentLease, u64, LspConfig), Error> {
373396
let all_offers = self.gather_lsps2_offers(connection_manager).await?;
374397
let mut rejected_for_fee = false;
375398
let (cheapest_lsp, min_prop_fee_ppm_msat, min_opening_params) = all_offers
@@ -411,8 +434,73 @@ where
411434
let negotiated_lease = self
412435
.lsps2_send_buy_request(None, min_opening_params, Some(&cheapest_lsp.node_id))
413436
.await?;
414-
let lease = self.consume_lease(&negotiated_lease.id).await?;
415-
Ok((lease, min_prop_fee_ppm_msat, cheapest_lsp, true))
437+
Ok((negotiated_lease, min_prop_fee_ppm_msat, cheapest_lsp))
438+
}
439+
440+
fn schedule_fixed_lease_refill(
441+
self: &Arc<Self>, amount_msat: u64, connection_manager: &Arc<ConnectionManager<L>>,
442+
) {
443+
let client = Arc::clone(self);
444+
let connection_manager = Arc::clone(connection_manager);
445+
tokio::spawn(async move {
446+
if let Err(error) = client.cache_fixed_lease(amount_msat, &connection_manager).await {
447+
log_warn!(client.logger, "Failed refilling LSPS2 payment lease: {}", error);
448+
}
449+
});
450+
}
451+
452+
fn schedule_variable_lease_refill(
453+
self: &Arc<Self>, connection_manager: &Arc<ConnectionManager<L>>,
454+
) {
455+
let client = Arc::clone(self);
456+
let connection_manager = Arc::clone(connection_manager);
457+
tokio::spawn(async move {
458+
if let Err(error) = client.cache_variable_lease(&connection_manager).await {
459+
log_warn!(client.logger, "Failed refilling LSPS2 payment lease: {}", error);
460+
}
461+
});
462+
}
463+
464+
async fn cache_fixed_lease(
465+
self: &Arc<Self>, amount_msat: u64, connection_manager: &Arc<ConnectionManager<L>>,
466+
) -> Result<(), Error> {
467+
let request_lock = self
468+
.pending_lease_request_state
469+
.lock()
470+
.expect("lock")
471+
.request_lock(LeaseRequestKey::Fixed(amount_msat));
472+
let _request_guard = request_lock.lock().await;
473+
if self
474+
.lease_state
475+
.lock()
476+
.expect("lock")
477+
.has_fixed_amount(amount_msat, self.config.lsps2_max_total_lsp_fee_limit_msat)
478+
{
479+
return Ok(());
480+
}
481+
self.negotiate_fixed_lease(amount_msat, connection_manager).await?;
482+
Ok(())
483+
}
484+
485+
async fn cache_variable_lease(
486+
self: &Arc<Self>, connection_manager: &Arc<ConnectionManager<L>>,
487+
) -> Result<(), Error> {
488+
let request_lock = self
489+
.pending_lease_request_state
490+
.lock()
491+
.expect("lock")
492+
.request_lock(LeaseRequestKey::Variable);
493+
let _request_guard = request_lock.lock().await;
494+
if self
495+
.lease_state
496+
.lock()
497+
.expect("lock")
498+
.has_variable_amount(self.config.lsps2_max_total_lsp_fee_limit_msat)
499+
{
500+
return Ok(());
501+
}
502+
self.negotiate_variable_lease(connection_manager).await?;
503+
Ok(())
416504
}
417505

418506
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
@@ -331,6 +331,21 @@ impl LSPS2LeaseState {
331331
self.leases.get(&id).cloned().map(|lease| (lease, fee_msat))
332332
}
333333

334+
pub(crate) fn has_fixed_amount(&self, amount_msat: u64, max_fee_msat: Option<u64>) -> bool {
335+
self.leases
336+
.values()
337+
.filter(|lease| lease.payment_size_msat == Some(amount_msat))
338+
.filter(|lease| is_lease_usable(lease))
339+
.filter_map(|lease| {
340+
compute_opening_fee(
341+
amount_msat,
342+
lease.params.min_fee_msat,
343+
lease.params.proportional as u64,
344+
)
345+
})
346+
.any(|fee_msat| max_fee_msat.map_or(true, |max| fee_msat <= max))
347+
}
348+
334349
pub(crate) fn variable_amount(
335350
&self, max_total_fee_msat: Option<u64>,
336351
) -> Option<(PaymentLease, u64)> {
@@ -347,6 +362,14 @@ impl LSPS2LeaseState {
347362
self.leases.get(&id).cloned().map(|lease| (lease, proportional_fee))
348363
}
349364

365+
pub(crate) fn has_variable_amount(&self, max_total_fee_msat: Option<u64>) -> bool {
366+
self.leases
367+
.values()
368+
.filter(|lease| lease.payment_size_msat.is_none())
369+
.filter(|lease| is_lease_usable(lease))
370+
.any(|lease| max_total_fee_msat.map_or(true, |max| lease.params.min_fee_msat <= max))
371+
}
372+
350373
pub(crate) fn prune(&mut self) {
351374
self.leases.retain(|_, lease| is_lease_usable(lease));
352375
}
@@ -439,6 +462,20 @@ mod tests {
439462
assert_eq!(state.variable_amount(Some(50)).unwrap().0.id, variable.id);
440463
}
441464

465+
#[test]
466+
fn detects_cached_leases_for_refill() {
467+
let valid_until = now_secs() + MIN_LEASE_REMAINING_SECS + 60;
468+
let fixed = lease(2, 46, 100, Some(1_000), valid_until);
469+
let variable = lease(3, 47, 50, None, valid_until);
470+
let state = LSPS2LeaseState::from_leases(vec![fixed, variable]);
471+
472+
assert!(state.has_fixed_amount(1_000, Some(100)));
473+
assert!(!state.has_fixed_amount(1_000, Some(99)));
474+
assert!(!state.has_fixed_amount(2_000, None));
475+
assert!(state.has_variable_amount(Some(50)));
476+
assert!(!state.has_variable_amount(Some(49)));
477+
}
478+
442479
fn cache_target(
443480
id: LeaseCacheTargetId, absolute_expiry: Option<u64>, last_accessed: u64,
444481
) -> LeaseCacheTarget {

0 commit comments

Comments
 (0)