Merge pull request #2828 from TheBlueMatt/2024-01-crypto-module
[rust-lightning] / lightning-invoice / src / utils.rs
index b3b7c2b91e8b2702ed5ad693cc8971143b625292..5e8b72467e5da655cd9f77876ac692b948498833 100644 (file)
@@ -1,10 +1,10 @@
 //! Convenient utilities to create an invoice.
 
-use crate::{CreationError, Currency, Invoice, InvoiceBuilder, SignOrCreationError};
+use crate::{Bolt11Invoice, CreationError, Currency, InvoiceBuilder, SignOrCreationError};
 
-use crate::{prelude::*, Description, InvoiceDescription, Sha256};
+use crate::{prelude::*, Description, Bolt11InvoiceDescription, Sha256};
 use bech32::ToBase32;
-use bitcoin_hashes::Hash;
+use bitcoin::hashes::Hash;
 use lightning::chain;
 use lightning::chain::chaininterface::{BroadcasterInterface, FeeEstimator};
 use lightning::sign::{Recipient, NodeSigner, SignerProvider, EntropySource};
@@ -14,10 +14,11 @@ use lightning::ln::channelmanager::{PhantomRouteHints, MIN_CLTV_EXPIRY_DELTA};
 use lightning::ln::inbound_payment::{create, create_from_hash, ExpandedKey};
 use lightning::routing::gossip::RoutingFees;
 use lightning::routing::router::{RouteHint, RouteHintHop, Router};
-use lightning::util::logger::Logger;
+use lightning::util::logger::{Logger, Record};
 use secp256k1::PublicKey;
 use core::ops::Deref;
 use core::time::Duration;
+use core::iter::Iterator;
 
 /// Utility to create an invoice that can be paid to one of multiple nodes, or a "phantom invoice."
 /// See [`PhantomKeysManager`] for more information on phantom node payments.
@@ -63,14 +64,14 @@ pub fn create_phantom_invoice<ES: Deref, NS: Deref, L: Deref>(
        amt_msat: Option<u64>, payment_hash: Option<PaymentHash>, description: String,
        invoice_expiry_delta_secs: u32, phantom_route_hints: Vec<PhantomRouteHints>, entropy_source: ES,
        node_signer: NS, logger: L, network: Currency, min_final_cltv_expiry_delta: Option<u16>, duration_since_epoch: Duration,
-) -> Result<Invoice, SignOrCreationError<()>>
+) -> Result<Bolt11Invoice, SignOrCreationError<()>>
 where
        ES::Target: EntropySource,
        NS::Target: NodeSigner,
        L::Target: Logger,
 {
        let description = Description::new(description).map_err(SignOrCreationError::CreationError)?;
-       let description = InvoiceDescription::Direct(&description,);
+       let description = Bolt11InvoiceDescription::Direct(&description,);
        _create_phantom_invoice::<ES, NS, L>(
                amt_msat, payment_hash, description, invoice_expiry_delta_secs, phantom_route_hints,
                entropy_source, node_signer, logger, network, min_final_cltv_expiry_delta, duration_since_epoch,
@@ -119,24 +120,26 @@ pub fn create_phantom_invoice_with_description_hash<ES: Deref, NS: Deref, L: Der
        amt_msat: Option<u64>, payment_hash: Option<PaymentHash>, invoice_expiry_delta_secs: u32,
        description_hash: Sha256, phantom_route_hints: Vec<PhantomRouteHints>, entropy_source: ES,
        node_signer: NS, logger: L, network: Currency, min_final_cltv_expiry_delta: Option<u16>, duration_since_epoch: Duration,
-) -> Result<Invoice, SignOrCreationError<()>>
+) -> Result<Bolt11Invoice, SignOrCreationError<()>>
 where
        ES::Target: EntropySource,
        NS::Target: NodeSigner,
        L::Target: Logger,
 {
        _create_phantom_invoice::<ES, NS, L>(
-               amt_msat, payment_hash, InvoiceDescription::Hash(&description_hash),
+               amt_msat, payment_hash, Bolt11InvoiceDescription::Hash(&description_hash),
                invoice_expiry_delta_secs, phantom_route_hints, entropy_source, node_signer, logger, network,
                min_final_cltv_expiry_delta, duration_since_epoch,
        )
 }
 
+const MAX_CHANNEL_HINTS: usize = 3;
+
 fn _create_phantom_invoice<ES: Deref, NS: Deref, L: Deref>(
-       amt_msat: Option<u64>, payment_hash: Option<PaymentHash>, description: InvoiceDescription,
+       amt_msat: Option<u64>, payment_hash: Option<PaymentHash>, description: Bolt11InvoiceDescription,
        invoice_expiry_delta_secs: u32, phantom_route_hints: Vec<PhantomRouteHints>, entropy_source: ES,
        node_signer: NS, logger: L, network: Currency, min_final_cltv_expiry_delta: Option<u16>, duration_since_epoch: Duration,
-) -> Result<Invoice, SignOrCreationError<()>>
+) -> Result<Bolt11Invoice, SignOrCreationError<()>>
 where
        ES::Target: EntropySource,
        NS::Target: NodeSigner,
@@ -154,10 +157,10 @@ where
        }
 
        let invoice = match description {
-               InvoiceDescription::Direct(description) => {
-                       InvoiceBuilder::new(network).description(description.0.clone())
+               Bolt11InvoiceDescription::Direct(description) => {
+                       InvoiceBuilder::new(network).description(description.0.0.clone())
                }
-               InvoiceDescription::Hash(hash) => InvoiceBuilder::new(network).description_hash(hash.0),
+               Bolt11InvoiceDescription::Hash(hash) => InvoiceBuilder::new(network).description_hash(hash.0),
        };
 
        // If we ever see performance here being too slow then we should probably take this ExpandedKey as a parameter instead.
@@ -188,7 +191,7 @@ where
        };
 
        log_trace!(logger, "Creating phantom invoice from {} participating nodes with payment hash {}",
-               phantom_route_hints.len(), log_bytes!(payment_hash.0));
+               phantom_route_hints.len(), &payment_hash);
 
        let mut invoice = invoice
                .duration_since_epoch(duration_since_epoch)
@@ -202,7 +205,8 @@ where
                invoice = invoice.amount_milli_satoshis(amt);
        }
 
-       for route_hint in select_phantom_hints(amt_msat, phantom_route_hints, logger) {
+
+       for route_hint in select_phantom_hints(amt_msat, phantom_route_hints, logger).take(MAX_CHANNEL_HINTS) {
                invoice = invoice.private_route(route_hint);
        }
 
@@ -215,7 +219,7 @@ where
        let data_without_signature = raw_invoice.data.to_base32();
        let signed_raw_invoice = raw_invoice.sign(|_| node_signer.sign_invoice(hrp_bytes, &data_without_signature, Recipient::PhantomNode));
        match signed_raw_invoice {
-               Ok(inv) => Ok(Invoice::from_signed(inv).unwrap()),
+               Ok(inv) => Ok(Bolt11Invoice::from_signed(inv).unwrap()),
                Err(e) => Err(SignOrCreationError::SignError(e))
        }
 }
@@ -229,36 +233,48 @@ where
 ///
 /// [`PhantomKeysManager`]: lightning::sign::PhantomKeysManager
 fn select_phantom_hints<L: Deref>(amt_msat: Option<u64>, phantom_route_hints: Vec<PhantomRouteHints>,
-       logger: L) -> Vec<RouteHint>
+       logger: L) -> impl Iterator<Item = RouteHint>
 where
        L::Target: Logger,
 {
-       let mut phantom_hints: Vec<Vec<RouteHint>> = Vec::new();
+       let mut phantom_hints: Vec<_> = Vec::new();
 
        for PhantomRouteHints { channels, phantom_scid, real_node_pubkey } in phantom_route_hints {
                log_trace!(logger, "Generating phantom route hints for node {}",
                        log_pubkey!(real_node_pubkey));
-               let mut route_hints = sort_and_filter_channels(channels, amt_msat, &logger);
+               let route_hints = sort_and_filter_channels(channels, amt_msat, &logger);
 
                // If we have any public channel, the route hints from `sort_and_filter_channels` will be
                // empty. In that case we create a RouteHint on which we will push a single hop with the
                // phantom route into the invoice, and let the sender find the path to the `real_node_pubkey`
                // node by looking at our public channels.
-               if route_hints.is_empty() {
-                       route_hints.push(RouteHint(vec![]))
-               }
-               for route_hint in &mut route_hints {
-                       route_hint.0.push(RouteHintHop {
-                               src_node_id: real_node_pubkey,
-                               short_channel_id: phantom_scid,
-                               fees: RoutingFees {
-                                       base_msat: 0,
-                                       proportional_millionths: 0,
-                               },
-                               cltv_expiry_delta: MIN_CLTV_EXPIRY_DELTA,
-                               htlc_minimum_msat: None,
-                               htlc_maximum_msat: None,});
-               }
+               let empty_route_hints = route_hints.len() == 0;
+               let mut have_pushed_empty = false;
+               let route_hints = route_hints
+                       .chain(core::iter::from_fn(move || {
+                               if empty_route_hints && !have_pushed_empty {
+                                       // set flag of having handled the empty route_hints and ensure empty vector
+                                       // returned only once
+                                       have_pushed_empty = true;
+                                       Some(RouteHint(Vec::new()))
+                               } else {
+                                       None
+                               }
+                       }))
+                       .map(move |mut hint| {
+                               hint.0.push(RouteHintHop {
+                                       src_node_id: real_node_pubkey,
+                                       short_channel_id: phantom_scid,
+                                       fees: RoutingFees {
+                                               base_msat: 0,
+                                               proportional_millionths: 0,
+                                       },
+                                       cltv_expiry_delta: MIN_CLTV_EXPIRY_DELTA,
+                                       htlc_minimum_msat: None,
+                                       htlc_maximum_msat: None,
+                               });
+                               hint
+                       });
 
                phantom_hints.push(route_hints);
        }
@@ -267,29 +283,34 @@ where
        // the hints across our real nodes we add one hint from each in turn until no node has any hints
        // left (if one node has more hints than any other, these will accumulate at the end of the
        // vector).
-       let mut invoice_hints: Vec<RouteHint> = Vec::new();
-       let mut hint_idx = 0;
+       rotate_through_iterators(phantom_hints)
+}
 
-       loop {
-               let mut remaining_hints = false;
+/// Draw items iteratively from multiple iterators.  The items are retrieved by index and
+/// rotates through the iterators - first the zero index then the first index then second index, etc.
+fn rotate_through_iterators<T, I: Iterator<Item = T>>(mut vecs: Vec<I>) -> impl Iterator<Item = T> {
+       let mut iterations = 0;
 
-               for hints in phantom_hints.iter() {
-                       if invoice_hints.len() == 3 {
-                               return invoice_hints
+       core::iter::from_fn(move || {
+               let mut exhausted_iterators = 0;
+               loop {
+                       if vecs.is_empty() {
+                               return None;
                        }
-
-                       if hint_idx < hints.len() {
-                               invoice_hints.push(hints[hint_idx].clone());
-                               remaining_hints = true
+                       let next_idx = iterations % vecs.len();
+                       iterations += 1;
+                       if let Some(item) = vecs[next_idx].next() {
+                               return Some(item);
+                       }
+                       // exhausted_vectors increase when the "next_idx" vector is exhausted
+                       exhausted_iterators += 1;
+                       // The check for exhausted iterators gets reset to 0 after each yield of `Some()`
+                       // The loop will return None when all of the nested iterators are exhausted
+                       if exhausted_iterators == vecs.len() {
+                               return None;
                        }
                }
-
-               if !remaining_hints {
-                       return invoice_hints
-               }
-
-               hint_idx +=1;
-       }
+       })
 }
 
 #[cfg(feature = "std")]
@@ -312,9 +333,9 @@ pub fn create_invoice_from_channelmanager<M: Deref, T: Deref, ES: Deref, NS: Der
        channelmanager: &ChannelManager<M, T, ES, NS, SP, F, R, L>, node_signer: NS, logger: L,
        network: Currency, amt_msat: Option<u64>, description: String, invoice_expiry_delta_secs: u32,
        min_final_cltv_expiry_delta: Option<u16>,
-) -> Result<Invoice, SignOrCreationError<()>>
+) -> Result<Bolt11Invoice, SignOrCreationError<()>>
 where
-       M::Target: chain::Watch<<SP::Target as SignerProvider>::Signer>,
+       M::Target: chain::Watch<<SP::Target as SignerProvider>::EcdsaSigner>,
        T::Target: BroadcasterInterface,
        ES::Target: EntropySource,
        NS::Target: NodeSigner,
@@ -353,9 +374,9 @@ pub fn create_invoice_from_channelmanager_with_description_hash<M: Deref, T: Der
        channelmanager: &ChannelManager<M, T, ES, NS, SP, F, R, L>, node_signer: NS, logger: L,
        network: Currency, amt_msat: Option<u64>, description_hash: Sha256,
        invoice_expiry_delta_secs: u32, min_final_cltv_expiry_delta: Option<u16>,
-) -> Result<Invoice, SignOrCreationError<()>>
+) -> Result<Bolt11Invoice, SignOrCreationError<()>>
 where
-       M::Target: chain::Watch<<SP::Target as SignerProvider>::Signer>,
+       M::Target: chain::Watch<<SP::Target as SignerProvider>::EcdsaSigner>,
        T::Target: BroadcasterInterface,
        ES::Target: EntropySource,
        NS::Target: NodeSigner,
@@ -383,9 +404,9 @@ pub fn create_invoice_from_channelmanager_with_description_hash_and_duration_sin
        channelmanager: &ChannelManager<M, T, ES, NS, SP, F, R, L>, node_signer: NS, logger: L,
        network: Currency, amt_msat: Option<u64>, description_hash: Sha256,
        duration_since_epoch: Duration, invoice_expiry_delta_secs: u32, min_final_cltv_expiry_delta: Option<u16>,
-) -> Result<Invoice, SignOrCreationError<()>>
+) -> Result<Bolt11Invoice, SignOrCreationError<()>>
                where
-                       M::Target: chain::Watch<<SP::Target as SignerProvider>::Signer>,
+                       M::Target: chain::Watch<<SP::Target as SignerProvider>::EcdsaSigner>,
                        T::Target: BroadcasterInterface,
                        ES::Target: EntropySource,
                        NS::Target: NodeSigner,
@@ -396,7 +417,7 @@ pub fn create_invoice_from_channelmanager_with_description_hash_and_duration_sin
 {
        _create_invoice_from_channelmanager_and_duration_since_epoch(
                channelmanager, node_signer, logger, network, amt_msat,
-               InvoiceDescription::Hash(&description_hash),
+               Bolt11InvoiceDescription::Hash(&description_hash),
                duration_since_epoch, invoice_expiry_delta_secs, min_final_cltv_expiry_delta,
        )
 }
@@ -408,9 +429,9 @@ pub fn create_invoice_from_channelmanager_and_duration_since_epoch<M: Deref, T:
        channelmanager: &ChannelManager<M, T, ES, NS, SP, F, R, L>, node_signer: NS, logger: L,
        network: Currency, amt_msat: Option<u64>, description: String, duration_since_epoch: Duration,
        invoice_expiry_delta_secs: u32, min_final_cltv_expiry_delta: Option<u16>,
-) -> Result<Invoice, SignOrCreationError<()>>
+) -> Result<Bolt11Invoice, SignOrCreationError<()>>
                where
-                       M::Target: chain::Watch<<SP::Target as SignerProvider>::Signer>,
+                       M::Target: chain::Watch<<SP::Target as SignerProvider>::EcdsaSigner>,
                        T::Target: BroadcasterInterface,
                        ES::Target: EntropySource,
                        NS::Target: NodeSigner,
@@ -421,7 +442,7 @@ pub fn create_invoice_from_channelmanager_and_duration_since_epoch<M: Deref, T:
 {
        _create_invoice_from_channelmanager_and_duration_since_epoch(
                channelmanager, node_signer, logger, network, amt_msat,
-               InvoiceDescription::Direct(
+               Bolt11InvoiceDescription::Direct(
                        &Description::new(description).map_err(SignOrCreationError::CreationError)?,
                ),
                duration_since_epoch, invoice_expiry_delta_secs, min_final_cltv_expiry_delta,
@@ -430,11 +451,11 @@ pub fn create_invoice_from_channelmanager_and_duration_since_epoch<M: Deref, T:
 
 fn _create_invoice_from_channelmanager_and_duration_since_epoch<M: Deref, T: Deref, ES: Deref, NS: Deref, SP: Deref, F: Deref, R: Deref, L: Deref>(
        channelmanager: &ChannelManager<M, T, ES, NS, SP, F, R, L>, node_signer: NS, logger: L,
-       network: Currency, amt_msat: Option<u64>, description: InvoiceDescription,
+       network: Currency, amt_msat: Option<u64>, description: Bolt11InvoiceDescription,
        duration_since_epoch: Duration, invoice_expiry_delta_secs: u32, min_final_cltv_expiry_delta: Option<u16>,
-) -> Result<Invoice, SignOrCreationError<()>>
+) -> Result<Bolt11Invoice, SignOrCreationError<()>>
                where
-                       M::Target: chain::Watch<<SP::Target as SignerProvider>::Signer>,
+                       M::Target: chain::Watch<<SP::Target as SignerProvider>::EcdsaSigner>,
                        T::Target: BroadcasterInterface,
                        ES::Target: EntropySource,
                        NS::Target: NodeSigner,
@@ -465,9 +486,9 @@ pub fn create_invoice_from_channelmanager_and_duration_since_epoch_with_payment_
        channelmanager: &ChannelManager<M, T, ES, NS, SP, F, R, L>, node_signer: NS, logger: L,
        network: Currency, amt_msat: Option<u64>, description: String, duration_since_epoch: Duration,
        invoice_expiry_delta_secs: u32, payment_hash: PaymentHash, min_final_cltv_expiry_delta: Option<u16>,
-) -> Result<Invoice, SignOrCreationError<()>>
+) -> Result<Bolt11Invoice, SignOrCreationError<()>>
        where
-               M::Target: chain::Watch<<SP::Target as SignerProvider>::Signer>,
+               M::Target: chain::Watch<<SP::Target as SignerProvider>::EcdsaSigner>,
                T::Target: BroadcasterInterface,
                ES::Target: EntropySource,
                NS::Target: NodeSigner,
@@ -482,7 +503,7 @@ pub fn create_invoice_from_channelmanager_and_duration_since_epoch_with_payment_
                .map_err(|()| SignOrCreationError::CreationError(CreationError::InvalidAmount))?;
        _create_invoice_from_channelmanager_and_duration_since_epoch_with_payment_hash(
                channelmanager, node_signer, logger, network, amt_msat,
-               InvoiceDescription::Direct(
+               Bolt11InvoiceDescription::Direct(
                        &Description::new(description).map_err(SignOrCreationError::CreationError)?,
                ),
                duration_since_epoch, invoice_expiry_delta_secs, payment_hash, payment_secret,
@@ -492,12 +513,12 @@ pub fn create_invoice_from_channelmanager_and_duration_since_epoch_with_payment_
 
 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, L: Deref>(
        channelmanager: &ChannelManager<M, T, ES, NS, SP, F, R, L>, node_signer: NS, logger: L,
-       network: Currency, amt_msat: Option<u64>, description: InvoiceDescription, duration_since_epoch: Duration,
-       invoice_expiry_delta_secs: u32, payment_hash: PaymentHash, payment_secret: PaymentSecret,
-       min_final_cltv_expiry_delta: Option<u16>,
-) -> Result<Invoice, SignOrCreationError<()>>
+       network: Currency, 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>::Signer>,
+               M::Target: chain::Watch<<SP::Target as SignerProvider>::EcdsaSigner>,
                T::Target: BroadcasterInterface,
                ES::Target: EntropySource,
                NS::Target: NodeSigner,
@@ -513,13 +534,13 @@ fn _create_invoice_from_channelmanager_and_duration_since_epoch_with_payment_has
                return Err(SignOrCreationError::CreationError(CreationError::MinFinalCltvExpiryDeltaTooShort));
        }
 
-       log_trace!(logger, "Creating invoice with payment hash {}", log_bytes!(payment_hash.0));
+       log_trace!(logger, "Creating invoice with payment hash {}", &payment_hash);
 
        let invoice = match description {
-               InvoiceDescription::Direct(description) => {
-                       InvoiceBuilder::new(network).description(description.0.clone())
+               Bolt11InvoiceDescription::Direct(description) => {
+                       InvoiceBuilder::new(network).description(description.0.0.clone())
                }
-               InvoiceDescription::Hash(hash) => InvoiceBuilder::new(network).description_hash(hash.0),
+               Bolt11InvoiceDescription::Hash(hash) => InvoiceBuilder::new(network).description_hash(hash.0),
        };
 
        let mut invoice = invoice
@@ -550,7 +571,7 @@ fn _create_invoice_from_channelmanager_and_duration_since_epoch_with_payment_has
        let data_without_signature = raw_invoice.data.to_base32();
        let signed_raw_invoice = raw_invoice.sign(|_| node_signer.sign_invoice(hrp_bytes, &data_without_signature, Recipient::Node));
        match signed_raw_invoice {
-               Ok(inv) => Ok(Invoice::from_signed(inv).unwrap()),
+               Ok(inv) => Ok(Bolt11Invoice::from_signed(inv).unwrap()),
                Err(e) => Err(SignOrCreationError::SignError(e))
        }
 }
@@ -575,8 +596,13 @@ fn _create_invoice_from_channelmanager_and_duration_since_epoch_with_payment_has
 /// * 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>(
-       channels: Vec<ChannelDetails>, min_inbound_capacity_msat: Option<u64>, logger: &L
-) -> Vec<RouteHint> where L::Target: Logger {
+       channels: Vec<ChannelDetails>,
+       min_inbound_capacity_msat: Option<u64>,
+       logger: &L,
+) -> impl ExactSizeIterator<Item = RouteHint>
+where
+       L::Target: Logger,
+{
        let mut filtered_channels: HashMap<PublicKey, ChannelDetails> = HashMap::new();
        let min_inbound_capacity = min_inbound_capacity_msat.unwrap_or(0);
        let mut min_capacity_channel_exists = false;
@@ -584,10 +610,25 @@ fn sort_and_filter_channels<L: Deref>(
        let mut online_min_capacity_channel_exists = false;
        let mut has_pub_unconf_chan = false;
 
+       let route_hint_from_channel = |channel: ChannelDetails| {
+               let forwarding_info = channel.counterparty.forwarding_info.as_ref().unwrap();
+               RouteHint(vec![RouteHintHop {
+                       src_node_id: channel.counterparty.node_id,
+                       short_channel_id: channel.get_inbound_payment_scid().unwrap(),
+                       fees: RoutingFees {
+                               base_msat: forwarding_info.fee_base_msat,
+                               proportional_millionths: forwarding_info.fee_proportional_millionths,
+                       },
+                       cltv_expiry_delta: forwarding_info.cltv_expiry_delta,
+                       htlc_minimum_msat: channel.inbound_htlc_minimum_msat,
+                       htlc_maximum_msat: channel.inbound_htlc_maximum_msat,}])
+       };
+
        log_trace!(logger, "Considering {} channels for invoice route hints", channels.len());
        for channel in channels.into_iter().filter(|chan| chan.is_channel_ready) {
+               let logger = WithChannelDetails::from(logger, &channel);
                if channel.get_inbound_payment_scid().is_none() || channel.counterparty.forwarding_info.is_none() {
-                       log_trace!(logger, "Ignoring channel {} for invoice route hints", log_bytes!(channel.channel_id));
+                       log_trace!(logger, "Ignoring channel {} for invoice route hints", &channel.channel_id);
                        continue;
                }
 
@@ -601,8 +642,8 @@ fn sort_and_filter_channels<L: Deref>(
                                // If any public channel exists, return no hints and let the sender
                                // look at the public channels instead.
                                log_trace!(logger, "Not including channels in invoice route hints on account of public channel {}",
-                                       log_bytes!(channel.channel_id));
-                               return vec![]
+                                       &channel.channel_id);
+                               return vec![].into_iter().take(MAX_CHANNEL_HINTS).map(route_hint_from_channel);
                        }
                }
 
@@ -641,18 +682,18 @@ fn sort_and_filter_channels<L: Deref>(
                                        log_trace!(logger,
                                                "Preferring counterparty {} channel {} (SCID {:?}, {} msats) over {} (SCID {:?}, {} msats) for invoice route hints",
                                                log_pubkey!(channel.counterparty.node_id),
-                                               log_bytes!(channel.channel_id), channel.short_channel_id,
+                                               &channel.channel_id, channel.short_channel_id,
                                                channel.inbound_capacity_msat,
-                                               log_bytes!(entry.get().channel_id), entry.get().short_channel_id,
+                                               &entry.get().channel_id, entry.get().short_channel_id,
                                                current_max_capacity);
                                        entry.insert(channel);
                                } else {
                                        log_trace!(logger,
                                                "Preferring counterparty {} channel {} (SCID {:?}, {} msats) over {} (SCID {:?}, {} msats) for invoice route hints",
                                                log_pubkey!(channel.counterparty.node_id),
-                                               log_bytes!(entry.get().channel_id), entry.get().short_channel_id,
+                                               &entry.get().channel_id, entry.get().short_channel_id,
                                                current_max_capacity,
-                                               log_bytes!(channel.channel_id), channel.short_channel_id,
+                                               &channel.channel_id, channel.short_channel_id,
                                                channel.inbound_capacity_msat);
                                }
                        }
@@ -662,19 +703,6 @@ fn sort_and_filter_channels<L: Deref>(
                }
        }
 
-       let route_hint_from_channel = |channel: ChannelDetails| {
-               let forwarding_info = channel.counterparty.forwarding_info.as_ref().unwrap();
-               RouteHint(vec![RouteHintHop {
-                       src_node_id: channel.counterparty.node_id,
-                       short_channel_id: channel.get_inbound_payment_scid().unwrap(),
-                       fees: RoutingFees {
-                               base_msat: forwarding_info.fee_base_msat,
-                               proportional_millionths: forwarding_info.fee_proportional_millionths,
-                       },
-                       cltv_expiry_delta: forwarding_info.cltv_expiry_delta,
-                       htlc_minimum_msat: channel.inbound_htlc_minimum_msat,
-                       htlc_maximum_msat: channel.inbound_htlc_maximum_msat,}])
-       };
        // If all channels are private, prefer to return route hints which have a higher capacity than
        // the payment value and where we're currently connected to the channel counterparty.
        // Even if we cannot satisfy both goals, always ensure we include *some* hints, preferring
@@ -683,6 +711,7 @@ fn sort_and_filter_channels<L: Deref>(
                .into_iter()
                .map(|(_, channel)| channel)
                .filter(|channel| {
+                       let logger = WithChannelDetails::from(logger, &channel);
                        let has_enough_capacity = channel.inbound_capacity_msat >= min_inbound_capacity;
                        let include_channel = if has_pub_unconf_chan {
                                // If we have a public channel, but it doesn't have enough confirmations to (yet)
@@ -704,14 +733,14 @@ fn sort_and_filter_channels<L: Deref>(
 
                        if include_channel {
                                log_trace!(logger, "Including channel {} in invoice route hints",
-                                       log_bytes!(channel.channel_id));
+                                       &channel.channel_id);
                        } else if !has_enough_capacity {
                                log_trace!(logger, "Ignoring channel {} without enough capacity for invoice route hints",
-                                       log_bytes!(channel.channel_id));
+                                       &channel.channel_id);
                        } else {
                                debug_assert!(!channel.is_usable || (has_pub_unconf_chan && !channel.is_public));
                                log_trace!(logger, "Ignoring channel {} with disconnected peer",
-                                       log_bytes!(channel.channel_id));
+                                       &channel.channel_id);
                        }
 
                        include_channel
@@ -724,7 +753,8 @@ fn sort_and_filter_channels<L: Deref>(
                        } else {
                                b.inbound_capacity_msat.cmp(&a.inbound_capacity_msat)
                        }});
-               eligible_channels.into_iter().take(3).map(route_hint_from_channel).collect::<Vec<RouteHint>>()
+
+               eligible_channels.into_iter().take(MAX_CHANNEL_HINTS).map(route_hint_from_channel)
 }
 
 /// prefer_current_channel chooses a channel to use for route hints between a currently selected and candidate
@@ -762,23 +792,48 @@ fn prefer_current_channel(min_inbound_capacity_msat: Option<u64>, current_channe
        current_channel > candidate_channel
 }
 
+/// Adds relevant context to a [`Record`] before passing it to the wrapped [`Logger`].
+struct WithChannelDetails<'a, 'b, L: Deref> where L::Target: Logger {
+       /// The logger to delegate to after adding context to the record.
+       logger: &'a L,
+       /// The [`ChannelDetails`] for adding relevant context to the logged record.
+       details: &'b ChannelDetails
+}
+
+impl<'a, 'b, L: Deref> Logger for WithChannelDetails<'a, 'b, L> where L::Target: Logger {
+       fn log(&self, mut record: Record) {
+               record.peer_id = Some(self.details.counterparty.node_id);
+               record.channel_id = Some(self.details.channel_id);
+               self.logger.log(record)
+       }
+}
+
+impl<'a, 'b, L: Deref> WithChannelDetails<'a, 'b, L> where L::Target: Logger {
+       fn from(logger: &'a L, details: &'b ChannelDetails) -> Self {
+               Self { logger, details }
+       }
+}
+
 #[cfg(test)]
 mod test {
        use core::time::Duration;
-       use crate::{Currency, Description, InvoiceDescription, SignOrCreationError, CreationError};
-       use bitcoin_hashes::{Hash, sha256};
-       use bitcoin_hashes::sha256::Hash as Sha256;
+       use crate::{Currency, Description, Bolt11InvoiceDescription, SignOrCreationError, CreationError};
+       use bitcoin::hashes::{Hash, sha256};
+       use bitcoin::hashes::sha256::Hash as Sha256;
        use lightning::sign::PhantomKeysManager;
-       use lightning::events::{MessageSendEvent, MessageSendEventsProvider, Event};
-       use lightning::ln::{PaymentPreimage, PaymentHash};
+       use lightning::events::{MessageSendEvent, MessageSendEventsProvider};
+       use lightning::ln::PaymentHash;
+       #[cfg(feature = "std")]
+       use lightning::ln::PaymentPreimage;
        use lightning::ln::channelmanager::{PhantomRouteHints, MIN_FINAL_CLTV_EXPIRY_DELTA, PaymentId, RecipientOnionFields, Retry};
        use lightning::ln::functional_test_utils::*;
        use lightning::ln::msgs::ChannelMessageHandler;
        use lightning::routing::router::{PaymentParameters, RouteParameters};
        use lightning::util::test_utils;
        use lightning::util::config::UserConfig;
-       use crate::utils::create_invoice_from_channelmanager_and_duration_since_epoch;
+       use crate::utils::{create_invoice_from_channelmanager_and_duration_since_epoch, rotate_through_iterators};
        use std::collections::HashSet;
+       use lightning::util::string::UntrustedString;
 
        #[test]
        fn test_prefer_current_channel() {
@@ -823,7 +878,7 @@ mod test {
                assert_eq!(invoice.amount_pico_btc(), Some(100_000));
                // If no `min_final_cltv_expiry_delta` is specified, then it should be `MIN_FINAL_CLTV_EXPIRY_DELTA`.
                assert_eq!(invoice.min_final_cltv_expiry_delta(), MIN_FINAL_CLTV_EXPIRY_DELTA as u64);
-               assert_eq!(invoice.description(), InvoiceDescription::Direct(&Description("test".to_string())));
+               assert_eq!(invoice.description(), Bolt11InvoiceDescription::Direct(&Description(UntrustedString("test".to_string()))));
                assert_eq!(invoice.expiry_time(), Duration::from_secs(non_default_invoice_expiry_secs.into()));
 
                // Invoice SCIDs should always use inbound SCID aliases over the real channel ID, if one is
@@ -840,13 +895,10 @@ mod test {
                                invoice.min_final_cltv_expiry_delta() as u32)
                        .with_bolt11_features(invoice.features().unwrap().clone()).unwrap()
                        .with_route_hints(invoice.route_hints()).unwrap();
-               let route_params = RouteParameters {
-                       payment_params,
-                       final_value_msat: invoice.amount_milli_satoshis().unwrap(),
-               };
+               let route_params = RouteParameters::from_payment_params_and_value(
+                       payment_params, invoice.amount_milli_satoshis().unwrap());
                let payment_event = {
-                       let mut payment_hash = PaymentHash([0; 32]);
-                       payment_hash.0.copy_from_slice(&invoice.payment_hash().as_ref()[0..32]);
+                       let payment_hash = PaymentHash(invoice.payment_hash().to_byte_array());
                        nodes[0].node.send_payment(payment_hash,
                                RecipientOnionFields::secret_only(*invoice.payment_secret()),
                                PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap();
@@ -919,7 +971,7 @@ mod test {
                ).unwrap();
                assert_eq!(invoice.amount_pico_btc(), Some(100_000));
                assert_eq!(invoice.min_final_cltv_expiry_delta(), MIN_FINAL_CLTV_EXPIRY_DELTA as u64);
-               assert_eq!(invoice.description(), InvoiceDescription::Hash(&crate::Sha256(Sha256::hash("Testing description_hash".as_bytes()))));
+               assert_eq!(invoice.description(), Bolt11InvoiceDescription::Hash(&crate::Sha256(Sha256::hash("Testing description_hash".as_bytes()))));
        }
 
        #[test]
@@ -936,7 +988,7 @@ mod test {
                ).unwrap();
                assert_eq!(invoice.amount_pico_btc(), Some(100_000));
                assert_eq!(invoice.min_final_cltv_expiry_delta(), MIN_FINAL_CLTV_EXPIRY_DELTA as u64);
-               assert_eq!(invoice.description(), InvoiceDescription::Direct(&Description("test".to_string())));
+               assert_eq!(invoice.description(), Bolt11InvoiceDescription::Direct(&Description(UntrustedString("test".to_string()))));
                assert_eq!(invoice.payment_hash(), &sha256::Hash::from_slice(&payment_hash.0[..]).unwrap());
        }
 
@@ -1114,7 +1166,7 @@ mod test {
                // is never handled, the `channel.counterparty.forwarding_info` is never assigned.
                let mut private_chan_cfg = UserConfig::default();
                private_chan_cfg.channel_handshake_config.announced_channel = false;
-               let temporary_channel_id = nodes[2].node.create_channel(nodes[0].node.get_our_node_id(), 1_000_000, 500_000_000, 42, Some(private_chan_cfg)).unwrap();
+               let temporary_channel_id = nodes[2].node.create_channel(nodes[0].node.get_our_node_id(), 1_000_000, 500_000_000, 42, None, Some(private_chan_cfg)).unwrap();
                let open_channel = get_event_msg!(nodes[2], MessageSendEvent::SendOpenChannel, nodes[0].node.get_our_node_id());
                nodes[0].node.handle_open_channel(&nodes[2].node.get_our_node_id(), &open_channel);
                let accept_channel = get_event_msg!(nodes[0], MessageSendEvent::SendAcceptChannel, nodes[2].node.get_our_node_id());
@@ -1243,6 +1295,9 @@ mod test {
 
        #[cfg(feature = "std")]
        fn do_test_multi_node_receive(user_generated_pmt_hash: bool) {
+               use lightning::events::{Event, EventsProvider};
+               use core::cell::RefCell;
+
                let mut chanmon_cfgs = create_chanmon_cfgs(3);
                let seed_1 = [42u8; 32];
                let seed_2 = [43u8; 32];
@@ -1267,19 +1322,20 @@ mod test {
 
                let user_payment_preimage = PaymentPreimage([1; 32]);
                let payment_hash = if user_generated_pmt_hash {
-                       Some(PaymentHash(Sha256::hash(&user_payment_preimage.0[..]).into_inner()))
+                       Some(PaymentHash(Sha256::hash(&user_payment_preimage.0[..]).to_byte_array()))
                } else {
                        None
                };
+               let genesis_timestamp = bitcoin::blockdata::constants::genesis_block(bitcoin::Network::Testnet).header.time as u64;
                let non_default_invoice_expiry_secs = 4200;
 
                let invoice =
                        crate::utils::create_phantom_invoice::<&test_utils::TestKeysInterface, &test_utils::TestKeysInterface, &test_utils::TestLogger>(
                                Some(payment_amt), payment_hash, "test".to_string(), non_default_invoice_expiry_secs,
                                route_hints, nodes[1].keys_manager, nodes[1].keys_manager, nodes[1].logger,
-                               Currency::BitcoinTestnet, None, Duration::from_secs(1234567)
+                               Currency::BitcoinTestnet, None, Duration::from_secs(genesis_timestamp)
                        ).unwrap();
-               let (payment_hash, payment_secret) = (PaymentHash(invoice.payment_hash().into_inner()), *invoice.payment_secret());
+               let (payment_hash, payment_secret) = (PaymentHash(invoice.payment_hash().to_byte_array()), *invoice.payment_secret());
                let payment_preimage = if user_generated_pmt_hash {
                        user_payment_preimage
                } else {
@@ -1287,7 +1343,7 @@ mod test {
                };
 
                assert_eq!(invoice.min_final_cltv_expiry_delta(), MIN_FINAL_CLTV_EXPIRY_DELTA as u64);
-               assert_eq!(invoice.description(), InvoiceDescription::Direct(&Description("test".to_string())));
+               assert_eq!(invoice.description(), Bolt11InvoiceDescription::Direct(&Description(UntrustedString("test".to_string()))));
                assert_eq!(invoice.route_hints().len(), 2);
                assert_eq!(invoice.expiry_time(), Duration::from_secs(non_default_invoice_expiry_secs.into()));
                assert!(!invoice.features().unwrap().supports_basic_mpp());
@@ -1296,13 +1352,10 @@ mod test {
                                invoice.min_final_cltv_expiry_delta() as u32)
                        .with_bolt11_features(invoice.features().unwrap().clone()).unwrap()
                        .with_route_hints(invoice.route_hints()).unwrap();
-               let params = RouteParameters {
-                       payment_params,
-                       final_value_msat: invoice.amount_milli_satoshis().unwrap(),
-               };
+               let params = RouteParameters::from_payment_params_and_value(
+                       payment_params, invoice.amount_milli_satoshis().unwrap());
                let (payment_event, fwd_idx) = {
-                       let mut payment_hash = PaymentHash([0; 32]);
-                       payment_hash.0.copy_from_slice(&invoice.payment_hash().as_ref()[0..32]);
+                       let payment_hash = PaymentHash(invoice.payment_hash().to_byte_array());
                        nodes[0].node.send_payment(payment_hash,
                                RecipientOnionFields::secret_only(*invoice.payment_secret()),
                                PaymentId(payment_hash.0), params, Retry::Attempts(0)).unwrap();
@@ -1328,30 +1381,22 @@ mod test {
                // Note that we have to "forward pending HTLCs" twice before we see the PaymentClaimable as
                // this "emulates" the payment taking two hops, providing some privacy to make phantom node
                // payments "look real" by taking more time.
-               expect_pending_htlcs_forwardable_ignore!(nodes[fwd_idx]);
-               nodes[fwd_idx].node.process_pending_htlc_forwards();
-               expect_pending_htlcs_forwardable_ignore!(nodes[fwd_idx]);
-               nodes[fwd_idx].node.process_pending_htlc_forwards();
+               let other_events = RefCell::new(Vec::new());
+               let forward_event_handler = |event: Event| {
+                       if let Event::PendingHTLCsForwardable { .. } = event {
+                               nodes[fwd_idx].node.process_pending_htlc_forwards();
+                       } else {
+                               other_events.borrow_mut().push(event);
+                       }
+               };
+               nodes[fwd_idx].node.process_pending_events(&forward_event_handler);
+               nodes[fwd_idx].node.process_pending_events(&forward_event_handler);
 
                let payment_preimage_opt = if user_generated_pmt_hash { None } else { Some(payment_preimage) };
-               expect_payment_claimable!(&nodes[fwd_idx], payment_hash, payment_secret, payment_amt, payment_preimage_opt, invoice.recover_payee_pub_key());
+               assert_eq!(other_events.borrow().len(), 1);
+               check_payment_claimable(&other_events.borrow()[0], payment_hash, payment_secret, payment_amt, payment_preimage_opt, invoice.recover_payee_pub_key());
                do_claim_payment_along_route(&nodes[0], &[&vec!(&nodes[fwd_idx])[..]], false, payment_preimage);
-               let events = nodes[0].node.get_and_clear_pending_events();
-               assert_eq!(events.len(), 2);
-               match events[0] {
-                       Event::PaymentSent { payment_preimage: ref ev_preimage, payment_hash: ref ev_hash, ref fee_paid_msat, .. } => {
-                               assert_eq!(payment_preimage, *ev_preimage);
-                               assert_eq!(payment_hash, *ev_hash);
-                               assert_eq!(fee_paid_msat, &Some(0));
-                       },
-                       _ => panic!("Unexpected event")
-               }
-               match events[1] {
-                       Event::PaymentPathSuccessful { payment_hash: hash, .. } => {
-                               assert_eq!(hash, Some(payment_hash));
-                       },
-                       _ => panic!("Unexpected event")
-               }
+               expect_payment_sent(&nodes[0], payment_preimage, None, true, true);
        }
 
        #[test]
@@ -1418,7 +1463,7 @@ mod test {
                assert_eq!(invoice.amount_pico_btc(), Some(200_000));
                assert_eq!(invoice.min_final_cltv_expiry_delta(), MIN_FINAL_CLTV_EXPIRY_DELTA as u64);
                assert_eq!(invoice.expiry_time(), Duration::from_secs(non_default_invoice_expiry_secs.into()));
-               assert_eq!(invoice.description(), InvoiceDescription::Hash(&crate::Sha256(Sha256::hash("Description hash phantom invoice".as_bytes()))));
+               assert_eq!(invoice.description(), Bolt11InvoiceDescription::Hash(&crate::Sha256(Sha256::hash("Description hash phantom invoice".as_bytes()))));
        }
 
        #[test]
@@ -1435,7 +1480,7 @@ mod test {
                        nodes[2].node.get_phantom_route_hints(),
                ];
                let user_payment_preimage = PaymentPreimage([1; 32]);
-               let payment_hash = Some(PaymentHash(Sha256::hash(&user_payment_preimage.0[..]).into_inner()));
+               let payment_hash = Some(PaymentHash(Sha256::hash(&user_payment_preimage.0[..]).to_byte_array()));
                let non_default_invoice_expiry_secs = 4200;
                let min_final_cltv_expiry_delta = Some(100);
                let duration_since_epoch = Duration::from_secs(1234567);
@@ -1529,7 +1574,7 @@ mod test {
                // is never handled, the `channel.counterparty.forwarding_info` is never assigned.
                let mut private_chan_cfg = UserConfig::default();
                private_chan_cfg.channel_handshake_config.announced_channel = false;
-               let temporary_channel_id = nodes[1].node.create_channel(nodes[3].node.get_our_node_id(), 1_000_000, 500_000_000, 42, Some(private_chan_cfg)).unwrap();
+               let temporary_channel_id = nodes[1].node.create_channel(nodes[3].node.get_our_node_id(), 1_000_000, 500_000_000, 42, None, Some(private_chan_cfg)).unwrap();
                let open_channel = get_event_msg!(nodes[1], MessageSendEvent::SendOpenChannel, nodes[3].node.get_our_node_id());
                nodes[3].node.handle_open_channel(&nodes[1].node.get_our_node_id(), &open_channel);
                let accept_channel = get_event_msg!(nodes[3], MessageSendEvent::SendAcceptChannel, nodes[1].node.get_our_node_id());
@@ -1886,4 +1931,111 @@ mod test {
                        _ => panic!(),
                }
        }
+
+       #[test]
+       fn test_rotate_through_iterators() {
+               // two nested vectors
+               let a = vec![vec!["a0", "b0", "c0"].into_iter(), vec!["a1", "b1"].into_iter()];
+               let result = rotate_through_iterators(a).collect::<Vec<_>>();
+
+               let expected = vec!["a0", "a1", "b0", "b1", "c0"];
+               assert_eq!(expected, result);
+
+               // test single nested vector
+               let a = vec![vec!["a0", "b0", "c0"].into_iter()];
+               let result = rotate_through_iterators(a).collect::<Vec<_>>();
+
+               let expected = vec!["a0", "b0", "c0"];
+               assert_eq!(expected, result);
+
+               // test second vector with only one element
+               let a = vec![vec!["a0", "b0", "c0"].into_iter(), vec!["a1"].into_iter()];
+               let result = rotate_through_iterators(a).collect::<Vec<_>>();
+
+               let expected = vec!["a0", "a1", "b0", "c0"];
+               assert_eq!(expected, result);
+
+               // test three nestend vectors
+               let a = vec![vec!["a0"].into_iter(), vec!["a1", "b1", "c1"].into_iter(), vec!["a2"].into_iter()];
+               let result = rotate_through_iterators(a).collect::<Vec<_>>();
+
+               let expected = vec!["a0", "a1", "a2", "b1", "c1"];
+               assert_eq!(expected, result);
+
+               // test single nested vector with a single value
+               let a = vec![vec!["a0"].into_iter()];
+               let result = rotate_through_iterators(a).collect::<Vec<_>>();
+
+               let expected = vec!["a0"];
+               assert_eq!(expected, result);
+
+               // test single empty nested vector
+               let a:Vec<std::vec::IntoIter<&str>> = vec![vec![].into_iter()];
+               let result = rotate_through_iterators(a).collect::<Vec<&str>>();
+               let expected:Vec<&str> = vec![];
+
+               assert_eq!(expected, result);
+
+               // test first nested vector is empty
+               let a:Vec<std::vec::IntoIter<&str>>= vec![vec![].into_iter(), vec!["a1", "b1", "c1"].into_iter()];
+               let result = rotate_through_iterators(a).collect::<Vec<&str>>();
+
+               let expected = vec!["a1", "b1", "c1"];
+               assert_eq!(expected, result);
+
+               // test two empty vectors
+               let a:Vec<std::vec::IntoIter<&str>> = vec![vec![].into_iter(), vec![].into_iter()];
+               let result = rotate_through_iterators(a).collect::<Vec<&str>>();
+
+               let expected:Vec<&str> = vec![];
+               assert_eq!(expected, result);
+
+               // test an empty vector amongst other filled vectors
+               let a = vec![
+                       vec!["a0", "b0", "c0"].into_iter(),
+                       vec![].into_iter(),
+                       vec!["a1", "b1", "c1"].into_iter(),
+                       vec!["a2", "b2", "c2"].into_iter(),
+               ];
+               let result = rotate_through_iterators(a).collect::<Vec<_>>();
+
+               let expected = vec!["a0", "a1", "a2", "b0", "b1", "b2", "c0", "c1", "c2"];
+               assert_eq!(expected, result);
+
+               // test a filled vector between two empty vectors
+               let a = vec![vec![].into_iter(), vec!["a1", "b1", "c1"].into_iter(), vec![].into_iter()];
+               let result = rotate_through_iterators(a).collect::<Vec<_>>();
+
+               let expected = vec!["a1", "b1", "c1"];
+               assert_eq!(expected, result);
+
+               // test an empty vector at the end of the vectors
+               let a = vec![vec!["a0", "b0", "c0"].into_iter(), vec![].into_iter()];
+               let result = rotate_through_iterators(a).collect::<Vec<_>>();
+
+               let expected = vec!["a0", "b0", "c0"];
+               assert_eq!(expected, result);
+
+               // test multiple empty vectors amongst multiple filled vectors
+               let a = vec![
+                       vec![].into_iter(),
+                       vec!["a1", "b1", "c1"].into_iter(),
+                       vec![].into_iter(),
+                       vec!["a3", "b3"].into_iter(),
+                       vec![].into_iter(),
+               ];
+
+               let result = rotate_through_iterators(a).collect::<Vec<_>>();
+
+               let expected = vec!["a1", "a3", "b1", "b3", "c1"];
+               assert_eq!(expected, result);
+
+               // test one element in the first nested vectore and two elements in the second nested
+               // vector
+               let a = vec![vec!["a0"].into_iter(), vec!["a1", "b1"].into_iter()];
+               let result = rotate_through_iterators(a).collect::<Vec<_>>();
+
+               let expected = vec!["a0", "a1", "b1"];
+               assert_eq!(expected, result);
+       }
 }