From cdc0c3b581527bfc389db1621bf24c638eab2dbf Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Tue, 17 Sep 2024 20:22:20 -0400 Subject: [PATCH 01/14] Fix unused warning in fuzz. --- fuzz/src/onion_message.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzz/src/onion_message.rs b/fuzz/src/onion_message.rs index 09d1d9b6ba8..a586cd61a86 100644 --- a/fuzz/src/onion_message.rs +++ b/fuzz/src/onion_message.rs @@ -122,7 +122,7 @@ struct TestAsyncPaymentsMessageHandler {} impl AsyncPaymentsMessageHandler for TestAsyncPaymentsMessageHandler { fn held_htlc_available( - &self, message: HeldHtlcAvailable, responder: Option, + &self, _message: HeldHtlcAvailable, responder: Option, ) -> Option<(ReleaseHeldHtlc, ResponseInstruction)> { let responder = match responder { Some(resp) => resp, From 3945bf86f95373e4ad63749d5dde64643b614790 Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Wed, 11 Sep 2024 13:53:39 -0400 Subject: [PATCH 02/14] Add new PaymentFailureReason::BlindedPathCreationFailed RouteNotFound did not fit here because that error is reserved for failing to find a route for a payment, whereas here we are failing to create a blinded path back to ourselves.. --- lightning/src/events/mod.rs | 9 +++++++++ lightning/src/ln/channelmanager.rs | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/lightning/src/events/mod.rs b/lightning/src/events/mod.rs index c450c1d91cd..cd4b140ffeb 100644 --- a/lightning/src/events/mod.rs +++ b/lightning/src/events/mod.rs @@ -577,6 +577,12 @@ pub enum PaymentFailureReason { /// /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest InvoiceRequestRejected, + /// Failed to create a blinded path back to ourselves. + /// We attempted to initiate payment to a static invoice but failed to create a reply path for our + /// [`HeldHtlcAvailable`] message. + /// + /// [`HeldHtlcAvailable`]: crate::onion_message::async_payments::HeldHtlcAvailable + BlindedPathCreationFailed, } impl_writeable_tlv_based_enum_upgradable!(PaymentFailureReason, @@ -587,6 +593,7 @@ impl_writeable_tlv_based_enum_upgradable!(PaymentFailureReason, (4, RetriesExhausted) => {}, (5, InvoiceRequestRejected) => {}, (6, PaymentExpired) => {}, + (7, BlindedPathCreationFailed) => {}, (8, RouteNotFound) => {}, (10, UnexpectedError) => {}, ); @@ -1651,6 +1658,8 @@ impl Writeable for Event { &Some(PaymentFailureReason::RetriesExhausted), Some(PaymentFailureReason::InvoiceRequestRejected) => &Some(PaymentFailureReason::RecipientRejected), + Some(PaymentFailureReason::BlindedPathCreationFailed) => + &Some(PaymentFailureReason::RouteNotFound) }; write_tlv_fields!(writer, { (0, payment_id, required), diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 3914384ca82..6a3fd22751b 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -4463,7 +4463,7 @@ where ) { Ok(paths) => paths, Err(()) => { - self.abandon_payment_with_reason(payment_id, PaymentFailureReason::RouteNotFound); + self.abandon_payment_with_reason(payment_id, PaymentFailureReason::BlindedPathCreationFailed); res = Err(Bolt12PaymentError::BlindedPathCreationFailed); return NotifyOption::DoPersist } From 88d689a323d7d03b2bcbba4716cc920513ae6164 Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Wed, 11 Sep 2024 13:57:19 -0400 Subject: [PATCH 03/14] Update docs for PaymentFailureReason::RouteNotFound Since we started using this error in send_payment_for_bolt12_invoice, this error type is no longer only used on retry but also on initial send. --- lightning/src/events/mod.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lightning/src/events/mod.rs b/lightning/src/events/mod.rs index cd4b140ffeb..3538aa36780 100644 --- a/lightning/src/events/mod.rs +++ b/lightning/src/events/mod.rs @@ -560,11 +560,15 @@ pub enum PaymentFailureReason { /// [`PaymentParameters::expiry_time`]: crate::routing::router::PaymentParameters::expiry_time /// [`InvoiceRequestExpired`]: Self::InvoiceRequestExpired PaymentExpired, - /// We failed to find a route while retrying the payment. + /// We failed to find a route while sending or retrying the payment. /// /// Note that this generally indicates that we've exhausted the available set of possible /// routes - we tried the payment over a few routes but were not able to find any further /// candidate routes beyond those. + /// + /// Also used for [`BlindedPathCreationFailed`] when downgrading to versions prior to 0.0.124. + /// + /// [`BlindedPathCreationFailed`]: Self::BlindedPathCreationFailed RouteNotFound, /// This error should generally never happen. This likely means that there is a problem with /// your router. From 2ff6524da40cd82fc259140e49d7bd51f94868c0 Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Tue, 17 Sep 2024 20:32:14 -0400 Subject: [PATCH 04/14] Prefix AsyncPaymentsMessageHandler methods with handle_* "Release" is overloaded in the trait's release_pending_messages method, since the latter releases pending async payments onion messages to the peer manager, vs the release_held_htlc method handles the release_held_htlc onion message by attempting to send an HTLC to the recipient. --- fuzz/src/onion_message.rs | 4 ++-- lightning/src/ln/channelmanager.rs | 4 ++-- lightning/src/ln/peer_handler.rs | 4 ++-- lightning/src/onion_message/async_payments.rs | 4 ++-- lightning/src/onion_message/functional_tests.rs | 4 ++-- lightning/src/onion_message/messenger.rs | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/fuzz/src/onion_message.rs b/fuzz/src/onion_message.rs index a586cd61a86..edf304b5467 100644 --- a/fuzz/src/onion_message.rs +++ b/fuzz/src/onion_message.rs @@ -121,7 +121,7 @@ impl OffersMessageHandler for TestOffersMessageHandler { struct TestAsyncPaymentsMessageHandler {} impl AsyncPaymentsMessageHandler for TestAsyncPaymentsMessageHandler { - fn held_htlc_available( + fn handle_held_htlc_available( &self, _message: HeldHtlcAvailable, responder: Option, ) -> Option<(ReleaseHeldHtlc, ResponseInstruction)> { let responder = match responder { @@ -130,7 +130,7 @@ impl AsyncPaymentsMessageHandler for TestAsyncPaymentsMessageHandler { }; Some((ReleaseHeldHtlc {}, responder.respond())) } - fn release_held_htlc(&self, _message: ReleaseHeldHtlc, _context: AsyncPaymentsContext) {} + fn handle_release_held_htlc(&self, _message: ReleaseHeldHtlc, _context: AsyncPaymentsContext) {} } #[derive(Debug)] diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 6a3fd22751b..691223fd3cf 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -11327,13 +11327,13 @@ where MR::Target: MessageRouter, L::Target: Logger, { - fn held_htlc_available( + fn handle_held_htlc_available( &self, _message: HeldHtlcAvailable, _responder: Option ) -> Option<(ReleaseHeldHtlc, ResponseInstruction)> { None } - fn release_held_htlc(&self, _message: ReleaseHeldHtlc, _context: AsyncPaymentsContext) { + fn handle_release_held_htlc(&self, _message: ReleaseHeldHtlc, _context: AsyncPaymentsContext) { #[cfg(async_payments)] { let AsyncPaymentsContext::OutboundPayment { payment_id, hmac, nonce } = _context; if payment_id.verify_for_async_payment(hmac, nonce, &self.inbound_payment_key).is_err() { return } diff --git a/lightning/src/ln/peer_handler.rs b/lightning/src/ln/peer_handler.rs index b5fc3443be5..99c2e001c4f 100644 --- a/lightning/src/ln/peer_handler.rs +++ b/lightning/src/ln/peer_handler.rs @@ -148,12 +148,12 @@ impl OffersMessageHandler for IgnoringMessageHandler { } } impl AsyncPaymentsMessageHandler for IgnoringMessageHandler { - fn held_htlc_available( + fn handle_held_htlc_available( &self, _message: HeldHtlcAvailable, _responder: Option, ) -> Option<(ReleaseHeldHtlc, ResponseInstruction)> { None } - fn release_held_htlc(&self, _message: ReleaseHeldHtlc, _context: AsyncPaymentsContext) {} + fn handle_release_held_htlc(&self, _message: ReleaseHeldHtlc, _context: AsyncPaymentsContext) {} } impl DNSResolverMessageHandler for IgnoringMessageHandler { fn handle_dnssec_query( diff --git a/lightning/src/onion_message/async_payments.rs b/lightning/src/onion_message/async_payments.rs index cc4ca5edfb0..d81010e5d5f 100644 --- a/lightning/src/onion_message/async_payments.rs +++ b/lightning/src/onion_message/async_payments.rs @@ -27,13 +27,13 @@ const RELEASE_HELD_HTLC_TLV_TYPE: u64 = 74; pub trait AsyncPaymentsMessageHandler { /// Handle a [`HeldHtlcAvailable`] message. A [`ReleaseHeldHtlc`] should be returned to release /// the held funds. - fn held_htlc_available( + fn handle_held_htlc_available( &self, message: HeldHtlcAvailable, responder: Option, ) -> Option<(ReleaseHeldHtlc, ResponseInstruction)>; /// Handle a [`ReleaseHeldHtlc`] message. If authentication of the message succeeds, an HTLC /// should be released to the corresponding payee. - fn release_held_htlc(&self, message: ReleaseHeldHtlc, context: AsyncPaymentsContext); + fn handle_release_held_htlc(&self, message: ReleaseHeldHtlc, context: AsyncPaymentsContext); /// Release any [`AsyncPaymentsMessage`]s that need to be sent. /// diff --git a/lightning/src/onion_message/functional_tests.rs b/lightning/src/onion_message/functional_tests.rs index 23fd30c9ec8..f9d73f05ff3 100644 --- a/lightning/src/onion_message/functional_tests.rs +++ b/lightning/src/onion_message/functional_tests.rs @@ -84,12 +84,12 @@ impl OffersMessageHandler for TestOffersMessageHandler { struct TestAsyncPaymentsMessageHandler {} impl AsyncPaymentsMessageHandler for TestAsyncPaymentsMessageHandler { - fn held_htlc_available( + fn handle_held_htlc_available( &self, _message: HeldHtlcAvailable, _responder: Option, ) -> Option<(ReleaseHeldHtlc, ResponseInstruction)> { None } - fn release_held_htlc(&self, _message: ReleaseHeldHtlc, _context: AsyncPaymentsContext) {} + fn handle_release_held_htlc(&self, _message: ReleaseHeldHtlc, _context: AsyncPaymentsContext) {} } struct TestDNSResolverMessageHandler {} diff --git a/lightning/src/onion_message/messenger.rs b/lightning/src/onion_message/messenger.rs index 8162c55ac08..0331a1060b8 100644 --- a/lightning/src/onion_message/messenger.rs +++ b/lightning/src/onion_message/messenger.rs @@ -1623,7 +1623,7 @@ where }, #[cfg(async_payments)] ParsedOnionMessageContents::AsyncPayments(AsyncPaymentsMessage::HeldHtlcAvailable(msg)) => { - let response_instructions = self.async_payments_handler.held_htlc_available( + let response_instructions = self.async_payments_handler.handle_held_htlc_available( msg, responder ); if let Some((msg, instructions)) = response_instructions { @@ -1640,7 +1640,7 @@ where }, None => return, }; - self.async_payments_handler.release_held_htlc(msg, context); + self.async_payments_handler.handle_release_held_htlc(msg, context); }, ParsedOnionMessageContents::DNSResolver(DNSResolverMessage::DNSSECQuery(msg)) => { let response_instructions = self.dns_resolver_handler.handle_dnssec_query(msg, responder); From 639446ad63bed1d70016a2fe116dce17a718213f Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Wed, 30 Oct 2024 11:39:52 -0400 Subject: [PATCH 05/14] Stop taking &self in outbound_payments' create_inbound_payment The method doesn't actually use its &self parameter, and this makes it more obvious that we aren't going to deadlock by calling the method if the outbound_payments lock is already acquired. --- lightning/src/ln/outbound_payment.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lightning/src/ln/outbound_payment.rs b/lightning/src/ln/outbound_payment.rs index 9c8998b8970..04736f2a8c2 100644 --- a/lightning/src/ln/outbound_payment.rs +++ b/lightning/src/ln/outbound_payment.rs @@ -945,7 +945,7 @@ impl OutboundPayments { }; let payment_params = Some(route_params.payment_params.clone()); - let (retryable_payment, onion_session_privs) = self.create_pending_payment( + 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 ); @@ -1546,7 +1546,7 @@ impl OutboundPayments { match pending_outbounds.entry(payment_id) { hash_map::Entry::Occupied(_) => Err(PaymentSendFailure::DuplicatePayment), hash_map::Entry::Vacant(entry) => { - let (payment, onion_session_privs) = self.create_pending_payment( + let (payment, onion_session_privs) = Self::create_pending_payment( payment_hash, recipient_onion, keysend_preimage, route, retry_strategy, payment_params, entropy_source, best_block_height ); @@ -1557,7 +1557,7 @@ impl OutboundPayments { } fn create_pending_payment( - &self, payment_hash: PaymentHash, recipient_onion: RecipientOnionFields, + payment_hash: PaymentHash, recipient_onion: RecipientOnionFields, keysend_preimage: Option, route: &Route, retry_strategy: Option, payment_params: Option, entropy_source: &ES, best_block_height: u32 ) -> (PendingOutboundPayment, Vec<[u8; 32]>) From a80af56af7c33dba0db5136a8c4b77f4db1a2ec1 Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Fri, 28 Jun 2024 11:40:53 -0400 Subject: [PATCH 06/14] Support encoding invreqs in outbound onion payloads Per BOLTs PR 1149, 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. We use an experimental TLV type for this new onion payload field, since the spec is still not merged in the BOLTs. --- lightning/src/ln/max_payment_path_len_tests.rs | 1 + lightning/src/ln/msgs.rs | 10 ++++++++-- lightning/src/ln/onion_utils.rs | 1 + lightning/src/ln/outbound_payment.rs | 1 + 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lightning/src/ln/max_payment_path_len_tests.rs b/lightning/src/ln/max_payment_path_len_tests.rs index 8c24ef36339..501ddcc5721 100644 --- a/lightning/src/ln/max_payment_path_len_tests.rs +++ b/lightning/src/ln/max_payment_path_len_tests.rs @@ -184,6 +184,7 @@ fn one_hop_blinded_path_with_custom_tlv() { encrypted_tlvs: &blinded_path.blinded_hops()[0].encrypted_payload, intro_node_blinding_point: Some(blinded_path.blinding_point()), keysend_preimage: None, + invoice_request: None, custom_tlvs: &Vec::new() }.serialized_length(); let max_custom_tlv_len = 1300 diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index 4837a40e104..6f508efa636 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -1747,6 +1747,7 @@ pub struct FinalOnionHopData { mod fuzzy_internal_msgs { use bitcoin::secp256k1::PublicKey; use crate::blinded_path::payment::{BlindedPaymentPath, PaymentConstraints, PaymentContext, PaymentRelay}; + use crate::offers::invoice_request::InvoiceRequest; use crate::types::payment::{PaymentPreimage, PaymentSecret}; use crate::types::features::{BlindedHopFeatures, Bolt12InvoiceFeatures}; use super::{FinalOnionHopData, TrampolineOnionPacket}; @@ -1827,6 +1828,7 @@ mod fuzzy_internal_msgs { intro_node_blinding_point: Option, // Set if the introduction node of the blinded path is the final node keysend_preimage: Option, custom_tlvs: &'a Vec<(u64, Vec)>, + invoice_request: Option<&'a InvoiceRequest>, } } @@ -2760,13 +2762,17 @@ impl<'a> Writeable for OutboundOnionPayload<'a> { }, Self::BlindedReceive { sender_intended_htlc_amt_msat, total_msat, cltv_expiry_height, encrypted_tlvs, - intro_node_blinding_point, keysend_preimage, ref custom_tlvs, + intro_node_blinding_point, keysend_preimage, ref invoice_request, ref custom_tlvs, } => { // We need to update [`ln::outbound_payment::RecipientOnionFields::with_custom_tlvs`] // to reject any reserved types in the experimental range if new ones are ever // standardized. + let invoice_request_tlv = invoice_request.map(|invreq| (77_777, invreq.encode())); // TODO: update TLV type once the async payments spec is merged let keysend_tlv = keysend_preimage.map(|preimage| (5482373484, preimage.encode())); - let mut custom_tlvs: Vec<&(u64, Vec)> = custom_tlvs.iter().chain(keysend_tlv.iter()).collect(); + let mut custom_tlvs: Vec<&(u64, Vec)> = custom_tlvs.iter() + .chain(invoice_request_tlv.iter()) + .chain(keysend_tlv.iter()) + .collect(); custom_tlvs.sort_unstable_by_key(|(typ, _)| *typ); _encode_varint_length_prefixed_tlv!(w, { (2, HighZeroBytesDroppedBigSize(*sender_intended_htlc_amt_msat), required), diff --git a/lightning/src/ln/onion_utils.rs b/lightning/src/ln/onion_utils.rs index 208096c128e..68866fc03f8 100644 --- a/lightning/src/ln/onion_utils.rs +++ b/lightning/src/ln/onion_utils.rs @@ -262,6 +262,7 @@ where encrypted_tlvs: &blinded_hop.encrypted_payload, intro_node_blinding_point: blinding_point.take(), keysend_preimage: *keysend_preimage, + invoice_request: None, custom_tlvs: &recipient_onion.custom_tlvs, }, ); diff --git a/lightning/src/ln/outbound_payment.rs b/lightning/src/ln/outbound_payment.rs index 04736f2a8c2..01f6c1d0963 100644 --- a/lightning/src/ln/outbound_payment.rs +++ b/lightning/src/ln/outbound_payment.rs @@ -641,6 +641,7 @@ impl RecipientOnionFields { for (typ, _) in custom_tlvs.iter() { if *typ < 1 << 16 { return Err(()); } if *typ == 5482373484 { return Err(()); } // keysend + if *typ == 77_777 { return Err(()); } // invoice requests for async payments match prev_type { Some(prev) if prev >= *typ => return Err(()), _ => {}, From 9b3e30707ec3b1d96fc83b4c340ccf14c940497f Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Fri, 28 Jun 2024 12:11:01 -0400 Subject: [PATCH 07/14] Support including invreqs when building onion payloads Add a new invoice request parameter to onion_utils::build_onion_payloads. As of this commit it will always be passed in as None, to be updated in future commits. Per BOLTs PR 1149, 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/blinded_payment_tests.rs | 4 ++-- lightning/src/ln/functional_tests.rs | 12 ++++++------ lightning/src/ln/max_payment_path_len_tests.rs | 2 +- lightning/src/ln/onion_payment.rs | 2 +- lightning/src/ln/onion_route_tests.rs | 10 +++++----- lightning/src/ln/onion_utils.rs | 10 ++++++++-- 6 files changed, 23 insertions(+), 17 deletions(-) diff --git a/lightning/src/ln/blinded_payment_tests.rs b/lightning/src/ln/blinded_payment_tests.rs index 79b24e06db3..3a6d996b5a4 100644 --- a/lightning/src/ln/blinded_payment_tests.rs +++ b/lightning/src/ln/blinded_payment_tests.rs @@ -318,7 +318,7 @@ fn do_forward_checks_failure(check: ForwardCheckFail, intro_fails: bool) { let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap(); let cur_height = nodes[0].best_block_info().1; let (mut onion_payloads, ..) = onion_utils::build_onion_payloads( - &route.paths[0], amt_msat, &recipient_onion_fields, cur_height, &None).unwrap(); + &route.paths[0], amt_msat, &recipient_onion_fields, cur_height, &None, None).unwrap(); // Remove the receive payload so the blinded forward payload is encoded as a final payload // (i.e. next_hop_hmac == [0; 32]) onion_payloads.pop(); @@ -897,7 +897,7 @@ fn do_multi_hop_receiver_fail(check: ReceiveCheckFail) { let cur_height = nodes[0].best_block_info().1; let recipient_onion_fields = RecipientOnionFields::spontaneous_empty(); let (mut onion_payloads, ..) = onion_utils::build_onion_payloads( - &route.paths[0], amt_msat, &recipient_onion_fields, cur_height, &None).unwrap(); + &route.paths[0], amt_msat, &recipient_onion_fields, cur_height, &None, None).unwrap(); let update_add = &mut payment_event_1_2.msgs[0]; onion_payloads.last_mut().map(|p| { diff --git a/lightning/src/ln/functional_tests.rs b/lightning/src/ln/functional_tests.rs index 42a57b114cb..cb6e8d521cc 100644 --- a/lightning/src/ln/functional_tests.rs +++ b/lightning/src/ln/functional_tests.rs @@ -1438,7 +1438,7 @@ fn test_fee_spike_violation_fails_htlc() { let onion_keys = onion_utils::construct_onion_keys(&secp_ctx, &route.paths[0], &session_priv).unwrap(); let recipient_onion_fields = RecipientOnionFields::secret_only(payment_secret); let (onion_payloads, htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads(&route.paths[0], - 3460001, &recipient_onion_fields, cur_height, &None).unwrap(); + 3460001, &recipient_onion_fields, cur_height, &None, None).unwrap(); let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &payment_hash).unwrap(); let msg = msgs::UpdateAddHTLC { channel_id: chan.2, @@ -1637,7 +1637,7 @@ fn test_chan_reserve_violation_inbound_htlc_outbound_channel() { let onion_keys = onion_utils::construct_onion_keys(&secp_ctx, &route.paths[0], &session_priv).unwrap(); let recipient_onion_fields = RecipientOnionFields::secret_only(payment_secret); let (onion_payloads, htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads(&route.paths[0], - 700_000, &recipient_onion_fields, cur_height, &None).unwrap(); + 700_000, &recipient_onion_fields, cur_height, &None, None).unwrap(); let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &payment_hash).unwrap(); let msg = msgs::UpdateAddHTLC { channel_id: chan.2, @@ -1817,7 +1817,7 @@ fn test_chan_reserve_violation_inbound_htlc_inbound_chan() { let onion_keys = onion_utils::construct_onion_keys(&secp_ctx, &route_2.paths[0], &session_priv).unwrap(); let recipient_onion_fields = RecipientOnionFields::spontaneous_empty(); let (onion_payloads, htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads( - &route_2.paths[0], recv_value_2, &recipient_onion_fields, cur_height, &None).unwrap(); + &route_2.paths[0], recv_value_2, &recipient_onion_fields, cur_height, &None, None).unwrap(); let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &our_payment_hash_1).unwrap(); let msg = msgs::UpdateAddHTLC { channel_id: chan.2, @@ -3556,7 +3556,7 @@ fn fail_backward_pending_htlc_upon_channel_failure() { let current_height = nodes[1].node.best_block.read().unwrap().height + 1; let recipient_onion_fields = RecipientOnionFields::secret_only(payment_secret); let (onion_payloads, _amount_msat, cltv_expiry) = onion_utils::build_onion_payloads( - &route.paths[0], 50_000, &recipient_onion_fields, current_height, &None).unwrap(); + &route.paths[0], 50_000, &recipient_onion_fields, current_height, &None, None).unwrap(); let onion_keys = onion_utils::construct_onion_keys(&secp_ctx, &route.paths[0], &session_priv).unwrap(); let onion_routing_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &payment_hash).unwrap(); @@ -6548,7 +6548,7 @@ fn test_update_add_htlc_bolt2_receiver_check_max_htlc_limit() { let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::signing_only(), &route.paths[0], &session_priv).unwrap(); let recipient_onion_fields = RecipientOnionFields::secret_only(our_payment_secret); let (onion_payloads, _htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads( - &route.paths[0], send_amt, &recipient_onion_fields, cur_height, &None).unwrap(); + &route.paths[0], send_amt, &recipient_onion_fields, cur_height, &None, None).unwrap(); let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &our_payment_hash).unwrap(); let mut msg = msgs::UpdateAddHTLC { @@ -8291,7 +8291,7 @@ fn test_onion_value_mpp_set_calculation() { let mut onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap(); let recipient_onion_fields = RecipientOnionFields::secret_only(our_payment_secret); let (mut onion_payloads, _, _) = onion_utils::build_onion_payloads(&route.paths[0], 100_000, - &recipient_onion_fields, height + 1, &None).unwrap(); + &recipient_onion_fields, height + 1, &None, None).unwrap(); // Edit amt_to_forward to simulate the sender having set // the final amount and the routing node taking less fee if let msgs::OutboundOnionPayload::Receive { diff --git a/lightning/src/ln/max_payment_path_len_tests.rs b/lightning/src/ln/max_payment_path_len_tests.rs index 501ddcc5721..90326b9c54c 100644 --- a/lightning/src/ln/max_payment_path_len_tests.rs +++ b/lightning/src/ln/max_payment_path_len_tests.rs @@ -276,7 +276,7 @@ fn blinded_path_with_custom_tlv() { let reserved_packet_bytes_without_custom_tlv: usize = onion_utils::build_onion_payloads( &route.paths[0], MIN_FINAL_VALUE_ESTIMATE_WITH_OVERPAY, &RecipientOnionFields::spontaneous_empty(), - nodes[0].best_block_info().1 + DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA, &None + nodes[0].best_block_info().1 + DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA, &None, None ) .unwrap() .0 diff --git a/lightning/src/ln/onion_payment.rs b/lightning/src/ln/onion_payment.rs index 528b5e7d886..d76c0d3a99d 100644 --- a/lightning/src/ln/onion_payment.rs +++ b/lightning/src/ln/onion_payment.rs @@ -541,7 +541,7 @@ mod tests { let path = Path { hops, blinded_tail: None, }; let onion_keys = super::onion_utils::construct_onion_keys(&secp_ctx, &path, &session_priv).unwrap(); let (onion_payloads, ..) = super::onion_utils::build_onion_payloads( - &path, total_amt_msat, &recipient_onion, cur_height + 1, &Some(keysend_preimage) + &path, total_amt_msat, &recipient_onion, cur_height + 1, &Some(keysend_preimage), None ).unwrap(); assert!(super::onion_utils::construct_onion_packet( diff --git a/lightning/src/ln/onion_route_tests.rs b/lightning/src/ln/onion_route_tests.rs index c0253166d99..3ab03ae91fe 100644 --- a/lightning/src/ln/onion_route_tests.rs +++ b/lightning/src/ln/onion_route_tests.rs @@ -356,7 +356,7 @@ fn test_onion_failure() { let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap(); let recipient_onion_fields = RecipientOnionFields::spontaneous_empty(); let (mut onion_payloads, _htlc_msat, _htlc_cltv) = onion_utils::build_onion_payloads( - &route.paths[0], 40000, &recipient_onion_fields, cur_height, &None).unwrap(); + &route.paths[0], 40000, &recipient_onion_fields, cur_height, &None, None).unwrap(); let mut new_payloads = Vec::new(); for payload in onion_payloads.drain(..) { new_payloads.push(BogusOnionHopData::new(payload)); @@ -375,7 +375,7 @@ fn test_onion_failure() { let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap(); let recipient_onion_fields = RecipientOnionFields::spontaneous_empty(); let (mut onion_payloads, _htlc_msat, _htlc_cltv) = onion_utils::build_onion_payloads( - &route.paths[0], 40000, &recipient_onion_fields, cur_height, &None).unwrap(); + &route.paths[0], 40000, &recipient_onion_fields, cur_height, &None, None).unwrap(); let mut new_payloads = Vec::new(); for payload in onion_payloads.drain(..) { new_payloads.push(BogusOnionHopData::new(payload)); @@ -627,7 +627,7 @@ fn test_onion_failure() { let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap(); let recipient_onion_fields = RecipientOnionFields::spontaneous_empty(); let (onion_payloads, _, htlc_cltv) = onion_utils::build_onion_payloads( - &route.paths[0], 40000, &recipient_onion_fields, height, &None).unwrap(); + &route.paths[0], 40000, &recipient_onion_fields, height, &None, None).unwrap(); let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &payment_hash).unwrap(); msg.cltv_expiry = htlc_cltv; msg.onion_routing_packet = onion_packet; @@ -964,7 +964,7 @@ fn test_always_create_tlv_format_onion_payloads() { let cur_height = nodes[0].best_block_info().1 + 1; let recipient_onion_fields = RecipientOnionFields::spontaneous_empty(); let (onion_payloads, _htlc_msat, _htlc_cltv) = onion_utils::build_onion_payloads( - &route.paths[0], 40000, &recipient_onion_fields, cur_height, &None).unwrap(); + &route.paths[0], 40000, &recipient_onion_fields, cur_height, &None, None).unwrap(); match onion_payloads[0] { msgs::OutboundOnionPayload::Forward {..} => {}, @@ -1221,7 +1221,7 @@ fn test_phantom_invalid_onion_payload() { let recipient_onion_fields = RecipientOnionFields::secret_only(payment_secret); let (mut onion_payloads, _, _) = onion_utils::build_onion_payloads( &route.paths[0], msgs::MAX_VALUE_MSAT + 1, - &recipient_onion_fields, height + 1, &None).unwrap(); + &recipient_onion_fields, height + 1, &None, None).unwrap(); // We only want to construct the onion packet for the last hop, not the entire route, so // remove the first hop's payload and its keys. onion_keys.remove(0); diff --git a/lightning/src/ln/onion_utils.rs b/lightning/src/ln/onion_utils.rs index 68866fc03f8..16b8885d2fb 100644 --- a/lightning/src/ln/onion_utils.rs +++ b/lightning/src/ln/onion_utils.rs @@ -13,6 +13,7 @@ use crate::crypto::streams::ChaChaReader; use crate::ln::channel::TOTAL_BITCOIN_SUPPLY_SATOSHIS; use crate::ln::channelmanager::{HTLCSource, RecipientOnionFields}; use crate::ln::msgs; +use crate::offers::invoice_request::InvoiceRequest; use crate::routing::gossip::NetworkUpdate; use crate::routing::router::{Path, RouteHop, RouteParameters}; use crate::sign::NodeSigner; @@ -179,6 +180,7 @@ pub(super) fn construct_onion_keys( pub(super) fn build_onion_payloads<'a>( path: &'a Path, total_msat: u64, recipient_onion: &'a RecipientOnionFields, starting_htlc_offset: u32, keysend_preimage: &Option, + invoice_request: Option<&'a InvoiceRequest>, ) -> Result<(Vec>, u64, u32), APIError> { let mut res: Vec = Vec::with_capacity( path.hops.len() + path.blinded_tail.as_ref().map_or(0, |t| t.hops.len()), @@ -197,6 +199,7 @@ pub(super) fn build_onion_payloads<'a>( recipient_onion, starting_htlc_offset, keysend_preimage, + invoice_request, |action, payload| match action { PayloadCallbackAction::PushBack => res.push(payload), PayloadCallbackAction::PushFront => res.insert(0, payload), @@ -218,7 +221,8 @@ enum PayloadCallbackAction { fn build_onion_payloads_callback<'a, H, B, F>( hops: H, mut blinded_tail: Option>, total_msat: u64, recipient_onion: &'a RecipientOnionFields, starting_htlc_offset: u32, - keysend_preimage: &Option, mut callback: F, + keysend_preimage: &Option, invoice_request: Option<&'a InvoiceRequest>, + mut callback: F, ) -> Result<(u64, u32), APIError> where H: DoubleEndedIterator, @@ -262,7 +266,7 @@ where encrypted_tlvs: &blinded_hop.encrypted_payload, intro_node_blinding_point: blinding_point.take(), keysend_preimage: *keysend_preimage, - invoice_request: None, + invoice_request, custom_tlvs: &recipient_onion.custom_tlvs, }, ); @@ -363,6 +367,7 @@ pub(crate) fn set_max_path_length( &recipient_onion, best_block_height, &keysend_preimage, + None, |_, payload| { num_reserved_bytes = num_reserved_bytes .saturating_add(payload.serialized_length()) @@ -1169,6 +1174,7 @@ pub fn create_payment_onion( recipient_onion, cur_block_height, keysend_preimage, + None, )?; let onion_packet = construct_onion_packet(onion_payloads, onion_keys, prng_seed, payment_hash) .map_err(|_| APIError::InvalidRoute { From 6a424536accfa6e9be469744f9e8314b172d4b33 Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Fri, 28 Jun 2024 13:45:28 -0400 Subject: [PATCH 08/14] Support including invreqs when building payment onions Add a new invoice request parameter to onion_utils::create_payment_onion. As of this commit it will always be passed in as None, to be updated in future commits. Per BOLTs PR 1149, 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/blinded_payment_tests.rs | 2 +- lightning/src/ln/channelmanager.rs | 2 +- lightning/src/ln/max_payment_path_len_tests.rs | 4 ++-- lightning/src/ln/onion_payment.rs | 2 +- lightning/src/ln/onion_utils.rs | 5 +++-- lightning/src/ln/payment_tests.rs | 2 +- 6 files changed, 9 insertions(+), 8 deletions(-) diff --git a/lightning/src/ln/blinded_payment_tests.rs b/lightning/src/ln/blinded_payment_tests.rs index 3a6d996b5a4..aa399b9d4d6 100644 --- a/lightning/src/ln/blinded_payment_tests.rs +++ b/lightning/src/ln/blinded_payment_tests.rs @@ -1447,7 +1447,7 @@ fn route_blinding_spec_test_vector() { }), }; let cur_height = 747_000; - let (bob_onion, _, _) = onion_utils::create_payment_onion(&secp_ctx, &path, &session_priv, amt_msat, &RecipientOnionFields::spontaneous_empty(), cur_height, &PaymentHash([0; 32]), &None, [0; 32]).unwrap(); + let (bob_onion, _, _) = onion_utils::create_payment_onion(&secp_ctx, &path, &session_priv, amt_msat, &RecipientOnionFields::spontaneous_empty(), cur_height, &PaymentHash([0; 32]), &None, None, [0; 32]).unwrap(); struct TestEcdhSigner { node_secret: SecretKey, diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 691223fd3cf..67efc889474 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -4195,7 +4195,7 @@ where let (onion_packet, htlc_msat, htlc_cltv) = onion_utils::create_payment_onion( &self.secp_ctx, &path, &session_priv, total_value, recipient_onion, cur_height, - payment_hash, keysend_preimage, prng_seed + payment_hash, keysend_preimage, None, prng_seed ).map_err(|e| { let logger = WithContext::from(&self.logger, Some(path.hops.first().unwrap().pubkey), None, Some(*payment_hash)); log_error!(logger, "Failed to build an onion for path for payment hash {}", payment_hash); diff --git a/lightning/src/ln/max_payment_path_len_tests.rs b/lightning/src/ln/max_payment_path_len_tests.rs index 90326b9c54c..af30003ee5d 100644 --- a/lightning/src/ln/max_payment_path_len_tests.rs +++ b/lightning/src/ln/max_payment_path_len_tests.rs @@ -110,7 +110,7 @@ fn large_payment_metadata() { let secp_ctx = Secp256k1::signing_only(); route_0_1.paths[0].hops[0].fee_msat = MIN_FINAL_VALUE_ESTIMATE_WITH_OVERPAY; route_0_1.paths[0].hops[0].cltv_expiry_delta = DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA; - let err = onion_utils::create_payment_onion(&secp_ctx, &route_0_1.paths[0], &test_utils::privkey(42), MIN_FINAL_VALUE_ESTIMATE_WITH_OVERPAY, &recipient_onion_too_large_md, nodes[0].best_block_info().1 + DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA, &payment_hash, &None, [0; 32]).unwrap_err(); + let err = onion_utils::create_payment_onion(&secp_ctx, &route_0_1.paths[0], &test_utils::privkey(42), MIN_FINAL_VALUE_ESTIMATE_WITH_OVERPAY, &recipient_onion_too_large_md, nodes[0].best_block_info().1 + DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA, &payment_hash, &None, None, [0; 32]).unwrap_err(); match err { APIError::InvalidRoute { err } => { assert_eq!(err, "Route size too large considering onion data"); @@ -318,7 +318,7 @@ fn blinded_path_with_custom_tlv() { let secp_ctx = Secp256k1::signing_only(); route.paths[0].hops[0].fee_msat = MIN_FINAL_VALUE_ESTIMATE_WITH_OVERPAY; route.paths[0].hops[0].cltv_expiry_delta = DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA; - let err = onion_utils::create_payment_onion(&secp_ctx, &route.paths[0], &test_utils::privkey(42), MIN_FINAL_VALUE_ESTIMATE_WITH_OVERPAY, &recipient_onion_too_large_custom_tlv, nodes[0].best_block_info().1 + DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA, &payment_hash, &None, [0; 32]).unwrap_err(); + let err = onion_utils::create_payment_onion(&secp_ctx, &route.paths[0], &test_utils::privkey(42), MIN_FINAL_VALUE_ESTIMATE_WITH_OVERPAY, &recipient_onion_too_large_custom_tlv, nodes[0].best_block_info().1 + DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA, &payment_hash, &None, None, [0; 32]).unwrap_err(); match err { APIError::InvalidRoute { err } => { assert_eq!(err, "Route size too large considering onion data"); diff --git a/lightning/src/ln/onion_payment.rs b/lightning/src/ln/onion_payment.rs index d76c0d3a99d..a62846f75cd 100644 --- a/lightning/src/ln/onion_payment.rs +++ b/lightning/src/ln/onion_payment.rs @@ -569,7 +569,7 @@ mod tests { let (onion, amount_msat, cltv_expiry) = create_payment_onion( &secp_ctx, &path, &session_priv, total_amt_msat, &recipient_onion, - cur_height, &payment_hash, &Some(preimage), prng_seed + cur_height, &payment_hash, &Some(preimage), None, prng_seed ).unwrap(); let msg = make_update_add_msg(amount_msat, cltv_expiry, payment_hash, onion); diff --git a/lightning/src/ln/onion_utils.rs b/lightning/src/ln/onion_utils.rs index 16b8885d2fb..3757d677320 100644 --- a/lightning/src/ln/onion_utils.rs +++ b/lightning/src/ln/onion_utils.rs @@ -1163,7 +1163,8 @@ where pub fn create_payment_onion( secp_ctx: &Secp256k1, path: &Path, session_priv: &SecretKey, total_msat: u64, recipient_onion: &RecipientOnionFields, cur_block_height: u32, payment_hash: &PaymentHash, - keysend_preimage: &Option, prng_seed: [u8; 32], + keysend_preimage: &Option, invoice_request: Option<&InvoiceRequest>, + prng_seed: [u8; 32], ) -> Result<(msgs::OnionPacket, u64, u32), APIError> { let onion_keys = construct_onion_keys(&secp_ctx, &path, &session_priv).map_err(|_| { APIError::InvalidRoute { err: "Pubkey along hop was maliciously selected".to_owned() } @@ -1174,7 +1175,7 @@ pub fn create_payment_onion( recipient_onion, cur_block_height, keysend_preimage, - None, + invoice_request, )?; let onion_packet = construct_onion_packet(onion_payloads, onion_keys, prng_seed, payment_hash) .map_err(|_| APIError::InvalidRoute { diff --git a/lightning/src/ln/payment_tests.rs b/lightning/src/ln/payment_tests.rs index 7839b49be77..ca386682fa0 100644 --- a/lightning/src/ln/payment_tests.rs +++ b/lightning/src/ln/payment_tests.rs @@ -4271,7 +4271,7 @@ fn peel_payment_onion_custom_tlvs() { let (onion_routing_packet, first_hop_msat, cltv_expiry) = onion_utils::create_payment_onion( &secp_ctx, &route.paths[0], &session_priv, amt_msat, &recipient_onion, - nodes[0].best_block_info().1, &payment_hash, &Some(keysend_preimage), prng_seed + nodes[0].best_block_info().1, &payment_hash, &Some(keysend_preimage), None, prng_seed ).unwrap(); let update_add = msgs::UpdateAddHTLC { From fead88faed57bf0fddcfe0904d9711f9c2b52d9e Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Fri, 28 Jun 2024 13:57:15 -0400 Subject: [PATCH 09/14] Support including invreqs when paying to a route Add a new invoice request parameter to outbound_payments and channelmanager send-to-route internal utils. As of this commit the invreq will always be passed in as None, to be updated in future commits. Per BOLTs PR 1149, 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/channelmanager.rs | 6 +++--- lightning/src/ln/outbound_payment.rs | 24 +++++++++++++----------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 67efc889474..7a974cc990e 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -4179,14 +4179,14 @@ where let _lck = self.total_consistency_lock.read().unwrap(); self.send_payment_along_path(SendAlongPathArgs { path, payment_hash, recipient_onion: &recipient_onion, total_value, - cur_height, payment_id, keysend_preimage, session_priv_bytes + cur_height, payment_id, keysend_preimage, invoice_request: None, session_priv_bytes }) } fn send_payment_along_path(&self, args: SendAlongPathArgs) -> Result<(), APIError> { let SendAlongPathArgs { path, payment_hash, recipient_onion, total_value, cur_height, payment_id, keysend_preimage, - session_priv_bytes + invoice_request, session_priv_bytes } = args; // The top-level caller should hold the total_consistency_lock read lock. debug_assert!(self.total_consistency_lock.try_write().is_err()); @@ -4195,7 +4195,7 @@ where let (onion_packet, htlc_msat, htlc_cltv) = onion_utils::create_payment_onion( &self.secp_ctx, &path, &session_priv, total_value, recipient_onion, cur_height, - payment_hash, keysend_preimage, None, prng_seed + payment_hash, keysend_preimage, invoice_request, prng_seed ).map_err(|e| { let logger = WithContext::from(&self.logger, Some(path.hops.first().unwrap().pubkey), None, Some(*payment_hash)); log_error!(logger, "Failed to build an onion for path for payment hash {}", payment_hash); diff --git a/lightning/src/ln/outbound_payment.rs b/lightning/src/ln/outbound_payment.rs index 01f6c1d0963..bf1fedd8a4b 100644 --- a/lightning/src/ln/outbound_payment.rs +++ b/lightning/src/ln/outbound_payment.rs @@ -716,6 +716,7 @@ pub(super) struct SendAlongPathArgs<'a> { pub cur_height: u32, pub payment_id: PaymentId, pub keysend_preimage: &'a Option, + pub invoice_request: Option<&'a InvoiceRequest>, pub session_priv_bytes: [u8; 32], } @@ -769,7 +770,7 @@ impl OutboundPayments { F: Fn(SendAlongPathArgs) -> Result<(), APIError> { let onion_session_privs = self.add_new_pending_payment(payment_hash, recipient_onion.clone(), payment_id, None, route, None, None, entropy_source, best_block_height)?; - self.pay_route_internal(route, payment_hash, &recipient_onion, None, payment_id, None, + self.pay_route_internal(route, payment_hash, &recipient_onion, None, None, payment_id, None, onion_session_privs, node_signer, best_block_height, &send_payment_along_path) .map_err(|e| { self.remove_outbound_if_all_failed(payment_id, &e); e }) } @@ -814,7 +815,7 @@ impl OutboundPayments { let onion_session_privs = self.add_new_pending_payment(payment_hash, recipient_onion.clone(), payment_id, Some(preimage), &route, None, None, entropy_source, best_block_height)?; - match self.pay_route_internal(route, payment_hash, &recipient_onion, Some(preimage), + match self.pay_route_internal(route, payment_hash, &recipient_onion, Some(preimage), None, payment_id, None, onion_session_privs, node_signer, best_block_height, &send_payment_along_path ) { Ok(()) => Ok(payment_hash), @@ -962,7 +963,7 @@ impl OutboundPayments { } let result = self.pay_route_internal( - &route, payment_hash, &recipient_onion, keysend_preimage, payment_id, + &route, payment_hash, &recipient_onion, keysend_preimage, None, payment_id, Some(route_params.final_value_msat), onion_session_privs, node_signer, best_block_height, &send_payment_along_path ); @@ -1242,7 +1243,7 @@ impl OutboundPayments { })?; let res = self.pay_route_internal(&route, payment_hash, &recipient_onion, - keysend_preimage, payment_id, None, onion_session_privs, node_signer, + keysend_preimage, None, payment_id, None, onion_session_privs, node_signer, best_block_height, &send_payment_along_path); log_info!(logger, "Sending payment with id {} and hash {} returned {:?}", payment_id, payment_hash, res); @@ -1396,7 +1397,7 @@ impl OutboundPayments { } }; let res = self.pay_route_internal(&route, payment_hash, &recipient_onion, keysend_preimage, - payment_id, Some(total_msat), onion_session_privs, node_signer, best_block_height, + None, 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 { @@ -1508,7 +1509,8 @@ impl OutboundPayments { let recipient_onion_fields = RecipientOnionFields::spontaneous_empty(); match self.pay_route_internal(&route, payment_hash, &recipient_onion_fields, - None, payment_id, None, onion_session_privs, node_signer, best_block_height, &send_payment_along_path + None, None, payment_id, None, onion_session_privs, node_signer, best_block_height, + &send_payment_along_path ) { Ok(()) => Ok((payment_hash, payment_id)), Err(e) => { @@ -1620,9 +1622,9 @@ impl OutboundPayments { fn pay_route_internal( &self, route: &Route, payment_hash: PaymentHash, recipient_onion: &RecipientOnionFields, - keysend_preimage: Option, payment_id: PaymentId, recv_value_msat: Option, - onion_session_privs: Vec<[u8; 32]>, node_signer: &NS, best_block_height: u32, - send_payment_along_path: &F + keysend_preimage: Option, invoice_request: Option<&InvoiceRequest>, + payment_id: PaymentId, recv_value_msat: Option, onion_session_privs: Vec<[u8; 32]>, + node_signer: &NS, best_block_height: u32, send_payment_along_path: &F ) -> Result<(), PaymentSendFailure> where NS::Target: NodeSigner, @@ -1675,7 +1677,7 @@ impl OutboundPayments { for (path, session_priv_bytes) in route.paths.iter().zip(onion_session_privs.into_iter()) { let mut path_res = send_payment_along_path(SendAlongPathArgs { path: &path, payment_hash: &payment_hash, recipient_onion, total_value, - cur_height, payment_id, keysend_preimage: &keysend_preimage, + cur_height, payment_id, keysend_preimage: &keysend_preimage, invoice_request, session_priv_bytes }); match path_res { @@ -1760,7 +1762,7 @@ impl OutboundPayments { F: Fn(SendAlongPathArgs) -> Result<(), APIError>, { self.pay_route_internal(route, payment_hash, &recipient_onion, - keysend_preimage, payment_id, recv_value_msat, onion_session_privs, + keysend_preimage, None, payment_id, recv_value_msat, onion_session_privs, node_signer, best_block_height, &send_payment_along_path) .map_err(|e| { self.remove_outbound_if_all_failed(payment_id, &e); e }) } From 520ae0b1f43d158ffc3ec5612efcfb11f6965e69 Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Fri, 28 Jun 2024 14:03:46 -0400 Subject: [PATCH 10/14] Store invreqs in StaticInvoiceReceived outbound payments When transitioning outbound payments from AwaitingInvoice to StaticInvoiceReceived, include the invreq in the new state's outbound payment storage for future inclusion in an async payment onion. Per BOLTs PR 1149, 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 | 31 +++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/lightning/src/ln/outbound_payment.rs b/lightning/src/ln/outbound_payment.rs index bf1fedd8a4b..4c37f5bac50 100644 --- a/lightning/src/ln/outbound_payment.rs +++ b/lightning/src/ln/outbound_payment.rs @@ -83,6 +83,7 @@ pub(crate) enum PendingOutboundPayment { keysend_preimage: PaymentPreimage, retry_strategy: Retry, route_params: RouteParameters, + invoice_request: InvoiceRequest, }, Retryable { retry_strategy: Option, @@ -1004,7 +1005,7 @@ impl OutboundPayments { } match self.pending_outbound_payments.lock().unwrap().entry(payment_id) { - hash_map::Entry::Occupied(mut entry) => match entry.get() { + hash_map::Entry::Occupied(mut entry) => match entry.get_mut() { PendingOutboundPayment::AwaitingInvoice { retry_strategy, retryable_invoice_request, max_total_routing_fee_msat, .. } => { @@ -1048,6 +1049,11 @@ impl OutboundPayments { keysend_preimage, retry_strategy: *retry_strategy, route_params, + invoice_request: + retryable_invoice_request + .take() + .ok_or(Bolt12PaymentError::UnexpectedInvoice)? + .invoice_request, }; return Ok(()) }, @@ -2256,6 +2262,7 @@ impl_writeable_tlv_based_enum_upgradable!(PendingOutboundPayment, (2, keysend_preimage, required), (4, retry_strategy, required), (6, route_params, required), + (8, invoice_request, required), }, ); @@ -2270,15 +2277,19 @@ mod tests { use crate::events::{Event, PathFailure, PaymentFailureReason}; use crate::types::payment::{PaymentHash, PaymentPreimage}; use crate::ln::channelmanager::{PaymentId, RecipientOnionFields}; + use crate::ln::inbound_payment::ExpandedKey; use crate::types::features::{Bolt12InvoiceFeatures, ChannelFeatures, NodeFeatures}; use crate::ln::msgs::{ErrorAction, LightningError}; use crate::ln::outbound_payment::{Bolt12PaymentError, OutboundPayments, PendingOutboundPayment, Retry, RetryableSendFailure, StaleExpiration}; #[cfg(feature = "std")] use crate::offers::invoice::DEFAULT_RELATIVE_EXPIRY; + use crate::offers::invoice_request::InvoiceRequest; + use crate::offers::nonce::Nonce; use crate::offers::offer::OfferBuilder; use crate::offers::test_utils::*; use crate::routing::gossip::NetworkGraph; use crate::routing::router::{InFlightHtlcs, Path, PaymentParameters, Route, RouteHop, RouteParameters}; + use crate::sign::KeyMaterial; use crate::sync::{Arc, Mutex, RwLock}; use crate::util::errors::APIError; use crate::util::hash_tables::new_hash_map; @@ -2823,6 +2834,22 @@ mod tests { assert!(pending_events.lock().unwrap().is_empty()); } + fn dummy_invoice_request() -> InvoiceRequest { + let expanded_key = ExpandedKey::new(&KeyMaterial([42; 32])); + let entropy = FixedEntropy {}; + let nonce = Nonce::from_entropy_source(&entropy); + let secp_ctx = Secp256k1::new(); + let payment_id = PaymentId([1; 32]); + + OfferBuilder::new(recipient_pubkey()) + .amount_msats(1000) + .build().unwrap() + .request_invoice_deriving_signing_pubkey(&expanded_key, nonce, &secp_ctx, payment_id) + .unwrap() + .build_and_sign() + .unwrap() + } + #[test] fn time_out_unreleased_async_payments() { let pending_events = Mutex::new(VecDeque::new()); @@ -2844,6 +2871,7 @@ mod tests { keysend_preimage: PaymentPreimage([0; 32]), retry_strategy: Retry::Attempts(0), route_params, + invoice_request: dummy_invoice_request(), }; outbounds.insert(payment_id, outbound); core::mem::drop(outbounds); @@ -2890,6 +2918,7 @@ mod tests { keysend_preimage: PaymentPreimage([0; 32]), retry_strategy: Retry::Attempts(0), route_params, + invoice_request: dummy_invoice_request(), }; outbounds.insert(payment_id, outbound); core::mem::drop(outbounds); From 925a0cb38be347806386595ea40e87b1cabe835d Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Wed, 18 Sep 2024 11:17:49 -0400 Subject: [PATCH 11/14] Include invreq in payment onion when sending async payments Past commits have set us up to include invoice requests in outbound async payment onions. Here we actually pull the invoice request from where it's stored in outbound_payments and pass it into the correct utility for inclusion in the onion on initial send. Per BOLTs PR 1149, 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 | 29 ++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/lightning/src/ln/outbound_payment.rs b/lightning/src/ln/outbound_payment.rs index 4c37f5bac50..b5c167236c6 100644 --- a/lightning/src/ln/outbound_payment.rs +++ b/lightning/src/ln/outbound_payment.rs @@ -881,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 ) @@ -891,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, invoice_request: Option<&InvoiceRequest>, + 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> @@ -964,9 +964,9 @@ impl OutboundPayments { } let result = self.pay_route_internal( - &route, payment_hash, &recipient_onion, keysend_preimage, None, payment_id, - Some(route_params.final_value_msat), onion_session_privs, node_signer, - best_block_height, &send_payment_along_path + &route, payment_hash, &recipient_onion, keysend_preimage, invoice_request, payment_id, + Some(route_params.final_value_msat), onion_session_privs, node_signer, best_block_height, + &send_payment_along_path ); log_info!( logger, "Sending payment with id {} and hash {} returned {:?}", payment_id, @@ -1082,13 +1082,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), }, @@ -1096,9 +1097,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 ) } From d574de95f54affc39ed9b93c561d0210bde39808 Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Wed, 18 Sep 2024 11:21:26 -0400 Subject: [PATCH 12/14] 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 BOLTs PR 1149, 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 | 51 +++++++++++++++++++--------- 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/lightning/src/ln/outbound_payment.rs b/lightning/src/ln/outbound_payment.rs index b5c167236c6..e7d32f9f8e1 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+. @@ -948,20 +949,33 @@ 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 - ); - match self.pending_outbound_payments.lock().unwrap().entry(payment_id) { + let mut outbounds = self.pending_outbound_payments.lock().unwrap(); + let onion_session_privs = match outbounds.entry(payment_id) { hash_map::Entry::Occupied(entry) => match entry.get() { - PendingOutboundPayment::InvoiceReceived { .. } - | PendingOutboundPayment::StaticInvoiceReceived { .. } => { + PendingOutboundPayment::InvoiceReceived { .. } => { + let (retryable_payment, onion_session_privs) = Self::create_pending_payment( + payment_hash, recipient_onion.clone(), keysend_preimage, None, &route, + Some(retry_strategy), payment_params, entropy_source, best_block_height + ); *entry.into_mut() = retryable_payment; + onion_session_privs + }, + PendingOutboundPayment::StaticInvoiceReceived { .. } => { + let invreq = if let PendingOutboundPayment::StaticInvoiceReceived { invoice_request, .. } = entry.remove() { + invoice_request + } else { unreachable!() }; + let (retryable_payment, onion_session_privs) = Self::create_pending_payment( + payment_hash, recipient_onion.clone(), keysend_preimage, Some(invreq), &route, + Some(retry_strategy), payment_params, entropy_source, best_block_height + ); + outbounds.insert(payment_id, retryable_payment); + onion_session_privs }, _ => return Err(Bolt12PaymentError::DuplicateInvoice), }, hash_map::Entry::Vacant(_) => return Err(Bolt12PaymentError::UnexpectedInvoice), - } + }; + core::mem::drop(outbounds); let result = self.pay_route_internal( &route, payment_hash, &recipient_onion, keysend_preimage, invoice_request, payment_id, @@ -1324,14 +1338,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(); @@ -1354,6 +1368,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() { @@ -1366,7 +1381,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"); @@ -1404,8 +1419,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); @@ -1557,7 +1572,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); @@ -1568,8 +1583,9 @@ impl OutboundPayments { fn create_pending_payment( 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, @@ -1590,6 +1606,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(), @@ -2151,6 +2168,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), @@ -2237,6 +2255,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())), }, From 34e710edd1f7c673bccdcc174620b74bb6b496b6 Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Wed, 18 Sep 2024 11:27:09 -0400 Subject: [PATCH 13/14] Factor invoice requests into payment path length limiting Async payments include the original invoice request in the payment onion. Since invreqs may include blinded paths, it's important to factor them into our max path length calculations since they may take up a significant portion of the 1300-byte onion. --- lightning/src/ln/onion_utils.rs | 5 +++-- lightning/src/ln/outbound_payment.rs | 15 ++++++++------- lightning/src/routing/router.rs | 3 ++- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/lightning/src/ln/onion_utils.rs b/lightning/src/ln/onion_utils.rs index 3757d677320..960209c0e0a 100644 --- a/lightning/src/ln/onion_utils.rs +++ b/lightning/src/ln/onion_utils.rs @@ -320,7 +320,8 @@ pub(crate) const MIN_FINAL_VALUE_ESTIMATE_WITH_OVERPAY: u64 = 100_000_000; pub(crate) fn set_max_path_length( route_params: &mut RouteParameters, recipient_onion: &RecipientOnionFields, - keysend_preimage: Option, best_block_height: u32, + keysend_preimage: Option, invoice_request: Option<&InvoiceRequest>, + best_block_height: u32, ) -> Result<(), ()> { const PAYLOAD_HMAC_LEN: usize = 32; let unblinded_intermed_payload_len = msgs::OutboundOnionPayload::Forward { @@ -367,7 +368,7 @@ pub(crate) fn set_max_path_length( &recipient_onion, best_block_height, &keysend_preimage, - None, + invoice_request, |_, payload| { num_reserved_bytes = num_reserved_bytes .saturating_add(payload.serialized_length()) diff --git a/lightning/src/ln/outbound_payment.rs b/lightning/src/ln/outbound_payment.rs index e7d32f9f8e1..4ba89ea993d 100644 --- a/lightning/src/ln/outbound_payment.rs +++ b/lightning/src/ln/outbound_payment.rs @@ -932,8 +932,9 @@ impl OutboundPayments { custom_tlvs: vec![], }; let route = match self.find_initial_route( - payment_id, payment_hash, &recipient_onion, None, &mut route_params, router, - &first_hops, &inflight_htlcs, node_signer, best_block_height, logger, + payment_id, payment_hash, &recipient_onion, keysend_preimage, invoice_request, + &mut route_params, router, &first_hops, &inflight_htlcs, node_signer, best_block_height, + logger, ) { Ok(route) => route, Err(e) => { @@ -1052,7 +1053,7 @@ impl OutboundPayments { if let Err(()) = onion_utils::set_max_path_length( &mut route_params, &RecipientOnionFields::spontaneous_empty(), Some(keysend_preimage), - best_block_height + Some(invreq), best_block_height ) { abandon_with_entry!(entry, PaymentFailureReason::RouteNotFound); return Err(Bolt12PaymentError::SendingFailed(RetryableSendFailure::OnionPacketSizeExceeded)) @@ -1182,8 +1183,8 @@ impl OutboundPayments { } fn find_initial_route( - &self, payment_id: PaymentId, payment_hash: PaymentHash, - recipient_onion: &RecipientOnionFields, keysend_preimage: Option, + &self, payment_id: PaymentId, payment_hash: PaymentHash, recipient_onion: &RecipientOnionFields, + keysend_preimage: Option, invoice_request: Option<&InvoiceRequest>, route_params: &mut RouteParameters, router: &R, first_hops: &Vec, inflight_htlcs: &IH, node_signer: &NS, best_block_height: u32, logger: &L, ) -> Result @@ -1202,7 +1203,7 @@ impl OutboundPayments { } onion_utils::set_max_path_length( - route_params, recipient_onion, keysend_preimage, best_block_height + route_params, recipient_onion, keysend_preimage, invoice_request, best_block_height ) .map_err(|()| { log_error!(logger, "Can't construct an onion packet without exceeding 1300-byte onion \ @@ -1250,7 +1251,7 @@ impl OutboundPayments { SP: Fn(SendAlongPathArgs) -> Result<(), APIError>, { let route = self.find_initial_route( - payment_id, payment_hash, &recipient_onion, keysend_preimage, &mut route_params, router, + payment_id, payment_hash, &recipient_onion, keysend_preimage, None, &mut route_params, router, &first_hops, &inflight_htlcs, node_signer, best_block_height, logger, )?; diff --git a/lightning/src/routing/router.rs b/lightning/src/routing/router.rs index 4c01bdcc4c0..b91b1b47d6e 100644 --- a/lightning/src/routing/router.rs +++ b/lightning/src/routing/router.rs @@ -613,8 +613,9 @@ impl RouteParameters { &mut self, recipient_onion: &RecipientOnionFields, is_keysend: bool, best_block_height: u32 ) -> Result<(), ()> { let keysend_preimage_opt = is_keysend.then(|| PaymentPreimage([42; 32])); + // TODO: no way to account for the invoice request here yet onion_utils::set_max_path_length( - self, recipient_onion, keysend_preimage_opt, best_block_height + self, recipient_onion, keysend_preimage_opt, None, best_block_height ) } } From 240dd0a9609335e40160330c181830c59f90382e Mon Sep 17 00:00:00 2001 From: Valentine Wallace Date: Wed, 30 Oct 2024 12:36:46 -0400 Subject: [PATCH 14/14] Fix outdated comment on PendingOutboundPayment::StaticInvoiceReceived --- lightning/src/ln/outbound_payment.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lightning/src/ln/outbound_payment.rs b/lightning/src/ln/outbound_payment.rs index 4ba89ea993d..c3fde629a83 100644 --- a/lightning/src/ln/outbound_payment.rs +++ b/lightning/src/ln/outbound_payment.rs @@ -2276,8 +2276,8 @@ impl_writeable_tlv_based_enum_upgradable!(PendingOutboundPayment, (2, retry_strategy, required), (4, max_total_routing_fee_msat, option), }, - // Added in 0.0.125. Prior versions will drop these outbounds on downgrade, which is safe because - // no HTLCs are in-flight. + // Added in 0.1. Prior versions will drop these outbounds on downgrade, which is safe because no + // HTLCs are in-flight. (9, StaticInvoiceReceived) => { (0, payment_hash, required), (2, keysend_preimage, required),