@@ -14,7 +14,7 @@ use bitcoin::hashes::sha256::Hash as Sha256;
14
14
use bitcoin:: secp256k1:: { self , Secp256k1 , SecretKey } ;
15
15
16
16
use crate :: chain:: keysinterface:: { EntropySource , NodeSigner , Recipient } ;
17
- use crate :: events;
17
+ use crate :: events:: { self , PaymentFailureReason } ;
18
18
use crate :: ln:: { PaymentHash , PaymentPreimage , PaymentSecret } ;
19
19
use crate :: ln:: channelmanager:: { ChannelDetails , HTLCSource , IDEMPOTENCY_TIMEOUT_TICKS , PaymentId } ;
20
20
use crate :: ln:: onion_utils:: HTLCFailReason ;
@@ -68,6 +68,8 @@ pub(crate) enum PendingOutboundPayment {
68
68
Abandoned {
69
69
session_privs : HashSet < [ u8 ; 32 ] > ,
70
70
payment_hash : PaymentHash ,
71
+ /// Will be `None` if the payment was serialized before 0.0.115.
72
+ reason : Option < PaymentFailureReason > ,
71
73
} ,
72
74
}
73
75
@@ -145,7 +147,7 @@ impl PendingOutboundPayment {
145
147
* self = PendingOutboundPayment :: Fulfilled { session_privs, payment_hash, timer_ticks_without_htlcs : 0 } ;
146
148
}
147
149
148
- fn mark_abandoned ( & mut self ) -> Result < ( ) , ( ) > {
150
+ fn mark_abandoned ( & mut self , reason : PaymentFailureReason ) -> Result < ( ) , ( ) > {
149
151
let mut session_privs = HashSet :: new ( ) ;
150
152
let our_payment_hash;
151
153
core:: mem:: swap ( & mut session_privs, match self {
@@ -158,7 +160,7 @@ impl PendingOutboundPayment {
158
160
session_privs
159
161
} ,
160
162
} ) ;
161
- * self = PendingOutboundPayment :: Abandoned { session_privs, payment_hash : our_payment_hash } ;
163
+ * self = PendingOutboundPayment :: Abandoned { session_privs, payment_hash : our_payment_hash, reason : Some ( reason ) } ;
162
164
Ok ( ( ) )
163
165
}
164
166
@@ -546,10 +548,14 @@ impl OutboundPayments {
546
548
outbounds. retain ( |pmt_id, pmt| {
547
549
let mut retain = true ;
548
550
if !pmt. is_auto_retryable_now ( ) && pmt. remaining_parts ( ) == 0 {
549
- if pmt. mark_abandoned ( ) . is_ok ( ) {
551
+ if !pmt. abandoned ( ) {
552
+ let _ = pmt. mark_abandoned ( PaymentFailureReason :: RetriesExhausted ) ;
553
+ }
554
+ if let PendingOutboundPayment :: Abandoned { payment_hash, reason, .. } = pmt {
550
555
pending_events. lock ( ) . unwrap ( ) . push ( events:: Event :: PaymentFailed {
551
556
payment_id : * pmt_id,
552
- payment_hash : pmt. payment_hash ( ) . expect ( "PendingOutboundPayments::Retryable always has a payment hash set" ) ,
557
+ payment_hash : * payment_hash,
558
+ reason : * reason,
553
559
} ) ;
554
560
retain = false ;
555
561
}
@@ -629,7 +635,7 @@ impl OutboundPayments {
629
635
#[ cfg( feature = "std" ) ] {
630
636
if has_expired ( & route_params) {
631
637
log_error ! ( logger, "Payment params expired on retry, abandoning payment {}" , log_bytes!( payment_id. 0 ) ) ;
632
- self . abandon_payment ( payment_id, pending_events) ;
638
+ self . abandon_payment ( payment_id, PaymentFailureReason :: PaymentExpired , pending_events) ;
633
639
return
634
640
}
635
641
}
@@ -642,14 +648,14 @@ impl OutboundPayments {
642
648
Ok ( route) => route,
643
649
Err ( e) => {
644
650
log_error ! ( logger, "Failed to find a route on retry, abandoning payment {}: {:#?}" , log_bytes!( payment_id. 0 ) , e) ;
645
- self . abandon_payment ( payment_id, pending_events) ;
651
+ self . abandon_payment ( payment_id, PaymentFailureReason :: RouteNotFound , pending_events) ;
646
652
return
647
653
}
648
654
} ;
649
655
for path in route. paths . iter ( ) {
650
656
if path. len ( ) == 0 {
651
657
log_error ! ( logger, "length-0 path in route" ) ;
652
- self . abandon_payment ( payment_id, pending_events) ;
658
+ self . abandon_payment ( payment_id, PaymentFailureReason :: UnexpectedError , pending_events) ;
653
659
return
654
660
}
655
661
}
@@ -661,13 +667,19 @@ impl OutboundPayments {
661
667
}
662
668
663
669
macro_rules! abandon_with_entry {
664
- ( $payment: expr) => {
665
- if $payment. get_mut( ) . mark_abandoned( ) . is_ok( ) && $payment. get( ) . remaining_parts( ) == 0 {
666
- pending_events. lock( ) . unwrap( ) . push( events:: Event :: PaymentFailed {
667
- payment_id,
668
- payment_hash,
669
- } ) ;
670
- $payment. remove( ) ;
670
+ ( $payment: expr, $reason: expr) => {
671
+ if !$payment. get( ) . abandoned( ) {
672
+ let _ = $payment. get_mut( ) . mark_abandoned( $reason) ;
673
+ }
674
+ if let PendingOutboundPayment :: Abandoned { reason, .. } = $payment. get( ) {
675
+ if $payment. get( ) . remaining_parts( ) == 0 {
676
+ pending_events. lock( ) . unwrap( ) . push( events:: Event :: PaymentFailed {
677
+ payment_id,
678
+ payment_hash,
679
+ reason: * reason,
680
+ } ) ;
681
+ $payment. remove( ) ;
682
+ }
671
683
}
672
684
}
673
685
}
@@ -682,7 +694,7 @@ impl OutboundPayments {
682
694
let retry_amt_msat: u64 = route. paths . iter ( ) . map ( |path| path. last ( ) . unwrap ( ) . fee_msat ) . sum ( ) ;
683
695
if retry_amt_msat + * pending_amt_msat > * total_msat * ( 100 + RETRY_OVERFLOW_PERCENTAGE ) / 100 {
684
696
log_error ! ( logger, "retry_amt_msat of {} will put pending_amt_msat (currently: {}) more than 10% over total_payment_amt_msat of {}" , retry_amt_msat, pending_amt_msat, total_msat) ;
685
- abandon_with_entry ! ( payment) ;
697
+ abandon_with_entry ! ( payment, PaymentFailureReason :: UnexpectedError ) ;
686
698
return
687
699
}
688
700
( * total_msat, * payment_secret, * keysend_preimage)
@@ -702,7 +714,7 @@ impl OutboundPayments {
702
714
} ;
703
715
if !payment. get ( ) . is_retryable_now ( ) {
704
716
log_error ! ( logger, "Retries exhausted for payment id {}" , log_bytes!( payment_id. 0 ) ) ;
705
- abandon_with_entry ! ( payment) ;
717
+ abandon_with_entry ! ( payment, PaymentFailureReason :: RetriesExhausted ) ;
706
718
return
707
719
}
708
720
payment. get_mut ( ) . increment_attempts ( ) ;
@@ -759,12 +771,13 @@ impl OutboundPayments {
759
771
// initial HTLC-Add messages yet.
760
772
} ,
761
773
PaymentSendFailure :: PathParameterError ( results) => {
774
+ log_error ! ( logger, "Failed to send to route due to parameter error in a single path. Your router is buggy" ) ;
762
775
Self :: push_path_failed_evs_and_scids ( payment_id, payment_hash, & mut route_params, route. paths , results. into_iter ( ) , pending_events) ;
763
- self . abandon_payment ( payment_id, pending_events) ;
776
+ self . abandon_payment ( payment_id, PaymentFailureReason :: UnexpectedError , pending_events) ;
764
777
} ,
765
778
PaymentSendFailure :: ParameterError ( e) => {
766
779
log_error ! ( logger, "Failed to send to route due to parameter error: {:?}. Your router is buggy" , e) ;
767
- self . abandon_payment ( payment_id, pending_events) ;
780
+ self . abandon_payment ( payment_id, PaymentFailureReason :: UnexpectedError , pending_events) ;
768
781
} ,
769
782
PaymentSendFailure :: DuplicatePayment => debug_assert ! ( false ) , // unreachable
770
783
}
@@ -1167,15 +1180,21 @@ impl OutboundPayments {
1167
1180
}
1168
1181
1169
1182
if payment_is_probe || !is_retryable_now || !payment_retryable {
1170
- let _ = payment. get_mut ( ) . mark_abandoned ( ) ; // we'll only Err if it's a legacy payment
1183
+ let reason = if !payment_retryable {
1184
+ PaymentFailureReason :: RecipientRejected
1185
+ } else {
1186
+ PaymentFailureReason :: RetriesExhausted
1187
+ } ;
1188
+ let _ = payment. get_mut ( ) . mark_abandoned ( reason) ; // we'll only Err if it's a legacy payment
1171
1189
is_retryable_now = false ;
1172
1190
}
1173
1191
if payment. get ( ) . remaining_parts ( ) == 0 {
1174
- if payment. get ( ) . abandoned ( ) {
1192
+ if let PendingOutboundPayment :: Abandoned { payment_hash , reason , .. } = payment. get ( ) {
1175
1193
if !payment_is_probe {
1176
1194
full_failure_ev = Some ( events:: Event :: PaymentFailed {
1177
1195
payment_id : * payment_id,
1178
- payment_hash : payment. get ( ) . payment_hash ( ) . expect ( "PendingOutboundPayments::RetriesExceeded always has a payment hash set" ) ,
1196
+ payment_hash : * payment_hash,
1197
+ reason : * reason,
1179
1198
} ) ;
1180
1199
}
1181
1200
payment. remove ( ) ;
@@ -1233,15 +1252,19 @@ impl OutboundPayments {
1233
1252
}
1234
1253
1235
1254
pub ( super ) fn abandon_payment (
1236
- & self , payment_id : PaymentId , pending_events : & Mutex < Vec < events:: Event > >
1255
+ & self , payment_id : PaymentId , reason : PaymentFailureReason , pending_events : & Mutex < Vec < events:: Event > >
1237
1256
) {
1238
1257
let mut outbounds = self . pending_outbound_payments . lock ( ) . unwrap ( ) ;
1239
1258
if let hash_map:: Entry :: Occupied ( mut payment) = outbounds. entry ( payment_id) {
1240
- if let Ok ( ( ) ) = payment. get_mut ( ) . mark_abandoned ( ) {
1259
+ if !payment. get ( ) . abandoned ( ) {
1260
+ let _ = payment. get_mut ( ) . mark_abandoned ( reason) ;
1261
+ }
1262
+ if let PendingOutboundPayment :: Abandoned { payment_hash, reason, .. } = payment. get ( ) {
1241
1263
if payment. get ( ) . remaining_parts ( ) == 0 {
1242
1264
pending_events. lock ( ) . unwrap ( ) . push ( events:: Event :: PaymentFailed {
1243
1265
payment_id,
1244
- payment_hash : payment. get ( ) . payment_hash ( ) . expect ( "PendingOutboundPayments::RetriesExceeded always has a payment hash set" ) ,
1266
+ payment_hash : * payment_hash,
1267
+ reason : * reason,
1245
1268
} ) ;
1246
1269
payment. remove ( ) ;
1247
1270
}
@@ -1303,6 +1326,7 @@ impl_writeable_tlv_based_enum_upgradable!(PendingOutboundPayment,
1303
1326
} ,
1304
1327
( 3 , Abandoned ) => {
1305
1328
( 0 , session_privs, required) ,
1329
+ ( 1 , reason, option) ,
1306
1330
( 2 , payment_hash, required) ,
1307
1331
} ,
1308
1332
) ;
@@ -1312,7 +1336,7 @@ mod tests {
1312
1336
use bitcoin:: network:: constants:: Network ;
1313
1337
use bitcoin:: secp256k1:: { PublicKey , Secp256k1 , SecretKey } ;
1314
1338
1315
- use crate :: events:: { Event , PathFailure } ;
1339
+ use crate :: events:: { Event , PathFailure , PaymentFailureReason } ;
1316
1340
use crate :: ln:: PaymentHash ;
1317
1341
use crate :: ln:: channelmanager:: PaymentId ;
1318
1342
use crate :: ln:: features:: { ChannelFeatures , NodeFeatures } ;
@@ -1360,7 +1384,9 @@ mod tests {
1360
1384
& pending_events, & |_, _, _, _, _, _, _, _| Ok ( ( ) ) ) ;
1361
1385
let events = pending_events. lock ( ) . unwrap ( ) ;
1362
1386
assert_eq ! ( events. len( ) , 1 ) ;
1363
- if let Event :: PaymentFailed { .. } = events[ 0 ] { } else { panic ! ( "Unexpected event" ) ; }
1387
+ if let Event :: PaymentFailed { ref reason, .. } = events[ 0 ] {
1388
+ assert_eq ! ( reason. unwrap( ) , PaymentFailureReason :: PaymentExpired ) ;
1389
+ } else { panic ! ( "Unexpected event" ) ; }
1364
1390
} else {
1365
1391
let err = outbound_payments. send_payment (
1366
1392
PaymentHash ( [ 0 ; 32 ] ) , & None , PaymentId ( [ 0 ; 32 ] ) , Retry :: Attempts ( 0 ) , expired_route_params,
0 commit comments