Allow setting an HRN in invoice_requests built by `pay_for_offer`
What changed, and why it matters
This commit adds a new public method, pay_for_offer_from_hrn, to ChannelManager so that users who looked up a Lightning offer via a human-readable name (BIP 353) can still pay it through the existing pay_for_offer machinery. The change is a feature addition: it threads the human-readable name into the invoice_request so the payment follows the BIP 353 protocol. There is no security bug being fixed here.
No security action required. Treat as a normal API/feature change.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch introduces OfferFromHrn import and a new ChannelManager::pay_for_offer_from_hrn wrapper. It delegates to the existing pay_for_offer_intern with the human-readable name passed as Some(offer.hrn). The existing pay_for_offer documentation is updated to direct BIP 353-derived offers to the new method. No vulnerability, bounds issue, cryptographic flaw, or unsafe code is present in the diff.
Changed components
lightning/src/ln/channelmanager.rsChannelManager::pay_for_offerChannelManager::pay_for_offer_from_hrnInspect captured patch +39 / −1
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 162e0e2..e1b293a 100644
--- a/lightning/src/ln/channelmanager.rs
+++ b/lightning/src/ln/channelmanager.rs
@@ -98,7 +98,7 @@ use crate::offers::invoice::{
use crate::offers::invoice_error::InvoiceError;
use crate::offers::invoice_request::InvoiceRequest;
use crate::offers::nonce::Nonce;
-use crate::offers::offer::Offer;
+use crate::offers::offer::{Offer, OfferFromHrn};
use crate::offers::parse::Bolt12SemanticError;
use crate::offers::refund::Refund;
use crate::offers::signer;
@@ -12288,6 +12288,9 @@ where
/// `amount_msats` allows you to overpay what is required to satisfy the offer, or may be
/// required if the offer does not require a specific amount.
///
+ /// If the [`Offer`] was built from a human readable name resolved using BIP 353, you *must*
+ /// instead call [`Self::pay_for_offer_from_hrn`].
+ ///
/// # Payment
///
/// The provided `payment_id` is used to ensure that only one invoice is paid for the request
@@ -12351,6 +12354,41 @@ where
)
}
+ /// Pays for an [`Offer`] which was built by resolving a human readable name. It is otherwise
+ /// identical to [`Self::pay_for_offer`].
+ pub fn pay_for_offer_from_hrn(
+ &self, offer: &OfferFromHrn, amount_msats: u64, payment_id: PaymentId,
+ optional_params: OptionalOfferPaymentParams,
+ ) -> Result<(), Bolt12SemanticError> {
+ let create_pending_payment_fn = |invoice_request: &InvoiceRequest, nonce| {
+ let expiration = StaleExpiration::TimerTicks(1);
+ let retryable_invoice_request = RetryableInvoiceRequest {
+ invoice_request: invoice_request.clone(),
+ nonce,
+ needs_retry: true,
+ };
+ self.pending_outbound_payments
+ .add_new_awaiting_invoice(
+ payment_id,
+ expiration,
+ optional_params.retry_strategy,
+ optional_params.route_params_config,
+ Some(retryable_invoice_request),
+ )
+ .map_err(|_| Bolt12SemanticError::DuplicatePaymentId)
+ };
+
+ self.pay_for_offer_intern(
+ &offer.offer,
+ if offer.offer.expects_quantity() { Some(1) } else { None },
+ Some(amount_msats),
+ optional_params.payer_note,
+ payment_id,
+ Some(offer.hrn),
+ create_pending_payment_fn,
+ )
+ }
+
/// Pays for an [`Offer`] using the given parameters, including a `quantity`, by creating an
/// [`InvoiceRequest`] and enqueuing it to be sent via an onion message. [`ChannelManager`] will
/// pay the actual [`Bolt12Invoice`] once it is received.
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.