Skip to content

Commit 76eac43

Browse files
author
tnull
committed
Add LSPS2 replay regression coverage
Persisting LSPS2 service state can race with replayed intercepted HTLC events after restart. Cover replaying the same intercepted HTLC after restoring peer state so duplicate queueing is caught. Co-Authored-By: HAL 9000
1 parent 467cd0b commit 76eac43

1 file changed

Lines changed: 48 additions & 0 deletions

File tree

lightning-liquidity/src/lsps2/service.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2361,6 +2361,8 @@ mod tests {
23612361

23622362
use bitcoin::{absolute::LockTime, transaction::Version};
23632363
use core::str::FromStr;
2364+
use lightning::io::Cursor;
2365+
use lightning::util::ser::{Readable, Writeable};
23642366

23652367
const MAX_VALUE_MSAT: u64 = 21_000_000_0000_0000_000;
23662368

@@ -2764,6 +2766,52 @@ mod tests {
27642766
}
27652767
}
27662768

2769+
#[test]
2770+
fn replayed_intercepted_htlc_after_persist_is_idempotent() {
2771+
let payment_size_msat = Some(500_000_000);
2772+
let opening_fee_params = LSPS2OpeningFeeParams {
2773+
min_fee_msat: 10_000_000,
2774+
proportional: 10_000,
2775+
valid_until: LSPSDateTime::from_str("2035-05-20T08:30:45Z").unwrap(),
2776+
min_lifetime: 4032,
2777+
max_client_to_self_delay: 2016,
2778+
min_payment_size_msat: 10_000_000,
2779+
max_payment_size_msat: 1_000_000_000,
2780+
promise: "ignore".to_string(),
2781+
};
2782+
let intercept_scid = 42;
2783+
let user_channel_id = 43;
2784+
let htlc = InterceptedHTLC {
2785+
intercept_id: InterceptId([1; 32]),
2786+
expected_outbound_amount_msat: 500_000_000,
2787+
payment_hash: PaymentHash([2; 32]),
2788+
};
2789+
2790+
let mut jit_channel =
2791+
OutboundJITChannel::new(payment_size_msat, opening_fee_params, user_channel_id, false);
2792+
assert!(matches!(
2793+
jit_channel.htlc_intercepted(htlc).unwrap(),
2794+
Some(HTLCInterceptedAction::OpenChannel(_))
2795+
));
2796+
2797+
let mut peer_state = PeerState::new();
2798+
peer_state.intercept_scid_by_user_channel_id.insert(user_channel_id, intercept_scid);
2799+
peer_state.insert_outbound_channel(intercept_scid, jit_channel);
2800+
2801+
let encoded_peer_state = peer_state.encode();
2802+
let mut decoded_peer_state = PeerState::read(&mut Cursor::new(encoded_peer_state)).unwrap();
2803+
let decoded_jit_channel = decoded_peer_state
2804+
.outbound_channels_by_intercept_scid
2805+
.get_mut(&intercept_scid)
2806+
.unwrap();
2807+
2808+
assert!(decoded_jit_channel.htlc_intercepted(htlc).unwrap().is_none());
2809+
2810+
let ForwardPaymentAction(_, fee_payment) =
2811+
decoded_jit_channel.channel_ready(ChannelId([3; 32])).unwrap();
2812+
assert_eq!(fee_payment.htlcs, vec![htlc]);
2813+
}
2814+
27672815
#[test]
27682816
fn broadcast_not_allowed_after_non_paying_fee_payment_claimed() {
27692817
let min_fee_msat: u64 = 12345;

0 commit comments

Comments
 (0)