@@ -45,6 +45,12 @@ use crate::jit_channel::msgs::{
45
45
46
46
const SUPPORTED_SPEC_VERSIONS : [ u16 ; 1 ] = [ 1 ] ;
47
47
48
+ #[ derive( Copy , Clone , PartialEq , Eq , Debug ) ]
49
+ struct InterceptedHTLC {
50
+ intercept_id : InterceptId ,
51
+ expected_outbound_amount_msat : u64 ,
52
+ }
53
+
48
54
struct ChannelStateError ( String ) ;
49
55
50
56
impl From < ChannelStateError > for LightningError {
@@ -192,13 +198,19 @@ enum OutboundJITChannelState {
192
198
payment_size_msat : Option < u64 > ,
193
199
opening_fee_params : OpeningFeeParams ,
194
200
} ,
201
+ PendingPaymentReceived {
202
+ htlcs : Vec < InterceptedHTLC > ,
203
+ payment_size_msat : u64 ,
204
+ amt_to_forward_msat : u64 ,
205
+ opening_fee_msat : u64 ,
206
+ } ,
195
207
PendingChannelOpen {
196
- intercept_id : InterceptId ,
208
+ htlcs : Vec < InterceptedHTLC > ,
197
209
opening_fee_msat : u64 ,
198
210
amt_to_forward_msat : u64 ,
199
211
} ,
200
212
ChannelReady {
201
- intercept_id : InterceptId ,
213
+ htlcs : Vec < InterceptedHTLC > ,
202
214
amt_to_forward_msat : u64 ,
203
215
} ,
204
216
}
@@ -216,20 +228,68 @@ impl OutboundJITChannelState {
216
228
}
217
229
}
218
230
219
- pub fn htlc_intercepted (
220
- & self , expected_outbound_amount_msat : u64 , intercept_id : InterceptId ,
221
- ) -> Result < Self , ChannelStateError > {
231
+ pub fn htlc_intercepted ( & self , htlc : InterceptedHTLC ) -> Result < Self , ChannelStateError > {
222
232
match self {
223
- OutboundJITChannelState :: InvoiceParametersGenerated { opening_fee_params, .. } => {
224
- compute_opening_fee (
225
- expected_outbound_amount_msat,
226
- opening_fee_params. min_fee_msat ,
227
- opening_fee_params. proportional ,
228
- ) . map ( |opening_fee_msat| OutboundJITChannelState :: PendingChannelOpen {
229
- intercept_id,
230
- opening_fee_msat,
231
- amt_to_forward_msat : expected_outbound_amount_msat - opening_fee_msat,
232
- } ) . ok_or ( ChannelStateError ( format ! ( "Could not compute valid opening fee with min_fee_msat = {}, proportional = {}, and expected_outbound_amount_msat = {}" , opening_fee_params. min_fee_msat, opening_fee_params. proportional, expected_outbound_amount_msat) ) )
233
+ OutboundJITChannelState :: InvoiceParametersGenerated {
234
+ opening_fee_params,
235
+ payment_size_msat,
236
+ ..
237
+ } => {
238
+ let htlcs = vec ! [ htlc] ;
239
+ let supports_mpp = payment_size_msat. is_some ( ) ;
240
+ let payment_size_msat =
241
+ payment_size_msat. unwrap_or ( htlc. expected_outbound_amount_msat ) ;
242
+ let opening_fee_msat = compute_opening_fee ( payment_size_msat, opening_fee_params. min_fee_msat , opening_fee_params. proportional ) . ok_or ( ChannelStateError ( format ! ( "Could not compute valid opening fee with min_fee_msat = {}, proportional = {}, and expected_outbound_amount_msat = {}" , opening_fee_params. min_fee_msat, opening_fee_params. proportional, htlc. expected_outbound_amount_msat) ) ) ?;
243
+ let amt_to_forward_msat = payment_size_msat - opening_fee_msat;
244
+
245
+ if htlc. expected_outbound_amount_msat >= payment_size_msat
246
+ && amt_to_forward_msat > 0
247
+ {
248
+ Ok ( OutboundJITChannelState :: PendingChannelOpen {
249
+ htlcs,
250
+ opening_fee_msat,
251
+ amt_to_forward_msat,
252
+ } )
253
+ } else {
254
+ if supports_mpp {
255
+ Ok ( OutboundJITChannelState :: PendingPaymentReceived {
256
+ htlcs,
257
+ amt_to_forward_msat,
258
+ opening_fee_msat,
259
+ payment_size_msat,
260
+ } )
261
+ } else {
262
+ Err ( ChannelStateError ( "HTLC is too small to pay opening fee" . to_string ( ) ) )
263
+ }
264
+ }
265
+ }
266
+ OutboundJITChannelState :: PendingPaymentReceived {
267
+ htlcs,
268
+ payment_size_msat,
269
+ amt_to_forward_msat,
270
+ opening_fee_msat,
271
+ } => {
272
+ let mut htlcs = htlcs. clone ( ) ;
273
+ htlcs. push ( htlc) ;
274
+
275
+ let total_expected_outbound_amount_msat = htlcs
276
+ . iter ( )
277
+ . fold ( 0 , |total_msat, htlc| total_msat + htlc. expected_outbound_amount_msat ) ;
278
+
279
+ if total_expected_outbound_amount_msat >= * payment_size_msat {
280
+ return Ok ( OutboundJITChannelState :: PendingPaymentReceived {
281
+ htlcs,
282
+ payment_size_msat : * payment_size_msat,
283
+ amt_to_forward_msat : * amt_to_forward_msat,
284
+ opening_fee_msat : * opening_fee_msat,
285
+ } ) ;
286
+ }
287
+
288
+ Ok ( OutboundJITChannelState :: PendingChannelOpen {
289
+ htlcs,
290
+ opening_fee_msat : * opening_fee_msat,
291
+ amt_to_forward_msat : * amt_to_forward_msat,
292
+ } )
233
293
}
234
294
state => Err ( ChannelStateError ( format ! (
235
295
"Invoice params received when JIT Channel was in state: {:?}" ,
@@ -240,14 +300,12 @@ impl OutboundJITChannelState {
240
300
241
301
pub fn channel_ready ( & self ) -> Result < Self , ChannelStateError > {
242
302
match self {
243
- OutboundJITChannelState :: PendingChannelOpen {
244
- intercept_id,
245
- amt_to_forward_msat,
246
- ..
247
- } => Ok ( OutboundJITChannelState :: ChannelReady {
248
- intercept_id : * intercept_id,
249
- amt_to_forward_msat : * amt_to_forward_msat,
250
- } ) ,
303
+ OutboundJITChannelState :: PendingChannelOpen { htlcs, amt_to_forward_msat, .. } => {
304
+ Ok ( OutboundJITChannelState :: ChannelReady {
305
+ htlcs : htlcs. clone ( ) ,
306
+ amt_to_forward_msat : * amt_to_forward_msat,
307
+ } )
308
+ }
251
309
state => Err ( ChannelStateError ( format ! (
252
310
"Channel ready received when JIT Channel was in state: {:?}" ,
253
311
state
@@ -276,16 +334,17 @@ impl OutboundJITChannel {
276
334
}
277
335
278
336
pub fn htlc_intercepted (
279
- & mut self , expected_outbound_amount_msat : u64 , intercept_id : InterceptId ,
280
- ) -> Result < ( u64 , u64 ) , LightningError > {
281
- self . state = self . state . htlc_intercepted ( expected_outbound_amount_msat , intercept_id ) ?;
337
+ & mut self , htlc : InterceptedHTLC ,
338
+ ) -> Result < Option < ( u64 , u64 ) > , LightningError > {
339
+ self . state = self . state . htlc_intercepted ( htlc ) ?;
282
340
283
341
match & self . state {
342
+ OutboundJITChannelState :: PendingPaymentReceived { .. } => Ok ( None ) ,
284
343
OutboundJITChannelState :: PendingChannelOpen {
285
344
opening_fee_msat,
286
345
amt_to_forward_msat,
287
346
..
288
- } => Ok ( ( * opening_fee_msat, * amt_to_forward_msat) ) ,
347
+ } => Ok ( Some ( ( * opening_fee_msat, * amt_to_forward_msat) ) ) ,
289
348
impossible_state => Err ( LightningError {
290
349
err : format ! (
291
350
"Impossible state transition during htlc_intercepted to {:?}" ,
@@ -296,12 +355,12 @@ impl OutboundJITChannel {
296
355
}
297
356
}
298
357
299
- pub fn channel_ready ( & mut self ) -> Result < ( InterceptId , u64 ) , LightningError > {
358
+ pub fn channel_ready ( & mut self ) -> Result < ( Vec < InterceptedHTLC > , u64 ) , LightningError > {
300
359
self . state = self . state . channel_ready ( ) ?;
301
360
302
361
match & self . state {
303
- OutboundJITChannelState :: ChannelReady { intercept_id , amt_to_forward_msat } => {
304
- Ok ( ( * intercept_id , * amt_to_forward_msat) )
362
+ OutboundJITChannelState :: ChannelReady { htlcs , amt_to_forward_msat } => {
363
+ Ok ( ( htlcs . clone ( ) , * amt_to_forward_msat) )
305
364
}
306
365
impossible_state => Err ( LightningError {
307
366
err : format ! (
@@ -615,8 +674,7 @@ where
615
674
}
616
675
617
676
pub ( crate ) fn htlc_intercepted (
618
- & self , scid : u64 , intercept_id : InterceptId , inbound_amount_msat : u64 ,
619
- expected_outbound_amount_msat : u64 ,
677
+ & self , scid : u64 , intercept_id : InterceptId , expected_outbound_amount_msat : u64 ,
620
678
) -> Result < ( ) , APIError > {
621
679
let peer_by_scid = self . peer_by_scid . read ( ) . unwrap ( ) ;
622
680
if let Some ( counterparty_node_id) = peer_by_scid. get ( & scid) {
@@ -625,25 +683,17 @@ where
625
683
Some ( inner_state_lock) => {
626
684
let mut peer_state = inner_state_lock. lock ( ) . unwrap ( ) ;
627
685
if let Some ( jit_channel) = peer_state. outbound_channels_by_scid . get_mut ( & scid) {
628
- // TODO: Need to support MPP payments. If payment_amount_msat is known, needs to queue intercepted HTLCs in a map by payment_hash
629
- // LiquidityManager will need to be regularly polled so it can continually check if the payment amount has been received
630
- // and can release the payment or if the channel valid_until has expired and should be failed.
631
- // Can perform check each time HTLC is received and on interval? I guess interval only needs to check expiration as
632
- // we can only reach threshold when htlc is intercepted.
633
-
634
- match jit_channel
635
- . htlc_intercepted ( expected_outbound_amount_msat, intercept_id)
636
- {
637
- Ok ( ( opening_fee_msat, amt_to_forward_msat) ) => {
686
+ let htlc = InterceptedHTLC { intercept_id, expected_outbound_amount_msat } ;
687
+ match jit_channel. htlc_intercepted ( htlc) {
688
+ Ok ( Some ( ( opening_fee_msat, amt_to_forward_msat) ) ) => {
638
689
self . enqueue_event ( Event :: LSPS2 ( LSPS2Event :: OpenChannel {
639
690
their_network_key : counterparty_node_id. clone ( ) ,
640
- inbound_amount_msat,
641
- expected_outbound_amount_msat,
642
691
amt_to_forward_msat,
643
692
opening_fee_msat,
644
693
user_channel_id : scid as u128 ,
645
694
} ) ) ;
646
695
}
696
+ Ok ( None ) => { }
647
697
Err ( e) => {
648
698
self . channel_manager . fail_intercepted_htlc ( intercept_id) ?;
649
699
peer_state. outbound_channels_by_scid . remove ( & scid) ;
@@ -675,13 +725,39 @@ where
675
725
let mut peer_state = inner_state_lock. lock ( ) . unwrap ( ) ;
676
726
if let Some ( jit_channel) = peer_state. outbound_channels_by_scid . get_mut ( & scid) {
677
727
match jit_channel. channel_ready ( ) {
678
- Ok ( ( intercept_id, amt_to_forward_msat) ) => {
679
- self . channel_manager . forward_intercepted_htlc (
680
- intercept_id,
681
- channel_id,
682
- * counterparty_node_id,
683
- amt_to_forward_msat,
684
- ) ?;
728
+ Ok ( ( htlcs, amt_to_forward_msat) ) => {
729
+ // TODO: review strategy for handling batch forwarding of these htlcs
730
+ let min_htlc_msat = self
731
+ . channel_manager
732
+ . list_channels_with_counterparty ( counterparty_node_id)
733
+ . iter ( )
734
+ . find ( |channel| channel. channel_id == * channel_id)
735
+ . map ( |details| details. next_outbound_htlc_minimum_msat )
736
+ . ok_or ( APIError :: APIMisuseError {
737
+ err : format ! ( "Channel with id {} not found" , channel_id) ,
738
+ } ) ?;
739
+
740
+ let total_msat = htlcs
741
+ . iter ( )
742
+ . fold ( 0 , |acc, htlc| acc + htlc. expected_outbound_amount_msat ) ;
743
+ let mut fee_msat = total_msat - amt_to_forward_msat;
744
+
745
+ for htlc in htlcs {
746
+ let max_we_can_take =
747
+ htlc. expected_outbound_amount_msat - min_htlc_msat;
748
+ let amount_of_fee_to_take =
749
+ std:: cmp:: min ( fee_msat, max_we_can_take) ;
750
+ let amount_to_forward_msat =
751
+ htlc. expected_outbound_amount_msat - amount_of_fee_to_take;
752
+ self . channel_manager . forward_intercepted_htlc (
753
+ htlc. intercept_id ,
754
+ channel_id,
755
+ * counterparty_node_id,
756
+ amount_to_forward_msat,
757
+ ) ?;
758
+
759
+ fee_msat -= amount_of_fee_to_take;
760
+ }
685
761
}
686
762
Err ( e) => {
687
763
return Err ( APIError :: APIMisuseError {
0 commit comments