Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions lightning-liquidity/src/lsps2/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ use crate::utils::async_poll::dummy_waker;

use lightning::chain::chaininterface::{BroadcasterInterface, TransactionType};
use lightning::events::HTLCHandlingFailureType;
use lightning::ln::channelmanager::{AChannelManager, FailureCode, InterceptId};
use lightning::ln::channelmanager::{AChannelManager, InterceptId};
use lightning::ln::msgs::{ErrorAction, LightningError};
use lightning::ln::types::ChannelId;
use lightning::util::errors::APIError;
Expand Down Expand Up @@ -1375,10 +1375,8 @@ where
{
let intercepted_htlcs = payment_queue.clear();
for htlc in intercepted_htlcs {
self.channel_manager.get_cm().fail_htlc_backwards_with_reason(
&htlc.payment_hash,
FailureCode::TemporaryNodeFailure,
);
// A missing intercept has already been released; still reset this LSPS2 state.
let _ = self.channel_manager.get_cm().fail_intercepted_htlc(htlc.intercept_id);
}

jit_channel.state = OutboundJITChannelState::PendingInitialPayment {
Expand Down
121 changes: 120 additions & 1 deletion lightning-liquidity/tests/lsps2_integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use common::{
get_lsps_message, LSPSNodes, LSPSNodesWithPayer, LiquidityNode,
};

use lightning::events::{ClosureReason, Event};
use lightning::events::{ClosureReason, Event, HTLCHandlingFailureType};
use lightning::get_event_msg;
use lightning::ln::channelmanager::{
OptionalBolt11PaymentParams, PaymentId, TrustedChannelFeatures,
Expand Down Expand Up @@ -453,6 +453,125 @@ fn channel_open_failed() {
};
}

#[test]
fn channel_open_failed_releases_intercepted_htlcs() {
let chanmon_cfgs = create_chanmon_cfgs(3);
let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
let mut service_node_config = test_default_channel_config();
service_node_config.htlc_interception_flags = HTLCInterceptionFlags::ToInterceptSCIDs as u8;

let mut client_node_config = test_default_channel_config();
client_node_config.channel_config.accept_underpaying_htlcs = true;

let node_chanmgrs = create_node_chanmgrs(
3,
&node_cfgs,
&[Some(service_node_config), Some(client_node_config), None],
);
let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
let (lsps_nodes, promise_secret) = setup_test_lsps2_nodes_with_payer(nodes);
let LSPSNodesWithPayer { ref service_node, ref client_node, ref payer_node } = lsps_nodes;

let payer_node_id = payer_node.node.get_our_node_id();
let service_node_id = service_node.inner.node.get_our_node_id();
let client_node_id = client_node.inner.node.get_our_node_id();

let service_handler = service_node.liquidity_manager.lsps2_service_handler().unwrap();
create_chan_between_nodes_with_value(&payer_node, &service_node.inner, 2_000_000, 100_000);

let intercept_scid = service_node.node.get_intercept_scid();
let user_channel_id = 42u128;
let cltv_expiry_delta: u32 = 144;
let payment_size_msat = Some(1_000_000);
let fee_base_msat: u64 = 1_000;

execute_lsps2_dance(
&lsps_nodes,
intercept_scid,
user_channel_id,
cltv_expiry_delta,
promise_secret,
payment_size_msat,
fee_base_msat,
);

let invoice = create_jit_invoice(
&client_node,
service_node_id,
intercept_scid,
cltv_expiry_delta,
payment_size_msat,
"channel-open-failed-cleanup",
3600,
)
.unwrap();

payer_node
.node
.pay_for_bolt11_invoice(
&invoice,
PaymentId(invoice.payment_hash().0),
None,
OptionalBolt11PaymentParams::default(),
)
.unwrap();

check_added_monitors(&payer_node, 1);
let events = payer_node.node.get_and_clear_pending_msg_events();
let ev = SendEvent::from_event(events[0].clone());
service_node.inner.node.handle_update_add_htlc(payer_node_id, &ev.msgs[0]);
do_commitment_signed_dance(&service_node.inner, &payer_node, &ev.commitment_msg, false, true);
service_node.inner.node.process_pending_htlc_forwards();

let events = service_node.inner.node.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
let intercept_id = match &events[0] {
Event::HTLCIntercepted {
intercept_id,
requested_next_hop_scid,
payment_hash,
expected_outbound_amount_msat,
..
} => {
assert_eq!(*requested_next_hop_scid, intercept_scid);
service_handler
.htlc_intercepted(
*requested_next_hop_scid,
*intercept_id,
*expected_outbound_amount_msat,
*payment_hash,
)
.unwrap();
*intercept_id
},
other => panic!("Expected HTLCIntercepted, got {:?}", other),
};

match service_node.liquidity_manager.next_event().unwrap() {
LiquidityEvent::LSPS2Service(LSPS2ServiceEvent::OpenChannel { .. }) => {},
other => panic!("Unexpected event: {:?}", other),
};

service_handler.channel_open_failed(&client_node_id, user_channel_id).unwrap();

let res = service_node.inner.node.fail_intercepted_htlc(intercept_id);
assert!(
res.is_err(),
"channel_open_failed must release the intercepted HTLC via fail_intercepted_htlc, but the entry is still pending: {:?}",
res,
);

let events = service_node.inner.node.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
match &events[0] {
Event::HTLCHandlingFailed {
failure_type: HTLCHandlingFailureType::InvalidForward { requested_forward_scid },
..
} => assert_eq!(*requested_forward_scid, intercept_scid),
other => panic!("Expected HTLCHandlingFailed, got {:?}", other),
}
}

#[test]
fn channel_open_failed_nonexistent_channel() {
let chanmon_cfgs = create_chanmon_cfgs(2);
Expand Down