@@ -102,6 +102,8 @@ use {
102102 crate::offers::refund::RefundMaybeWithDerivedMetadataBuilder,
103103};
104104
105+ use lightning_invoice::{Bolt11Invoice, Bolt11InvoiceDescription, CreationError, Currency, Description, InvoiceBuilder as Bolt11InvoiceBuilder, SignOrCreationError, DEFAULT_EXPIRY_TIME};
106+
105107use alloc::collections::{btree_map, BTreeMap};
106108
107109use crate::io;
@@ -2199,7 +2201,7 @@ where
21992201 L::Target: Logger,
22002202{
22012203 default_configuration: UserConfig,
2202- pub(super) chain_hash: ChainHash,
2204+ chain_hash: ChainHash,
22032205 fee_estimator: LowerBoundedFeeEstimator<F>,
22042206 chain_monitor: M,
22052207 tx_broadcaster: T,
@@ -9093,6 +9095,145 @@ where
90939095 self.finish_close_channel(failure);
90949096 }
90959097 }
9098+
9099+ /// Utility for creating a BOLT11 invoice that can be verified by [`ChannelManager`] without
9100+ /// storing any additional state. It achieves this by including a [`PaymentSecret`] in the
9101+ /// invoice which it uses to verify that the invoice has not expired and the payment amount is
9102+ /// sufficient, reproducing the [`PaymentPreimage`] if applicable.
9103+ pub fn create_bolt11_invoice(
9104+ &self, params: Bolt11InvoiceParameters,
9105+ ) -> Result<Bolt11Invoice, SignOrCreationError<()>> {
9106+ let Bolt11InvoiceParameters {
9107+ amount_msats, description, invoice_expiry_delta_secs, min_final_cltv_expiry_delta,
9108+ payment_hash,
9109+ } = params;
9110+
9111+ let currency =
9112+ Network::from_chain_hash(self.chain_hash).map(Into::into).unwrap_or(Currency::Bitcoin);
9113+
9114+ #[cfg(feature = "std")]
9115+ let duration_since_epoch = {
9116+ use std::time::SystemTime;
9117+ SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)
9118+ .expect("for the foreseeable future this shouldn't happen")
9119+ };
9120+ #[cfg(not(feature = "std"))]
9121+ let duration_since_epoch =
9122+ Duration::from_secs(self.highest_seen_timestamp.load(Ordering::Acquire) as u64);
9123+
9124+ if let Some(min_final_cltv_expiry_delta) = min_final_cltv_expiry_delta {
9125+ if min_final_cltv_expiry_delta.saturating_add(3) < MIN_FINAL_CLTV_EXPIRY_DELTA {
9126+ return Err(SignOrCreationError::CreationError(CreationError::MinFinalCltvExpiryDeltaTooShort));
9127+ }
9128+ }
9129+
9130+ let (payment_hash, payment_secret) = match payment_hash {
9131+ Some(payment_hash) => {
9132+ let payment_secret = self
9133+ .create_inbound_payment_for_hash(
9134+ payment_hash, amount_msats,
9135+ invoice_expiry_delta_secs.unwrap_or(DEFAULT_EXPIRY_TIME as u32),
9136+ min_final_cltv_expiry_delta,
9137+ )
9138+ .map_err(|()| SignOrCreationError::CreationError(CreationError::InvalidAmount))?;
9139+ (payment_hash, payment_secret)
9140+ },
9141+ None => {
9142+ self
9143+ .create_inbound_payment(
9144+ amount_msats, invoice_expiry_delta_secs.unwrap_or(DEFAULT_EXPIRY_TIME as u32),
9145+ min_final_cltv_expiry_delta,
9146+ )
9147+ .map_err(|()| SignOrCreationError::CreationError(CreationError::InvalidAmount))?
9148+ },
9149+ };
9150+
9151+ log_trace!(self.logger, "Creating invoice with payment hash {}", &payment_hash);
9152+
9153+ let invoice = Bolt11InvoiceBuilder::new(currency);
9154+ let invoice = match description {
9155+ Bolt11InvoiceDescription::Direct(description) => invoice.description(description.into_inner().0),
9156+ Bolt11InvoiceDescription::Hash(hash) => invoice.description_hash(hash.0),
9157+ };
9158+
9159+ let mut invoice = invoice
9160+ .duration_since_epoch(duration_since_epoch)
9161+ .payee_pub_key(self.get_our_node_id())
9162+ .payment_hash(Hash::from_slice(&payment_hash.0).unwrap())
9163+ .payment_secret(payment_secret)
9164+ .basic_mpp()
9165+ .min_final_cltv_expiry_delta(
9166+ // Add a buffer of 3 to the delta if present, otherwise use LDK's minimum.
9167+ min_final_cltv_expiry_delta.map(|x| x.saturating_add(3)).unwrap_or(MIN_FINAL_CLTV_EXPIRY_DELTA).into()
9168+ );
9169+
9170+ if let Some(invoice_expiry_delta_secs) = invoice_expiry_delta_secs{
9171+ invoice = invoice.expiry_time(Duration::from_secs(invoice_expiry_delta_secs.into()));
9172+ }
9173+
9174+ if let Some(amount_msats) = amount_msats {
9175+ invoice = invoice.amount_milli_satoshis(amount_msats);
9176+ }
9177+
9178+ let channels = self.list_channels();
9179+ let route_hints = super::invoice_utils::sort_and_filter_channels(channels, amount_msats, &self.logger);
9180+ for hint in route_hints {
9181+ invoice = invoice.private_route(hint);
9182+ }
9183+
9184+ let raw_invoice = invoice.build_raw().map_err(|e| SignOrCreationError::CreationError(e))?;
9185+ let signature = self.node_signer.sign_invoice(&raw_invoice, Recipient::Node);
9186+
9187+ raw_invoice
9188+ .sign(|_| signature)
9189+ .map(|invoice| Bolt11Invoice::from_signed(invoice).unwrap())
9190+ .map_err(|e| SignOrCreationError::SignError(e))
9191+ }
9192+ }
9193+
9194+ /// Parameters used with [`create_bolt11_invoice`].
9195+ ///
9196+ /// [`create_bolt11_invoice`]: ChannelManager::create_bolt11_invoice
9197+ pub struct Bolt11InvoiceParameters {
9198+ /// The amount for the invoice, if any.
9199+ pub amount_msats: Option<u64>,
9200+
9201+ /// The description for what the invoice is for, or hash of such description.
9202+ pub description: Bolt11InvoiceDescription,
9203+
9204+ /// The invoice expiration relative to its creation time. If not set, the invoice will expire in
9205+ /// [`DEFAULT_EXPIRY_TIME`] by default.
9206+ ///
9207+ /// The creation time used is the duration since the Unix epoch for `std` builds. For non-`std`
9208+ /// builds, the highest block timestamp seen is used instead.
9209+ pub invoice_expiry_delta_secs: Option<u32>,
9210+
9211+ /// The minimum `cltv_expiry` for the last HTLC in the route. If not set, will use
9212+ /// [`MIN_FINAL_CLTV_EXPIRY_DELTA`].
9213+ ///
9214+ /// If set, must be at least [`MIN_FINAL_CLTV_EXPIRY_DELTA`], and a three-block buffer will be
9215+ /// added as well to allow for up to a few new block confirmations during routing.
9216+ pub min_final_cltv_expiry_delta: Option<u16>,
9217+
9218+ /// The payment hash used in the invoice. If not set, a payment hash will be generated using a
9219+ /// preimage that can be reproduced by [`ChannelManager`] without storing any state.
9220+ ///
9221+ /// Uses the payment hash if set. This may be useful if you're building an on-chain swap or
9222+ /// involving another protocol where the payment hash is also involved outside the scope of
9223+ /// lightning.
9224+ pub payment_hash: Option<PaymentHash>,
9225+ }
9226+
9227+ impl Default for Bolt11InvoiceParameters {
9228+ fn default() -> Self {
9229+ Self {
9230+ amount_msats: None,
9231+ description: Bolt11InvoiceDescription::Direct(Description::empty()),
9232+ invoice_expiry_delta_secs: None,
9233+ min_final_cltv_expiry_delta: None,
9234+ payment_hash: None,
9235+ }
9236+ }
90969237}
90979238
90989239macro_rules! create_offer_builder { ($self: ident, $builder: ty) => {
0 commit comments