@@ -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,21 +147,22 @@ 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 < ( ) , ( ) > {
149
- let mut session_privs = HashSet :: new ( ) ;
150
- let our_payment_hash;
151
- core:: mem:: swap ( & mut session_privs, match self {
150
+ fn mark_abandoned ( & mut self , reason : PaymentFailureReason ) -> Result < ( ) , ( ) > {
151
+ match self {
152
152
PendingOutboundPayment :: Legacy { .. } |
153
- PendingOutboundPayment :: Fulfilled { .. } =>
154
- return Err ( ( ) ) ,
155
- PendingOutboundPayment :: Retryable { session_privs, payment_hash, .. } |
156
- PendingOutboundPayment :: Abandoned { session_privs, payment_hash, .. } => {
157
- our_payment_hash = * payment_hash;
158
- session_privs
153
+ PendingOutboundPayment :: Fulfilled { .. } => Err ( ( ) ) ,
154
+ PendingOutboundPayment :: Retryable { session_privs, payment_hash, .. } => {
155
+ let mut our_session_privs = HashSet :: new ( ) ;
156
+ core:: mem:: swap ( & mut our_session_privs, session_privs) ;
157
+ * self = PendingOutboundPayment :: Abandoned {
158
+ session_privs : our_session_privs,
159
+ payment_hash : * payment_hash,
160
+ reason : Some ( reason)
161
+ } ;
162
+ Ok ( ( ) )
159
163
} ,
160
- } ) ;
161
- * self = PendingOutboundPayment :: Abandoned { session_privs, payment_hash : our_payment_hash } ;
162
- Ok ( ( ) )
164
+ PendingOutboundPayment :: Abandoned { .. } => Ok ( ( ) ) ,
165
+ }
163
166
}
164
167
165
168
/// panics if path is None and !self.is_fulfilled
@@ -546,10 +549,12 @@ impl OutboundPayments {
546
549
outbounds. retain ( |pmt_id, pmt| {
547
550
let mut retain = true ;
548
551
if !pmt. is_auto_retryable_now ( ) && pmt. remaining_parts ( ) == 0 {
549
- if pmt. mark_abandoned ( ) . is_ok ( ) {
552
+ let _ = pmt. mark_abandoned ( PaymentFailureReason :: RetriesExhausted ) ;
553
+ if let PendingOutboundPayment :: Abandoned { payment_hash, reason, .. } = pmt {
550
554
pending_events. lock ( ) . unwrap ( ) . push ( events:: Event :: PaymentFailed {
551
555
payment_id : * pmt_id,
552
- payment_hash : pmt. payment_hash ( ) . expect ( "PendingOutboundPayments::Retryable always has a payment hash set" ) ,
556
+ payment_hash : * payment_hash,
557
+ reason : * reason,
553
558
} ) ;
554
559
retain = false ;
555
560
}
@@ -629,7 +634,7 @@ impl OutboundPayments {
629
634
#[ cfg( feature = "std" ) ] {
630
635
if has_expired ( & route_params) {
631
636
log_error ! ( logger, "Payment params expired on retry, abandoning payment {}" , log_bytes!( payment_id. 0 ) ) ;
632
- self . abandon_payment ( payment_id, pending_events) ;
637
+ self . abandon_payment ( payment_id, PaymentFailureReason :: PaymentExpired , pending_events) ;
633
638
return
634
639
}
635
640
}
@@ -642,14 +647,14 @@ impl OutboundPayments {
642
647
Ok ( route) => route,
643
648
Err ( e) => {
644
649
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) ;
650
+ self . abandon_payment ( payment_id, PaymentFailureReason :: RouteNotFound , pending_events) ;
646
651
return
647
652
}
648
653
} ;
649
654
for path in route. paths . iter ( ) {
650
655
if path. len ( ) == 0 {
651
656
log_error ! ( logger, "length-0 path in route" ) ;
652
- self . abandon_payment ( payment_id, pending_events) ;
657
+ self . abandon_payment ( payment_id, PaymentFailureReason :: UnexpectedError , pending_events) ;
653
658
return
654
659
}
655
660
}
@@ -661,13 +666,17 @@ impl OutboundPayments {
661
666
}
662
667
663
668
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( ) ;
669
+ ( $payment: expr, $reason: expr) => {
670
+ let _ = $payment. get_mut( ) . mark_abandoned( $reason) ;
671
+ if let PendingOutboundPayment :: Abandoned { reason, .. } = $payment. get( ) {
672
+ if $payment. get( ) . remaining_parts( ) == 0 {
673
+ pending_events. lock( ) . unwrap( ) . push( events:: Event :: PaymentFailed {
674
+ payment_id,
675
+ payment_hash,
676
+ reason: * reason,
677
+ } ) ;
678
+ $payment. remove( ) ;
679
+ }
671
680
}
672
681
}
673
682
}
@@ -682,7 +691,7 @@ impl OutboundPayments {
682
691
let retry_amt_msat: u64 = route. paths . iter ( ) . map ( |path| path. last ( ) . unwrap ( ) . fee_msat ) . sum ( ) ;
683
692
if retry_amt_msat + * pending_amt_msat > * total_msat * ( 100 + RETRY_OVERFLOW_PERCENTAGE ) / 100 {
684
693
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) ;
694
+ abandon_with_entry ! ( payment, PaymentFailureReason :: UnexpectedError ) ;
686
695
return
687
696
}
688
697
( * total_msat, * payment_secret, * keysend_preimage)
@@ -702,7 +711,7 @@ impl OutboundPayments {
702
711
} ;
703
712
if !payment. get ( ) . is_retryable_now ( ) {
704
713
log_error ! ( logger, "Retries exhausted for payment id {}" , log_bytes!( payment_id. 0 ) ) ;
705
- abandon_with_entry ! ( payment) ;
714
+ abandon_with_entry ! ( payment, PaymentFailureReason :: RetriesExhausted ) ;
706
715
return
707
716
}
708
717
payment. get_mut ( ) . increment_attempts ( ) ;
@@ -759,12 +768,13 @@ impl OutboundPayments {
759
768
// initial HTLC-Add messages yet.
760
769
} ,
761
770
PaymentSendFailure :: PathParameterError ( results) => {
771
+ log_error ! ( logger, "Failed to send to route due to parameter error in a single path. Your router is buggy" ) ;
762
772
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) ;
773
+ self . abandon_payment ( payment_id, PaymentFailureReason :: UnexpectedError , pending_events) ;
764
774
} ,
765
775
PaymentSendFailure :: ParameterError ( e) => {
766
776
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) ;
777
+ self . abandon_payment ( payment_id, PaymentFailureReason :: UnexpectedError , pending_events) ;
768
778
} ,
769
779
PaymentSendFailure :: DuplicatePayment => debug_assert ! ( false ) , // unreachable
770
780
}
@@ -1167,15 +1177,21 @@ impl OutboundPayments {
1167
1177
}
1168
1178
1169
1179
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
1180
+ let reason = if !payment_retryable {
1181
+ PaymentFailureReason :: RecipientRejected
1182
+ } else {
1183
+ PaymentFailureReason :: RetriesExhausted
1184
+ } ;
1185
+ let _ = payment. get_mut ( ) . mark_abandoned ( reason) ; // we'll only Err if it's a legacy payment
1171
1186
is_retryable_now = false ;
1172
1187
}
1173
1188
if payment. get ( ) . remaining_parts ( ) == 0 {
1174
- if payment. get ( ) . abandoned ( ) {
1189
+ if let PendingOutboundPayment :: Abandoned { payment_hash , reason , .. } = payment. get ( ) {
1175
1190
if !payment_is_probe {
1176
1191
full_failure_ev = Some ( events:: Event :: PaymentFailed {
1177
1192
payment_id : * payment_id,
1178
- payment_hash : payment. get ( ) . payment_hash ( ) . expect ( "PendingOutboundPayments::RetriesExceeded always has a payment hash set" ) ,
1193
+ payment_hash : * payment_hash,
1194
+ reason : * reason,
1179
1195
} ) ;
1180
1196
}
1181
1197
payment. remove ( ) ;
@@ -1233,15 +1249,17 @@ impl OutboundPayments {
1233
1249
}
1234
1250
1235
1251
pub ( super ) fn abandon_payment (
1236
- & self , payment_id : PaymentId , pending_events : & Mutex < Vec < events:: Event > >
1252
+ & self , payment_id : PaymentId , reason : PaymentFailureReason , pending_events : & Mutex < Vec < events:: Event > >
1237
1253
) {
1238
1254
let mut outbounds = self . pending_outbound_payments . lock ( ) . unwrap ( ) ;
1239
1255
if let hash_map:: Entry :: Occupied ( mut payment) = outbounds. entry ( payment_id) {
1240
- if let Ok ( ( ) ) = payment. get_mut ( ) . mark_abandoned ( ) {
1256
+ let _ = payment. get_mut ( ) . mark_abandoned ( reason) ;
1257
+ if let PendingOutboundPayment :: Abandoned { payment_hash, reason, .. } = payment. get ( ) {
1241
1258
if payment. get ( ) . remaining_parts ( ) == 0 {
1242
1259
pending_events. lock ( ) . unwrap ( ) . push ( events:: Event :: PaymentFailed {
1243
1260
payment_id,
1244
- payment_hash : payment. get ( ) . payment_hash ( ) . expect ( "PendingOutboundPayments::RetriesExceeded always has a payment hash set" ) ,
1261
+ payment_hash : * payment_hash,
1262
+ reason : * reason,
1245
1263
} ) ;
1246
1264
payment. remove ( ) ;
1247
1265
}
@@ -1303,6 +1321,7 @@ impl_writeable_tlv_based_enum_upgradable!(PendingOutboundPayment,
1303
1321
} ,
1304
1322
( 3 , Abandoned ) => {
1305
1323
( 0 , session_privs, required) ,
1324
+ ( 1 , reason, option) ,
1306
1325
( 2 , payment_hash, required) ,
1307
1326
} ,
1308
1327
) ;
@@ -1312,7 +1331,7 @@ mod tests {
1312
1331
use bitcoin:: network:: constants:: Network ;
1313
1332
use bitcoin:: secp256k1:: { PublicKey , Secp256k1 , SecretKey } ;
1314
1333
1315
- use crate :: events:: { Event , PathFailure } ;
1334
+ use crate :: events:: { Event , PathFailure , PaymentFailureReason } ;
1316
1335
use crate :: ln:: PaymentHash ;
1317
1336
use crate :: ln:: channelmanager:: PaymentId ;
1318
1337
use crate :: ln:: features:: { ChannelFeatures , NodeFeatures } ;
@@ -1360,7 +1379,9 @@ mod tests {
1360
1379
& pending_events, & |_, _, _, _, _, _, _, _| Ok ( ( ) ) ) ;
1361
1380
let events = pending_events. lock ( ) . unwrap ( ) ;
1362
1381
assert_eq ! ( events. len( ) , 1 ) ;
1363
- if let Event :: PaymentFailed { .. } = events[ 0 ] { } else { panic ! ( "Unexpected event" ) ; }
1382
+ if let Event :: PaymentFailed { ref reason, .. } = events[ 0 ] {
1383
+ assert_eq ! ( reason. unwrap( ) , PaymentFailureReason :: PaymentExpired ) ;
1384
+ } else { panic ! ( "Unexpected event" ) ; }
1364
1385
} else {
1365
1386
let err = outbound_payments. send_payment (
1366
1387
PaymentHash ( [ 0 ; 32 ] ) , & None , PaymentId ( [ 0 ; 32 ] ) , Retry :: Attempts ( 0 ) , expired_route_params,
0 commit comments