From d11fc6bb52752db5471e09d4243e55e33a1dcc3e Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Thu, 14 Sep 2023 13:32:51 -0500 Subject: [PATCH] Utility for creating and sending InvoiceRequests Add a utility to ChannelManager for creating an InvoiceRequest for an Offer such that derived keys are used for the payer id. This allows for stateless verification of any Invoice messages before it is paid. Also tracks future payments using the given PaymentId such that the corresponding Invoice is paid only once. --- lightning/src/ln/channelmanager.rs | 68 +++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 2 deletions(-) diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 4e1099e15d9..598f3ff81ea 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -56,10 +56,10 @@ use crate::ln::msgs::{ChannelMessageHandler, DecodeError, LightningError}; use crate::ln::outbound_payment; use crate::ln::outbound_payment::{OutboundPayments, PaymentAttempts, PendingOutboundPayment, SendAlongPathArgs}; use crate::ln::wire::Encode; -use crate::offers::offer::{DerivedMetadata, OfferBuilder}; +use crate::offers::offer::{DerivedMetadata, Offer, OfferBuilder}; use crate::offers::parse::Bolt12SemanticError; use crate::offers::refund::RefundBuilder; -use crate::onion_message::{OffersMessage, PendingOnionMessage}; +use crate::onion_message::{Destination, OffersMessage, PendingOnionMessage}; use crate::sign::{EntropySource, KeysManager, NodeSigner, Recipient, SignerProvider, WriteableEcdsaChannelSigner}; use crate::util::config::{UserConfig, ChannelConfig, ChannelConfigUpdate}; use crate::util::wakers::{Future, Notifier}; @@ -6880,6 +6880,70 @@ where Ok(builder) } + /// Creates an [`InvoiceRequest`] for an [`Offer`] from the given parameters and enqueues it to + /// be sent via an onion message. + /// + /// Uses [`InvoiceRequestBuilder`] such that the [`InvoiceRequest`] it builds is recognized by + /// the [`ChannelManager`] when handling [`Bolt12Invoice`] messages for the request. + /// + /// The provided `payment_id` is used to ensure that only one invoice is paid for the request. + /// + /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest + /// [`InvoiceRequestBuilder`]: crate::offers::invoice_request::InvoiceRequestBuilder + /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice + pub fn request_invoice( + &self, offer: &Offer, quantity: Option, amount_msats: Option, + payer_note: Option, payment_id: PaymentId, retry_strategy: Retry + ) -> Result<(), Bolt12SemanticError> { + let expanded_key = &self.inbound_payment_key; + let entropy = &*self.entropy_source; + let secp_ctx = &self.secp_ctx; + + let builder = offer.request_invoice_deriving_payer_id( + expanded_key, entropy, secp_ctx, payment_id + )?; + let builder = match quantity { + None => builder, + Some(quantity) => builder.quantity(quantity)?, + }; + let builder = match amount_msats { + None => builder, + Some(amount_msats) => builder.amount_msats(amount_msats)?, + }; + let builder = match payer_note { + None => builder, + Some(payer_note) => builder.payer_note(payer_note), + }; + + let invoice_request = builder.build_and_sign()?; + let reply_path = self.create_one_hop_blinded_path(); + + self.pending_outbound_payments + .add_new_awaiting_invoice(payment_id, retry_strategy) + .map_err(|_| Bolt12SemanticError::DuplicatePaymentId)?; + + let mut pending_offers_messages = self.pending_offers_messages.lock().unwrap(); + if offer.paths().is_empty() { + let message = PendingOnionMessage { + contents: OffersMessage::InvoiceRequest(invoice_request), + destination: Destination::Node(offer.signing_pubkey()), + reply_path: Some(reply_path), + }; + pending_offers_messages.push(message); + } else { + for path in offer.paths() { + let message = PendingOnionMessage { + contents: OffersMessage::InvoiceRequest(invoice_request.clone()), + destination: Destination::BlindedPath(path.clone()), + reply_path: Some(reply_path.clone()), + }; + pending_offers_messages.push(message); + } + } + + Ok(()) + } + /// Gets a payment secret and payment hash for use in an invoice given to a third party wishing /// to pay us. ///