@@ -16,7 +16,7 @@ use bitcoin::secp256k1::{self, Secp256k1, SecretKey};
16
16
use crate :: sign:: { EntropySource , NodeSigner , Recipient } ;
17
17
use crate :: events:: { self , PaymentFailureReason } ;
18
18
use crate :: ln:: { PaymentHash , PaymentPreimage , PaymentSecret } ;
19
- use crate :: ln:: channelmanager:: { ChannelDetails , EventCompletionAction , HTLCSource , IDEMPOTENCY_TIMEOUT_TICKS , PaymentId } ;
19
+ use crate :: ln:: channelmanager:: { ChannelDetails , EventCompletionAction , HTLCSource , IDEMPOTENCY_TIMEOUT_TICKS , INVOICE_REQUEST_TIMEOUT_TICKS , PaymentId } ;
20
20
use crate :: ln:: onion_utils:: HTLCFailReason ;
21
21
use crate :: routing:: router:: { InFlightHtlcs , Path , PaymentParameters , Route , RouteParameters , Router } ;
22
22
use crate :: util:: errors:: APIError ;
@@ -38,6 +38,9 @@ pub(crate) enum PendingOutboundPayment {
38
38
Legacy {
39
39
session_privs : HashSet < [ u8 ; 32 ] > ,
40
40
} ,
41
+ AwaitingInvoice {
42
+ timer_ticks_without_response : u8 ,
43
+ } ,
41
44
Retryable {
42
45
retry_strategy : Option < Retry > ,
43
46
attempts : PaymentAttempts ,
@@ -107,6 +110,12 @@ impl PendingOutboundPayment {
107
110
params. previously_failed_channels . push ( scid) ;
108
111
}
109
112
}
113
+ fn is_awaiting_invoice ( & self ) -> bool {
114
+ match self {
115
+ PendingOutboundPayment :: AwaitingInvoice { .. } => true ,
116
+ _ => false ,
117
+ }
118
+ }
110
119
pub ( super ) fn is_fulfilled ( & self ) -> bool {
111
120
match self {
112
121
PendingOutboundPayment :: Fulfilled { .. } => true ,
@@ -129,6 +138,7 @@ impl PendingOutboundPayment {
129
138
fn payment_hash ( & self ) -> Option < PaymentHash > {
130
139
match self {
131
140
PendingOutboundPayment :: Legacy { .. } => None ,
141
+ PendingOutboundPayment :: AwaitingInvoice { .. } => None ,
132
142
PendingOutboundPayment :: Retryable { payment_hash, .. } => Some ( * payment_hash) ,
133
143
PendingOutboundPayment :: Fulfilled { payment_hash, .. } => * payment_hash,
134
144
PendingOutboundPayment :: Abandoned { payment_hash, .. } => Some ( * payment_hash) ,
@@ -141,8 +151,8 @@ impl PendingOutboundPayment {
141
151
PendingOutboundPayment :: Legacy { session_privs } |
142
152
PendingOutboundPayment :: Retryable { session_privs, .. } |
143
153
PendingOutboundPayment :: Fulfilled { session_privs, .. } |
144
- PendingOutboundPayment :: Abandoned { session_privs, .. }
145
- => session_privs ,
154
+ PendingOutboundPayment :: Abandoned { session_privs, .. } => session_privs ,
155
+ PendingOutboundPayment :: AwaitingInvoice { .. } => return ,
146
156
} ) ;
147
157
let payment_hash = self . payment_hash ( ) ;
148
158
* self = PendingOutboundPayment :: Fulfilled { session_privs, payment_hash, timer_ticks_without_htlcs : 0 } ;
@@ -168,7 +178,8 @@ impl PendingOutboundPayment {
168
178
PendingOutboundPayment :: Fulfilled { session_privs, .. } |
169
179
PendingOutboundPayment :: Abandoned { session_privs, .. } => {
170
180
session_privs. remove ( session_priv)
171
- }
181
+ } ,
182
+ PendingOutboundPayment :: AwaitingInvoice { .. } => false ,
172
183
} ;
173
184
if remove_res {
174
185
if let PendingOutboundPayment :: Retryable { ref mut pending_amt_msat, ref mut pending_fee_msat, .. } = self {
@@ -188,6 +199,7 @@ impl PendingOutboundPayment {
188
199
PendingOutboundPayment :: Retryable { session_privs, .. } => {
189
200
session_privs. insert ( session_priv)
190
201
}
202
+ PendingOutboundPayment :: AwaitingInvoice { .. } => false ,
191
203
PendingOutboundPayment :: Fulfilled { .. } => false ,
192
204
PendingOutboundPayment :: Abandoned { .. } => false ,
193
205
} ;
@@ -209,7 +221,8 @@ impl PendingOutboundPayment {
209
221
PendingOutboundPayment :: Fulfilled { session_privs, .. } |
210
222
PendingOutboundPayment :: Abandoned { session_privs, .. } => {
211
223
session_privs. len ( )
212
- }
224
+ } ,
225
+ PendingOutboundPayment :: AwaitingInvoice { .. } => 0 ,
213
226
}
214
227
}
215
228
}
@@ -626,7 +639,7 @@ impl OutboundPayments {
626
639
let mut outbounds = self . pending_outbound_payments . lock ( ) . unwrap ( ) ;
627
640
outbounds. retain ( |pmt_id, pmt| {
628
641
let mut retain = true ;
629
- if !pmt. is_auto_retryable_now ( ) && pmt. remaining_parts ( ) == 0 {
642
+ if !pmt. is_auto_retryable_now ( ) && pmt. remaining_parts ( ) == 0 && !pmt . is_awaiting_invoice ( ) {
630
643
pmt. mark_abandoned ( PaymentFailureReason :: RetriesExhausted ) ;
631
644
if let PendingOutboundPayment :: Abandoned { payment_hash, reason, .. } = pmt {
632
645
pending_events. lock ( ) . unwrap ( ) . push_back ( ( events:: Event :: PaymentFailed {
@@ -644,7 +657,8 @@ impl OutboundPayments {
644
657
pub ( super ) fn needs_abandon ( & self ) -> bool {
645
658
let outbounds = self . pending_outbound_payments . lock ( ) . unwrap ( ) ;
646
659
outbounds. iter ( ) . any ( |( _, pmt) |
647
- !pmt. is_auto_retryable_now ( ) && pmt. remaining_parts ( ) == 0 && !pmt. is_fulfilled ( ) )
660
+ !pmt. is_auto_retryable_now ( ) && pmt. remaining_parts ( ) == 0 && !pmt. is_fulfilled ( ) &&
661
+ !pmt. is_awaiting_invoice ( ) )
648
662
}
649
663
650
664
/// Errors immediately on [`RetryableSendFailure`] error conditions. Otherwise, further errors may
@@ -779,6 +793,10 @@ impl OutboundPayments {
779
793
log_error ! ( logger, "Unable to retry payments that were initially sent on LDK versions prior to 0.0.102" ) ;
780
794
return
781
795
} ,
796
+ PendingOutboundPayment :: AwaitingInvoice { .. } => {
797
+ log_error ! ( logger, "Payment not yet sent" ) ;
798
+ return
799
+ } ,
782
800
PendingOutboundPayment :: Fulfilled { .. } => {
783
801
log_error ! ( logger, "Payment already completed" ) ;
784
802
return
@@ -951,35 +969,57 @@ impl OutboundPayments {
951
969
keysend_preimage : Option < PaymentPreimage > , route : & Route , retry_strategy : Option < Retry > ,
952
970
payment_params : Option < PaymentParameters > , entropy_source : & ES , best_block_height : u32
953
971
) -> Result < Vec < [ u8 ; 32 ] > , PaymentSendFailure > where ES :: Target : EntropySource {
972
+ let mut pending_outbounds = self . pending_outbound_payments . lock ( ) . unwrap ( ) ;
973
+ let entry = pending_outbounds. entry ( payment_id) ;
974
+ if let hash_map:: Entry :: Occupied ( entry) = & entry {
975
+ if let PendingOutboundPayment :: AwaitingInvoice { .. } = entry. get ( ) { } else {
976
+ return Err ( PaymentSendFailure :: DuplicatePayment )
977
+ }
978
+ }
979
+
954
980
let mut onion_session_privs = Vec :: with_capacity ( route. paths . len ( ) ) ;
955
981
for _ in 0 ..route. paths . len ( ) {
956
982
onion_session_privs. push ( entropy_source. get_secure_random_bytes ( ) ) ;
957
983
}
958
984
985
+ let mut payment = PendingOutboundPayment :: Retryable {
986
+ retry_strategy,
987
+ attempts : PaymentAttempts :: new ( ) ,
988
+ payment_params,
989
+ session_privs : HashSet :: new ( ) ,
990
+ pending_amt_msat : 0 ,
991
+ pending_fee_msat : Some ( 0 ) ,
992
+ payment_hash,
993
+ payment_secret : recipient_onion. payment_secret ,
994
+ payment_metadata : recipient_onion. payment_metadata ,
995
+ keysend_preimage,
996
+ starting_block_height : best_block_height,
997
+ total_msat : route. get_total_amount ( ) ,
998
+ } ;
999
+
1000
+ for ( path, session_priv_bytes) in route. paths . iter ( ) . zip ( onion_session_privs. iter ( ) ) {
1001
+ assert ! ( payment. insert( * session_priv_bytes, path) ) ;
1002
+ }
1003
+
1004
+ match entry {
1005
+ hash_map:: Entry :: Occupied ( mut entry) => { entry. insert ( payment) ; } ,
1006
+ hash_map:: Entry :: Vacant ( entry) => { entry. insert ( payment) ; } ,
1007
+ }
1008
+
1009
+ Ok ( onion_session_privs)
1010
+ }
1011
+
1012
+ #[ allow( unused) ]
1013
+ pub ( super ) fn add_new_awaiting_invoice ( & self , payment_id : PaymentId ) -> Result < ( ) , ( ) > {
959
1014
let mut pending_outbounds = self . pending_outbound_payments . lock ( ) . unwrap ( ) ;
960
1015
match pending_outbounds. entry ( payment_id) {
961
- hash_map:: Entry :: Occupied ( _) => Err ( PaymentSendFailure :: DuplicatePayment ) ,
1016
+ hash_map:: Entry :: Occupied ( _) => Err ( ( ) ) ,
962
1017
hash_map:: Entry :: Vacant ( entry) => {
963
- let payment = entry. insert ( PendingOutboundPayment :: Retryable {
964
- retry_strategy,
965
- attempts : PaymentAttempts :: new ( ) ,
966
- payment_params,
967
- session_privs : HashSet :: new ( ) ,
968
- pending_amt_msat : 0 ,
969
- pending_fee_msat : Some ( 0 ) ,
970
- payment_hash,
971
- payment_secret : recipient_onion. payment_secret ,
972
- payment_metadata : recipient_onion. payment_metadata ,
973
- keysend_preimage,
974
- starting_block_height : best_block_height,
975
- total_msat : route. get_total_amount ( ) ,
1018
+ entry. insert ( PendingOutboundPayment :: AwaitingInvoice {
1019
+ timer_ticks_without_response : 0 ,
976
1020
} ) ;
977
1021
978
- for ( path, session_priv_bytes) in route. paths . iter ( ) . zip ( onion_session_privs. iter ( ) ) {
979
- assert ! ( payment. insert( * session_priv_bytes, path) ) ;
980
- }
981
-
982
- Ok ( onion_session_privs)
1022
+ Ok ( ( ) )
983
1023
} ,
984
1024
}
985
1025
}
@@ -1188,19 +1228,19 @@ impl OutboundPayments {
1188
1228
}
1189
1229
}
1190
1230
1191
- pub ( super ) fn remove_stale_resolved_payments ( & self ,
1192
- pending_events : & Mutex < VecDeque < ( events:: Event , Option < EventCompletionAction > ) > > )
1231
+ pub ( super ) fn remove_stale_payments (
1232
+ & self , pending_events : & Mutex < VecDeque < ( events:: Event , Option < EventCompletionAction > ) > > )
1193
1233
{
1194
- // If an outbound payment was completed, and no pending HTLCs remain, we should remove it
1195
- // from the map. However, if we did that immediately when the last payment HTLC is claimed,
1196
- // this could race the user making a duplicate send_payment call and our idempotency
1197
- // guarantees would be violated. Instead, we wait a few timer ticks to do the actual
1198
- // removal. This should be more than sufficient to ensure the idempotency of any
1199
- // `send_payment` calls that were made at the same time the `PaymentSent` event was being
1200
- // processed.
1201
1234
let mut pending_outbound_payments = self . pending_outbound_payments . lock ( ) . unwrap ( ) ;
1202
- let pending_events = pending_events. lock ( ) . unwrap ( ) ;
1235
+ let mut pending_events = pending_events. lock ( ) . unwrap ( ) ;
1203
1236
pending_outbound_payments. retain ( |payment_id, payment| {
1237
+ // If an outbound payment was completed, and no pending HTLCs remain, we should remove it
1238
+ // from the map. However, if we did that immediately when the last payment HTLC is claimed,
1239
+ // this could race the user making a duplicate send_payment call and our idempotency
1240
+ // guarantees would be violated. Instead, we wait a few timer ticks to do the actual
1241
+ // removal. This should be more than sufficient to ensure the idempotency of any
1242
+ // `send_payment` calls that were made at the same time the `PaymentSent` event was being
1243
+ // processed.
1204
1244
if let PendingOutboundPayment :: Fulfilled { session_privs, timer_ticks_without_htlcs, .. } = payment {
1205
1245
let mut no_remaining_entries = session_privs. is_empty ( ) ;
1206
1246
if no_remaining_entries {
@@ -1225,6 +1265,16 @@ impl OutboundPayments {
1225
1265
* timer_ticks_without_htlcs = 0 ;
1226
1266
true
1227
1267
}
1268
+ } else if let PendingOutboundPayment :: AwaitingInvoice { timer_ticks_without_response, .. } = payment {
1269
+ * timer_ticks_without_response += 1 ;
1270
+ if * timer_ticks_without_response <= INVOICE_REQUEST_TIMEOUT_TICKS {
1271
+ true
1272
+ } else {
1273
+ pending_events. push_back (
1274
+ ( events:: Event :: InvoiceRequestFailed { payment_id : * payment_id } , None )
1275
+ ) ;
1276
+ false
1277
+ }
1228
1278
} else { true }
1229
1279
} ) ;
1230
1280
}
@@ -1429,6 +1479,9 @@ impl_writeable_tlv_based_enum_upgradable!(PendingOutboundPayment,
1429
1479
( 1 , reason, option) ,
1430
1480
( 2 , payment_hash, required) ,
1431
1481
} ,
1482
+ ( 5 , AwaitingInvoice ) => {
1483
+ ( 0 , timer_ticks_without_response, required) ,
1484
+ } ,
1432
1485
) ;
1433
1486
1434
1487
#[ cfg( test) ]
0 commit comments