Skip to content

Commit 68e71c2

Browse files
tnulltnull
authored andcommitted
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 bab66f6 commit 68e71c2

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
@@ -2439,6 +2439,8 @@ mod tests {
24392439

24402440
use bitcoin::{absolute::LockTime, transaction::Version};
24412441
use core::str::FromStr;
2442+
use lightning::io::Cursor;
2443+
use lightning::util::ser::{Readable, Writeable};
24422444

24432445
const MAX_VALUE_MSAT: u64 = 21_000_000_0000_0000_000;
24442446

@@ -2842,6 +2844,52 @@ mod tests {
28422844
}
28432845
}
28442846

2847+
#[test]
2848+
fn replayed_intercepted_htlc_after_persist_is_idempotent() {
2849+
let payment_size_msat = Some(500_000_000);
2850+
let opening_fee_params = LSPS2OpeningFeeParams {
2851+
min_fee_msat: 10_000_000,
2852+
proportional: 10_000,
2853+
valid_until: LSPSDateTime::from_str("2035-05-20T08:30:45Z").unwrap(),
2854+
min_lifetime: 4032,
2855+
max_client_to_self_delay: 2016,
2856+
min_payment_size_msat: 10_000_000,
2857+
max_payment_size_msat: 1_000_000_000,
2858+
promise: "ignore".to_string(),
2859+
};
2860+
let intercept_scid = 42;
2861+
let user_channel_id = 43;
2862+
let htlc = InterceptedHTLC {
2863+
intercept_id: InterceptId([1; 32]),
2864+
expected_outbound_amount_msat: 500_000_000,
2865+
payment_hash: PaymentHash([2; 32]),
2866+
};
2867+
2868+
let mut jit_channel =
2869+
OutboundJITChannel::new(payment_size_msat, opening_fee_params, user_channel_id, false);
2870+
assert!(matches!(
2871+
jit_channel.htlc_intercepted(htlc).unwrap(),
2872+
Some(HTLCInterceptedAction::OpenChannel(_))
2873+
));
2874+
2875+
let mut peer_state = PeerState::new();
2876+
peer_state.intercept_scid_by_user_channel_id.insert(user_channel_id, intercept_scid);
2877+
peer_state.insert_outbound_channel(intercept_scid, jit_channel);
2878+
2879+
let encoded_peer_state = peer_state.encode();
2880+
let mut decoded_peer_state = PeerState::read(&mut Cursor::new(encoded_peer_state)).unwrap();
2881+
let decoded_jit_channel = decoded_peer_state
2882+
.outbound_channels_by_intercept_scid
2883+
.get_mut(&intercept_scid)
2884+
.unwrap();
2885+
2886+
assert!(decoded_jit_channel.htlc_intercepted(htlc).unwrap().is_none());
2887+
2888+
let ForwardPaymentAction(_, fee_payment) =
2889+
decoded_jit_channel.channel_ready(ChannelId([3; 32])).unwrap();
2890+
assert_eq!(fee_payment.htlcs, vec![htlc]);
2891+
}
2892+
28452893
#[test]
28462894
fn removes_terminal_state_for_closed_channel() {
28472895
let opening_fee_params = LSPS2OpeningFeeParams {

0 commit comments

Comments
 (0)