Skip to content

Commit

Permalink
Utility for creating and sending InvoiceRequests
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
jkczyz committed Sep 14, 2023
1 parent 4ce9aad commit be8bf45
Showing 1 changed file with 65 additions and 2 deletions.
67 changes: 65 additions & 2 deletions lightning/src/ln/channelmanager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -6880,6 +6880,69 @@ where
Ok(builder)
}

/// Creates an [`InvoiceRequest`] for an [`Offer`] from the given parameters and enqueues it to
/// be sent via an onion message.
///
/// Use [`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
/// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
pub fn request_invoice(
&self, offer: &Offer, quantity: Option<u64>, amount_msats: Option<u64>,
payer_note: Option<String>, 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.
///
Expand Down

0 comments on commit be8bf45

Please sign in to comment.