Skip to content

Commit 6b75e5a

Browse files
committed
Release LSPS2 intercepted HTLCs on open failure
When a JIT channel open fails, release queued intercepted HTLCs through the intercept API so they are not held until expiry. Keep resetting the LSPS2 state if an intercept has already been released. Co-Authored-By: HAL 9000 This finding was discovered by Project Loupe
1 parent c9260ee commit 6b75e5a

2 files changed

Lines changed: 123 additions & 6 deletions

File tree

lightning-liquidity/src/lsps2/service.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ use crate::utils::async_poll::dummy_waker;
4242

4343
use lightning::chain::chaininterface::{BroadcasterInterface, TransactionType};
4444
use lightning::events::HTLCHandlingFailureType;
45-
use lightning::ln::channelmanager::{AChannelManager, FailureCode, InterceptId};
45+
use lightning::ln::channelmanager::{AChannelManager, InterceptId};
4646
use lightning::ln::msgs::{ErrorAction, LightningError};
4747
use lightning::ln::types::ChannelId;
4848
use lightning::util::errors::APIError;
@@ -1375,10 +1375,8 @@ where
13751375
{
13761376
let intercepted_htlcs = payment_queue.clear();
13771377
for htlc in intercepted_htlcs {
1378-
self.channel_manager.get_cm().fail_htlc_backwards_with_reason(
1379-
&htlc.payment_hash,
1380-
FailureCode::TemporaryNodeFailure,
1381-
);
1378+
// A missing intercept has already been released; still reset this LSPS2 state.
1379+
let _ = self.channel_manager.get_cm().fail_intercepted_htlc(htlc.intercept_id);
13821380
}
13831381

13841382
jit_channel.state = OutboundJITChannelState::PendingInitialPayment {

lightning-liquidity/tests/lsps2_integration_tests.rs

Lines changed: 120 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use common::{
77
get_lsps_message, LSPSNodes, LSPSNodesWithPayer, LiquidityNode,
88
};
99

10-
use lightning::events::{ClosureReason, Event};
10+
use lightning::events::{ClosureReason, Event, HTLCHandlingFailureType};
1111
use lightning::get_event_msg;
1212
use lightning::ln::channelmanager::{
1313
OptionalBolt11PaymentParams, PaymentId, TrustedChannelFeatures,
@@ -453,6 +453,125 @@ fn channel_open_failed() {
453453
};
454454
}
455455

456+
#[test]
457+
fn channel_open_failed_releases_intercepted_htlcs() {
458+
let chanmon_cfgs = create_chanmon_cfgs(3);
459+
let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
460+
let mut service_node_config = test_default_channel_config();
461+
service_node_config.htlc_interception_flags = HTLCInterceptionFlags::ToInterceptSCIDs as u8;
462+
463+
let mut client_node_config = test_default_channel_config();
464+
client_node_config.channel_config.accept_underpaying_htlcs = true;
465+
466+
let node_chanmgrs = create_node_chanmgrs(
467+
3,
468+
&node_cfgs,
469+
&[Some(service_node_config), Some(client_node_config), None],
470+
);
471+
let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
472+
let (lsps_nodes, promise_secret) = setup_test_lsps2_nodes_with_payer(nodes);
473+
let LSPSNodesWithPayer { ref service_node, ref client_node, ref payer_node } = lsps_nodes;
474+
475+
let payer_node_id = payer_node.node.get_our_node_id();
476+
let service_node_id = service_node.inner.node.get_our_node_id();
477+
let client_node_id = client_node.inner.node.get_our_node_id();
478+
479+
let service_handler = service_node.liquidity_manager.lsps2_service_handler().unwrap();
480+
create_chan_between_nodes_with_value(&payer_node, &service_node.inner, 2_000_000, 100_000);
481+
482+
let intercept_scid = service_node.node.get_intercept_scid();
483+
let user_channel_id = 42u128;
484+
let cltv_expiry_delta: u32 = 144;
485+
let payment_size_msat = Some(1_000_000);
486+
let fee_base_msat: u64 = 1_000;
487+
488+
execute_lsps2_dance(
489+
&lsps_nodes,
490+
intercept_scid,
491+
user_channel_id,
492+
cltv_expiry_delta,
493+
promise_secret,
494+
payment_size_msat,
495+
fee_base_msat,
496+
);
497+
498+
let invoice = create_jit_invoice(
499+
&client_node,
500+
service_node_id,
501+
intercept_scid,
502+
cltv_expiry_delta,
503+
payment_size_msat,
504+
"channel-open-failed-cleanup",
505+
3600,
506+
)
507+
.unwrap();
508+
509+
payer_node
510+
.node
511+
.pay_for_bolt11_invoice(
512+
&invoice,
513+
PaymentId(invoice.payment_hash().0),
514+
None,
515+
OptionalBolt11PaymentParams::default(),
516+
)
517+
.unwrap();
518+
519+
check_added_monitors(&payer_node, 1);
520+
let events = payer_node.node.get_and_clear_pending_msg_events();
521+
let ev = SendEvent::from_event(events[0].clone());
522+
service_node.inner.node.handle_update_add_htlc(payer_node_id, &ev.msgs[0]);
523+
do_commitment_signed_dance(&service_node.inner, &payer_node, &ev.commitment_msg, false, true);
524+
service_node.inner.node.process_pending_htlc_forwards();
525+
526+
let events = service_node.inner.node.get_and_clear_pending_events();
527+
assert_eq!(events.len(), 1);
528+
let intercept_id = match &events[0] {
529+
Event::HTLCIntercepted {
530+
intercept_id,
531+
requested_next_hop_scid,
532+
payment_hash,
533+
expected_outbound_amount_msat,
534+
..
535+
} => {
536+
assert_eq!(*requested_next_hop_scid, intercept_scid);
537+
service_handler
538+
.htlc_intercepted(
539+
*requested_next_hop_scid,
540+
*intercept_id,
541+
*expected_outbound_amount_msat,
542+
*payment_hash,
543+
)
544+
.unwrap();
545+
*intercept_id
546+
},
547+
other => panic!("Expected HTLCIntercepted, got {:?}", other),
548+
};
549+
550+
match service_node.liquidity_manager.next_event().unwrap() {
551+
LiquidityEvent::LSPS2Service(LSPS2ServiceEvent::OpenChannel { .. }) => {},
552+
other => panic!("Unexpected event: {:?}", other),
553+
};
554+
555+
service_handler.channel_open_failed(&client_node_id, user_channel_id).unwrap();
556+
557+
let res = service_node.inner.node.fail_intercepted_htlc(intercept_id);
558+
assert!(
559+
res.is_err(),
560+
"channel_open_failed must release the intercepted HTLC via fail_intercepted_htlc, but the entry is still pending: {:?}",
561+
res,
562+
);
563+
564+
let events = service_node.inner.node.get_and_clear_pending_events();
565+
assert_eq!(events.len(), 1);
566+
match &events[0] {
567+
Event::HTLCHandlingFailed {
568+
failure_type: HTLCHandlingFailureType::InvalidForward { requested_forward_scid },
569+
..
570+
} => assert_eq!(*requested_forward_scid, intercept_scid),
571+
other => panic!("Expected HTLCHandlingFailed, got {:?}", other),
572+
}
573+
}
574+
456575
#[test]
457576
fn channel_open_failed_nonexistent_channel() {
458577
let chanmon_cfgs = create_chanmon_cfgs(2);

0 commit comments

Comments
 (0)