From b82779c3a5c9dbb12ea561b674559ad33c7bee84 Mon Sep 17 00:00:00 2001 From: John Cantrell Date: Fri, 17 May 2024 10:03:24 -0400 Subject: [PATCH 1/3] ability to signal channel open failure --- src/lsps2/payment_queue.rs | 17 ++++---- src/lsps2/service.rs | 83 +++++++++++++++++++++++++++++++++++++- 2 files changed, 91 insertions(+), 9 deletions(-) diff --git a/src/lsps2/payment_queue.rs b/src/lsps2/payment_queue.rs index dfd97ad..3459648 100644 --- a/src/lsps2/payment_queue.rs +++ b/src/lsps2/payment_queue.rs @@ -49,8 +49,8 @@ impl PaymentQueue { position.map(|position| self.payments.remove(position)) } - pub(crate) fn clear(&mut self) -> Vec { - self.payments.drain(..).map(|(_k, v)| v).flatten().collect() + pub(crate) fn clear(&mut self) -> Vec<(PaymentHash, Vec)> { + self.payments.drain(..).collect() } } @@ -109,11 +109,14 @@ mod tests { ); assert_eq!( payment_queue.clear(), - vec![InterceptedHTLC { - intercept_id: InterceptId([1; 32]), - expected_outbound_amount_msat: 300_000_000, - payment_hash: PaymentHash([101; 32]), - }] + vec![( + PaymentHash([101; 32]), + vec![InterceptedHTLC { + intercept_id: InterceptId([1; 32]), + expected_outbound_amount_msat: 300_000_000, + payment_hash: PaymentHash([101; 32]), + }] + )] ); } } diff --git a/src/lsps2/service.rs b/src/lsps2/service.rs index 34025c8..d51ad30 100644 --- a/src/lsps2/service.rs +++ b/src/lsps2/service.rs @@ -19,7 +19,7 @@ use crate::prelude::{HashMap, String, ToString, Vec}; use crate::sync::{Arc, Mutex, RwLock}; use lightning::events::HTLCDestination; -use lightning::ln::channelmanager::{AChannelManager, InterceptId}; +use lightning::ln::channelmanager::{AChannelManager, FailureCode, InterceptId}; use lightning::ln::msgs::{ErrorAction, LightningError}; use lightning::ln::{ChannelId, PaymentHash}; use lightning::util::errors::APIError; @@ -362,7 +362,13 @@ impl OutboundJITChannelState { let mut payment_queue_lock = payment_queue.lock().unwrap(); let payment_forwarded = OutboundJITChannelState::PaymentForwarded { channel_id: *channel_id }; - let forward_htlcs = ForwardHTLCsAction(*channel_id, payment_queue_lock.clear()); + let htlcs = payment_queue_lock + .clear() + .into_iter() + .map(|(_, htlcs)| htlcs) + .flatten() + .collect(); + let forward_htlcs = ForwardHTLCsAction(*channel_id, htlcs); Ok((payment_forwarded, Some(forward_htlcs))) }, OutboundJITChannelState::PaymentForwarded { channel_id } => { @@ -898,6 +904,79 @@ where Ok(()) } + /// Used by LSP to fail intercepted htlcs backwards when the channel open fails for any reason. + /// + /// Should be called in response to receiving a [`LSPS2ServiceEvent::OpenChannel`] event. + /// + /// The JIT channel state is reset such that the payer can attempt payment again. + /// [`LSPS2ServiceEvent::OpenChannel`]: crate::lsps2::event::LSPS2ServiceEvent::OpenChannel + pub fn channel_open_failed( + &self, counterparty_node_id: &PublicKey, user_channel_id: u128, + ) -> Result<(), APIError> { + let outer_state_lock = self.per_peer_state.read().unwrap(); + match outer_state_lock.get(counterparty_node_id) { + Some(inner_state_lock) => { + let mut peer_state = inner_state_lock.lock().unwrap(); + + if let Some(intercept_scid) = + peer_state.intercept_scid_by_user_channel_id.get(&user_channel_id).copied() + { + if let Some(jit_channel) = + peer_state.outbound_channels_by_intercept_scid.get_mut(&intercept_scid) + { + let new_state = if let OutboundJITChannelState::PendingChannelOpen { + payment_queue, + .. + } = &jit_channel.state + { + let mut queue = payment_queue.lock().unwrap(); + let payment_hashes = queue + .clear() + .into_iter() + .map(|(payment_hash, _)| payment_hash) + .collect::>(); + for payment_hash in payment_hashes { + self.channel_manager.get_cm().fail_htlc_backwards_with_reason( + &payment_hash, + FailureCode::TemporaryNodeFailure, + ); + } + OutboundJITChannelState::PendingInitialPayment { + payment_queue: payment_queue.clone(), + } + } else { + return Err(APIError::APIMisuseError { + err: format!("Channel is not in the PendingChannelOpen state.",), + }); + }; + jit_channel.state = new_state; + } else { + return Err(APIError::APIMisuseError { + err: format!( + "Failed to map the stored intercept_scid {} for the provided user_channel_id {} to a channel.", + intercept_scid, + user_channel_id, + ), + }); + } + } else { + return Err(APIError::APIMisuseError { + err: format!( + "Could not find a channel with user_channel_id {}", + user_channel_id + ), + }); + } + }, + None => { + return Err(APIError::APIMisuseError { + err: format!("No counterparty state for: {}", counterparty_node_id), + }); + }, + } + Ok(()) + } + /// Forward [`Event::ChannelReady`] event parameters into this function. /// /// Will forward the intercepted HTLC if it matches a channel From c4bb07d5e6f438eb571343944dbdd0a8cd96d6db Mon Sep 17 00:00:00 2001 From: John Cantrell Date: Wed, 15 May 2024 11:35:32 -0400 Subject: [PATCH 2/3] reuse ForwardPaymentAction inside of HTLCInterceptedAction --- src/lsps2/service.rs | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/lsps2/service.rs b/src/lsps2/service.rs index d51ad30..0c757c1 100644 --- a/src/lsps2/service.rs +++ b/src/lsps2/service.rs @@ -78,7 +78,7 @@ enum HTLCInterceptedAction { OpenChannel(OpenChannelParams), /// The forwarding of the intercepted HTLC. ForwardHTLC(ChannelId), - ForwardPayment(ChannelId, FeePayment), + ForwardPayment(ForwardPaymentAction), } /// The forwarding of a payment while skimming the JIT channel opening fee. @@ -240,10 +240,11 @@ impl OutboundJITChannelState { opening_fee_msat: *opening_fee_msat, channel_id: *channel_id, }; - let forward_payment = HTLCInterceptedAction::ForwardPayment( - *channel_id, - FeePayment { htlcs, opening_fee_msat: *opening_fee_msat }, - ); + let forward_payment = + HTLCInterceptedAction::ForwardPayment(ForwardPaymentAction( + *channel_id, + FeePayment { htlcs, opening_fee_msat: *opening_fee_msat }, + )); Ok((pending_payment_forward, Some(forward_payment))) } else { let pending_payment = OutboundJITChannelState::PendingPayment { @@ -732,8 +733,10 @@ where )?; }, Ok(Some(HTLCInterceptedAction::ForwardPayment( - channel_id, - FeePayment { opening_fee_msat, htlcs }, + ForwardPaymentAction( + channel_id, + FeePayment { opening_fee_msat, htlcs }, + ), ))) => { let amounts_to_forward_msat = calculate_amount_to_forward_per_htlc(&htlcs, opening_fee_msat); @@ -1474,7 +1477,10 @@ mod tests { .unwrap(); assert!(matches!(new_state, OutboundJITChannelState::PendingPaymentForward { .. })); match action { - Some(HTLCInterceptedAction::ForwardPayment(channel_id, payment)) => { + Some(HTLCInterceptedAction::ForwardPayment(ForwardPaymentAction( + channel_id, + payment, + ))) => { assert_eq!(channel_id, ChannelId([200; 32])); assert_eq!(payment.opening_fee_msat, 10_000_000); assert_eq!( From 5172df699d6569fe55a22af5b901192354182d3b Mon Sep 17 00:00:00 2001 From: John Cantrell Date: Wed, 15 May 2024 12:25:07 -0400 Subject: [PATCH 3/3] extract shared logic for htlc_intercepted, htlc_handling_failed, and channel_ready --- src/lsps2/service.rs | 101 ++++++++++++++++++------------------------- 1 file changed, 43 insertions(+), 58 deletions(-) diff --git a/src/lsps2/service.rs b/src/lsps2/service.rs index 0c757c1..ecb7a8c 100644 --- a/src/lsps2/service.rs +++ b/src/lsps2/service.rs @@ -16,7 +16,7 @@ use crate::lsps2::payment_queue::{InterceptedHTLC, PaymentQueue}; use crate::lsps2::utils::{compute_opening_fee, is_valid_opening_fee_params}; use crate::message_queue::MessageQueue; use crate::prelude::{HashMap, String, ToString, Vec}; -use crate::sync::{Arc, Mutex, RwLock}; +use crate::sync::{Arc, Mutex, MutexGuard, RwLock}; use lightning::events::HTLCDestination; use lightning::ln::channelmanager::{AChannelManager, FailureCode, InterceptId}; @@ -232,28 +232,13 @@ impl OutboundJITChannelState { } => { let mut payment_queue_lock = payment_queue.lock().unwrap(); payment_queue_lock.add_htlc(htlc); - if let Some((_payment_hash, htlcs)) = - payment_queue_lock.pop_greater_than_msat(*opening_fee_msat) - { - let pending_payment_forward = OutboundJITChannelState::PendingPaymentForward { - payment_queue: payment_queue.clone(), - opening_fee_msat: *opening_fee_msat, - channel_id: *channel_id, - }; - let forward_payment = - HTLCInterceptedAction::ForwardPayment(ForwardPaymentAction( - *channel_id, - FeePayment { htlcs, opening_fee_msat: *opening_fee_msat }, - )); - Ok((pending_payment_forward, Some(forward_payment))) - } else { - let pending_payment = OutboundJITChannelState::PendingPayment { - payment_queue: payment_queue.clone(), - opening_fee_msat: *opening_fee_msat, - channel_id: *channel_id, - }; - Ok((pending_payment, None)) - } + let (state, payment_action) = try_get_payment( + Arc::clone(payment_queue), + payment_queue_lock, + *channel_id, + *opening_fee_msat, + ); + Ok((state, payment_action.map(|pa| HTLCInterceptedAction::ForwardPayment(pa)))) }, OutboundJITChannelState::PaymentForwarded { channel_id } => { let payment_forwarded = @@ -269,19 +254,13 @@ impl OutboundJITChannelState { ) -> Result<(Self, ForwardPaymentAction), ChannelStateError> { match self { OutboundJITChannelState::PendingChannelOpen { payment_queue, opening_fee_msat } => { - let mut payment_queue_lock = payment_queue.lock().unwrap(); - if let Some((_payment_hash, htlcs)) = - payment_queue_lock.pop_greater_than_msat(*opening_fee_msat) - { - let pending_payment_forward = OutboundJITChannelState::PendingPaymentForward { - payment_queue: Arc::clone(&payment_queue), - opening_fee_msat: *opening_fee_msat, - channel_id, - }; - let forward_payment = ForwardPaymentAction( - channel_id, - FeePayment { opening_fee_msat: *opening_fee_msat, htlcs }, - ); + let payment_queue_lock = payment_queue.lock().unwrap(); + if let (pending_payment_forward, Some(forward_payment)) = try_get_payment( + Arc::clone(payment_queue), + payment_queue_lock, + channel_id, + *opening_fee_msat, + ) { Ok((pending_payment_forward, forward_payment)) } else { Err(ChannelStateError( @@ -306,28 +285,13 @@ impl OutboundJITChannelState { opening_fee_msat, channel_id, } => { - let mut payment_queue_lock = payment_queue.lock().unwrap(); - if let Some((_payment_hash, htlcs)) = - payment_queue_lock.pop_greater_than_msat(*opening_fee_msat) - { - let pending_payment_forward = OutboundJITChannelState::PendingPaymentForward { - payment_queue: payment_queue.clone(), - opening_fee_msat: *opening_fee_msat, - channel_id: *channel_id, - }; - let forward_payment = ForwardPaymentAction( - *channel_id, - FeePayment { htlcs, opening_fee_msat: *opening_fee_msat }, - ); - Ok((pending_payment_forward, Some(forward_payment))) - } else { - let pending_payment = OutboundJITChannelState::PendingPayment { - payment_queue: payment_queue.clone(), - opening_fee_msat: *opening_fee_msat, - channel_id: *channel_id, - }; - Ok((pending_payment, None)) - } + let payment_queue_lock = payment_queue.lock().unwrap(); + Ok(try_get_payment( + Arc::clone(payment_queue), + payment_queue_lock, + *channel_id, + *opening_fee_msat, + )) }, OutboundJITChannelState::PendingPayment { payment_queue, @@ -1253,6 +1217,27 @@ fn calculate_amount_to_forward_per_htlc( per_htlc_forwards } +fn try_get_payment( + payment_queue: Arc>, mut payment_queue_lock: MutexGuard, + channel_id: ChannelId, opening_fee_msat: u64, +) -> (OutboundJITChannelState, Option) { + if let Some((_payment_hash, htlcs)) = payment_queue_lock.pop_greater_than_msat(opening_fee_msat) + { + let pending_payment_forward = OutboundJITChannelState::PendingPaymentForward { + payment_queue, + opening_fee_msat, + channel_id, + }; + let forward_payment = + ForwardPaymentAction(channel_id, FeePayment { htlcs, opening_fee_msat }); + (pending_payment_forward, Some(forward_payment)) + } else { + let pending_payment = + OutboundJITChannelState::PendingPayment { payment_queue, opening_fee_msat, channel_id }; + (pending_payment, None) + } +} + #[cfg(test)] mod tests {