]> git.bitcoin.ninja Git - rust-lightning/commitdiff
Add a BOLT11 invoice utility to ChannelManager
authorJeffrey Czyz <jkczyz@gmail.com>
Mon, 28 Oct 2024 22:28:10 +0000 (17:28 -0500)
committerJeffrey Czyz <jkczyz@gmail.com>
Fri, 8 Nov 2024 17:56:59 +0000 (11:56 -0600)
Now that the lightning crate depends on the lightning_invoice crate, the
utility functions previously living in the latter can be implemented on
ChannelManager. Additionally, the parameters are now moved to a struct
in order to remove the increasingly combinatorial blow-up of methods.

The new Bolt11InvoiceParameters is used to determine what values to set
in the invoice. Using None for any given parameter results in a
reasonable the default or a behavior determined by the ChannelManager as
detailed in the documentation.

lightning/src/ln/channelmanager.rs
lightning/src/ln/invoice_utils.rs

index 5c39e90177220c74235812de1ece268493be0e33..9631851eb737cca7d41c1858c22a7d8da8c6b334 100644 (file)
@@ -102,6 +102,8 @@ use {
        crate::offers::refund::RefundMaybeWithDerivedMetadataBuilder,
 };
 
+use lightning_invoice::{Bolt11Invoice, Bolt11InvoiceDescription, CreationError, Currency, Description, InvoiceBuilder as Bolt11InvoiceBuilder, SignOrCreationError, DEFAULT_EXPIRY_TIME};
+
 use alloc::collections::{btree_map, BTreeMap};
 
 use crate::io;
@@ -2199,7 +2201,7 @@ where
        L::Target: Logger,
 {
        default_configuration: UserConfig,
-       pub(super) chain_hash: ChainHash,
+       chain_hash: ChainHash,
        fee_estimator: LowerBoundedFeeEstimator<F>,
        chain_monitor: M,
        tx_broadcaster: T,
@@ -9093,6 +9095,145 @@ where
                        self.finish_close_channel(failure);
                }
        }
+
+       /// Utility for creating a BOLT11 invoice that can be verified by [`ChannelManager`] without
+       /// storing any additional state. It achieves this by including a [`PaymentSecret`] in the
+       /// invoice which it uses to verify that the invoice has not expired and the payment amount is
+       /// sufficient, reproducing the [`PaymentPreimage`] if applicable.
+       pub fn create_bolt11_invoice(
+               &self, params: Bolt11InvoiceParameters,
+       ) -> Result<Bolt11Invoice, SignOrCreationError<()>> {
+               let Bolt11InvoiceParameters {
+                       amount_msats, description, invoice_expiry_delta_secs, min_final_cltv_expiry_delta,
+                       payment_hash,
+               } = params;
+
+               let currency =
+                       Network::from_chain_hash(self.chain_hash).map(Into::into).unwrap_or(Currency::Bitcoin);
+
+               #[cfg(feature = "std")]
+               let duration_since_epoch = {
+                       use std::time::SystemTime;
+                       SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)
+                               .expect("for the foreseeable future this shouldn't happen")
+               };
+               #[cfg(not(feature = "std"))]
+               let duration_since_epoch =
+                       Duration::from_secs(self.highest_seen_timestamp.load(Ordering::Acquire) as u64);
+
+               if let Some(min_final_cltv_expiry_delta) = min_final_cltv_expiry_delta {
+                       if min_final_cltv_expiry_delta.saturating_add(3) < MIN_FINAL_CLTV_EXPIRY_DELTA {
+                               return Err(SignOrCreationError::CreationError(CreationError::MinFinalCltvExpiryDeltaTooShort));
+                       }
+               }
+
+               let (payment_hash, payment_secret) = match payment_hash {
+                       Some(payment_hash) => {
+                               let payment_secret = self
+                                       .create_inbound_payment_for_hash(
+                                               payment_hash, amount_msats,
+                                               invoice_expiry_delta_secs.unwrap_or(DEFAULT_EXPIRY_TIME as u32),
+                                               min_final_cltv_expiry_delta,
+                                       )
+                                       .map_err(|()| SignOrCreationError::CreationError(CreationError::InvalidAmount))?;
+                               (payment_hash, payment_secret)
+                       },
+                       None => {
+                               self
+                                       .create_inbound_payment(
+                                               amount_msats, invoice_expiry_delta_secs.unwrap_or(DEFAULT_EXPIRY_TIME as u32),
+                                               min_final_cltv_expiry_delta,
+                                       )
+                                       .map_err(|()| SignOrCreationError::CreationError(CreationError::InvalidAmount))?
+                       },
+               };
+
+               log_trace!(self.logger, "Creating invoice with payment hash {}", &payment_hash);
+
+               let invoice = Bolt11InvoiceBuilder::new(currency);
+               let invoice = match description {
+                       Bolt11InvoiceDescription::Direct(description) => invoice.description(description.into_inner().0),
+                       Bolt11InvoiceDescription::Hash(hash) => invoice.description_hash(hash.0),
+               };
+
+               let mut invoice = invoice
+                       .duration_since_epoch(duration_since_epoch)
+                       .payee_pub_key(self.get_our_node_id())
+                       .payment_hash(Hash::from_slice(&payment_hash.0).unwrap())
+                       .payment_secret(payment_secret)
+                       .basic_mpp()
+                       .min_final_cltv_expiry_delta(
+                               // Add a buffer of 3 to the delta if present, otherwise use LDK's minimum.
+                               min_final_cltv_expiry_delta.map(|x| x.saturating_add(3)).unwrap_or(MIN_FINAL_CLTV_EXPIRY_DELTA).into()
+                       );
+
+               if let Some(invoice_expiry_delta_secs) = invoice_expiry_delta_secs{
+                       invoice = invoice.expiry_time(Duration::from_secs(invoice_expiry_delta_secs.into()));
+               }
+
+               if let Some(amount_msats) = amount_msats {
+                       invoice = invoice.amount_milli_satoshis(amount_msats);
+               }
+
+               let channels = self.list_channels();
+               let route_hints = super::invoice_utils::sort_and_filter_channels(channels, amount_msats, &self.logger);
+               for hint in route_hints {
+                       invoice = invoice.private_route(hint);
+               }
+
+               let raw_invoice = invoice.build_raw().map_err(|e| SignOrCreationError::CreationError(e))?;
+               let signature = self.node_signer.sign_invoice(&raw_invoice, Recipient::Node);
+
+               raw_invoice
+                       .sign(|_| signature)
+                       .map(|invoice| Bolt11Invoice::from_signed(invoice).unwrap())
+                       .map_err(|e| SignOrCreationError::SignError(e))
+       }
+}
+
+/// Parameters used with [`create_bolt11_invoice`].
+///
+/// [`create_bolt11_invoice`]: ChannelManager::create_bolt11_invoice
+pub struct Bolt11InvoiceParameters {
+       /// The amount for the invoice, if any.
+       pub amount_msats: Option<u64>,
+
+       /// The description for what the invoice is for, or hash of such description.
+       pub description: Bolt11InvoiceDescription,
+
+       /// The invoice expiration relative to its creation time. If not set, the invoice will expire in
+       /// [`DEFAULT_EXPIRY_TIME`] by default.
+       ///
+       /// The creation time used is the duration since the Unix epoch for `std` builds. For non-`std`
+       /// builds, the highest block timestamp seen is used instead.
+       pub invoice_expiry_delta_secs: Option<u32>,
+
+       /// The minimum `cltv_expiry` for the last HTLC in the route. If not set, will use
+       /// [`MIN_FINAL_CLTV_EXPIRY_DELTA`].
+       ///
+       /// If set, must be at least [`MIN_FINAL_CLTV_EXPIRY_DELTA`], and a three-block buffer will be
+       /// added as well to allow for up to a few new block confirmations during routing.
+       pub min_final_cltv_expiry_delta: Option<u16>,
+
+       /// The payment hash used in the invoice. If not set, a payment hash will be generated using a
+       /// preimage that can be reproduced by [`ChannelManager`] without storing any state.
+       ///
+       /// Uses the payment hash if set. This may be useful if you're building an on-chain swap or
+       /// involving another protocol where the payment hash is also involved outside the scope of
+       /// lightning.
+       pub payment_hash: Option<PaymentHash>,
+}
+
+impl Default for Bolt11InvoiceParameters {
+       fn default() -> Self {
+               Self {
+                       amount_msats: None,
+                       description: Bolt11InvoiceDescription::Direct(Description::empty()),
+                       invoice_expiry_delta_secs: None,
+                       min_final_cltv_expiry_delta: None,
+                       payment_hash: None,
+               }
+       }
 }
 
 macro_rules! create_offer_builder { ($self: ident, $builder: ty) => {
index 1df7bfa08a8dc3b0bc9c78b30ada5887852d8286..01767ab05a3f64deec1d9961d728d756d79917c8 100644 (file)
@@ -6,13 +6,12 @@ use lightning_invoice::{Description, Bolt11InvoiceDescription, Sha256};
 use crate::prelude::*;
 
 use bitcoin::hashes::Hash;
-use bitcoin::network::Network;
 use crate::chain;
 use crate::chain::chaininterface::{BroadcasterInterface, FeeEstimator};
 use crate::sign::{Recipient, NodeSigner, SignerProvider, EntropySource};
-use crate::types::payment::{PaymentHash, PaymentSecret};
+use crate::types::payment::PaymentHash;
 use crate::ln::channel_state::ChannelDetails;
-use crate::ln::channelmanager::{ChannelManager, PhantomRouteHints, MIN_CLTV_EXPIRY_DELTA, MIN_FINAL_CLTV_EXPIRY_DELTA};
+use crate::ln::channelmanager::{Bolt11InvoiceParameters, ChannelManager, PhantomRouteHints, MIN_CLTV_EXPIRY_DELTA, MIN_FINAL_CLTV_EXPIRY_DELTA};
 use crate::ln::inbound_payment::{create, create_from_hash, ExpandedKey};
 use crate::routing::gossip::RoutingFees;
 use crate::routing::router::{RouteHint, RouteHintHop, Router};
@@ -314,7 +313,6 @@ fn rotate_through_iterators<T, I: Iterator<Item = T>>(mut vecs: Vec<I>) -> impl
        })
 }
 
-#[cfg(feature = "std")]
 /// Utility to construct an invoice. Generally, unless you want to do something like a custom
 /// cltv_expiry, this is what you should be using to create an invoice. The reason being, this
 /// method stores the invoice's payment secret and preimage in `ChannelManager`, so (a) the user
@@ -331,9 +329,8 @@ fn rotate_through_iterators<T, I: Iterator<Item = T>>(mut vecs: Vec<I>) -> impl
 ///
 /// [`MIN_FINAL_CLTV_EXPIRY_DETLA`]: crate::ln::channelmanager::MIN_FINAL_CLTV_EXPIRY_DELTA
 pub fn create_invoice_from_channelmanager<M: Deref, T: Deref, ES: Deref, NS: Deref, SP: Deref, F: Deref, R: Deref, MR: Deref, L: Deref>(
-       channelmanager: &ChannelManager<M, T, ES, NS, SP, F, R, MR, L>, node_signer: NS, logger: L,
-       amt_msat: Option<u64>, description: String, invoice_expiry_delta_secs: u32,
-       min_final_cltv_expiry_delta: Option<u16>,
+       channelmanager: &ChannelManager<M, T, ES, NS, SP, F, R, MR, L>, amt_msat: Option<u64>,
+       description: String, invoice_expiry_delta_secs: u32, min_final_cltv_expiry_delta: Option<u16>,
 ) -> Result<Bolt11Invoice, SignOrCreationError<()>>
 where
        M::Target: chain::Watch<<SP::Target as SignerProvider>::EcdsaSigner>,
@@ -346,21 +343,17 @@ where
        MR::Target: MessageRouter,
        L::Target: Logger,
 {
-       use std::time::SystemTime;
-       let duration_since_epoch = SystemTime::now()
-               .duration_since(SystemTime::UNIX_EPOCH)
-               .expect("for the foreseeable future this shouldn't happen");
-
-       _create_invoice_from_channelmanager_and_duration_since_epoch(
-               channelmanager, node_signer, logger, amt_msat,
-               Bolt11InvoiceDescription::Direct(
-                       Description::new(description).map_err(SignOrCreationError::CreationError)?,
-               ),
-               duration_since_epoch, invoice_expiry_delta_secs, min_final_cltv_expiry_delta,
-       )
+       let description = Description::new(description).map_err(SignOrCreationError::CreationError)?;
+       let params = Bolt11InvoiceParameters {
+               amount_msats: amt_msat,
+               description: Bolt11InvoiceDescription::Direct(description),
+               invoice_expiry_delta_secs: Some(invoice_expiry_delta_secs),
+               min_final_cltv_expiry_delta,
+               payment_hash: None,
+       };
+       channelmanager.create_bolt11_invoice(params)
 }
 
-#[cfg(feature = "std")]
 /// Utility to construct an invoice. Generally, unless you want to do something like a custom
 /// cltv_expiry, this is what you should be using to create an invoice. The reason being, this
 /// method stores the invoice's payment secret and preimage in `ChannelManager`, so (a) the user
@@ -378,37 +371,9 @@ where
 ///
 /// [`MIN_FINAL_CLTV_EXPIRY_DETLA`]: crate::ln::channelmanager::MIN_FINAL_CLTV_EXPIRY_DELTA
 pub fn create_invoice_from_channelmanager_with_description_hash<M: Deref, T: Deref, ES: Deref, NS: Deref, SP: Deref, F: Deref, R: Deref, MR: Deref, L: Deref>(
-       channelmanager: &ChannelManager<M, T, ES, NS, SP, F, R, MR, L>, node_signer: NS, logger: L,
-       amt_msat: Option<u64>, description_hash: Sha256,
-       invoice_expiry_delta_secs: u32, min_final_cltv_expiry_delta: Option<u16>,
-) -> Result<Bolt11Invoice, SignOrCreationError<()>>
-where
-       M::Target: chain::Watch<<SP::Target as SignerProvider>::EcdsaSigner>,
-       T::Target: BroadcasterInterface,
-       ES::Target: EntropySource,
-       NS::Target: NodeSigner,
-       SP::Target: SignerProvider,
-       F::Target: FeeEstimator,
-       R::Target: Router,
-       MR::Target: MessageRouter,
-       L::Target: Logger,
-{
-       use std::time::SystemTime;
-       let duration_since_epoch = SystemTime::now()
-               .duration_since(SystemTime::UNIX_EPOCH)
-               .expect("for the foreseeable future this shouldn't happen");
-
-       _create_invoice_from_channelmanager_and_duration_since_epoch(
-               channelmanager, node_signer, logger, amt_msat,
-               Bolt11InvoiceDescription::Hash(description_hash),
-               duration_since_epoch, invoice_expiry_delta_secs, min_final_cltv_expiry_delta,
-       )
-}
-
-fn _create_invoice_from_channelmanager_and_duration_since_epoch<M: Deref, T: Deref, ES: Deref, NS: Deref, SP: Deref, F: Deref, R: Deref, MR: Deref, L: Deref>(
-       channelmanager: &ChannelManager<M, T, ES, NS, SP, F, R, MR, L>, node_signer: NS, logger: L,
-       amt_msat: Option<u64>, description: Bolt11InvoiceDescription,
-       duration_since_epoch: Duration, invoice_expiry_delta_secs: u32, min_final_cltv_expiry_delta: Option<u16>,
+       channelmanager: &ChannelManager<M, T, ES, NS, SP, F, R, MR, L>, amt_msat: Option<u64>,
+       description_hash: Sha256, invoice_expiry_delta_secs: u32,
+       min_final_cltv_expiry_delta: Option<u16>,
 ) -> Result<Bolt11Invoice, SignOrCreationError<()>>
 where
        M::Target: chain::Watch<<SP::Target as SignerProvider>::EcdsaSigner>,
@@ -421,21 +386,16 @@ where
        MR::Target: MessageRouter,
        L::Target: Logger,
 {
-       if min_final_cltv_expiry_delta.is_some() && min_final_cltv_expiry_delta.unwrap().saturating_add(3) < MIN_FINAL_CLTV_EXPIRY_DELTA {
-               return Err(SignOrCreationError::CreationError(CreationError::MinFinalCltvExpiryDeltaTooShort));
-       }
-
-       // `create_inbound_payment` only returns an error if the amount is greater than the total bitcoin
-       // supply.
-       let (payment_hash, payment_secret) = channelmanager
-               .create_inbound_payment(amt_msat, invoice_expiry_delta_secs, min_final_cltv_expiry_delta)
-               .map_err(|()| SignOrCreationError::CreationError(CreationError::InvalidAmount))?;
-       _create_invoice_from_channelmanager_and_duration_since_epoch_with_payment_hash(
-               channelmanager, node_signer, logger, amt_msat, description, duration_since_epoch,
-               invoice_expiry_delta_secs, payment_hash, payment_secret, min_final_cltv_expiry_delta)
+       let params = Bolt11InvoiceParameters {
+               amount_msats: amt_msat,
+               description: Bolt11InvoiceDescription::Hash(description_hash),
+               invoice_expiry_delta_secs: Some(invoice_expiry_delta_secs),
+               min_final_cltv_expiry_delta,
+               payment_hash: None,
+       };
+       channelmanager.create_bolt11_invoice(params)
 }
 
-#[cfg(feature = "std")]
 /// See [`create_invoice_from_channelmanager`].
 ///
 /// This version allows for providing custom [`PaymentHash`] and description hash for the invoice.
@@ -444,9 +404,9 @@ where
 /// the payment hash is also involved outside the scope of lightning and want to set the
 /// description hash.
 pub fn create_invoice_from_channelmanager_with_description_hash_and_payment_hash<M: Deref, T: Deref, ES: Deref, NS: Deref, SP: Deref, F: Deref, R: Deref, MR: Deref, L: Deref>(
-       channelmanager: &ChannelManager<M, T, ES, NS, SP, F, R, MR, L>, node_signer: NS, logger: L,
-       amt_msat: Option<u64>, description_hash: Sha256,
-       invoice_expiry_delta_secs: u32, payment_hash: PaymentHash, min_final_cltv_expiry_delta: Option<u16>,
+       channelmanager: &ChannelManager<M, T, ES, NS, SP, F, R, MR, L>, amt_msat: Option<u64>,
+       description_hash: Sha256, invoice_expiry_delta_secs: u32, payment_hash: PaymentHash,
+       min_final_cltv_expiry_delta: Option<u16>,
 ) -> Result<Bolt11Invoice, SignOrCreationError<()>>
 where
        M::Target: chain::Watch<<SP::Target as SignerProvider>::EcdsaSigner>,
@@ -459,33 +419,25 @@ where
        MR::Target: MessageRouter,
        L::Target: Logger,
 {
-       use std::time::SystemTime;
-       let duration_since_epoch = SystemTime::now()
-               .duration_since(SystemTime::UNIX_EPOCH)
-               .expect("for the foreseeable future this shouldn't happen");
-
-       let payment_secret = channelmanager
-               .create_inbound_payment_for_hash(payment_hash, amt_msat, invoice_expiry_delta_secs,
-                       min_final_cltv_expiry_delta)
-               .map_err(|()| SignOrCreationError::CreationError(CreationError::InvalidAmount))?;
-       _create_invoice_from_channelmanager_and_duration_since_epoch_with_payment_hash(
-               channelmanager, node_signer, logger, amt_msat,
-               Bolt11InvoiceDescription::Hash(description_hash),
-               duration_since_epoch, invoice_expiry_delta_secs, payment_hash, payment_secret,
+       let params = Bolt11InvoiceParameters {
+               amount_msats: amt_msat,
+               description: Bolt11InvoiceDescription::Hash(description_hash),
+               invoice_expiry_delta_secs: Some(invoice_expiry_delta_secs),
                min_final_cltv_expiry_delta,
-       )
+               payment_hash: Some(payment_hash),
+       };
+       channelmanager.create_bolt11_invoice(params)
 }
 
-#[cfg(feature = "std")]
 /// See [`create_invoice_from_channelmanager`].
 ///
 /// This version allows for providing a custom [`PaymentHash`] for the invoice.
 /// This may be useful if you're building an on-chain swap or involving another protocol where
 /// the payment hash is also involved outside the scope of lightning.
 pub fn create_invoice_from_channelmanager_with_payment_hash<M: Deref, T: Deref, ES: Deref, NS: Deref, SP: Deref, F: Deref, R: Deref, MR: Deref, L: Deref>(
-       channelmanager: &ChannelManager<M, T, ES, NS, SP, F, R, MR, L>, node_signer: NS, logger: L,
-       amt_msat: Option<u64>, description: String, invoice_expiry_delta_secs: u32,
-       payment_hash: PaymentHash, min_final_cltv_expiry_delta: Option<u16>,
+       channelmanager: &ChannelManager<M, T, ES, NS, SP, F, R, MR, L>, amt_msat: Option<u64>,
+       description: String, invoice_expiry_delta_secs: u32, payment_hash: PaymentHash,
+       min_final_cltv_expiry_delta: Option<u16>,
 ) -> Result<Bolt11Invoice, SignOrCreationError<()>>
 where
        M::Target: chain::Watch<<SP::Target as SignerProvider>::EcdsaSigner>,
@@ -498,91 +450,15 @@ where
        MR::Target: MessageRouter,
        L::Target: Logger,
 {
-       use std::time::SystemTime;
-       let duration_since_epoch = SystemTime::now()
-               .duration_since(SystemTime::UNIX_EPOCH)
-               .expect("for the foreseeable future this shouldn't happen");
-
-       let payment_secret = channelmanager
-               .create_inbound_payment_for_hash(payment_hash, amt_msat, invoice_expiry_delta_secs,
-                       min_final_cltv_expiry_delta)
-               .map_err(|()| SignOrCreationError::CreationError(CreationError::InvalidAmount))?;
-       _create_invoice_from_channelmanager_and_duration_since_epoch_with_payment_hash(
-               channelmanager, node_signer, logger, amt_msat,
-               Bolt11InvoiceDescription::Direct(
-                       Description::new(description).map_err(SignOrCreationError::CreationError)?,
-               ),
-               duration_since_epoch, invoice_expiry_delta_secs, payment_hash, payment_secret,
+       let description = Description::new(description).map_err(SignOrCreationError::CreationError)?;
+       let params = Bolt11InvoiceParameters {
+               amount_msats: amt_msat,
+               description: Bolt11InvoiceDescription::Direct(description),
+               invoice_expiry_delta_secs: Some(invoice_expiry_delta_secs),
                min_final_cltv_expiry_delta,
-       )
-}
-
-fn _create_invoice_from_channelmanager_and_duration_since_epoch_with_payment_hash<M: Deref, T: Deref, ES: Deref, NS: Deref, SP: Deref, F: Deref, R: Deref, MR: Deref, L: Deref>(
-       channelmanager: &ChannelManager<M, T, ES, NS, SP, F, R, MR, L>, node_signer: NS, logger: L,
-       amt_msat: Option<u64>, description: Bolt11InvoiceDescription,
-       duration_since_epoch: Duration, invoice_expiry_delta_secs: u32, payment_hash: PaymentHash,
-       payment_secret: PaymentSecret, min_final_cltv_expiry_delta: Option<u16>,
-) -> Result<Bolt11Invoice, SignOrCreationError<()>>
-where
-       M::Target: chain::Watch<<SP::Target as SignerProvider>::EcdsaSigner>,
-       T::Target: BroadcasterInterface,
-       ES::Target: EntropySource,
-       NS::Target: NodeSigner,
-       SP::Target: SignerProvider,
-       F::Target: FeeEstimator,
-       R::Target: Router,
-       MR::Target: MessageRouter,
-       L::Target: Logger,
-{
-       let our_node_pubkey = channelmanager.get_our_node_id();
-       let channels = channelmanager.list_channels();
-
-       let network = Network::from_chain_hash(channelmanager.chain_hash)
-               .map(Into::into)
-               .unwrap_or(Currency::Bitcoin);
-
-       if min_final_cltv_expiry_delta.is_some() && min_final_cltv_expiry_delta.unwrap().saturating_add(3) < MIN_FINAL_CLTV_EXPIRY_DELTA {
-               return Err(SignOrCreationError::CreationError(CreationError::MinFinalCltvExpiryDeltaTooShort));
-       }
-
-       log_trace!(logger, "Creating invoice with payment hash {}", &payment_hash);
-
-       let invoice = match description {
-               Bolt11InvoiceDescription::Direct(description) => {
-                       InvoiceBuilder::new(network).description(description.into_inner().0)
-               }
-               Bolt11InvoiceDescription::Hash(hash) => InvoiceBuilder::new(network).description_hash(hash.0),
+               payment_hash: Some(payment_hash),
        };
-
-       let mut invoice = invoice
-               .duration_since_epoch(duration_since_epoch)
-               .payee_pub_key(our_node_pubkey)
-               .payment_hash(Hash::from_slice(&payment_hash.0).unwrap())
-               .payment_secret(payment_secret)
-               .basic_mpp()
-               .min_final_cltv_expiry_delta(
-                       // Add a buffer of 3 to the delta if present, otherwise use LDK's minimum.
-                       min_final_cltv_expiry_delta.map(|x| x.saturating_add(3)).unwrap_or(MIN_FINAL_CLTV_EXPIRY_DELTA).into())
-               .expiry_time(Duration::from_secs(invoice_expiry_delta_secs.into()));
-       if let Some(amt) = amt_msat {
-               invoice = invoice.amount_milli_satoshis(amt);
-       }
-
-       let route_hints = sort_and_filter_channels(channels, amt_msat, &logger);
-       for hint in route_hints {
-               invoice = invoice.private_route(hint);
-       }
-
-       let raw_invoice = match invoice.build_raw() {
-               Ok(inv) => inv,
-               Err(e) => return Err(SignOrCreationError::CreationError(e))
-       };
-       let signature = node_signer.sign_invoice(&raw_invoice, Recipient::Node);
-       let signed_raw_invoice = raw_invoice.sign(|_| signature);
-       match signed_raw_invoice {
-               Ok(inv) => Ok(Bolt11Invoice::from_signed(inv).unwrap()),
-               Err(e) => Err(SignOrCreationError::SignError(e))
-       }
+       channelmanager.create_bolt11_invoice(params)
 }
 
 /// Sorts and filters the `channels` for an invoice, and returns the corresponding `RouteHint`s to include
@@ -604,7 +480,7 @@ where
 /// * Limited to a total of 3 channels.
 /// * Sorted by lowest inbound capacity if an online channel with the minimum amount requested exists,
 ///   otherwise sort by highest inbound capacity to give the payment the best chance of succeeding.
-fn sort_and_filter_channels<L: Deref>(
+pub(super) fn sort_and_filter_channels<L: Deref>(
        channels: Vec<ChannelDetails>,
        min_inbound_capacity_msat: Option<u64>,
        logger: &L,
@@ -878,8 +754,7 @@ mod test {
                create_unannounced_chan_between_nodes_with_value(&nodes, 0, 1, 100000, 10001);
                let non_default_invoice_expiry_secs = 4200;
                let invoice = create_invoice_from_channelmanager(
-                       nodes[1].node, nodes[1].keys_manager, nodes[1].logger,
-                       Some(10_000), "test".to_string(), non_default_invoice_expiry_secs, None,
+                       nodes[1].node, Some(10_000), "test".to_string(), non_default_invoice_expiry_secs, None,
                ).unwrap();
                assert_eq!(invoice.amount_milli_satoshis(), Some(10_000));
                // If no `min_final_cltv_expiry_delta` is specified, then it should be `MIN_FINAL_CLTV_EXPIRY_DELTA`.
@@ -929,8 +804,7 @@ mod test {
                let custom_min_final_cltv_expiry_delta = Some(50);
 
                let invoice = create_invoice_from_channelmanager(
-                       nodes[1].node, nodes[1].keys_manager, nodes[1].logger,
-                       Some(10_000), "".into(), 3600,
+                       nodes[1].node, Some(10_000), "".into(), 3600,
                        if with_custom_delta { custom_min_final_cltv_expiry_delta } else { None },
                ).unwrap();
                assert_eq!(invoice.min_final_cltv_expiry_delta(), if with_custom_delta {
@@ -952,8 +826,7 @@ mod test {
                let custom_min_final_cltv_expiry_delta = Some(21);
 
                let invoice = create_invoice_from_channelmanager(
-                       nodes[1].node, nodes[1].keys_manager, nodes[1].logger,
-                       Some(10_000), "".into(), 3600, custom_min_final_cltv_expiry_delta,
+                       nodes[1].node, Some(10_000), "".into(), 3600, custom_min_final_cltv_expiry_delta,
                ).unwrap();
                assert_eq!(invoice.min_final_cltv_expiry_delta(), MIN_FINAL_CLTV_EXPIRY_DELTA as u64);
        }
@@ -966,8 +839,7 @@ mod test {
                let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
                let description_hash = Sha256(Hash::hash("Testing description_hash".as_bytes()));
                let invoice = create_invoice_from_channelmanager_with_description_hash(
-                       nodes[1].node, nodes[1].keys_manager, nodes[1].logger,
-                       Some(10_000), description_hash, 3600, None,
+                       nodes[1].node, Some(10_000), description_hash, 3600, None,
                ).unwrap();
                assert_eq!(invoice.amount_milli_satoshis(), Some(10_000));
                assert_eq!(invoice.min_final_cltv_expiry_delta(), MIN_FINAL_CLTV_EXPIRY_DELTA as u64);
@@ -982,8 +854,7 @@ mod test {
                let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
                let payment_hash = PaymentHash([0; 32]);
                let invoice = create_invoice_from_channelmanager_with_payment_hash(
-                       nodes[1].node, nodes[1].keys_manager, nodes[1].logger,
-                       Some(10_000), "test".to_string(), 3600, payment_hash, None,
+                       nodes[1].node, Some(10_000), "test".to_string(), 3600, payment_hash, None,
                ).unwrap();
                assert_eq!(invoice.amount_milli_satoshis(), Some(10_000));
                assert_eq!(invoice.min_final_cltv_expiry_delta(), MIN_FINAL_CLTV_EXPIRY_DELTA as u64);
@@ -1273,8 +1144,7 @@ mod test {
                mut chan_ids_to_match: HashSet<u64>
        ) {
                let invoice = create_invoice_from_channelmanager(
-                       invoice_node.node, invoice_node.keys_manager, invoice_node.logger,
-                       invoice_amt, "test".to_string(), 3600, None,
+                       invoice_node.node, invoice_amt, "test".to_string(), 3600, None,
                ).unwrap();
                let hints = invoice.private_routes();
 
@@ -1911,8 +1781,8 @@ mod test {
                let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
                let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
                let result = create_invoice_from_channelmanager(
-                       nodes[1].node, nodes[1].keys_manager, nodes[1].logger,
-                       Some(10_000), "Some description".into(), 3600, Some(MIN_FINAL_CLTV_EXPIRY_DELTA - 4),
+                       nodes[1].node, Some(10_000), "Some description".into(), 3600,
+                       Some(MIN_FINAL_CLTV_EXPIRY_DELTA - 4),
                );
                match result {
                        Err(SignOrCreationError::CreationError(CreationError::MinFinalCltvExpiryDeltaTooShort)) => {},