From b16e61370cff3fc460f31c2e85065a9a44633b59 Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Wed, 18 Sep 2024 11:21:26 -0400 Subject: [PATCH] Include invreq in payment onion when retrying async payments While in the last commit we began including invoice requests in async payment onions on initial send, further work is needed to include them on retry. Here we begin storing invreqs in our retry data, and pass them along for inclusion in the onion on payment retry. Per , when paying a static invoice we need to include our original invoice request in the HTLC onion since the recipient wouldn't have received it previously. --- lightning/src/ln/outbound_payment.rs | 49 ++++++++++++++++------------ 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/lightning/src/ln/outbound_payment.rs b/lightning/src/ln/outbound_payment.rs index 48715bd9d08..31043c84de3 100644 --- a/lightning/src/ln/outbound_payment.rs +++ b/lightning/src/ln/outbound_payment.rs @@ -94,6 +94,7 @@ pub(crate) enum PendingOutboundPayment { payment_secret: Option, payment_metadata: Option>, keysend_preimage: Option, + invoice_request: Option, custom_tlvs: Vec<(u64, Vec)>, pending_amt_msat: u64, /// Used to track the fee paid. Present iff the payment was serialized on 0.0.103+. @@ -880,7 +881,7 @@ impl OutboundPayments { route_params.max_total_routing_fee_msat = Some(max_fee_msat); } self.send_payment_for_bolt12_invoice_internal( - payment_id, payment_hash, None, route_params, retry_strategy, router, first_hops, + payment_id, payment_hash, None, None, route_params, retry_strategy, router, first_hops, inflight_htlcs, entropy_source, node_signer, node_id_lookup, secp_ctx, best_block_height, logger, pending_events, send_payment_along_path ) @@ -890,10 +891,10 @@ impl OutboundPayments { R: Deref, ES: Deref, NS: Deref, NL: Deref, IH, SP, L: Deref >( &self, payment_id: PaymentId, payment_hash: PaymentHash, - keysend_preimage: Option, mut route_params: RouteParameters, - retry_strategy: Retry, router: &R, first_hops: Vec, inflight_htlcs: IH, - entropy_source: &ES, node_signer: &NS, node_id_lookup: &NL, - secp_ctx: &Secp256k1, best_block_height: u32, logger: &L, + keysend_preimage: Option, mut invoice_request: Option, + mut route_params: RouteParameters, retry_strategy: Retry, router: &R, + first_hops: Vec, inflight_htlcs: IH, entropy_source: &ES, node_signer: &NS, + node_id_lookup: &NL, secp_ctx: &Secp256k1, best_block_height: u32, logger: &L, pending_events: &Mutex)>>, send_payment_along_path: SP, ) -> Result<(), Bolt12PaymentError> @@ -948,8 +949,8 @@ impl OutboundPayments { let payment_params = Some(route_params.payment_params.clone()); let (retryable_payment, onion_session_privs) = self.create_pending_payment( - payment_hash, recipient_onion.clone(), keysend_preimage, &route, Some(retry_strategy), - payment_params, entropy_source, best_block_height + payment_hash, recipient_onion.clone(), keysend_preimage, invoice_request.take(), &route, + Some(retry_strategy), payment_params, entropy_source, best_block_height ); let mut invoice_request_opt = None; let mut outbounds = self.pending_outbound_payments.lock().unwrap(); @@ -1087,13 +1088,14 @@ impl OutboundPayments { IH: Fn() -> InFlightHtlcs, SP: Fn(SendAlongPathArgs) -> Result<(), APIError>, { - let (payment_hash, keysend_preimage, route_params, retry_strategy) = + let (payment_hash, keysend_preimage, route_params, retry_strategy, invoice_request) = match self.pending_outbound_payments.lock().unwrap().entry(payment_id) { hash_map::Entry::Occupied(entry) => match entry.get() { PendingOutboundPayment::StaticInvoiceReceived { - payment_hash, route_params, retry_strategy, keysend_preimage, .. + payment_hash, route_params, retry_strategy, keysend_preimage, invoice_request, .. } => { - (*payment_hash, *keysend_preimage, route_params.clone(), *retry_strategy) + (*payment_hash, *keysend_preimage, route_params.clone(), *retry_strategy, + invoice_request.clone()) }, _ => return Err(Bolt12PaymentError::DuplicateInvoice), }, @@ -1101,9 +1103,9 @@ impl OutboundPayments { }; self.send_payment_for_bolt12_invoice_internal( - payment_id, payment_hash, Some(keysend_preimage), route_params, retry_strategy, router, - first_hops, inflight_htlcs, entropy_source, node_signer, node_id_lookup, secp_ctx, - best_block_height, logger, pending_events, send_payment_along_path + payment_id, payment_hash, Some(keysend_preimage), Some(invoice_request), route_params, + retry_strategy, router, first_hops, inflight_htlcs, entropy_source, node_signer, + node_id_lookup, secp_ctx, best_block_height, logger, pending_events, send_payment_along_path ) } @@ -1323,14 +1325,14 @@ impl OutboundPayments { } } } - let (total_msat, recipient_onion, keysend_preimage, onion_session_privs) = { + let (total_msat, recipient_onion, keysend_preimage, onion_session_privs, invoice_request) = { let mut outbounds = self.pending_outbound_payments.lock().unwrap(); match outbounds.entry(payment_id) { hash_map::Entry::Occupied(mut payment) => { match payment.get() { PendingOutboundPayment::Retryable { total_msat, keysend_preimage, payment_secret, payment_metadata, - custom_tlvs, pending_amt_msat, .. + custom_tlvs, pending_amt_msat, invoice_request, .. } => { const RETRY_OVERFLOW_PERCENTAGE: u64 = 10; let retry_amt_msat = route.get_total_amount(); @@ -1353,6 +1355,7 @@ impl OutboundPayments { custom_tlvs: custom_tlvs.clone(), }; let keysend_preimage = *keysend_preimage; + let invoice_request = invoice_request.clone(); let mut onion_session_privs = Vec::with_capacity(route.paths.len()); for _ in 0..route.paths.len() { @@ -1365,7 +1368,7 @@ impl OutboundPayments { payment.get_mut().increment_attempts(); - (total_msat, recipient_onion, keysend_preimage, onion_session_privs) + (total_msat, recipient_onion, keysend_preimage, onion_session_privs, invoice_request) }, PendingOutboundPayment::Legacy { .. } => { log_error!(logger, "Unable to retry payments that were initially sent on LDK versions prior to 0.0.102"); @@ -1403,8 +1406,8 @@ impl OutboundPayments { } }; let res = self.pay_route_internal(&route, payment_hash, &recipient_onion, keysend_preimage, - None, payment_id, Some(total_msat), onion_session_privs, node_signer, best_block_height, - &send_payment_along_path); + invoice_request.as_ref(), payment_id, Some(total_msat), onion_session_privs, node_signer, + best_block_height, &send_payment_along_path); log_info!(logger, "Result retrying payment id {}: {:?}", &payment_id, res); if let Err(e) = res { self.handle_pay_route_err(e, payment_id, payment_hash, route, route_params, router, first_hops, inflight_htlcs, entropy_source, node_signer, best_block_height, logger, pending_events, send_payment_along_path); @@ -1556,7 +1559,7 @@ impl OutboundPayments { hash_map::Entry::Occupied(_) => Err(PaymentSendFailure::DuplicatePayment), hash_map::Entry::Vacant(entry) => { let (payment, onion_session_privs) = self.create_pending_payment( - payment_hash, recipient_onion, keysend_preimage, route, retry_strategy, + payment_hash, recipient_onion, keysend_preimage, None, route, retry_strategy, payment_params, entropy_source, best_block_height ); entry.insert(payment); @@ -1567,8 +1570,9 @@ impl OutboundPayments { fn create_pending_payment( &self, payment_hash: PaymentHash, recipient_onion: RecipientOnionFields, - keysend_preimage: Option, route: &Route, retry_strategy: Option, - payment_params: Option, entropy_source: &ES, best_block_height: u32 + keysend_preimage: Option, invoice_request: Option, + route: &Route, retry_strategy: Option, payment_params: Option, + entropy_source: &ES, best_block_height: u32 ) -> (PendingOutboundPayment, Vec<[u8; 32]>) where ES::Target: EntropySource, @@ -1589,6 +1593,7 @@ impl OutboundPayments { payment_secret: recipient_onion.payment_secret, payment_metadata: recipient_onion.payment_metadata, keysend_preimage, + invoice_request, custom_tlvs: recipient_onion.custom_tlvs, starting_block_height: best_block_height, total_msat: route.get_total_amount(), @@ -2150,6 +2155,7 @@ impl OutboundPayments { payment_secret: None, // only used for retries, and we'll never retry on startup payment_metadata: None, // only used for retries, and we'll never retry on startup keysend_preimage: None, // only used for retries, and we'll never retry on startup + invoice_request: None, // only used for retries, and we'll never retry on startup custom_tlvs: Vec::new(), // only used for retries, and we'll never retry on startup pending_amt_msat: path_amt, pending_fee_msat: Some(path_fee), @@ -2236,6 +2242,7 @@ impl_writeable_tlv_based_enum_upgradable!(PendingOutboundPayment, (9, custom_tlvs, optional_vec), (10, starting_block_height, required), (11, remaining_max_total_routing_fee_msat, option), + (13, invoice_request, option), (not_written, retry_strategy, (static_value, None)), (not_written, attempts, (static_value, PaymentAttempts::new())), },