@@ -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:: 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 ,
@@ -107,6 +110,12 @@ impl PendingOutboundPayment {
107110 params. previously_failed_channels . push ( scid) ;
108111 }
109112 }
113+ fn is_awaiting_invoice ( & self ) -> bool {
114+ match self {
115+ PendingOutboundPayment :: AwaitingInvoice { .. } => true ,
116+ _ => false ,
117+ }
118+ }
110119 pub ( super ) fn is_fulfilled ( & self ) -> bool {
111120 match self {
112121 PendingOutboundPayment :: Fulfilled { .. } => true ,
@@ -129,6 +138,7 @@ impl PendingOutboundPayment {
129138 fn payment_hash ( & self ) -> Option < PaymentHash > {
130139 match self {
131140 PendingOutboundPayment :: Legacy { .. } => None ,
141+ PendingOutboundPayment :: AwaitingInvoice { .. } => None ,
132142 PendingOutboundPayment :: Retryable { payment_hash, .. } => Some ( * payment_hash) ,
133143 PendingOutboundPayment :: Fulfilled { payment_hash, .. } => * payment_hash,
134144 PendingOutboundPayment :: Abandoned { payment_hash, .. } => Some ( * payment_hash) ,
@@ -141,8 +151,8 @@ impl PendingOutboundPayment {
141151 PendingOutboundPayment :: Legacy { session_privs } |
142152 PendingOutboundPayment :: Retryable { session_privs, .. } |
143153 PendingOutboundPayment :: Fulfilled { session_privs, .. } |
144- PendingOutboundPayment :: Abandoned { session_privs, .. }
145- => session_privs ,
154+ PendingOutboundPayment :: Abandoned { session_privs, .. } => session_privs ,
155+ PendingOutboundPayment :: AwaitingInvoice { .. } => return ,
146156 } ) ;
147157 let payment_hash = self . payment_hash ( ) ;
148158 * self = PendingOutboundPayment :: Fulfilled { session_privs, payment_hash, timer_ticks_without_htlcs : 0 } ;
@@ -168,7 +178,8 @@ impl PendingOutboundPayment {
168178 PendingOutboundPayment :: Fulfilled { session_privs, .. } |
169179 PendingOutboundPayment :: Abandoned { session_privs, .. } => {
170180 session_privs. remove ( session_priv)
171- }
181+ } ,
182+ PendingOutboundPayment :: AwaitingInvoice { .. } => false ,
172183 } ;
173184 if remove_res {
174185 if let PendingOutboundPayment :: Retryable { ref mut pending_amt_msat, ref mut pending_fee_msat, .. } = self {
@@ -188,6 +199,7 @@ impl PendingOutboundPayment {
188199 PendingOutboundPayment :: Retryable { session_privs, .. } => {
189200 session_privs. insert ( session_priv)
190201 }
202+ PendingOutboundPayment :: AwaitingInvoice { .. } => false ,
191203 PendingOutboundPayment :: Fulfilled { .. } => false ,
192204 PendingOutboundPayment :: Abandoned { .. } => false ,
193205 } ;
@@ -209,7 +221,8 @@ impl PendingOutboundPayment {
209221 PendingOutboundPayment :: Fulfilled { session_privs, .. } |
210222 PendingOutboundPayment :: Abandoned { session_privs, .. } => {
211223 session_privs. len ( )
212- }
224+ } ,
225+ PendingOutboundPayment :: AwaitingInvoice { .. } => 0 ,
213226 }
214227 }
215228}
@@ -626,7 +639,7 @@ impl OutboundPayments {
626639 let mut outbounds = self . pending_outbound_payments . lock ( ) . unwrap ( ) ;
627640 outbounds. retain ( |pmt_id, pmt| {
628641 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 ( ) {
630643 pmt. mark_abandoned ( PaymentFailureReason :: RetriesExhausted ) ;
631644 if let PendingOutboundPayment :: Abandoned { payment_hash, reason, .. } = pmt {
632645 pending_events. lock ( ) . unwrap ( ) . push_back ( ( events:: Event :: PaymentFailed {
@@ -644,7 +657,8 @@ impl OutboundPayments {
644657 pub ( super ) fn needs_abandon ( & self ) -> bool {
645658 let outbounds = self . pending_outbound_payments . lock ( ) . unwrap ( ) ;
646659 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 ( ) )
648662 }
649663
650664 /// Errors immediately on [`RetryableSendFailure`] error conditions. Otherwise, further errors may
@@ -779,6 +793,10 @@ impl OutboundPayments {
779793 log_error ! ( logger, "Unable to retry payments that were initially sent on LDK versions prior to 0.0.102" ) ;
780794 return
781795 } ,
796+ PendingOutboundPayment :: AwaitingInvoice { .. } => {
797+ log_error ! ( logger, "Payment not yet sent" ) ;
798+ return
799+ } ,
782800 PendingOutboundPayment :: Fulfilled { .. } => {
783801 log_error ! ( logger, "Payment already completed" ) ;
784802 return
@@ -951,35 +969,57 @@ impl OutboundPayments {
951969 keysend_preimage : Option < PaymentPreimage > , route : & Route , retry_strategy : Option < Retry > ,
952970 payment_params : Option < PaymentParameters > , entropy_source : & ES , best_block_height : u32
953971 ) -> 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+
954980 let mut onion_session_privs = Vec :: with_capacity ( route. paths . len ( ) ) ;
955981 for _ in 0 ..route. paths . len ( ) {
956982 onion_session_privs. push ( entropy_source. get_secure_random_bytes ( ) ) ;
957983 }
958984
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 < ( ) , ( ) > {
9591014 let mut pending_outbounds = self . pending_outbound_payments . lock ( ) . unwrap ( ) ;
9601015 match pending_outbounds. entry ( payment_id) {
961- hash_map:: Entry :: Occupied ( _) => Err ( PaymentSendFailure :: DuplicatePayment ) ,
1016+ hash_map:: Entry :: Occupied ( _) => Err ( ( ) ) ,
9621017 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 ,
9761020 } ) ;
9771021
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 ( ( ) )
9831023 } ,
9841024 }
9851025 }
@@ -1188,19 +1228,19 @@ impl OutboundPayments {
11881228 }
11891229 }
11901230
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 > ) > > )
11931233 {
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.
12011234 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 ( ) ;
12031236 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.
12041244 if let PendingOutboundPayment :: Fulfilled { session_privs, timer_ticks_without_htlcs, .. } = payment {
12051245 let mut no_remaining_entries = session_privs. is_empty ( ) ;
12061246 if no_remaining_entries {
@@ -1225,6 +1265,16 @@ impl OutboundPayments {
12251265 * timer_ticks_without_htlcs = 0 ;
12261266 true
12271267 }
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+ }
12281278 } else { true }
12291279 } ) ;
12301280 }
@@ -1429,6 +1479,9 @@ impl_writeable_tlv_based_enum_upgradable!(PendingOutboundPayment,
14291479 ( 1 , reason, option) ,
14301480 ( 2 , payment_hash, required) ,
14311481 } ,
1482+ ( 5 , AwaitingInvoice ) => {
1483+ ( 0 , timer_ticks_without_response, required) ,
1484+ } ,
14321485) ;
14331486
14341487#[ cfg( test) ]
0 commit comments