]> git.bitcoin.ninja Git - rust-lightning/commitdiff
Support sending async payments as an always-online sender.
authorValentine Wallace <vwallace@protonmail.com>
Wed, 4 Sep 2024 19:24:26 +0000 (15:24 -0400)
committerValentine Wallace <vwallace@protonmail.com>
Fri, 13 Sep 2024 14:40:06 +0000 (10:40 -0400)
Async receive is not yet supported.

Here we process inbound release_htlc onion messages, check that they actually
correspond to one of our outbound payments, and actually forward the HTLCs.
Valid release_htlc receipt indicates that the recipient has now come online to
receive.

lightning/src/ln/channelmanager.rs
lightning/src/ln/outbound_payment.rs

index f5254285516af66bacf22920780f2737aa5049f1..fe5008eac5f15da25c0bee782a8be7834c43623b 100644 (file)
@@ -4378,6 +4378,21 @@ where
                res
        }
 
+       #[cfg(async_payments)]
+       fn send_payment_for_static_invoice(
+               &self, payment_id: PaymentId, payment_release_secret: [u8; 32]
+       ) -> Result<(), Bolt12PaymentError> {
+               let best_block_height = self.best_block.read().unwrap().height;
+               let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
+               self.pending_outbound_payments
+                       .send_payment_for_static_invoice(
+                               payment_id, payment_release_secret, &self.router, self.list_usable_channels(),
+                               || self.compute_inflight_htlcs(), &self.entropy_source, &self.node_signer, &self,
+                               &self.secp_ctx, best_block_height, &self.logger, &self.pending_events,
+                               |args| self.send_payment_along_path(args)
+                       )
+       }
+
        /// Signals that no further attempts for the given payment should occur. Useful if you have a
        /// pending outbound payment with retries remaining, but wish to stop retrying the payment before
        /// retries are exhausted.
@@ -11171,7 +11186,17 @@ where
                None
        }
 
-       fn release_held_htlc(&self, _message: ReleaseHeldHtlc, _context: AsyncPaymentsContext) {}
+       fn release_held_htlc(&self, _message: ReleaseHeldHtlc, _context: AsyncPaymentsContext) {
+               #[cfg(async_payments)] {
+                       let AsyncPaymentsContext::OutboundPayment { payment_id } = _context;
+                       if let Err(e) = self.send_payment_for_static_invoice(payment_id, _message.payment_release_secret) {
+                               log_trace!(
+                                       self.logger, "Failed to release held HTLC with payment id {} and release secret {:02x?}: {:?}",
+                                       payment_id, _message.payment_release_secret, e
+                               );
+                       }
+               }
+       }
 
        fn release_pending_messages(&self) -> Vec<(AsyncPaymentsMessage, MessageSendInstructions)> {
                core::mem::take(&mut self.pending_async_payments_messages.lock().unwrap())
index bc1123ee2e4f7ec568c29b5acf3f0f350acf8ec7..c86aab511420a140907c8dc3642fe0bf34dcc510 100644 (file)
@@ -856,16 +856,17 @@ impl OutboundPayments {
                        route_params.max_total_routing_fee_msat = Some(max_fee_msat);
                }
                self.send_payment_for_bolt12_invoice_internal(
-                       payment_id, payment_hash, 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, 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
                )
        }
 
        fn send_payment_for_bolt12_invoice_internal<
                R: Deref, ES: Deref, NS: Deref, NL: Deref, IH, SP, L: Deref
        >(
-               &self, payment_id: PaymentId, payment_hash: PaymentHash, mut route_params: RouteParameters,
+               &self, payment_id: PaymentId, payment_hash: PaymentHash,
+               keysend_preimage: Option<PaymentPreimage>, mut route_params: RouteParameters,
                retry_strategy: Retry, router: &R, first_hops: Vec<ChannelDetails>, inflight_htlcs: IH,
                entropy_source: &ES, node_signer: &NS, node_id_lookup: &NL,
                secp_ctx: &Secp256k1<secp256k1::All>, best_block_height: u32, logger: &L,
@@ -923,12 +924,13 @@ 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(), None, &route, Some(retry_strategy), payment_params,
-                       entropy_source, best_block_height
+                       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) {
                        hash_map::Entry::Occupied(entry) => match entry.get() {
-                               PendingOutboundPayment::InvoiceReceived { .. } => {
+                               PendingOutboundPayment::InvoiceReceived { .. }
+                               | PendingOutboundPayment::StaticInvoiceReceived { .. } => {
                                        *entry.into_mut() = retryable_payment;
                                },
                                _ => return Err(Bolt12PaymentError::DuplicateInvoice),
@@ -937,7 +939,7 @@ impl OutboundPayments {
                }
 
                let result = self.pay_route_internal(
-                       &route, payment_hash, &recipient_onion, None, payment_id,
+                       &route, payment_hash, &recipient_onion, keysend_preimage, payment_id,
                        Some(route_params.final_value_msat), onion_session_privs, node_signer,
                        best_block_height, &send_payment_along_path
                );
@@ -1033,6 +1035,49 @@ impl OutboundPayments {
                };
        }
 
+       #[cfg(async_payments)]
+       pub(super) fn send_payment_for_static_invoice<
+               R: Deref, ES: Deref, NS: Deref, NL: Deref, IH, SP, L: Deref
+       >(
+               &self, payment_id: PaymentId, payment_release_secret: [u8; 32], router: &R,
+               first_hops: Vec<ChannelDetails>, inflight_htlcs: IH, entropy_source: &ES, node_signer: &NS,
+               node_id_lookup: &NL, secp_ctx: &Secp256k1<secp256k1::All>, best_block_height: u32, logger: &L,
+               pending_events: &Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>,
+               send_payment_along_path: SP,
+       ) -> Result<(), Bolt12PaymentError>
+       where
+               R::Target: Router,
+               ES::Target: EntropySource,
+               NS::Target: NodeSigner,
+               NL::Target: NodeIdLookUp,
+               L::Target: Logger,
+               IH: Fn() -> InFlightHtlcs,
+               SP: Fn(SendAlongPathArgs) -> Result<(), APIError>,
+       {
+               let (payment_hash, keysend_preimage, route_params, retry_strategy) =
+                       match self.pending_outbound_payments.lock().unwrap().entry(payment_id) {
+                               hash_map::Entry::Occupied(entry) => match entry.get() {
+                                       PendingOutboundPayment::StaticInvoiceReceived {
+                                               payment_hash, payment_release_secret: release_secret, route_params, retry_strategy,
+                                               keysend_preimage, ..
+                                       } => {
+                                               if payment_release_secret != *release_secret {
+                                                       return Err(Bolt12PaymentError::UnexpectedInvoice)
+                                               }
+                                               (*payment_hash, *keysend_preimage, route_params.clone(), *retry_strategy)
+                                       },
+                                       _ => return Err(Bolt12PaymentError::DuplicateInvoice),
+                               },
+                               hash_map::Entry::Vacant(_) => return Err(Bolt12PaymentError::UnexpectedInvoice),
+                       };
+
+               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
+               )
+       }
+
        pub(super) fn check_retry_payments<R: Deref, ES: Deref, NS: Deref, SP, IH, FH, L: Deref>(
                &self, router: &R, first_hops: FH, inflight_htlcs: IH, entropy_source: &ES, node_signer: &NS,
                best_block_height: u32,