Skip to content

Commit cae1d1a

Browse files
committed
lsps2: Test restored BOLT12 leases
Rebuild the receiver from its persisted store and pay the same long-lived offer again. Verify the pending offer survives and the exact cached lease is consumed before its replacement is negotiated. Co-Authored-By: HAL 9000
1 parent afb6792 commit cae1d1a

1 file changed

Lines changed: 93 additions & 4 deletions

File tree

tests/integration_tests_rust.rs

Lines changed: 93 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3684,10 +3684,14 @@ async fn bolt12_lsps2_client_service_integration() {
36843684
let service_addr = service_node.listening_addresses().unwrap().first().unwrap().clone();
36853685

36863686
let client_config = random_config();
3687+
let client_store =
3688+
TestSyncStore::new(client_config.node_config.storage_dir_path.clone().into());
36873689
setup_builder!(client_builder, client_config.node_config);
36883690
client_builder.set_chain_source_esplora(esplora_url.clone(), Some(sync_config));
3689-
client_builder.add_liquidity_source(service_node_id, service_addr, None, true);
3690-
let client_node = client_builder.build(client_config.node_entropy.into()).unwrap();
3691+
client_builder.add_liquidity_source(service_node_id, service_addr.clone(), None, true);
3692+
let client_node = client_builder
3693+
.build_with_store(client_config.node_entropy.into(), client_store.clone())
3694+
.unwrap();
36913695
client_node.start().unwrap();
36923696

36933697
let payer_config = random_config();
@@ -3779,8 +3783,93 @@ async fn bolt12_lsps2_client_service_integration() {
37793783
assert_eq!(client_node.list_channels().len(), client_channel_count);
37803784
assert_eq!(service_node.list_channels().len(), service_channel_count);
37813785

3782-
// The first JIT channel cannot carry this larger variable payment. The variable offer disables
3783-
// MPP, so the payer must use the fresh JIT path rather than splitting across both paths.
3786+
let persisted_lease_key =
3787+
tokio::time::timeout(std::time::Duration::from_secs(common::INTEROP_TIMEOUT_SECS), async {
3788+
loop {
3789+
let keys = KVStore::list(&client_store, "lsps2_leases", "").await.unwrap();
3790+
if let Some(key) = keys.first() {
3791+
return key.clone();
3792+
}
3793+
tokio::time::sleep(std::time::Duration::from_millis(10)).await;
3794+
}
3795+
})
3796+
.await
3797+
.expect("replacement lease should be persisted");
3798+
assert!(KVStore::read(&client_store, "lsps2_leases", "", &persisted_lease_key).await.is_ok());
3799+
3800+
let client_node_id = client_node.node_id();
3801+
client_node.stop().unwrap();
3802+
drop(client_node);
3803+
tokio::time::timeout(std::time::Duration::from_secs(common::INTEROP_TIMEOUT_SECS), async {
3804+
while payer_node.list_peers().iter().any(|peer| peer.node_id == client_node_id)
3805+
|| service_node.list_peers().iter().any(|peer| peer.node_id == client_node_id)
3806+
{
3807+
tokio::time::sleep(std::time::Duration::from_millis(10)).await;
3808+
}
3809+
})
3810+
.await
3811+
.expect("peers should observe the stopped client");
3812+
3813+
setup_builder!(restarted_client_builder, client_config.node_config);
3814+
restarted_client_builder.set_chain_source_esplora(esplora_url.clone(), Some(sync_config));
3815+
restarted_client_builder.add_liquidity_source(
3816+
service_node_id,
3817+
service_addr.clone(),
3818+
None,
3819+
true,
3820+
);
3821+
let client_node = restarted_client_builder
3822+
.build_with_store(client_config.node_entropy.into(), client_store.clone())
3823+
.unwrap();
3824+
assert_eq!(client_node.node_id(), client_node_id);
3825+
assert!(KVStore::read(&client_store, "lsps2_leases", "", &persisted_lease_key).await.is_ok());
3826+
client_node.start().unwrap();
3827+
let client_addr = client_node.listening_addresses().unwrap().first().unwrap().clone();
3828+
payer_node.connect(client_node.node_id(), client_addr, false).unwrap();
3829+
3830+
let restored_payment_id =
3831+
payer_node.bolt12_payment().send(&fixed_offer, None, None, None).unwrap();
3832+
expect_channel_pending_event!(service_node, client_node.node_id());
3833+
expect_channel_ready_event!(service_node, client_node.node_id());
3834+
expect_event!(service_node, PaymentForwarded);
3835+
loop {
3836+
let event = tokio::time::timeout(
3837+
std::time::Duration::from_secs(common::INTEROP_TIMEOUT_SECS),
3838+
client_node.next_event_async(),
3839+
)
3840+
.await
3841+
.expect("restored client should receive a payment event");
3842+
match event {
3843+
Event::ChannelPending { counterparty_node_id, .. } => {
3844+
assert_eq!(counterparty_node_id, service_node.node_id());
3845+
client_node.event_handled().unwrap();
3846+
break;
3847+
},
3848+
Event::PaymentReceived { payment_id, amount_msat, .. } => {
3849+
assert_eq!(payment_id, ordinary_receiver_payment_id);
3850+
assert_eq!(amount_msat, ordinary_amount_msat);
3851+
client_node.event_handled().unwrap();
3852+
},
3853+
unexpected => panic!("Unexpected event after restoring client: {unexpected:?}"),
3854+
}
3855+
}
3856+
expect_channel_ready_event!(client_node, service_node.node_id());
3857+
expect_payment_successful_event!(payer_node, restored_payment_id, None);
3858+
let restored_receiver_payment_id =
3859+
expect_payment_received_event!(client_node, fixed_received_msat);
3860+
match client_node.payment(&restored_receiver_payment_id).unwrap().kind {
3861+
PaymentKind::Bolt12Offer { counterparty_skimmed_fee_msat, .. } => {
3862+
assert_eq!(counterparty_skimmed_fee_msat, Some(fixed_fee_msat));
3863+
},
3864+
_ => panic!("Unexpected payment kind"),
3865+
}
3866+
assert!(
3867+
KVStore::read(&client_store, "lsps2_leases", "", &persisted_lease_key).await.is_err(),
3868+
"the restored lease must be consumed instead of renegotiating"
3869+
);
3870+
3871+
// The fixed JIT channels cannot carry this larger variable payment. The variable offer disables
3872+
// MPP, so the payer must use the fresh JIT path rather than splitting across the existing paths.
37843873
let variable_amount_msat = 200_000_000;
37853874
let variable_offer =
37863875
client_node.bolt12_payment().receive_variable_amount("variable", None).unwrap();

0 commit comments

Comments
 (0)