Skip to content

Commit 000c0a3

Browse files
add failed & abandoned HTLC open handlers
Add two LSPS2Service methods: 'Abandoned' prunes all channel open state. 'Failed' resets JIT channel to fail HTLCs. It allows a retry on channel open. Closes #3479.
1 parent 89f5217 commit 000c0a3

File tree

1 file changed

+127
-1
lines changed

1 file changed

+127
-1
lines changed

lightning-liquidity/src/lsps2/service.rs

+127-1
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,11 @@ use crate::prelude::{new_hash_map, HashMap};
3232
use crate::sync::{Arc, Mutex, MutexGuard, RwLock};
3333

3434
use lightning::events::HTLCHandlingFailureType;
35-
use lightning::ln::channelmanager::{AChannelManager, InterceptId};
35+
use lightning::ln::channelmanager::{AChannelManager, FailureCode, InterceptId};
3636
use lightning::ln::msgs::{ErrorAction, LightningError};
3737
use lightning::ln::types::ChannelId;
3838
use lightning::util::errors::APIError;
39+
use lightning::util::hash_tables::HashSet;
3940
use lightning::util::logger::Level;
4041

4142
use lightning_types::payment::PaymentHash;
@@ -985,6 +986,131 @@ where
985986
Ok(())
986987
}
987988

989+
/// Abandons a pending JIT‐open flow for `user_channel_id`, removing all local state.
990+
///
991+
/// This removes the intercept SCID, any outbound channel state, and associated
992+
/// channel‐ID mappings for the specified `user_channel_id`, but only while the JIT
993+
/// channel is still in `PendingInitialPayment` or `PendingChannelOpen`.
994+
///
995+
/// Returns an error if:
996+
/// - there is no channel matching `user_channel_id`, or
997+
/// - the channel has already advanced past `PendingChannelOpen` (e.g. to
998+
/// `PendingPaymentForward` or beyond).
999+
///
1000+
/// Note: this does *not* close or roll back any on‐chain channel which may already
1001+
/// have been opened. The caller must only invoke this before a channel is actually
1002+
/// confirmed (or else provide its own on‐chain cleanup) and is responsible for
1003+
/// managing any pending channel open attempts separately.
1004+
pub fn channel_open_abandoned(
1005+
&self, counterparty_node_id: &PublicKey, user_channel_id: u128,
1006+
) -> Result<(), APIError> {
1007+
let outer_state_lock = self.per_peer_state.read().unwrap();
1008+
let inner_state_lock =
1009+
outer_state_lock.get(counterparty_node_id).ok_or_else(|| APIError::APIMisuseError {
1010+
err: format!("No counterparty state for: {}", counterparty_node_id),
1011+
})?;
1012+
let mut peer_state = inner_state_lock.lock().unwrap();
1013+
1014+
let intercept_scid = peer_state
1015+
.intercept_scid_by_user_channel_id
1016+
.remove(&user_channel_id)
1017+
.ok_or_else(|| APIError::APIMisuseError {
1018+
err: format!("Could not find a channel with user_channel_id {}", user_channel_id),
1019+
})?;
1020+
1021+
if let Some(jit_channel) =
1022+
peer_state.outbound_channels_by_intercept_scid.get(&intercept_scid)
1023+
{
1024+
if !matches!(
1025+
jit_channel.state,
1026+
OutboundJITChannelState::PendingInitialPayment { .. }
1027+
| OutboundJITChannelState::PendingChannelOpen { .. }
1028+
) {
1029+
return Err(APIError::APIMisuseError {
1030+
err: "Cannot abandon channel open after channel creation or payment forwarding"
1031+
.to_string(),
1032+
});
1033+
}
1034+
}
1035+
1036+
peer_state.outbound_channels_by_intercept_scid.remove(&intercept_scid);
1037+
1038+
peer_state.intercept_scid_by_channel_id.retain(|_, &mut scid| scid != intercept_scid);
1039+
1040+
Ok(())
1041+
}
1042+
1043+
/// Used to fail intercepted HTLCs backwards when a channel open attempt ultimately fails.
1044+
///
1045+
/// This function should be called after receiving an [`LSPS2ServiceEvent::OpenChannel`] event
1046+
/// but only if the channel could not be successfully established. It resets the JIT channel
1047+
/// state so that the payer may try the payment again.
1048+
///
1049+
/// [`LSPS2ServiceEvent::OpenChannel`]: crate::lsps2::event::LSPS2ServiceEvent::OpenChannel
1050+
pub fn channel_open_failed(
1051+
&self, counterparty_node_id: &PublicKey, user_channel_id: u128,
1052+
) -> Result<(), APIError> {
1053+
let outer_state_lock = self.per_peer_state.read().unwrap();
1054+
1055+
let inner_state_lock =
1056+
outer_state_lock.get(counterparty_node_id).ok_or_else(|| APIError::APIMisuseError {
1057+
err: format!("No counterparty state for: {}", counterparty_node_id),
1058+
})?;
1059+
1060+
let mut peer_state = inner_state_lock.lock().unwrap();
1061+
1062+
let intercept_scid = peer_state
1063+
.intercept_scid_by_user_channel_id
1064+
.get(&user_channel_id)
1065+
.copied()
1066+
.ok_or_else(|| APIError::APIMisuseError {
1067+
err: format!("Could not find a channel with user_channel_id {}", user_channel_id),
1068+
})?;
1069+
1070+
let jit_channel = peer_state
1071+
.outbound_channels_by_intercept_scid
1072+
.get_mut(&intercept_scid)
1073+
.ok_or_else(|| APIError::APIMisuseError {
1074+
err: format!(
1075+
"Failed to map intercept_scid {} for user_channel_id {} to a channel.",
1076+
intercept_scid, user_channel_id,
1077+
),
1078+
})?;
1079+
1080+
if !matches!(jit_channel.state, OutboundJITChannelState::PendingChannelOpen { .. }) {
1081+
return Err(APIError::APIMisuseError {
1082+
err: "Channel is not in the PendingChannelOpen state.".to_string(),
1083+
});
1084+
}
1085+
1086+
let mut payment_queue = match &jit_channel.state {
1087+
OutboundJITChannelState::PendingChannelOpen { payment_queue, .. } => {
1088+
payment_queue.clone()
1089+
},
1090+
_ => {
1091+
return Err(APIError::APIMisuseError {
1092+
err: "Channel is not in the PendingChannelOpen state.".to_string(),
1093+
});
1094+
},
1095+
};
1096+
let payment_hashes: Vec<_> = payment_queue
1097+
.clear()
1098+
.into_iter()
1099+
.map(|htlc| htlc.payment_hash)
1100+
.collect::<HashSet<_>>()
1101+
.into_iter()
1102+
.collect();
1103+
for payment_hash in payment_hashes {
1104+
self.channel_manager
1105+
.get_cm()
1106+
.fail_htlc_backwards_with_reason(&payment_hash, FailureCode::TemporaryNodeFailure);
1107+
}
1108+
1109+
jit_channel.state = OutboundJITChannelState::PendingInitialPayment { payment_queue };
1110+
1111+
Ok(())
1112+
}
1113+
9881114
/// Forward [`Event::ChannelReady`] event parameters into this function.
9891115
///
9901116
/// Will forward the intercepted HTLC if it matches a channel

0 commit comments

Comments
 (0)