Expose send_payment_for_bolt12_invoice

UserConfig::manually_handle_bolt12_invoices allows deferring payment of
BOLT12 invoices by generating Event::InvoiceReceived. Expose
ChannelManager::send_payment_for_bolt12_invoice to allow users to pay
the Bolt12Invoice included in the event. While the event contains the
PaymentId for reference, that parameter is now removed from the method
in favor of extracting the PaymentId from the invoice's payer_metadata.
This commit is contained in:
Jeffrey Czyz 2024-05-21 18:47:32 -05:00
parent 232959cd27
commit 65efd92c4a
No known key found for this signature in database
GPG key ID: 912EF12EA67705F5
4 changed files with 48 additions and 7 deletions

View file

@ -720,8 +720,16 @@ pub enum Event {
/// Indicates a [`Bolt12Invoice`] in response to an [`InvoiceRequest`] or a [`Refund`] was /// Indicates a [`Bolt12Invoice`] in response to an [`InvoiceRequest`] or a [`Refund`] was
/// received. /// received.
/// ///
/// This event will only be generated if [`UserConfig::manually_handle_bolt12_invoices`] is set.
/// Use [`ChannelManager::send_payment_for_bolt12_invoice`] to pay the invoice or
/// [`ChannelManager::abandon_payment`] to abandon the associated payment. See those docs for
/// further details.
///
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
/// [`Refund`]: crate::offers::refund::Refund /// [`Refund`]: crate::offers::refund::Refund
/// [`UserConfig::manually_handle_bolt12_invoices`]: crate::util::config::UserConfig::manually_handle_bolt12_invoices
/// [`ChannelManager::send_payment_for_bolt12_invoice`]: crate::ln::channelmanager::ChannelManager::send_payment_for_bolt12_invoice
/// [`ChannelManager::abandon_payment`]: crate::ln::channelmanager::ChannelManager::abandon_payment
InvoiceReceived { InvoiceReceived {
/// The `payment_id` associated with payment for the invoice. /// The `payment_id` associated with payment for the invoice.
payment_id: PaymentId, payment_id: PaymentId,

View file

@ -58,7 +58,7 @@ use crate::ln::onion_utils::{HTLCFailReason, INVALID_ONION_BLINDING};
use crate::ln::msgs::{ChannelMessageHandler, DecodeError, LightningError}; use crate::ln::msgs::{ChannelMessageHandler, DecodeError, LightningError};
#[cfg(test)] #[cfg(test)]
use crate::ln::outbound_payment; use crate::ln::outbound_payment;
use crate::ln::outbound_payment::{Bolt12PaymentError, OutboundPayments, PaymentAttempts, PendingOutboundPayment, SendAlongPathArgs, StaleExpiration}; use crate::ln::outbound_payment::{OutboundPayments, PaymentAttempts, PendingOutboundPayment, SendAlongPathArgs, StaleExpiration};
use crate::ln::wire::Encode; use crate::ln::wire::Encode;
use crate::offers::invoice::{BlindedPayInfo, Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, DerivedSigningPubkey, ExplicitSigningPubkey, InvoiceBuilder, UnsignedBolt12Invoice}; use crate::offers::invoice::{BlindedPayInfo, Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, DerivedSigningPubkey, ExplicitSigningPubkey, InvoiceBuilder, UnsignedBolt12Invoice};
use crate::offers::invoice_error::InvoiceError; use crate::offers::invoice_error::InvoiceError;
@ -105,7 +105,7 @@ use core::time::Duration;
use core::ops::Deref; use core::ops::Deref;
// Re-export this for use in the public API. // Re-export this for use in the public API.
pub use crate::ln::outbound_payment::{PaymentSendFailure, ProbeSendFailure, Retry, RetryableSendFailure, RecipientOnionFields}; pub use crate::ln::outbound_payment::{Bolt12PaymentError, PaymentSendFailure, ProbeSendFailure, Retry, RetryableSendFailure, RecipientOnionFields};
use crate::ln::script::ShutdownScript; use crate::ln::script::ShutdownScript;
// We hold various information about HTLC relay in the HTLC objects in Channel itself: // We hold various information about HTLC relay in the HTLC objects in Channel itself:
@ -3996,7 +3996,36 @@ where
self.pending_outbound_payments.test_set_payment_metadata(payment_id, new_payment_metadata); self.pending_outbound_payments.test_set_payment_metadata(payment_id, new_payment_metadata);
} }
pub(super) fn send_payment_for_bolt12_invoice(&self, invoice: &Bolt12Invoice, payment_id: PaymentId) -> Result<(), Bolt12PaymentError> { /// Pays the [`Bolt12Invoice`] associated with the `payment_id` encoded in its `payer_metadata`.
///
/// The invoice's `payer_metadata` is used to authenticate that the invoice was indeed requested
/// before attempting a payment. [`Bolt12PaymentError::UnexpectedInvoice`] is returned if this
/// fails or if the encoded `payment_id` is not recognized. The latter may happen once the
/// payment is no longer tracked because the payment was attempted after:
/// - an invoice for the `payment_id` was already paid,
/// - one full [timer tick] has elapsed since initially requesting the invoice when paying an
/// offer, or
/// - the refund corresponding to the invoice has already expired.
///
/// To retry the payment, request another invoice using a new `payment_id`.
///
/// Attempting to pay the same invoice twice while the first payment is still pending will
/// result in a [`Bolt12PaymentError::DuplicateInvoice`].
///
/// Otherwise, either [`Event::PaymentSent`] or [`Event::PaymentFailed`] are used to indicate
/// whether or not the payment was successful.
///
/// [timer tick]: Self::timer_tick_occurred
pub fn send_payment_for_bolt12_invoice(&self, invoice: &Bolt12Invoice) -> Result<(), Bolt12PaymentError> {
let secp_ctx = &self.secp_ctx;
let expanded_key = &self.inbound_payment_key;
match invoice.verify(expanded_key, secp_ctx) {
Ok(payment_id) => self.send_payment_for_verified_bolt12_invoice(invoice, payment_id),
Err(()) => Err(Bolt12PaymentError::UnexpectedInvoice),
}
}
fn send_payment_for_verified_bolt12_invoice(&self, invoice: &Bolt12Invoice, payment_id: PaymentId) -> Result<(), Bolt12PaymentError> {
let best_block_height = self.best_block.read().unwrap().height; let best_block_height = self.best_block.read().unwrap().height;
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self); let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
self.pending_outbound_payments self.pending_outbound_payments
@ -10287,7 +10316,7 @@ where
self.pending_events.lock().unwrap().push_back((event, None)); self.pending_events.lock().unwrap().push_back((event, None));
return ResponseInstruction::NoResponse; return ResponseInstruction::NoResponse;
} else { } else {
self.send_payment_for_bolt12_invoice(&invoice, payment_id) self.send_payment_for_verified_bolt12_invoice(&invoice, payment_id)
.map_err(|e| { .map_err(|e| {
log_trace!(self.logger, "Failed paying invoice: {:?}", e); log_trace!(self.logger, "Failed paying invoice: {:?}", e);
InvoiceError::from_string(format!("{:?}", e)) InvoiceError::from_string(format!("{:?}", e))

View file

@ -501,9 +501,9 @@ pub enum PaymentSendFailure {
}, },
} }
/// An error when attempting to pay a BOLT 12 invoice. /// An error when attempting to pay a [`Bolt12Invoice`].
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub(super) enum Bolt12PaymentError { pub enum Bolt12PaymentError {
/// The invoice was not requested. /// The invoice was not requested.
UnexpectedInvoice, UnexpectedInvoice,
/// Payment for an invoice with the corresponding [`PaymentId`] was already initiated. /// Payment for an invoice with the corresponding [`PaymentId`] was already initiated.

View file

@ -859,12 +859,16 @@ pub struct UserConfig {
/// If this is set to `true`, the user needs to manually pay [`Bolt12Invoice`]s when received. /// If this is set to `true`, the user needs to manually pay [`Bolt12Invoice`]s when received.
/// ///
/// When set to `true`, [`Event::InvoiceReceived`] will be generated for each received /// When set to `true`, [`Event::InvoiceReceived`] will be generated for each received
/// [`Bolt12Invoice`] instead of being automatically paid after verification. /// [`Bolt12Invoice`] instead of being automatically paid after verification. Use
/// [`ChannelManager::send_payment_for_bolt12_invoice`] to pay the invoice or
/// [`ChannelManager::abandon_payment`] to abandon the associated payment.
/// ///
/// Default value: `false` /// Default value: `false`
/// ///
/// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
/// [`Event::InvoiceReceived`]: crate::events::Event::InvoiceReceived /// [`Event::InvoiceReceived`]: crate::events::Event::InvoiceReceived
/// [`ChannelManager::send_payment_for_bolt12_invoice`]: crate::ln::channelmanager::ChannelManager::send_payment_for_bolt12_invoice
/// [`ChannelManager::abandon_payment`]: crate::ln::channelmanager::ChannelManager::abandon_payment
pub manually_handle_bolt12_invoices: bool, pub manually_handle_bolt12_invoices: bool,
} }