@@ -193,6 +193,20 @@ pub enum PaymentError {
193193 Sending ( PaymentSendFailure ) ,
194194}
195195
196+ type PaymentEntry < ' a > = hash_map:: OccupiedEntry < ' a , PaymentHash , usize > ;
197+
198+ macro_rules! look_up_or_insert {
199+ ( $payment_cache: ident, $payment_hash: expr) => {
200+ loop {
201+ let entry = $payment_cache. entry( $payment_hash) ;
202+ match entry {
203+ hash_map:: Entry :: Occupied ( entry) => break entry,
204+ hash_map:: Entry :: Vacant ( entry) => entry. insert( 0 ) ,
205+ } ;
206+ } ;
207+ }
208+ }
209+
196210impl < P : Deref , R , S : Deref , L : Deref , E > InvoicePayer < P , R , S , L , E >
197211where
198212 P :: Target : Payer ,
@@ -228,7 +242,7 @@ where
228242 if invoice. amount_milli_satoshis ( ) . is_none ( ) {
229243 Err ( PaymentError :: Invoice ( "amount missing" ) )
230244 } else {
231- self . pay_invoice_internal ( invoice, None , 0 )
245+ self . pay_invoice_using_amount ( invoice, None )
232246 }
233247 }
234248
@@ -244,137 +258,126 @@ where
244258 if invoice. amount_milli_satoshis ( ) . is_some ( ) {
245259 Err ( PaymentError :: Invoice ( "amount unexpected" ) )
246260 } else {
247- self . pay_invoice_internal ( invoice, Some ( amount_msats) , 0 )
261+ self . pay_invoice_using_amount ( invoice, Some ( amount_msats) )
248262 }
249263 }
250264
251- fn pay_invoice_internal (
252- & self , invoice : & Invoice , amount_msats : Option < u64 > , retry_count : usize
265+ fn pay_invoice_using_amount (
266+ & self , invoice : & Invoice , amount_msats : Option < u64 >
253267 ) -> Result < PaymentId , PaymentError > {
254268 debug_assert ! ( invoice. amount_milli_satoshis( ) . is_some( ) ^ amount_msats. is_some( ) ) ;
269+
255270 let payment_hash = PaymentHash ( invoice. payment_hash ( ) . clone ( ) . into_inner ( ) ) ;
256- if invoice. is_expired ( ) {
257- log_trace ! ( self . logger, "Invoice expired prior to first send for payment {}" , log_bytes!( payment_hash. 0 ) ) ;
271+ let mut payment_cache = self . payment_cache . lock ( ) . unwrap ( ) ;
272+ let mut entry = match payment_cache. entry ( payment_hash) {
273+ hash_map:: Entry :: Occupied ( _) => return Err ( PaymentError :: Invoice ( "payment pending" ) ) ,
274+ hash_map:: Entry :: Vacant ( _) => look_up_or_insert ! ( payment_cache, payment_hash) ,
275+ } ;
276+
277+ let payment_secret = Some ( invoice. payment_secret ( ) . clone ( ) ) ;
278+ let mut payee = Payee :: from_node_id ( invoice. recover_payee_pub_key ( ) )
279+ . with_expiry_time ( expiry_time_from_unix_epoch ( & invoice) . as_secs ( ) )
280+ . with_route_hints ( invoice. route_hints ( ) ) ;
281+ if let Some ( features) = invoice. features ( ) {
282+ payee = payee. with_features ( features. clone ( ) ) ;
283+ }
284+ let params = RouteParameters {
285+ payee,
286+ final_value_msat : invoice. amount_milli_satoshis ( ) . or ( amount_msats) . unwrap ( ) ,
287+ final_cltv_expiry_delta : invoice. min_final_cltv_expiry ( ) as u32 ,
288+ } ;
289+
290+ self . pay_internal ( & mut entry, & params, payment_hash, & payment_secret)
291+ . map_err ( |e| { entry. remove ( ) ; e } )
292+ }
293+
294+ fn pay_internal (
295+ & self , entry : & mut PaymentEntry , params : & RouteParameters , payment_hash : PaymentHash ,
296+ payment_secret : & Option < PaymentSecret > ,
297+ ) -> Result < PaymentId , PaymentError > {
298+ if has_expired ( params) {
299+ log_trace ! ( self . logger, "Invoice expired prior to send for payment {}" , log_bytes!( payment_hash. 0 ) ) ;
258300 return Err ( PaymentError :: Invoice ( "Invoice expired prior to send" ) ) ;
259301 }
260- let retry_data_payment_id = loop {
261- let mut payment_cache = self . payment_cache . lock ( ) . unwrap ( ) ;
262- match payment_cache. entry ( payment_hash) {
263- hash_map:: Entry :: Vacant ( entry) => {
264- let payer = self . payer . node_id ( ) ;
265- let mut payee = Payee :: from_node_id ( invoice. recover_payee_pub_key ( ) )
266- . with_expiry_time ( expiry_time_from_unix_epoch ( & invoice) . as_secs ( ) )
267- . with_route_hints ( invoice. route_hints ( ) ) ;
268- if let Some ( features) = invoice. features ( ) {
269- payee = payee. with_features ( features. clone ( ) ) ;
302+
303+ let payer = self . payer . node_id ( ) ;
304+ let first_hops = self . payer . first_hops ( ) ;
305+ let route = self . router . find_route (
306+ & payer,
307+ params,
308+ Some ( & first_hops. iter ( ) . collect :: < Vec < _ > > ( ) ) ,
309+ & self . scorer . lock ( ) ,
310+ ) . map_err ( |e| PaymentError :: Routing ( e) ) ?;
311+
312+ match self . payer . send_payment ( & route, payment_hash, payment_secret) {
313+ Ok ( payment_id) => Ok ( payment_id) ,
314+ Err ( e) => match e {
315+ PaymentSendFailure :: ParameterError ( _) => Err ( e) ,
316+ PaymentSendFailure :: PathParameterError ( _) => Err ( e) ,
317+ PaymentSendFailure :: AllFailedRetrySafe ( _) => {
318+ let retry_count = entry. get_mut ( ) ;
319+ if * retry_count >= self . retry_attempts . 0 {
320+ Err ( e)
321+ } else {
322+ * retry_count += 1 ;
323+ Ok ( self . pay_internal ( entry, params, payment_hash, payment_secret) ?)
270324 }
271- let params = RouteParameters {
272- payee,
273- final_value_msat : invoice. amount_milli_satoshis ( ) . or ( amount_msats) . unwrap ( ) ,
274- final_cltv_expiry_delta : invoice. min_final_cltv_expiry ( ) as u32 ,
275- } ;
276- let first_hops = self . payer . first_hops ( ) ;
277- let route = self . router . find_route (
278- & payer,
279- & params,
280- Some ( & first_hops. iter ( ) . collect :: < Vec < _ > > ( ) ) ,
281- & self . scorer . lock ( ) ,
282- ) . map_err ( |e| PaymentError :: Routing ( e) ) ?;
283-
284- let payment_secret = Some ( invoice. payment_secret ( ) . clone ( ) ) ;
285- let payment_id = match self . payer . send_payment ( & route, payment_hash, & payment_secret) {
286- Ok ( payment_id) => payment_id,
287- Err ( PaymentSendFailure :: ParameterError ( e) ) =>
288- return Err ( PaymentError :: Sending ( PaymentSendFailure :: ParameterError ( e) ) ) ,
289- Err ( PaymentSendFailure :: PathParameterError ( e) ) =>
290- return Err ( PaymentError :: Sending ( PaymentSendFailure :: PathParameterError ( e) ) ) ,
291- Err ( PaymentSendFailure :: AllFailedRetrySafe ( e) ) => {
292- if retry_count >= self . retry_attempts . 0 {
293- return Err ( PaymentError :: Sending ( PaymentSendFailure :: AllFailedRetrySafe ( e) ) )
294- }
295- break None ;
296- } ,
297- Err ( PaymentSendFailure :: PartialFailure { results : _, failed_paths_retry, payment_id } ) => {
298- if let Some ( retry_data) = failed_paths_retry {
299- entry. insert ( retry_count) ;
300- break Some ( ( retry_data, payment_id) ) ;
301- } else {
302- // This may happen if we send a payment and some paths fail, but
303- // only due to a temporary monitor failure or the like, implying
304- // they're really in-flight, but we haven't sent the initial
305- // HTLC-Add messages yet.
306- payment_id
307- }
308- } ,
309- } ;
310- entry. insert ( retry_count) ;
311- return Ok ( payment_id) ;
312325 } ,
313- hash_map:: Entry :: Occupied ( _) => return Err ( PaymentError :: Invoice ( "payment pending" ) ) ,
314- }
315- } ;
316- if let Some ( ( retry_data, payment_id) ) = retry_data_payment_id {
317- // Some paths were sent, even if we failed to send the full MPP value our recipient may
318- // misbehave and claim the funds, at which point we have to consider the payment sent,
319- // so return `Ok()` here, ignoring any retry errors.
320- let _ = self . retry_payment ( payment_id, payment_hash, & retry_data) ;
321- Ok ( payment_id)
322- } else {
323- self . pay_invoice_internal ( invoice, amount_msats, retry_count + 1 )
324- }
326+ PaymentSendFailure :: PartialFailure { failed_paths_retry, payment_id, .. } => {
327+ if let Some ( retry_data) = failed_paths_retry {
328+ let _ = self . retry_payment ( entry, payment_id, & retry_data) ;
329+ Ok ( payment_id)
330+ } else {
331+ // This may happen if we send a payment and some paths fail, but
332+ // only due to a temporary monitor failure or the like, implying
333+ // they're really in-flight, but we haven't sent the initial
334+ // HTLC-Add messages yet.
335+ Ok ( payment_id)
336+ }
337+ } ,
338+ } ,
339+ } . map_err ( |e| PaymentError :: Sending ( e) )
325340 }
326341
327- fn retry_payment ( & self , payment_id : PaymentId , payment_hash : PaymentHash , params : & RouteParameters )
328- -> Result < ( ) , ( ) > {
329- let route;
330- {
331- let mut payment_cache = self . payment_cache . lock ( ) . unwrap ( ) ;
332- let entry = loop {
333- let entry = payment_cache. entry ( payment_hash) ;
334- match entry {
335- hash_map:: Entry :: Occupied ( _) => break entry,
336- hash_map:: Entry :: Vacant ( entry) => entry. insert ( 0 ) ,
337- } ;
338- } ;
339- if let hash_map:: Entry :: Occupied ( mut entry) = entry {
340- let max_payment_attempts = self . retry_attempts . 0 + 1 ;
341- let attempts = entry. get_mut ( ) ;
342- * attempts += 1 ;
343-
344- if * attempts >= max_payment_attempts {
345- log_trace ! ( self . logger, "Payment {} exceeded maximum attempts; not retrying (attempts: {})" , log_bytes!( payment_hash. 0 ) , attempts) ;
346- return Err ( ( ) ) ;
347- } else if has_expired ( params) {
348- log_trace ! ( self . logger, "Invoice expired for payment {}; not retrying (attempts: {})" , log_bytes!( payment_hash. 0 ) , attempts) ;
349- return Err ( ( ) ) ;
350- }
342+ fn retry_payment (
343+ & self , entry : & mut PaymentEntry , payment_id : PaymentId , params : & RouteParameters
344+ ) -> Result < ( ) , ( ) > {
345+ let payment_hash = * entry. key ( ) ;
346+ let max_payment_attempts = self . retry_attempts . 0 + 1 ;
347+ let attempts = entry. get_mut ( ) ;
348+ * attempts += 1 ;
349+
350+ if * attempts >= max_payment_attempts {
351+ log_trace ! ( self . logger, "Payment {} exceeded maximum attempts; not retrying (attempts: {})" , log_bytes!( payment_hash. 0 ) , attempts) ;
352+ return Err ( ( ) ) ;
353+ }
351354
352- let payer = self . payer . node_id ( ) ;
353- let first_hops = self . payer . first_hops ( ) ;
354- route = self . router . find_route ( & payer, & params, Some ( & first_hops. iter ( ) . collect :: < Vec < _ > > ( ) ) , & self . scorer . lock ( ) ) ;
355- if route. is_err ( ) {
356- log_trace ! ( self . logger, "Failed to find a route for payment {}; not retrying (attempts: {})" , log_bytes!( payment_hash. 0 ) , attempts) ;
357- return Err ( ( ) ) ;
358- }
359- } else {
360- unreachable ! ( ) ;
361- }
355+ if has_expired ( params) {
356+ log_trace ! ( self . logger, "Invoice expired for payment {}; not retrying (attempts: {})" , log_bytes!( payment_hash. 0 ) , attempts) ;
357+ return Err ( ( ) ) ;
362358 }
363359
364- let retry_res = self . payer . retry_payment ( & route. unwrap ( ) , payment_id) ;
365- match retry_res {
360+ let payer = self . payer . node_id ( ) ;
361+ let first_hops = self . payer . first_hops ( ) ;
362+ let route = self . router . find_route ( & payer, & params, Some ( & first_hops. iter ( ) . collect :: < Vec < _ > > ( ) ) , & self . scorer . lock ( ) ) ;
363+ if route. is_err ( ) {
364+ log_trace ! ( self . logger, "Failed to find a route for payment {}; not retrying (attempts: {})" , log_bytes!( payment_hash. 0 ) , attempts) ;
365+ return Err ( ( ) ) ;
366+ }
367+
368+ match self . payer . retry_payment ( & route. unwrap ( ) , payment_id) {
366369 Ok ( ( ) ) => Ok ( ( ) ) ,
367370 Err ( PaymentSendFailure :: ParameterError ( _) ) |
368371 Err ( PaymentSendFailure :: PathParameterError ( _) ) => {
369372 log_trace ! ( self . logger, "Failed to retry for payment {} due to bogus route/payment data, not retrying." , log_bytes!( payment_hash. 0 ) ) ;
370- return Err ( ( ) ) ;
373+ Err ( ( ) )
371374 } ,
372375 Err ( PaymentSendFailure :: AllFailedRetrySafe ( _) ) => {
373- self . retry_payment ( payment_id , payment_hash , params)
376+ self . retry_payment ( entry , payment_id , params)
374377 } ,
375- Err ( PaymentSendFailure :: PartialFailure { results : _ , failed_paths_retry, .. } ) => {
378+ Err ( PaymentSendFailure :: PartialFailure { failed_paths_retry, .. } ) => {
376379 if let Some ( retry) = failed_paths_retry {
377- self . retry_payment ( payment_id , payment_hash , & retry)
380+ self . retry_payment ( entry , payment_id , & retry)
378381 } else {
379382 Ok ( ( ) )
380383 }
@@ -412,26 +415,31 @@ where
412415 fn handle_event ( & self , event : & Event ) {
413416 match event {
414417 Event :: PaymentPathFailed {
415- all_paths_failed, payment_id, payment_hash, rejected_by_dest, path, short_channel_id, retry, ..
418+ all_paths_failed, payment_id, payment_hash, rejected_by_dest, path,
419+ short_channel_id, retry, ..
416420 } => {
417421 if let Some ( short_channel_id) = short_channel_id {
418- let t = path. iter ( ) . collect :: < Vec < _ > > ( ) ;
419- self . scorer . lock ( ) . payment_path_failed ( & t , * short_channel_id) ;
422+ let path = path. iter ( ) . collect :: < Vec < _ > > ( ) ;
423+ self . scorer . lock ( ) . payment_path_failed ( & path , * short_channel_id) ;
420424 }
421425
426+ let mut payment_cache = self . payment_cache . lock ( ) . unwrap ( ) ;
427+ let mut entry = look_up_or_insert ! ( payment_cache, * payment_hash) ;
428+
422429 if * rejected_by_dest {
423430 log_trace ! ( self . logger, "Payment {} rejected by destination; not retrying" , log_bytes!( payment_hash. 0 ) ) ;
424431 } else if payment_id. is_none ( ) {
425432 log_trace ! ( self . logger, "Payment {} has no id; not retrying" , log_bytes!( payment_hash. 0 ) ) ;
426- } else if let Some ( params) = retry {
427- if self . retry_payment ( payment_id. unwrap ( ) , * payment_hash, params) . is_ok ( ) {
433+ } else if retry. is_none ( ) {
434+ log_trace ! ( self . logger, "Payment {} missing retry params; not retrying" , log_bytes!( payment_hash. 0 ) ) ;
435+ } else {
436+ if self . retry_payment ( & mut entry, payment_id. unwrap ( ) , retry. as_ref ( ) . unwrap ( ) ) . is_ok ( ) {
428437 // We retried at least somewhat, don't provide the PaymentPathFailed event to the user.
429438 return ;
430439 }
431- } else {
432- log_trace ! ( self . logger, "Payment {} missing retry params; not retrying" , log_bytes!( payment_hash. 0 ) ) ;
433440 }
434- if * all_paths_failed { self . payment_cache . lock ( ) . unwrap ( ) . remove ( payment_hash) ; }
441+
442+ if * all_paths_failed { entry. remove ( ) ; }
435443 } ,
436444 Event :: PaymentSent { payment_hash, .. } => {
437445 let mut payment_cache = self . payment_cache . lock ( ) . unwrap ( ) ;
0 commit comments