@@ -16,7 +16,7 @@ use bitcoin::secp256k1::{self, Secp256k1, SecretKey};
1616use crate :: sign:: { EntropySource , NodeSigner , Recipient } ;
1717use crate :: events:: { self , PaymentFailureReason } ;
1818use 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 } ;
2020use crate :: ln:: onion_utils:: { DecodedOnionFailure , HTLCFailReason } ;
2121use crate :: routing:: router:: { InFlightHtlcs , Path , PaymentParameters , Route , RouteParameters , Router } ;
2222use crate :: util:: errors:: APIError ;
@@ -38,6 +38,9 @@ pub(crate) enum PendingOutboundPayment {
3838 Legacy {
3939 session_privs : HashSet < [ u8 ; 32 ] > ,
4040 } ,
41+ AwaitingInvoice {
42+ timer_ticks_without_response : u8 ,
43+ } ,
4144 Retryable {
4245 retry_strategy : Option < Retry > ,
4346 attempts : PaymentAttempts ,
@@ -108,6 +111,12 @@ impl PendingOutboundPayment {
108111 params. previously_failed_channels . push ( scid) ;
109112 }
110113 }
114+ fn is_awaiting_invoice ( & self ) -> bool {
115+ match self {
116+ PendingOutboundPayment :: AwaitingInvoice { .. } => true ,
117+ _ => false ,
118+ }
119+ }
111120 pub ( super ) fn is_fulfilled ( & self ) -> bool {
112121 match self {
113122 PendingOutboundPayment :: Fulfilled { .. } => true ,
@@ -130,6 +139,7 @@ impl PendingOutboundPayment {
130139 fn payment_hash ( & self ) -> Option < PaymentHash > {
131140 match self {
132141 PendingOutboundPayment :: Legacy { .. } => None ,
142+ PendingOutboundPayment :: AwaitingInvoice { .. } => None ,
133143 PendingOutboundPayment :: Retryable { payment_hash, .. } => Some ( * payment_hash) ,
134144 PendingOutboundPayment :: Fulfilled { payment_hash, .. } => * payment_hash,
135145 PendingOutboundPayment :: Abandoned { payment_hash, .. } => Some ( * payment_hash) ,
@@ -142,8 +152,8 @@ impl PendingOutboundPayment {
142152 PendingOutboundPayment :: Legacy { session_privs } |
143153 PendingOutboundPayment :: Retryable { session_privs, .. } |
144154 PendingOutboundPayment :: Fulfilled { session_privs, .. } |
145- PendingOutboundPayment :: Abandoned { session_privs, .. }
146- => session_privs ,
155+ PendingOutboundPayment :: Abandoned { session_privs, .. } => session_privs ,
156+ PendingOutboundPayment :: AwaitingInvoice { .. } => return ,
147157 } ) ;
148158 let payment_hash = self . payment_hash ( ) ;
149159 * self = PendingOutboundPayment :: Fulfilled { session_privs, payment_hash, timer_ticks_without_htlcs : 0 } ;
@@ -169,7 +179,8 @@ impl PendingOutboundPayment {
169179 PendingOutboundPayment :: Fulfilled { session_privs, .. } |
170180 PendingOutboundPayment :: Abandoned { session_privs, .. } => {
171181 session_privs. remove ( session_priv)
172- }
182+ } ,
183+ PendingOutboundPayment :: AwaitingInvoice { .. } => false ,
173184 } ;
174185 if remove_res {
175186 if let PendingOutboundPayment :: Retryable { ref mut pending_amt_msat, ref mut pending_fee_msat, .. } = self {
@@ -189,6 +200,7 @@ impl PendingOutboundPayment {
189200 PendingOutboundPayment :: Retryable { session_privs, .. } => {
190201 session_privs. insert ( session_priv)
191202 }
203+ PendingOutboundPayment :: AwaitingInvoice { .. } => false ,
192204 PendingOutboundPayment :: Fulfilled { .. } => false ,
193205 PendingOutboundPayment :: Abandoned { .. } => false ,
194206 } ;
@@ -210,7 +222,8 @@ impl PendingOutboundPayment {
210222 PendingOutboundPayment :: Fulfilled { session_privs, .. } |
211223 PendingOutboundPayment :: Abandoned { session_privs, .. } => {
212224 session_privs. len ( )
213- }
225+ } ,
226+ PendingOutboundPayment :: AwaitingInvoice { .. } => 0 ,
214227 }
215228 }
216229}
@@ -679,7 +692,7 @@ impl OutboundPayments {
679692 let mut outbounds = self . pending_outbound_payments . lock ( ) . unwrap ( ) ;
680693 outbounds. retain ( |pmt_id, pmt| {
681694 let mut retain = true ;
682- if !pmt. is_auto_retryable_now ( ) && pmt. remaining_parts ( ) == 0 {
695+ if !pmt. is_auto_retryable_now ( ) && pmt. remaining_parts ( ) == 0 && !pmt . is_awaiting_invoice ( ) {
683696 pmt. mark_abandoned ( PaymentFailureReason :: RetriesExhausted ) ;
684697 if let PendingOutboundPayment :: Abandoned { payment_hash, reason, .. } = pmt {
685698 pending_events. lock ( ) . unwrap ( ) . push_back ( ( events:: Event :: PaymentFailed {
@@ -697,7 +710,8 @@ impl OutboundPayments {
697710 pub ( super ) fn needs_abandon ( & self ) -> bool {
698711 let outbounds = self . pending_outbound_payments . lock ( ) . unwrap ( ) ;
699712 outbounds. iter ( ) . any ( |( _, pmt) |
700- !pmt. is_auto_retryable_now ( ) && pmt. remaining_parts ( ) == 0 && !pmt. is_fulfilled ( ) )
713+ !pmt. is_auto_retryable_now ( ) && pmt. remaining_parts ( ) == 0 && !pmt. is_fulfilled ( ) &&
714+ !pmt. is_awaiting_invoice ( ) )
701715 }
702716
703717 /// Errors immediately on [`RetryableSendFailure`] error conditions. Otherwise, further errors may
@@ -834,6 +848,10 @@ impl OutboundPayments {
834848 log_error ! ( logger, "Unable to retry payments that were initially sent on LDK versions prior to 0.0.102" ) ;
835849 return
836850 } ,
851+ PendingOutboundPayment :: AwaitingInvoice { .. } => {
852+ log_error ! ( logger, "Payment not yet sent" ) ;
853+ return
854+ } ,
837855 PendingOutboundPayment :: Fulfilled { .. } => {
838856 log_error ! ( logger, "Payment already completed" ) ;
839857 return
@@ -1006,36 +1024,58 @@ impl OutboundPayments {
10061024 keysend_preimage : Option < PaymentPreimage > , route : & Route , retry_strategy : Option < Retry > ,
10071025 payment_params : Option < PaymentParameters > , entropy_source : & ES , best_block_height : u32
10081026 ) -> Result < Vec < [ u8 ; 32 ] > , PaymentSendFailure > where ES :: Target : EntropySource {
1027+ let mut pending_outbounds = self . pending_outbound_payments . lock ( ) . unwrap ( ) ;
1028+ let entry = pending_outbounds. entry ( payment_id) ;
1029+ if let hash_map:: Entry :: Occupied ( entry) = & entry {
1030+ if let PendingOutboundPayment :: AwaitingInvoice { .. } = entry. get ( ) { } else {
1031+ return Err ( PaymentSendFailure :: DuplicatePayment )
1032+ }
1033+ }
1034+
10091035 let mut onion_session_privs = Vec :: with_capacity ( route. paths . len ( ) ) ;
10101036 for _ in 0 ..route. paths . len ( ) {
10111037 onion_session_privs. push ( entropy_source. get_secure_random_bytes ( ) ) ;
10121038 }
10131039
1040+ let mut payment = PendingOutboundPayment :: Retryable {
1041+ retry_strategy,
1042+ attempts : PaymentAttempts :: new ( ) ,
1043+ payment_params,
1044+ session_privs : HashSet :: new ( ) ,
1045+ pending_amt_msat : 0 ,
1046+ pending_fee_msat : Some ( 0 ) ,
1047+ payment_hash,
1048+ payment_secret : recipient_onion. payment_secret ,
1049+ payment_metadata : recipient_onion. payment_metadata ,
1050+ keysend_preimage,
1051+ custom_tlvs : recipient_onion. custom_tlvs ,
1052+ starting_block_height : best_block_height,
1053+ total_msat : route. get_total_amount ( ) ,
1054+ } ;
1055+
1056+ for ( path, session_priv_bytes) in route. paths . iter ( ) . zip ( onion_session_privs. iter ( ) ) {
1057+ assert ! ( payment. insert( * session_priv_bytes, path) ) ;
1058+ }
1059+
1060+ match entry {
1061+ hash_map:: Entry :: Occupied ( mut entry) => { entry. insert ( payment) ; } ,
1062+ hash_map:: Entry :: Vacant ( entry) => { entry. insert ( payment) ; } ,
1063+ }
1064+
1065+ Ok ( onion_session_privs)
1066+ }
1067+
1068+ #[ allow( unused) ]
1069+ pub ( super ) fn add_new_awaiting_invoice ( & self , payment_id : PaymentId ) -> Result < ( ) , ( ) > {
10141070 let mut pending_outbounds = self . pending_outbound_payments . lock ( ) . unwrap ( ) ;
10151071 match pending_outbounds. entry ( payment_id) {
1016- hash_map:: Entry :: Occupied ( _) => Err ( PaymentSendFailure :: DuplicatePayment ) ,
1072+ hash_map:: Entry :: Occupied ( _) => Err ( ( ) ) ,
10171073 hash_map:: Entry :: Vacant ( entry) => {
1018- let payment = entry. insert ( PendingOutboundPayment :: Retryable {
1019- retry_strategy,
1020- attempts : PaymentAttempts :: new ( ) ,
1021- payment_params,
1022- session_privs : HashSet :: new ( ) ,
1023- pending_amt_msat : 0 ,
1024- pending_fee_msat : Some ( 0 ) ,
1025- payment_hash,
1026- payment_secret : recipient_onion. payment_secret ,
1027- payment_metadata : recipient_onion. payment_metadata ,
1028- keysend_preimage,
1029- custom_tlvs : recipient_onion. custom_tlvs ,
1030- starting_block_height : best_block_height,
1031- total_msat : route. get_total_amount ( ) ,
1074+ entry. insert ( PendingOutboundPayment :: AwaitingInvoice {
1075+ timer_ticks_without_response : 0 ,
10321076 } ) ;
10331077
1034- for ( path, session_priv_bytes) in route. paths . iter ( ) . zip ( onion_session_privs. iter ( ) ) {
1035- assert ! ( payment. insert( * session_priv_bytes, path) ) ;
1036- }
1037-
1038- Ok ( onion_session_privs)
1078+ Ok ( ( ) )
10391079 } ,
10401080 }
10411081 }
@@ -1244,19 +1284,19 @@ impl OutboundPayments {
12441284 }
12451285 }
12461286
1247- pub ( super ) fn remove_stale_resolved_payments ( & self ,
1248- pending_events : & Mutex < VecDeque < ( events:: Event , Option < EventCompletionAction > ) > > )
1287+ pub ( super ) fn remove_stale_payments (
1288+ & self , pending_events : & Mutex < VecDeque < ( events:: Event , Option < EventCompletionAction > ) > > )
12491289 {
1250- // If an outbound payment was completed, and no pending HTLCs remain, we should remove it
1251- // from the map. However, if we did that immediately when the last payment HTLC is claimed,
1252- // this could race the user making a duplicate send_payment call and our idempotency
1253- // guarantees would be violated. Instead, we wait a few timer ticks to do the actual
1254- // removal. This should be more than sufficient to ensure the idempotency of any
1255- // `send_payment` calls that were made at the same time the `PaymentSent` event was being
1256- // processed.
12571290 let mut pending_outbound_payments = self . pending_outbound_payments . lock ( ) . unwrap ( ) ;
1258- let pending_events = pending_events. lock ( ) . unwrap ( ) ;
1291+ let mut pending_events = pending_events. lock ( ) . unwrap ( ) ;
12591292 pending_outbound_payments. retain ( |payment_id, payment| {
1293+ // If an outbound payment was completed, and no pending HTLCs remain, we should remove it
1294+ // from the map. However, if we did that immediately when the last payment HTLC is claimed,
1295+ // this could race the user making a duplicate send_payment call and our idempotency
1296+ // guarantees would be violated. Instead, we wait a few timer ticks to do the actual
1297+ // removal. This should be more than sufficient to ensure the idempotency of any
1298+ // `send_payment` calls that were made at the same time the `PaymentSent` event was being
1299+ // processed.
12601300 if let PendingOutboundPayment :: Fulfilled { session_privs, timer_ticks_without_htlcs, .. } = payment {
12611301 let mut no_remaining_entries = session_privs. is_empty ( ) ;
12621302 if no_remaining_entries {
@@ -1281,6 +1321,16 @@ impl OutboundPayments {
12811321 * timer_ticks_without_htlcs = 0 ;
12821322 true
12831323 }
1324+ } else if let PendingOutboundPayment :: AwaitingInvoice { timer_ticks_without_response, .. } = payment {
1325+ * timer_ticks_without_response += 1 ;
1326+ if * timer_ticks_without_response <= INVOICE_REQUEST_TIMEOUT_TICKS {
1327+ true
1328+ } else {
1329+ pending_events. push_back (
1330+ ( events:: Event :: InvoiceRequestFailed { payment_id : * payment_id } , None )
1331+ ) ;
1332+ false
1333+ }
12841334 } else { true }
12851335 } ) ;
12861336 }
@@ -1489,6 +1539,9 @@ impl_writeable_tlv_based_enum_upgradable!(PendingOutboundPayment,
14891539 ( 1 , reason, option) ,
14901540 ( 2 , payment_hash, required) ,
14911541 } ,
1542+ ( 5 , AwaitingInvoice ) => {
1543+ ( 0 , timer_ticks_without_response, required) ,
1544+ } ,
14921545) ;
14931546
14941547#[ cfg( test) ]
0 commit comments