@@ -400,6 +400,63 @@ struct PendingInboundPayment {
400
400
min_value_msat : Option < u64 > ,
401
401
}
402
402
403
+ /// Stores the session_priv for each part of a payment that is still pending. For versions 0.0.102
404
+ /// and later, also stores information for retrying the payment.
405
+ enum PendingOutboundPayment {
406
+ Legacy {
407
+ session_privs : HashSet < [ u8 ; 32 ] > ,
408
+ } ,
409
+ Retryable {
410
+ session_privs : HashSet < [ u8 ; 32 ] > ,
411
+ payment_hash : PaymentHash ,
412
+ payment_secret : Option < PaymentSecret > ,
413
+ pending_amt_msat : u64 ,
414
+ /// The total payment amount across all paths, used to verify that a retry is not overpaying.
415
+ total_msat : u64 ,
416
+ } ,
417
+ }
418
+
419
+ impl PendingOutboundPayment {
420
+ fn remove ( & mut self , session_priv : & [ u8 ; 32 ] , part_amt_msat : u64 ) -> bool {
421
+ let remove_res = match self {
422
+ PendingOutboundPayment :: Legacy { session_privs } |
423
+ PendingOutboundPayment :: Retryable { session_privs, .. } => {
424
+ session_privs. remove ( session_priv)
425
+ }
426
+ } ;
427
+ if remove_res {
428
+ if let PendingOutboundPayment :: Retryable { ref mut pending_amt_msat, .. } = self {
429
+ * pending_amt_msat -= part_amt_msat;
430
+ }
431
+ }
432
+ remove_res
433
+ }
434
+
435
+ fn insert ( & mut self , session_priv : [ u8 ; 32 ] , part_amt_msat : u64 ) -> bool {
436
+ let insert_res = match self {
437
+ PendingOutboundPayment :: Legacy { session_privs } |
438
+ PendingOutboundPayment :: Retryable { session_privs, .. } => {
439
+ session_privs. insert ( session_priv)
440
+ }
441
+ } ;
442
+ if insert_res {
443
+ if let PendingOutboundPayment :: Retryable { ref mut pending_amt_msat, .. } = self {
444
+ * pending_amt_msat += part_amt_msat;
445
+ }
446
+ }
447
+ insert_res
448
+ }
449
+
450
+ fn remaining_parts ( & self ) -> usize {
451
+ match self {
452
+ PendingOutboundPayment :: Legacy { session_privs } |
453
+ PendingOutboundPayment :: Retryable { session_privs, .. } => {
454
+ session_privs. len ( )
455
+ }
456
+ }
457
+ }
458
+ }
459
+
403
460
/// SimpleArcChannelManager is useful when you need a ChannelManager with a static lifetime, e.g.
404
461
/// when you're using lightning-net-tokio (since tokio::spawn requires parameters with static
405
462
/// lifetimes). Other times you can afford a reference, which is more efficient, in which case
@@ -486,7 +543,7 @@ pub struct ChannelManager<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref,
486
543
/// Locked *after* channel_state.
487
544
pending_inbound_payments : Mutex < HashMap < PaymentHash , PendingInboundPayment > > ,
488
545
489
- /// The session_priv bytes of outbound payments which are pending resolution.
546
+ /// The session_priv bytes and retry metadata of outbound payments which are pending resolution.
490
547
/// The authoritative state of these HTLCs resides either within Channels or ChannelMonitors
491
548
/// (if the channel has been force-closed), however we track them here to prevent duplicative
492
549
/// PaymentSent/PaymentPathFailed events. Specifically, in the case of a duplicative
@@ -495,11 +552,10 @@ pub struct ChannelManager<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref,
495
552
/// which may generate a claim event, we may receive similar duplicate claim/fail MonitorEvents
496
553
/// after reloading from disk while replaying blocks against ChannelMonitors.
497
554
///
498
- /// Each payment has each of its MPP part's session_priv bytes in the HashSet of the map (even
499
- /// payments over a single path).
555
+ /// See `PendingOutboundPayment` documentation for more info.
500
556
///
501
557
/// Locked *after* channel_state.
502
- pending_outbound_payments : Mutex < HashMap < PaymentId , HashSet < [ u8 ; 32 ] > > > ,
558
+ pending_outbound_payments : Mutex < HashMap < PaymentId , PendingOutboundPayment > > ,
503
559
504
560
our_network_key : SecretKey ,
505
561
our_network_pubkey : PublicKey ,
@@ -1894,8 +1950,14 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
1894
1950
1895
1951
let _persistence_guard = PersistenceNotifierGuard :: notify_on_drop ( & self . total_consistency_lock , & self . persistence_notifier ) ;
1896
1952
let mut pending_outbounds = self . pending_outbound_payments . lock ( ) . unwrap ( ) ;
1897
- let sessions = pending_outbounds. entry ( payment_id) . or_insert ( HashSet :: new ( ) ) ;
1898
- assert ! ( sessions. insert( session_priv_bytes) ) ;
1953
+ let payment = pending_outbounds. entry ( payment_id) . or_insert_with ( || PendingOutboundPayment :: Retryable {
1954
+ session_privs : HashSet :: new ( ) ,
1955
+ pending_amt_msat : 0 ,
1956
+ payment_hash : * payment_hash,
1957
+ payment_secret : * payment_secret,
1958
+ total_msat : total_value,
1959
+ } ) ;
1960
+ payment. insert ( session_priv_bytes, path. last ( ) . unwrap ( ) . fee_msat ) ;
1899
1961
1900
1962
let err: Result < ( ) , _ > = loop {
1901
1963
let mut channel_lock = self . channel_state . lock ( ) . unwrap ( ) ;
@@ -2883,23 +2945,23 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
2883
2945
let mut session_priv_bytes = [ 0 ; 32 ] ;
2884
2946
session_priv_bytes. copy_from_slice ( & session_priv[ ..] ) ;
2885
2947
let mut outbounds = self . pending_outbound_payments . lock ( ) . unwrap ( ) ;
2886
- if let hash_map:: Entry :: Occupied ( mut sessions ) = outbounds. entry ( payment_id) {
2887
- if sessions . get_mut ( ) . remove ( & session_priv_bytes) {
2948
+ if let hash_map:: Entry :: Occupied ( mut payment ) = outbounds. entry ( payment_id) {
2949
+ if payment . get_mut ( ) . remove ( & session_priv_bytes, path . last ( ) . unwrap ( ) . fee_msat ) {
2888
2950
self . pending_events . lock ( ) . unwrap ( ) . push (
2889
2951
events:: Event :: PaymentPathFailed {
2890
2952
payment_hash,
2891
2953
rejected_by_dest : false ,
2892
2954
network_update : None ,
2893
- all_paths_failed : sessions . get ( ) . len ( ) == 0 ,
2955
+ all_paths_failed : payment . get ( ) . remaining_parts ( ) == 0 ,
2894
2956
path : path. clone ( ) ,
2895
2957
#[ cfg( test) ]
2896
2958
error_code : None ,
2897
2959
#[ cfg( test) ]
2898
2960
error_data : None ,
2899
2961
}
2900
2962
) ;
2901
- if sessions . get ( ) . len ( ) == 0 {
2902
- sessions . remove ( ) ;
2963
+ if payment . get ( ) . remaining_parts ( ) == 0 {
2964
+ payment . remove ( ) ;
2903
2965
}
2904
2966
}
2905
2967
} else {
@@ -2932,11 +2994,11 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
2932
2994
let mut outbounds = self . pending_outbound_payments . lock ( ) . unwrap ( ) ;
2933
2995
let mut all_paths_failed = false ;
2934
2996
if let hash_map:: Entry :: Occupied ( mut sessions) = outbounds. entry ( payment_id) {
2935
- if !sessions. get_mut ( ) . remove ( & session_priv_bytes) {
2997
+ if !sessions. get_mut ( ) . remove ( & session_priv_bytes, path . last ( ) . unwrap ( ) . fee_msat ) {
2936
2998
log_trace ! ( self . logger, "Received duplicative fail for HTLC with payment_hash {}" , log_bytes!( payment_hash. 0 ) ) ;
2937
2999
return ;
2938
3000
}
2939
- if sessions. get ( ) . len ( ) == 0 {
3001
+ if sessions. get ( ) . remaining_parts ( ) == 0 {
2940
3002
all_paths_failed = true ;
2941
3003
sessions. remove ( ) ;
2942
3004
}
@@ -3185,13 +3247,13 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
3185
3247
3186
3248
fn claim_funds_internal ( & self , mut channel_state_lock : MutexGuard < ChannelHolder < Signer > > , source : HTLCSource , payment_preimage : PaymentPreimage , forwarded_htlc_value_msat : Option < u64 > , from_onchain : bool ) {
3187
3249
match source {
3188
- HTLCSource :: OutboundRoute { session_priv, payment_id, .. } => {
3250
+ HTLCSource :: OutboundRoute { session_priv, payment_id, path , .. } => {
3189
3251
mem:: drop ( channel_state_lock) ;
3190
3252
let mut session_priv_bytes = [ 0 ; 32 ] ;
3191
3253
session_priv_bytes. copy_from_slice ( & session_priv[ ..] ) ;
3192
3254
let mut outbounds = self . pending_outbound_payments . lock ( ) . unwrap ( ) ;
3193
3255
let found_payment = if let Some ( mut sessions) = outbounds. remove ( & payment_id) {
3194
- sessions. remove ( & session_priv_bytes)
3256
+ sessions. remove ( & session_priv_bytes, path . last ( ) . unwrap ( ) . fee_msat )
3195
3257
} else { false } ;
3196
3258
if found_payment {
3197
3259
self . pending_events . lock ( ) . unwrap ( ) . push (
@@ -5161,6 +5223,19 @@ impl_writeable_tlv_based!(PendingInboundPayment, {
5161
5223
( 8 , min_value_msat, required) ,
5162
5224
} ) ;
5163
5225
5226
+ impl_writeable_tlv_based_enum ! ( PendingOutboundPayment ,
5227
+ ( 0 , Legacy ) => {
5228
+ ( 0 , session_privs, required) ,
5229
+ } ,
5230
+ ( 2 , Retryable ) => {
5231
+ ( 0 , session_privs, required) ,
5232
+ ( 2 , payment_hash, required) ,
5233
+ ( 4 , payment_secret, option) ,
5234
+ ( 6 , total_msat, required) ,
5235
+ ( 8 , pending_amt_msat, required) ,
5236
+ } ,
5237
+ ; ) ;
5238
+
5164
5239
impl < Signer : Sign , M : Deref , T : Deref , K : Deref , F : Deref , L : Deref > Writeable for ChannelManager < Signer , M , T , K , F , L >
5165
5240
where M :: Target : chain:: Watch < Signer > ,
5166
5241
T :: Target : BroadcasterInterface ,
@@ -5251,18 +5326,34 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> Writeable f
5251
5326
let pending_outbound_payments = self . pending_outbound_payments . lock ( ) . unwrap ( ) ;
5252
5327
// For backwards compat, write the session privs and their total length.
5253
5328
let mut num_pending_outbounds_compat: u64 = 0 ;
5254
- for ( _, outbounds ) in pending_outbound_payments. iter ( ) {
5255
- num_pending_outbounds_compat += outbounds . len ( ) as u64 ;
5329
+ for ( _, outbound ) in pending_outbound_payments. iter ( ) {
5330
+ num_pending_outbounds_compat += outbound . remaining_parts ( ) as u64 ;
5256
5331
}
5257
5332
num_pending_outbounds_compat. write ( writer) ?;
5258
- for ( _, outbounds) in pending_outbound_payments. iter ( ) {
5259
- for outbound in outbounds. iter ( ) {
5260
- outbound. write ( writer) ?;
5333
+ for ( _, outbound) in pending_outbound_payments. iter ( ) {
5334
+ match outbound {
5335
+ PendingOutboundPayment :: Legacy { session_privs } |
5336
+ PendingOutboundPayment :: Retryable { session_privs, .. } => {
5337
+ for session_priv in session_privs. iter ( ) {
5338
+ session_priv. write ( writer) ?;
5339
+ }
5340
+ }
5261
5341
}
5262
5342
}
5263
5343
5344
+ // Encode without retry info for 0.0.101 compatibility.
5345
+ let mut pending_outbound_payments_no_retry: HashMap < PaymentId , HashSet < [ u8 ; 32 ] > > = HashMap :: new ( ) ;
5346
+ for ( id, outbound) in pending_outbound_payments. iter ( ) {
5347
+ match outbound {
5348
+ PendingOutboundPayment :: Legacy { session_privs } |
5349
+ PendingOutboundPayment :: Retryable { session_privs, .. } => {
5350
+ pending_outbound_payments_no_retry. insert ( * id, session_privs. clone ( ) ) ;
5351
+ }
5352
+ }
5353
+ }
5264
5354
write_tlv_fields ! ( writer, {
5265
- ( 1 , pending_outbound_payments, required) ,
5355
+ ( 1 , pending_outbound_payments_no_retry, required) ,
5356
+ ( 3 , pending_outbound_payments, required) ,
5266
5357
} ) ;
5267
5358
5268
5359
Ok ( ( ) )
@@ -5522,21 +5613,33 @@ impl<'a, Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
5522
5613
}
5523
5614
5524
5615
let pending_outbound_payments_count_compat: u64 = Readable :: read ( reader) ?;
5525
- let mut pending_outbound_payments_compat: HashMap < PaymentId , HashSet < [ u8 ; 32 ] > > =
5616
+ let mut pending_outbound_payments_compat: HashMap < PaymentId , PendingOutboundPayment > =
5526
5617
HashMap :: with_capacity ( cmp:: min ( pending_outbound_payments_count_compat as usize , MAX_ALLOC_SIZE /32 ) ) ;
5527
5618
for _ in 0 ..pending_outbound_payments_count_compat {
5528
5619
let session_priv = Readable :: read ( reader) ?;
5529
- if pending_outbound_payments_compat. insert ( PaymentId ( session_priv) , [ session_priv] . iter ( ) . cloned ( ) . collect ( ) ) . is_some ( ) {
5620
+ let payment = PendingOutboundPayment :: Legacy {
5621
+ session_privs : [ session_priv] . iter ( ) . cloned ( ) . collect ( )
5622
+ } ;
5623
+ if pending_outbound_payments_compat. insert ( PaymentId ( session_priv) , payment) . is_some ( ) {
5530
5624
return Err ( DecodeError :: InvalidValue )
5531
5625
} ;
5532
5626
}
5533
5627
5628
+ // pending_outbound_payments_no_retry is for compatibility with 0.0.101 clients.
5629
+ let mut pending_outbound_payments_no_retry: Option < HashMap < PaymentId , HashSet < [ u8 ; 32 ] > > > = None ;
5534
5630
let mut pending_outbound_payments = None ;
5535
5631
read_tlv_fields ! ( reader, {
5536
- ( 1 , pending_outbound_payments, option) ,
5632
+ ( 1 , pending_outbound_payments_no_retry, option) ,
5633
+ ( 3 , pending_outbound_payments, option) ,
5537
5634
} ) ;
5538
- if pending_outbound_payments. is_none ( ) {
5635
+ if pending_outbound_payments. is_none ( ) && pending_outbound_payments_no_retry . is_none ( ) {
5539
5636
pending_outbound_payments = Some ( pending_outbound_payments_compat) ;
5637
+ } else if pending_outbound_payments. is_none ( ) {
5638
+ let mut outbounds = HashMap :: new ( ) ;
5639
+ for ( id, session_privs) in pending_outbound_payments_no_retry. unwrap ( ) . drain ( ) {
5640
+ outbounds. insert ( id, PendingOutboundPayment :: Legacy { session_privs } ) ;
5641
+ }
5642
+ pending_outbound_payments = Some ( outbounds) ;
5540
5643
}
5541
5644
5542
5645
let mut secp_ctx = Secp256k1 :: new ( ) ;
0 commit comments