Expose `onion_message` items directly rather than via re-exports
[rust-lightning] / lightning / src / ln / channelmanager.rs
index ac790a57e9c6c559fcd1ac6c8745fd57644d8fe0..09c64df1dbf04b816195d3133e4b3f9dd411e9b1 100644 (file)
@@ -43,13 +43,11 @@ use crate::events::{Event, EventHandler, EventsProvider, MessageSendEvent, Messa
 // Since this struct is returned in `list_channels` methods, expose it here in case users want to
 // construct one themselves.
 use crate::ln::{inbound_payment, ChannelId, PaymentHash, PaymentPreimage, PaymentSecret};
-use crate::ln::channel::{Channel, ChannelPhase, ChannelContext, ChannelError, ChannelUpdateStatus, ShutdownResult, UnfundedChannelContext, UpdateFulfillCommitFetch, OutboundV1Channel, InboundV1Channel, WithChannelContext};
+use crate::ln::channel::{self, Channel, ChannelPhase, ChannelContext, ChannelError, ChannelUpdateStatus, ShutdownResult, UnfundedChannelContext, UpdateFulfillCommitFetch, OutboundV1Channel, InboundV1Channel, WithChannelContext};
 use crate::ln::features::{Bolt12InvoiceFeatures, ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures};
 #[cfg(any(feature = "_test_utils", test))]
 use crate::ln::features::Bolt11InvoiceFeatures;
-use crate::routing::gossip::NetworkGraph;
-use crate::routing::router::{BlindedTail, DefaultRouter, InFlightHtlcs, Path, Payee, PaymentParameters, Route, RouteParameters, Router};
-use crate::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringFeeParameters};
+use crate::routing::router::{BlindedTail, InFlightHtlcs, Path, Payee, PaymentParameters, Route, RouteParameters, Router};
 use crate::ln::onion_payment::{check_incoming_htlc_cltv, create_recv_pending_htlc_info, create_fwd_pending_htlc_info, decode_incoming_update_add_htlc_onion, InboundOnionErr, NextPacketDetails};
 use crate::ln::msgs;
 use crate::ln::onion_utils;
@@ -65,8 +63,9 @@ use crate::offers::merkle::SignError;
 use crate::offers::offer::{DerivedMetadata, Offer, OfferBuilder};
 use crate::offers::parse::Bolt12SemanticError;
 use crate::offers::refund::{Refund, RefundBuilder};
-use crate::onion_message::{Destination, OffersMessage, OffersMessageHandler, PendingOnionMessage, new_pending_onion_message};
-use crate::sign::{EntropySource, KeysManager, NodeSigner, Recipient, SignerProvider};
+use crate::onion_message::messenger::{Destination, MessageRouter, PendingOnionMessage, new_pending_onion_message};
+use crate::onion_message::offers::{OffersMessage, OffersMessageHandler};
+use crate::sign::{EntropySource, NodeSigner, Recipient, SignerProvider};
 use crate::sign::ecdsa::WriteableEcdsaChannelSigner;
 use crate::util::config::{UserConfig, ChannelConfig, ChannelConfigUpdate};
 use crate::util::wakers::{Future, Notifier};
@@ -75,6 +74,13 @@ use crate::util::string::UntrustedString;
 use crate::util::ser::{BigSize, FixedLengthReader, Readable, ReadableArgs, MaybeReadable, Writeable, Writer, VecWriter};
 use crate::util::logger::{Level, Logger, WithContext};
 use crate::util::errors::APIError;
+#[cfg(not(c_bindings))]
+use {
+       crate::routing::router::DefaultRouter,
+       crate::routing::gossip::NetworkGraph,
+       crate::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringFeeParameters},
+       crate::sign::KeysManager,
+};
 
 use alloc::collections::{btree_map, BTreeMap};
 
@@ -109,56 +115,88 @@ use crate::ln::script::ShutdownScript;
 // Alternatively, we can fill an outbound HTLC with a HTLCSource::OutboundRoute indicating this is
 // our payment, which we can use to decode errors or inform the user that the payment was sent.
 
-/// Routing info for an inbound HTLC onion.
+/// Information about where a received HTLC('s onion) has indicated the HTLC should go.
 #[derive(Clone)] // See Channel::revoke_and_ack for why, tl;dr: Rust bug
+#[cfg_attr(test, derive(Debug, PartialEq))]
 pub enum PendingHTLCRouting {
-       /// A forwarded HTLC.
+       /// An HTLC which should be forwarded on to another node.
        Forward {
-               /// BOLT 4 onion packet.
+               /// The onion which should be included in the forwarded HTLC, telling the next hop what to
+               /// do with the HTLC.
                onion_packet: msgs::OnionPacket,
-               /// The SCID from the onion that we should forward to. This could be a real SCID or a fake one
-               /// generated using `get_fake_scid` from the scid_utils::fake_scid module.
+               /// The short channel ID of the channel which we were instructed to forward this HTLC to.
+               ///
+               /// This could be a real on-chain SCID, an SCID alias, or some other SCID which has meaning
+               /// to the receiving node, such as one returned from
+               /// [`ChannelManager::get_intercept_scid`] or [`ChannelManager::get_phantom_scid`].
                short_channel_id: u64, // This should be NonZero<u64> eventually when we bump MSRV
                /// Set if this HTLC is being forwarded within a blinded path.
                blinded: Option<BlindedForward>,
        },
-       /// An HTLC paid to an invoice (supposedly) generated by us.
-       /// At this point, we have not checked that the invoice being paid was actually generated by us,
-       /// but rather it's claiming to pay an invoice of ours.
+       /// The onion indicates that this is a payment for an invoice (supposedly) generated by us.
+       ///
+       /// Note that at this point, we have not checked that the invoice being paid was actually
+       /// generated by us, but rather it's claiming to pay an invoice of ours.
        Receive {
-               /// Payment secret and total msat received.
+               /// Information about the amount the sender intended to pay and (potential) proof that this
+               /// is a payment for an invoice we generated. This proof of payment is is also used for
+               /// linking MPP parts of a larger payment.
                payment_data: msgs::FinalOnionHopData,
-               /// See [`RecipientOnionFields::payment_metadata`] for more info.
+               /// Additional data which we (allegedly) instructed the sender to include in the onion.
+               ///
+               /// For HTLCs received by LDK, this will ultimately be exposed in
+               /// [`Event::PaymentClaimable::onion_fields`] as
+               /// [`RecipientOnionFields::payment_metadata`].
                payment_metadata: Option<Vec<u8>>,
                /// CLTV expiry of the received HTLC.
+               ///
                /// Used to track when we should expire pending HTLCs that go unclaimed.
                incoming_cltv_expiry: u32,
-               /// Shared secret derived using a phantom node secret key. If this field is Some, the
-               /// payment was sent to a phantom node (one hop beyond the current node), but can be
-               /// settled by this node.
+               /// If the onion had forwarding instructions to one of our phantom node SCIDs, this will
+               /// provide the onion shared secret used to decrypt the next level of forwarding
+               /// instructions.
                phantom_shared_secret: Option<[u8; 32]>,
-               /// See [`RecipientOnionFields::custom_tlvs`] for more info.
+               /// Custom TLVs which were set by the sender.
+               ///
+               /// For HTLCs received by LDK, this will ultimately be exposed in
+               /// [`Event::PaymentClaimable::onion_fields`] as
+               /// [`RecipientOnionFields::custom_tlvs`].
                custom_tlvs: Vec<(u64, Vec<u8>)>,
+               /// Set if this HTLC is the final hop in a multi-hop blinded path.
+               requires_blinded_error: bool,
        },
-       /// Incoming keysend (sender provided the preimage in a TLV).
+       /// The onion indicates that this is for payment to us but which contains the preimage for
+       /// claiming included, and is unrelated to any invoice we'd previously generated (aka a
+       /// "keysend" or "spontaneous" payment).
        ReceiveKeysend {
-               /// This was added in 0.0.116 and will break deserialization on downgrades.
+               /// Information about the amount the sender intended to pay and possibly a token to
+               /// associate MPP parts of a larger payment.
+               ///
+               /// This will only be filled in if receiving MPP keysend payments is enabled, and it being
+               /// present will cause deserialization to fail on versions of LDK prior to 0.0.116.
                payment_data: Option<msgs::FinalOnionHopData>,
                /// Preimage for this onion payment. This preimage is provided by the sender and will be
                /// used to settle the spontaneous payment.
                payment_preimage: PaymentPreimage,
-               /// See [`RecipientOnionFields::payment_metadata`] for more info.
+               /// Additional data which we (allegedly) instructed the sender to include in the onion.
+               ///
+               /// For HTLCs received by LDK, this will ultimately bubble back up as
+               /// [`RecipientOnionFields::payment_metadata`].
                payment_metadata: Option<Vec<u8>>,
                /// CLTV expiry of the received HTLC.
+               ///
                /// Used to track when we should expire pending HTLCs that go unclaimed.
                incoming_cltv_expiry: u32,
-               /// See [`RecipientOnionFields::custom_tlvs`] for more info.
+               /// Custom TLVs which were set by the sender.
+               ///
+               /// For HTLCs received by LDK, these will ultimately bubble back up as
+               /// [`RecipientOnionFields::custom_tlvs`].
                custom_tlvs: Vec<(u64, Vec<u8>)>,
        },
 }
 
 /// Information used to forward or fail this HTLC that is being forwarded within a blinded path.
-#[derive(Clone, Copy, Hash, PartialEq, Eq)]
+#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
 pub struct BlindedForward {
        /// The `blinding_point` that was set in the inbound [`msgs::UpdateAddHTLC`], or in the inbound
        /// onion payload if we're the introduction node. Useful for calculating the next hop's
@@ -170,34 +208,57 @@ pub struct BlindedForward {
 impl PendingHTLCRouting {
        // Used to override the onion failure code and data if the HTLC is blinded.
        fn blinded_failure(&self) -> Option<BlindedFailure> {
-               // TODO: needs update when we support receiving to multi-hop blinded paths
-               if let Self::Forward { blinded: Some(_), .. } = self {
-                       Some(BlindedFailure::FromIntroductionNode)
-               } else {
-                       None
+               // TODO: needs update when we support forwarding blinded HTLCs as non-intro node
+               match self {
+                       Self::Forward { blinded: Some(_), .. } => Some(BlindedFailure::FromIntroductionNode),
+                       Self::Receive { requires_blinded_error: true, .. } => Some(BlindedFailure::FromBlindedNode),
+                       _ => None,
                }
        }
 }
 
-/// Full details of an incoming HTLC, including routing info.
+/// Information about an incoming HTLC, including the [`PendingHTLCRouting`] describing where it
+/// should go next.
 #[derive(Clone)] // See Channel::revoke_and_ack for why, tl;dr: Rust bug
+#[cfg_attr(test, derive(Debug, PartialEq))]
 pub struct PendingHTLCInfo {
        /// Further routing details based on whether the HTLC is being forwarded or received.
        pub routing: PendingHTLCRouting,
-       /// Shared secret from the previous hop.
-       /// Used encrypt failure packets in the event that the HTLC needs to be failed backwards.
+       /// The onion shared secret we build with the sender used to decrypt the onion.
+       ///
+       /// This is later used to encrypt failure packets in the event that the HTLC is failed.
        pub incoming_shared_secret: [u8; 32],
        /// Hash of the payment preimage, to lock the payment until the receiver releases the preimage.
        pub payment_hash: PaymentHash,
-       /// Amount offered by this HTLC.
-       pub incoming_amt_msat: Option<u64>, // Added in 0.0.113
-       /// Sender intended amount to forward or receive (actual amount received
-       /// may overshoot this in either case)
+       /// Amount received in the incoming HTLC.
+       ///
+       /// This field was added in LDK 0.0.113 and will be `None` for objects written by prior
+       /// versions.
+       pub incoming_amt_msat: Option<u64>,
+       /// The amount the sender indicated should be forwarded on to the next hop or amount the sender
+       /// intended for us to receive for received payments.
+       ///
+       /// If the received amount is less than this for received payments, an intermediary hop has
+       /// attempted to steal some of our funds and we should fail the HTLC (the sender should retry
+       /// it along another path).
+       ///
+       /// Because nodes can take less than their required fees, and because senders may wish to
+       /// improve their own privacy, this amount may be less than [`Self::incoming_amt_msat`] for
+       /// received payments. In such cases, recipients must handle this HTLC as if it had received
+       /// [`Self::outgoing_amt_msat`].
        pub outgoing_amt_msat: u64,
-       /// Outgoing timelock expiration blockheight.
+       /// The CLTV the sender has indicated we should set on the forwarded HTLC (or has indicated
+       /// should have been set on the received HTLC for received payments).
        pub outgoing_cltv_value: u32,
-       /// The fee being skimmed off the top of this HTLC. If this is a forward, it'll be the fee we are
-       /// skimming. If we're receiving this HTLC, it's the fee that our counterparty skimmed.
+       /// The fee taken for this HTLC in addition to the standard protocol HTLC fees.
+       ///
+       /// If this is a payment for forwarding, this is the fee we are taking before forwarding the
+       /// HTLC.
+       ///
+       /// If this is a received payment, this is the fee that our counterparty took.
+       ///
+       /// This is used to allow LSPs to take fees as a part of payments, without the sender having to
+       /// shoulder them.
        pub skimmed_fee_msat: Option<u64>,
 }
 
@@ -214,6 +275,7 @@ pub(super) enum PendingHTLCStatus {
        Fail(HTLCFailureMsg),
 }
 
+#[cfg_attr(test, derive(Clone, Debug, PartialEq))]
 pub(super) struct PendingAddHTLCInfo {
        pub(super) forward_info: PendingHTLCInfo,
 
@@ -229,19 +291,25 @@ pub(super) struct PendingAddHTLCInfo {
        prev_user_channel_id: u128,
 }
 
+#[cfg_attr(test, derive(Clone, Debug, PartialEq))]
 pub(super) enum HTLCForwardInfo {
        AddHTLC(PendingAddHTLCInfo),
        FailHTLC {
                htlc_id: u64,
                err_packet: msgs::OnionErrorPacket,
        },
+       FailMalformedHTLC {
+               htlc_id: u64,
+               failure_code: u16,
+               sha256_of_onion: [u8; 32],
+       },
 }
 
 // Used for failing blinded HTLCs backwards correctly.
-#[derive(Clone, Debug, Hash, PartialEq, Eq)]
+#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
 enum BlindedFailure {
        FromIntroductionNode,
-       // Another variant will be added here for non-intro nodes.
+       FromBlindedNode,
 }
 
 /// Tracks the inbound corresponding to an outbound HTLC
@@ -1088,7 +1156,7 @@ where
 //              |
 //              |__`peer_state`
 //                  |
-//                  |__`id_to_peer`
+//                  |__`outpoint_to_peer`
 //                  |
 //                  |__`short_to_chan_info`
 //                  |
@@ -1182,11 +1250,7 @@ where
        /// See `ChannelManager` struct-level documentation for lock order requirements.
        outbound_scid_aliases: Mutex<HashSet<u64>>,
 
-       /// `channel_id` -> `counterparty_node_id`.
-       ///
-       /// Only `channel_id`s are allowed as keys in this map, and not `temporary_channel_id`s. As
-       /// multiple channels with the same `temporary_channel_id` to different peers can exist,
-       /// allowing `temporary_channel_id`s in this map would cause collisions for such channels.
+       /// Channel funding outpoint -> `counterparty_node_id`.
        ///
        /// Note that this map should only be used for `MonitorEvent` handling, to be able to access
        /// the corresponding channel for the event, as we only have access to the `channel_id` during
@@ -1204,7 +1268,10 @@ where
        /// required to access the channel with the `counterparty_node_id`.
        ///
        /// See `ChannelManager` struct-level documentation for lock order requirements.
-       id_to_peer: Mutex<HashMap<ChannelId, PublicKey>>,
+       #[cfg(not(test))]
+       outpoint_to_peer: Mutex<HashMap<OutPoint, PublicKey>>,
+       #[cfg(test)]
+       pub(crate) outpoint_to_peer: Mutex<HashMap<OutPoint, PublicKey>>,
 
        /// SCIDs (and outbound SCID aliases) -> `counterparty_node_id`s and `channel_id`s.
        ///
@@ -1470,13 +1537,11 @@ pub const MIN_FINAL_CLTV_EXPIRY_DELTA: u16 = HTLC_FAIL_BACK_BUFFER as u16 + 3;
 // then waiting ANTI_REORG_DELAY to be reorg-safe on the outbound HLTC and
 // failing the corresponding htlc backward, and us now seeing the last block of ANTI_REORG_DELAY before
 // LATENCY_GRACE_PERIOD_BLOCKS.
-#[deny(const_err)]
 #[allow(dead_code)]
 const CHECK_CLTV_EXPIRY_SANITY: u32 = MIN_CLTV_EXPIRY_DELTA as u32 - LATENCY_GRACE_PERIOD_BLOCKS - CLTV_CLAIM_BUFFER - ANTI_REORG_DELAY - LATENCY_GRACE_PERIOD_BLOCKS;
 
 // Check for ability of an attacker to make us fail on-chain by delaying an HTLC claim. See
 // ChannelMonitor::should_broadcast_holder_commitment_txn for a description of why this is needed.
-#[deny(const_err)]
 #[allow(dead_code)]
 const CHECK_CLTV_EXPIRY_SANITY_2: u32 = MIN_CLTV_EXPIRY_DELTA as u32 - LATENCY_GRACE_PERIOD_BLOCKS - 2*CLTV_CLAIM_BUFFER;
 
@@ -1946,7 +2011,9 @@ macro_rules! handle_error {
 
 macro_rules! update_maps_on_chan_removal {
        ($self: expr, $channel_context: expr) => {{
-               $self.id_to_peer.lock().unwrap().remove(&$channel_context.channel_id());
+               if let Some(outpoint) = $channel_context.get_funding_txo() {
+                       $self.outpoint_to_peer.lock().unwrap().remove(&outpoint);
+               }
                let mut short_to_chan_info = $self.short_to_chan_info.write().unwrap();
                if let Some(short_id) = $channel_context.get_short_channel_id() {
                        short_to_chan_info.remove(&short_id);
@@ -2365,7 +2432,7 @@ where
                        forward_htlcs: Mutex::new(HashMap::new()),
                        claimable_payments: Mutex::new(ClaimablePayments { claimable_payments: HashMap::new(), pending_claiming_payments: HashMap::new() }),
                        pending_intercepted_htlcs: Mutex::new(HashMap::new()),
-                       id_to_peer: Mutex::new(HashMap::new()),
+                       outpoint_to_peer: Mutex::new(HashMap::new()),
                        short_to_chan_info: FairRwLock::new(HashMap::new()),
 
                        our_network_pubkey: node_signer.get_node_id(Recipient::Node).unwrap(),
@@ -2516,7 +2583,7 @@ where
        fn list_funded_channels_with_filter<Fn: FnMut(&(&ChannelId, &Channel<SP>)) -> bool + Copy>(&self, f: Fn) -> Vec<ChannelDetails> {
                // Allocate our best estimate of the number of channels we have in the `res`
                // Vec. Sadly the `short_to_chan_info` map doesn't cover channels without
-               // a scid or a scid alias, and the `id_to_peer` shouldn't be used outside
+               // a scid or a scid alias, and the `outpoint_to_peer` shouldn't be used outside
                // of the ChannelMonitor handling. Therefore reallocations may still occur, but is
                // unlikely as the `short_to_chan_info` map often contains 2 entries for
                // the same channel.
@@ -2549,7 +2616,7 @@ where
        pub fn list_channels(&self) -> Vec<ChannelDetails> {
                // Allocate our best estimate of the number of channels we have in the `res`
                // Vec. Sadly the `short_to_chan_info` map doesn't cover channels without
-               // a scid or a scid alias, and the `id_to_peer` shouldn't be used outside
+               // a scid or a scid alias, and the `outpoint_to_peer` shouldn't be used outside
                // of the ChannelMonitor handling. Therefore reallocations may still occur, but is
                // unlikely as the `short_to_chan_info` map often contains 2 entries for
                // the same channel.
@@ -2663,9 +2730,10 @@ where
        fn close_channel_internal(&self, channel_id: &ChannelId, counterparty_node_id: &PublicKey, target_feerate_sats_per_1000_weight: Option<u32>, override_shutdown_script: Option<ShutdownScript>) -> Result<(), APIError> {
                let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
 
-               let mut failed_htlcs: Vec<(HTLCSource, PaymentHash)>;
-               let shutdown_result;
-               loop {
+               let mut failed_htlcs: Vec<(HTLCSource, PaymentHash)> = Vec::new();
+               let mut shutdown_result = None;
+
+               {
                        let per_peer_state = self.per_peer_state.read().unwrap();
 
                        let peer_state_mutex = per_peer_state.get(counterparty_node_id)
@@ -2679,11 +2747,9 @@ where
                                        if let ChannelPhase::Funded(chan) = chan_phase_entry.get_mut() {
                                                let funding_txo_opt = chan.context.get_funding_txo();
                                                let their_features = &peer_state.latest_features;
-                                               let (shutdown_msg, mut monitor_update_opt, htlcs, local_shutdown_result) =
+                                               let (shutdown_msg, mut monitor_update_opt, htlcs) =
                                                        chan.get_shutdown(&self.signer_provider, their_features, target_feerate_sats_per_1000_weight, override_shutdown_script)?;
                                                failed_htlcs = htlcs;
-                                               shutdown_result = local_shutdown_result;
-                                               debug_assert_eq!(shutdown_result.is_some(), chan.is_shutdown());
 
                                                // We can send the `shutdown` message before updating the `ChannelMonitor`
                                                // here as we don't need the monitor update to complete until we send a
@@ -2700,30 +2766,20 @@ where
                                                if let Some(monitor_update) = monitor_update_opt.take() {
                                                        handle_new_monitor_update!(self, funding_txo_opt.unwrap(), monitor_update,
                                                                peer_state_lock, peer_state, per_peer_state, chan);
-                                                       break;
                                                }
-
-                                               if chan.is_shutdown() {
-                                                       if let ChannelPhase::Funded(chan) = remove_channel_phase!(self, chan_phase_entry) {
-                                                               if let Ok(channel_update) = self.get_channel_update_for_broadcast(&chan) {
-                                                                       peer_state.pending_msg_events.push(events::MessageSendEvent::BroadcastChannelUpdate {
-                                                                               msg: channel_update
-                                                                       });
-                                                               }
-                                                               self.issue_channel_close_events(&chan.context, ClosureReason::HolderForceClosed);
-                                                       }
-                                               }
-                                               break;
+                                       } else {
+                                               self.issue_channel_close_events(chan_phase_entry.get().context(), ClosureReason::HolderForceClosed);
+                                               let mut chan_phase = remove_channel_phase!(self, chan_phase_entry);
+                                               shutdown_result = Some(chan_phase.context_mut().force_shutdown(false));
                                        }
                                },
                                hash_map::Entry::Vacant(_) => {
-                                       // If we reach this point, it means that the channel_id either refers to an unfunded channel or
-                                       // it does not exist for this peer. Either way, we can attempt to force-close it.
-                                       //
-                                       // An appropriate error will be returned for non-existence of the channel if that's the case.
-                                       mem::drop(peer_state_lock);
-                                       mem::drop(per_peer_state);
-                                       return self.force_close_channel_with_peer(&channel_id, counterparty_node_id, None, false).map(|_| ())
+                                       return Err(APIError::ChannelUnavailable {
+                                               err: format!(
+                                                       "Channel with id {} not found for the passed counterparty node_id {}",
+                                                       channel_id, counterparty_node_id,
+                                               )
+                                       });
                                },
                        }
                }
@@ -2970,7 +3026,7 @@ where
        }
 
        fn decode_update_add_htlc_onion(
-               &self, msg: &msgs::UpdateAddHTLC
+               &self, msg: &msgs::UpdateAddHTLC, counterparty_node_id: &PublicKey,
        ) -> Result<
                (onion_utils::Hop, [u8; 32], Option<Result<PublicKey, secp256k1::Error>>), HTLCFailureMsg
        > {
@@ -2978,21 +3034,32 @@ where
                        msg, &self.node_signer, &self.logger, &self.secp_ctx
                )?;
 
-               let is_blinded = match next_hop {
+               let is_intro_node_forward = match next_hop {
                        onion_utils::Hop::Forward {
+                               // TODO: update this when we support blinded forwarding as non-intro node
                                next_hop_data: msgs::InboundOnionPayload::BlindedForward { .. }, ..
                        } => true,
-                       _ => false, // TODO: update this when we support receiving to multi-hop blinded paths
+                       _ => false,
                };
 
                macro_rules! return_err {
                        ($msg: expr, $err_code: expr, $data: expr) => {
                                {
                                        log_info!(
-                                               WithContext::from(&self.logger, None, Some(msg.channel_id)),
+                                               WithContext::from(&self.logger, Some(*counterparty_node_id), Some(msg.channel_id)),
                                                "Failed to accept/forward incoming HTLC: {}", $msg
                                        );
-                                       let (err_code, err_data) = if is_blinded {
+                                       // If `msg.blinding_point` is set, we must always fail with malformed.
+                                       if msg.blinding_point.is_some() {
+                                               return Err(HTLCFailureMsg::Malformed(msgs::UpdateFailMalformedHTLC {
+                                                       channel_id: msg.channel_id,
+                                                       htlc_id: msg.htlc_id,
+                                                       sha256_of_onion: [0; 32],
+                                                       failure_code: INVALID_ONION_BLINDING,
+                                               }));
+                                       }
+
+                                       let (err_code, err_data) = if is_intro_node_forward {
                                                (INVALID_ONION_BLINDING, &[0; 32][..])
                                        } else { ($err_code, $data) };
                                        return Err(HTLCFailureMsg::Relay(msgs::UpdateFailHTLC {
@@ -3136,13 +3203,25 @@ where
        }
 
        fn construct_pending_htlc_status<'a>(
-               &self, msg: &msgs::UpdateAddHTLC, shared_secret: [u8; 32], decoded_hop: onion_utils::Hop,
-               allow_underpay: bool, next_packet_pubkey_opt: Option<Result<PublicKey, secp256k1::Error>>
+               &self, msg: &msgs::UpdateAddHTLC, counterparty_node_id: &PublicKey, shared_secret: [u8; 32],
+               decoded_hop: onion_utils::Hop, allow_underpay: bool,
+               next_packet_pubkey_opt: Option<Result<PublicKey, secp256k1::Error>>,
        ) -> PendingHTLCStatus {
                macro_rules! return_err {
                        ($msg: expr, $err_code: expr, $data: expr) => {
                                {
-                                       log_info!(WithContext::from(&self.logger, None, Some(msg.channel_id)), "Failed to accept/forward incoming HTLC: {}", $msg);
+                                       let logger = WithContext::from(&self.logger, Some(*counterparty_node_id), Some(msg.channel_id));
+                                       log_info!(logger, "Failed to accept/forward incoming HTLC: {}", $msg);
+                                       if msg.blinding_point.is_some() {
+                                               return PendingHTLCStatus::Fail(HTLCFailureMsg::Malformed(
+                                                       msgs::UpdateFailMalformedHTLC {
+                                                               channel_id: msg.channel_id,
+                                                               htlc_id: msg.htlc_id,
+                                                               sha256_of_onion: [0; 32],
+                                                               failure_code: INVALID_ONION_BLINDING,
+                                                       }
+                                               ))
+                                       }
                                        return PendingHTLCStatus::Fail(HTLCFailureMsg::Relay(msgs::UpdateFailHTLC {
                                                channel_id: msg.channel_id,
                                                htlc_id: msg.htlc_id,
@@ -3218,7 +3297,7 @@ where
        /// [`internal_closing_signed`]: Self::internal_closing_signed
        fn get_channel_update_for_unicast(&self, chan: &Channel<SP>) -> Result<msgs::ChannelUpdate, LightningError> {
                let logger = WithChannelContext::from(&self.logger, &chan.context);
-               log_trace!(logger, "Attempting to generate channel update for channel {}", log_bytes!(chan.context.channel_id().0));
+               log_trace!(logger, "Attempting to generate channel update for channel {}", chan.context.channel_id());
                let short_channel_id = match chan.context.get_short_channel_id().or(chan.context.latest_inbound_scid_alias()) {
                        None => return Err(LightningError{err: "Channel not yet established".to_owned(), action: msgs::ErrorAction::IgnoreError}),
                        Some(id) => id,
@@ -3229,7 +3308,7 @@ where
 
        fn get_channel_update_for_onion(&self, short_channel_id: u64, chan: &Channel<SP>) -> Result<msgs::ChannelUpdate, LightningError> {
                let logger = WithChannelContext::from(&self.logger, &chan.context);
-               log_trace!(logger, "Generating channel update for channel {}", log_bytes!(chan.context.channel_id().0));
+               log_trace!(logger, "Generating channel update for channel {}", chan.context.channel_id());
                let were_node_one = self.our_network_pubkey.serialize()[..] < chan.context.get_counterparty_node_id().serialize()[..];
 
                let enabled = chan.context.is_usable() && match chan.channel_update_status() {
@@ -3279,23 +3358,33 @@ where
                } = args;
                // The top-level caller should hold the total_consistency_lock read lock.
                debug_assert!(self.total_consistency_lock.try_write().is_err());
-               log_trace!(WithContext::from(&self.logger, Some(path.hops.first().unwrap().pubkey), None),
-                       "Attempting to send payment with payment hash {} along path with next hop {}",
-                       payment_hash, path.hops.first().unwrap().short_channel_id);
                let prng_seed = self.entropy_source.get_secure_random_bytes();
                let session_priv = SecretKey::from_slice(&session_priv_bytes[..]).expect("RNG is busted");
 
                let (onion_packet, htlc_msat, htlc_cltv) = onion_utils::create_payment_onion(
                        &self.secp_ctx, &path, &session_priv, total_value, recipient_onion, cur_height,
                        payment_hash, keysend_preimage, prng_seed
-               )?;
+               ).map_err(|e| {
+                       let logger = WithContext::from(&self.logger, Some(path.hops.first().unwrap().pubkey), None);
+                       log_error!(logger, "Failed to build an onion for path for payment hash {}", payment_hash);
+                       e
+               })?;
 
                let err: Result<(), _> = loop {
                        let (counterparty_node_id, id) = match self.short_to_chan_info.read().unwrap().get(&path.hops.first().unwrap().short_channel_id) {
-                               None => return Err(APIError::ChannelUnavailable{err: "No channel available with first hop!".to_owned()}),
+                               None => {
+                                       let logger = WithContext::from(&self.logger, Some(path.hops.first().unwrap().pubkey), None);
+                                       log_error!(logger, "Failed to find first-hop for payment hash {}", payment_hash);
+                                       return Err(APIError::ChannelUnavailable{err: "No channel available with first hop!".to_owned()})
+                               },
                                Some((cp_id, chan_id)) => (cp_id.clone(), chan_id.clone()),
                        };
 
+                       let logger = WithContext::from(&self.logger, Some(counterparty_node_id), Some(id));
+                       log_trace!(logger,
+                               "Attempting to send payment with payment hash {} along path with next hop {}",
+                               payment_hash, path.hops.first().unwrap().short_channel_id);
+
                        let per_peer_state = self.per_peer_state.read().unwrap();
                        let peer_state_mutex = per_peer_state.get(&counterparty_node_id)
                                .ok_or_else(|| APIError::ChannelUnavailable{err: "No peer matching the path's first hop found!".to_owned() })?;
@@ -3666,9 +3755,10 @@ where
 
                let mut peer_state_lock = peer_state_mutex.lock().unwrap();
                let peer_state = &mut *peer_state_lock;
+               let funding_txo;
                let (chan, msg_opt) = match peer_state.channel_by_id.remove(temporary_channel_id) {
-                       Some(ChannelPhase::UnfundedOutboundV1(chan)) => {
-                               let funding_txo = find_funding_output(&chan, &funding_transaction)?;
+                       Some(ChannelPhase::UnfundedOutboundV1(mut chan)) => {
+                               funding_txo = find_funding_output(&chan, &funding_transaction)?;
 
                                let logger = WithChannelContext::from(&self.logger, &chan.context);
                                let funding_res = chan.get_funding_created(funding_transaction, funding_txo, is_batch_funding, &&logger)
@@ -3680,7 +3770,7 @@ where
                                                (chan, MsgHandleErrInternal::from_finish_shutdown(msg, channel_id, user_id, shutdown_res, None, channel_capacity))
                                        } else { unreachable!(); });
                                match funding_res {
-                                       Ok((chan, funding_msg)) => (chan, funding_msg),
+                                       Ok(funding_msg) => (chan, funding_msg),
                                        Err((chan, err)) => {
                                                mem::drop(peer_state_lock);
                                                mem::drop(per_peer_state);
@@ -3716,11 +3806,11 @@ where
                                panic!("Generated duplicate funding txid?");
                        },
                        hash_map::Entry::Vacant(e) => {
-                               let mut id_to_peer = self.id_to_peer.lock().unwrap();
-                               if id_to_peer.insert(chan.context.channel_id(), chan.context.get_counterparty_node_id()).is_some() {
-                                       panic!("id_to_peer map already contained funding txid, which shouldn't be possible");
+                               let mut outpoint_to_peer = self.outpoint_to_peer.lock().unwrap();
+                               if outpoint_to_peer.insert(funding_txo, chan.context.get_counterparty_node_id()).is_some() {
+                                       panic!("outpoint_to_peer map already contained funding outpoint, which shouldn't be possible");
                                }
-                               e.insert(ChannelPhase::Funded(chan));
+                               e.insert(ChannelPhase::UnfundedOutboundV1(chan));
                        }
                }
                Ok(())
@@ -4047,7 +4137,8 @@ where
                                None => {
                                        let error = format!("Channel with id {} not found for the passed counterparty node_id {}",
                                                next_hop_channel_id, next_node_id);
-                                       log_error!(self.logger, "{} when attempting to forward intercepted HTLC", error);
+                                       let logger = WithContext::from(&self.logger, Some(next_node_id), Some(*next_hop_channel_id));
+                                       log_error!(logger, "{} when attempting to forward intercepted HTLC", error);
                                        return Err(APIError::ChannelUnavailable {
                                                err: error
                                        })
@@ -4135,6 +4226,7 @@ where
 
                        for (short_chan_id, mut pending_forwards) in forward_htlcs {
                                if short_chan_id != 0 {
+                                       let mut forwarding_counterparty = None;
                                        macro_rules! forwarding_channel_not_found {
                                                () => {
                                                        for forward_info in pending_forwards.drain(..) {
@@ -4148,7 +4240,8 @@ where
                                                                        }) => {
                                                                                macro_rules! failure_handler {
                                                                                        ($msg: expr, $err_code: expr, $err_data: expr, $phantom_ss: expr, $next_hop_unknown: expr) => {
-                                                                                               log_info!(self.logger, "Failed to accept/forward incoming HTLC: {}", $msg);
+                                                                                               let logger = WithContext::from(&self.logger, forwarding_counterparty, Some(prev_funding_outpoint.to_channel_id()));
+                                                                                               log_info!(logger, "Failed to accept/forward incoming HTLC: {}", $msg);
 
                                                                                                let htlc_source = HTLCSource::PreviousHopData(HTLCPreviousHopData {
                                                                                                        short_channel_id: prev_short_channel_id,
@@ -4193,7 +4286,7 @@ where
                                                                                                let phantom_shared_secret = self.node_signer.ecdh(Recipient::PhantomNode, &onion_packet.public_key.unwrap(), None).unwrap().secret_bytes();
                                                                                                let next_hop = match onion_utils::decode_next_payment_hop(
                                                                                                        phantom_shared_secret, &onion_packet.hop_data, onion_packet.hmac,
-                                                                                                       payment_hash, &self.node_signer
+                                                                                                       payment_hash, None, &self.node_signer
                                                                                                ) {
                                                                                                        Ok(res) => res,
                                                                                                        Err(onion_utils::OnionDecodeErr::Malformed { err_msg, err_code }) => {
@@ -4229,7 +4322,7 @@ where
                                                                                        fail_forward!(format!("Unknown short channel id {} for forward HTLC", short_chan_id), 0x4000 | 10, Vec::new(), None);
                                                                                }
                                                                        },
-                                                                       HTLCForwardInfo::FailHTLC { .. } => {
+                                                                       HTLCForwardInfo::FailHTLC { .. } | HTLCForwardInfo::FailMalformedHTLC { .. } => {
                                                                                // Channel went away before we could fail it. This implies
                                                                                // the channel is now on chain and our counterparty is
                                                                                // trying to broadcast the HTLC-Timeout, but that's their
@@ -4247,6 +4340,7 @@ where
                                                        continue;
                                                }
                                        };
+                                       forwarding_counterparty = Some(counterparty_node_id);
                                        let per_peer_state = self.per_peer_state.read().unwrap();
                                        let peer_state_mutex_opt = per_peer_state.get(&counterparty_node_id);
                                        if peer_state_mutex_opt.is_none() {
@@ -4324,6 +4418,20 @@ where
                                                                                continue;
                                                                        }
                                                                },
+                                                               HTLCForwardInfo::FailMalformedHTLC { htlc_id, failure_code, sha256_of_onion } => {
+                                                                       log_trace!(self.logger, "Failing malformed HTLC back to channel with short id {} (backward HTLC ID {}) after delay", short_chan_id, htlc_id);
+                                                                       if let Err(e) = chan.queue_fail_malformed_htlc(htlc_id, failure_code, sha256_of_onion, &self.logger) {
+                                                                               if let ChannelError::Ignore(msg) = e {
+                                                                                       log_trace!(self.logger, "Failed to fail HTLC with ID {} backwards to short_id {}: {}", htlc_id, short_chan_id, msg);
+                                                                               } else {
+                                                                                       panic!("Stated return value requirements in queue_fail_malformed_htlc() were not met");
+                                                                               }
+                                                                               // fail-backs are best-effort, we probably already have one
+                                                                               // pending, and if not that's OK, if not, the channel is on
+                                                                               // the chain and sending the HTLC-Timeout is their problem.
+                                                                               continue;
+                                                                       }
+                                                               },
                                                        }
                                                }
                                        } else {
@@ -4342,7 +4450,10 @@ where
                                                        }) => {
                                                                let blinded_failure = routing.blinded_failure();
                                                                let (cltv_expiry, onion_payload, payment_data, phantom_shared_secret, mut onion_fields) = match routing {
-                                                                       PendingHTLCRouting::Receive { payment_data, payment_metadata, incoming_cltv_expiry, phantom_shared_secret, custom_tlvs } => {
+                                                                       PendingHTLCRouting::Receive {
+                                                                               payment_data, payment_metadata, incoming_cltv_expiry, phantom_shared_secret,
+                                                                               custom_tlvs, requires_blinded_error: _
+                                                                       } => {
                                                                                let _legacy_hop_data = Some(payment_data.clone());
                                                                                let onion_fields = RecipientOnionFields { payment_secret: Some(payment_data.payment_secret),
                                                                                                payment_metadata, custom_tlvs };
@@ -4401,7 +4512,7 @@ where
                                                                                                htlc_id: $htlc.prev_hop.htlc_id,
                                                                                                incoming_packet_shared_secret: $htlc.prev_hop.incoming_packet_shared_secret,
                                                                                                phantom_shared_secret,
-                                                                                               blinded_failure: None,
+                                                                                               blinded_failure,
                                                                                        }), payment_hash,
                                                                                        HTLCFailReason::reason(0x4000 | 15, htlc_msat_height_data),
                                                                                        HTLCDestination::FailedPayment { payment_hash: $payment_hash },
@@ -4575,7 +4686,7 @@ where
                                                                        },
                                                                };
                                                        },
-                                                       HTLCForwardInfo::FailHTLC { .. } => {
+                                                       HTLCForwardInfo::FailHTLC { .. } | HTLCForwardInfo::FailMalformedHTLC { .. } => {
                                                                panic!("Got pending fail of our own HTLC");
                                                        }
                                                }
@@ -5180,15 +5291,26 @@ where
                                        "Failing {}HTLC with payment_hash {} backwards from us: {:?}",
                                        if blinded_failure.is_some() { "blinded " } else { "" }, &payment_hash, onion_error
                                );
-                               let err_packet = match blinded_failure {
+                               let failure = match blinded_failure {
                                        Some(BlindedFailure::FromIntroductionNode) => {
                                                let blinded_onion_error = HTLCFailReason::reason(INVALID_ONION_BLINDING, vec![0; 32]);
-                                               blinded_onion_error.get_encrypted_failure_packet(
+                                               let err_packet = blinded_onion_error.get_encrypted_failure_packet(
                                                        incoming_packet_shared_secret, phantom_shared_secret
-                                               )
+                                               );
+                                               HTLCForwardInfo::FailHTLC { htlc_id: *htlc_id, err_packet }
+                                       },
+                                       Some(BlindedFailure::FromBlindedNode) => {
+                                               HTLCForwardInfo::FailMalformedHTLC {
+                                                       htlc_id: *htlc_id,
+                                                       failure_code: INVALID_ONION_BLINDING,
+                                                       sha256_of_onion: [0; 32]
+                                               }
                                        },
                                        None => {
-                                               onion_error.get_encrypted_failure_packet(incoming_packet_shared_secret, phantom_shared_secret)
+                                               let err_packet = onion_error.get_encrypted_failure_packet(
+                                                       incoming_packet_shared_secret, phantom_shared_secret
+                                               );
+                                               HTLCForwardInfo::FailHTLC { htlc_id: *htlc_id, err_packet }
                                        }
                                };
 
@@ -5199,10 +5321,10 @@ where
                                }
                                match forward_htlcs.entry(*short_channel_id) {
                                        hash_map::Entry::Occupied(mut entry) => {
-                                               entry.get_mut().push(HTLCForwardInfo::FailHTLC { htlc_id: *htlc_id, err_packet });
+                                               entry.get_mut().push(failure);
                                        },
                                        hash_map::Entry::Vacant(entry) => {
-                                               entry.insert(vec!(HTLCForwardInfo::FailHTLC { htlc_id: *htlc_id, err_packet }));
+                                               entry.insert(vec!(failure));
                                        }
                                }
                                mem::drop(forward_htlcs);
@@ -5350,6 +5472,7 @@ where
                }
                if valid_mpp {
                        for htlc in sources.drain(..) {
+                               let prev_hop_chan_id = htlc.prev_hop.outpoint.to_channel_id();
                                if let Err((pk, err)) = self.claim_funds_from_hop(
                                        htlc.prev_hop, payment_preimage,
                                        |_, definitely_duplicate| {
@@ -5360,7 +5483,8 @@ where
                                        if let msgs::ErrorAction::IgnoreError = err.err.action {
                                                // We got a temporary failure updating monitor, but will claim the
                                                // HTLC when the monitor updating is restored (or on chain).
-                                               log_error!(self.logger, "Temporary failure claiming HTLC, treating as success: {}", err.err.err);
+                                               let logger = WithContext::from(&self.logger, None, Some(prev_hop_chan_id));
+                                               log_error!(logger, "Temporary failure claiming HTLC, treating as success: {}", err.err.err);
                                        } else { errs.push((pk, err)); }
                                }
                        }
@@ -5494,6 +5618,7 @@ where
                }
                let preimage_update = ChannelMonitorUpdate {
                        update_id: CLOSED_CHANNEL_UPDATE_ID,
+                       counterparty_node_id: None,
                        updates: vec![ChannelMonitorUpdateStep::PaymentPreimage {
                                payment_preimage,
                        }],
@@ -5795,9 +5920,9 @@ where
                        Some(cp_id) => cp_id.clone(),
                        None => {
                                // TODO: Once we can rely on the counterparty_node_id from the
-                               // monitor event, this and the id_to_peer map should be removed.
-                               let id_to_peer = self.id_to_peer.lock().unwrap();
-                               match id_to_peer.get(&funding_txo.to_channel_id()) {
+                               // monitor event, this and the outpoint_to_peer map should be removed.
+                               let outpoint_to_peer = self.outpoint_to_peer.lock().unwrap();
+                               match outpoint_to_peer.get(&funding_txo) {
                                        Some(cp_id) => cp_id.clone(),
                                        None => return,
                                }
@@ -5878,13 +6003,20 @@ where
        }
 
        fn do_accept_inbound_channel(&self, temporary_channel_id: &ChannelId, counterparty_node_id: &PublicKey, accept_0conf: bool, user_channel_id: u128) -> Result<(), APIError> {
+
+               let logger = WithContext::from(&self.logger, Some(*counterparty_node_id), Some(*temporary_channel_id));
                let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
 
                let peers_without_funded_channels =
                        self.peers_without_funded_channels(|peer| { peer.total_channel_count() > 0 });
                let per_peer_state = self.per_peer_state.read().unwrap();
                let peer_state_mutex = per_peer_state.get(counterparty_node_id)
-                       .ok_or_else(|| APIError::ChannelUnavailable { err: format!("Can't find a peer matching the passed counterparty node_id {}", counterparty_node_id) })?;
+               .ok_or_else(|| {
+                       let err_str = format!("Can't find a peer matching the passed counterparty node_id {}", counterparty_node_id); 
+                       log_error!(logger, "{}", err_str);
+
+                       APIError::ChannelUnavailable { err: err_str } 
+               })?;
                let mut peer_state_lock = peer_state_mutex.lock().unwrap();
                let peer_state = &mut *peer_state_lock;
                let is_only_peer_channel = peer_state.total_channel_count() == 1;
@@ -5899,9 +6031,19 @@ where
                                InboundV1Channel::new(&self.fee_estimator, &self.entropy_source, &self.signer_provider,
                                        counterparty_node_id.clone(), &self.channel_type_features(), &peer_state.latest_features,
                                        &unaccepted_channel.open_channel_msg, user_channel_id, &self.default_configuration, best_block_height,
-                                       &self.logger, accept_0conf).map_err(|e| APIError::ChannelUnavailable { err: e.to_string() })
+                                       &self.logger, accept_0conf).map_err(|e| {
+                                               let err_str = e.to_string();
+                                               log_error!(logger, "{}", err_str);
+
+                                               APIError::ChannelUnavailable { err: err_str }
+                                       })
+                               }
+                       _ => { 
+                               let err_str = "No such channel awaiting to be accepted.".to_owned();
+                               log_error!(logger, "{}", err_str);
+
+                               Err(APIError::APIMisuseError { err: err_str })
                        }
-                       _ => Err(APIError::APIMisuseError { err: "No such channel awaiting to be accepted.".to_owned() })
                }?;
 
                if accept_0conf {
@@ -5915,7 +6057,10 @@ where
                                }
                        };
                        peer_state.pending_msg_events.push(send_msg_err_event);
-                       return Err(APIError::APIMisuseError { err: "Please use accept_inbound_channel_from_trusted_peer_0conf to accept channels with zero confirmations.".to_owned() });
+                       let err_str = "Please use accept_inbound_channel_from_trusted_peer_0conf to accept channels with zero confirmations.".to_owned();
+                       log_error!(logger, "{}", err_str);
+
+                       return Err(APIError::APIMisuseError { err: err_str });
                } else {
                        // If this peer already has some channels, a new channel won't increase our number of peers
                        // with unfunded channels, so as long as we aren't over the maximum number of unfunded
@@ -5928,7 +6073,10 @@ where
                                        }
                                };
                                peer_state.pending_msg_events.push(send_msg_err_event);
-                               return Err(APIError::APIMisuseError { err: "Too many peers with unfunded channels, refusing to accept new ones".to_owned() });
+                               let err_str = "Too many peers with unfunded channels, refusing to accept new ones".to_owned();
+                               log_error!(logger, "{}", err_str);
+
+                               return Err(APIError::APIMisuseError { err: err_str });
                        }
                }
 
@@ -6051,13 +6199,18 @@ where
 
                // If we're doing manual acceptance checks on the channel, then defer creation until we're sure we want to accept.
                if self.default_configuration.manually_accept_inbound_channels {
+                       let channel_type = channel::channel_type_from_open_channel(
+                                       &msg, &peer_state.latest_features, &self.channel_type_features()
+                               ).map_err(|e|
+                                       MsgHandleErrInternal::from_chan_no_close(e, msg.temporary_channel_id)
+                               )?;
                        let mut pending_events = self.pending_events.lock().unwrap();
                        pending_events.push_back((events::Event::OpenChannelRequest {
                                temporary_channel_id: msg.temporary_channel_id.clone(),
                                counterparty_node_id: counterparty_node_id.clone(),
                                funding_satoshis: msg.funding_satoshis,
                                push_msat: msg.push_msat,
-                               channel_type: msg.channel_type.clone().unwrap(),
+                               channel_type,
                        }, None));
                        peer_state.inbound_channel_request_by_id.insert(channel_id, InboundChannelRequest {
                                open_channel_msg: msg.clone(),
@@ -6149,50 +6302,61 @@ where
 
                let mut peer_state_lock = peer_state_mutex.lock().unwrap();
                let peer_state = &mut *peer_state_lock;
-               let (chan, funding_msg_opt, monitor) =
+               let (mut chan, funding_msg_opt, monitor) =
                        match peer_state.channel_by_id.remove(&msg.temporary_channel_id) {
                                Some(ChannelPhase::UnfundedInboundV1(inbound_chan)) => {
                                        let logger = WithChannelContext::from(&self.logger, &inbound_chan.context);
                                        match inbound_chan.funding_created(msg, best_block, &self.signer_provider, &&logger) {
                                                Ok(res) => res,
-                                               Err((mut inbound_chan, err)) => {
+                                               Err((inbound_chan, err)) => {
                                                        // We've already removed this inbound channel from the map in `PeerState`
                                                        // above so at this point we just need to clean up any lingering entries
                                                        // concerning this channel as it is safe to do so.
-                                                       update_maps_on_chan_removal!(self, &inbound_chan.context);
-                                                       let user_id = inbound_chan.context.get_user_id();
-                                                       let shutdown_res = inbound_chan.context.force_shutdown(false);
-                                                       return Err(MsgHandleErrInternal::from_finish_shutdown(format!("{}", err),
-                                                               msg.temporary_channel_id, user_id, shutdown_res, None, inbound_chan.context.get_value_satoshis()));
+                                                       debug_assert!(matches!(err, ChannelError::Close(_)));
+                                                       // Really we should be returning the channel_id the peer expects based
+                                                       // on their funding info here, but they're horribly confused anyway, so
+                                                       // there's not a lot we can do to save them.
+                                                       return Err(convert_chan_phase_err!(self, err, &mut ChannelPhase::UnfundedInboundV1(inbound_chan), &msg.temporary_channel_id).1);
                                                },
                                        }
                                },
-                               Some(ChannelPhase::Funded(_)) | Some(ChannelPhase::UnfundedOutboundV1(_)) => {
-                                       return Err(MsgHandleErrInternal::send_err_msg_no_close(format!("Got an unexpected funding_created message from peer with counterparty_node_id {}", counterparty_node_id), msg.temporary_channel_id));
+                               Some(mut phase) => {
+                                       let err_msg = format!("Got an unexpected funding_created message from peer with counterparty_node_id {}", counterparty_node_id);
+                                       let err = ChannelError::Close(err_msg);
+                                       return Err(convert_chan_phase_err!(self, err, &mut phase, &msg.temporary_channel_id).1);
                                },
                                None => return Err(MsgHandleErrInternal::send_err_msg_no_close(format!("Got a message for a channel from the wrong node! No such channel for the passed counterparty_node_id {}", counterparty_node_id), msg.temporary_channel_id))
                        };
 
-               match peer_state.channel_by_id.entry(chan.context.channel_id()) {
+               let funded_channel_id = chan.context.channel_id();
+
+               macro_rules! fail_chan { ($err: expr) => { {
+                       // Note that at this point we've filled in the funding outpoint on our
+                       // channel, but its actually in conflict with another channel. Thus, if
+                       // we call `convert_chan_phase_err` immediately (thus calling
+                       // `update_maps_on_chan_removal`), we'll remove the existing channel
+                       // from `outpoint_to_peer`. Thus, we must first unset the funding outpoint
+                       // on the channel.
+                       let err = ChannelError::Close($err.to_owned());
+                       chan.unset_funding_info(msg.temporary_channel_id);
+                       return Err(convert_chan_phase_err!(self, err, chan, &funded_channel_id, UNFUNDED_CHANNEL).1);
+               } } }
+
+               match peer_state.channel_by_id.entry(funded_channel_id) {
                        hash_map::Entry::Occupied(_) => {
-                               Err(MsgHandleErrInternal::send_err_msg_no_close(
-                                       "Already had channel with the new channel_id".to_owned(),
-                                       chan.context.channel_id()
-                               ))
+                               fail_chan!("Already had channel with the new channel_id");
                        },
                        hash_map::Entry::Vacant(e) => {
-                               let mut id_to_peer_lock = self.id_to_peer.lock().unwrap();
-                               match id_to_peer_lock.entry(chan.context.channel_id()) {
+                               let mut outpoint_to_peer_lock = self.outpoint_to_peer.lock().unwrap();
+                               match outpoint_to_peer_lock.entry(monitor.get_funding_txo().0) {
                                        hash_map::Entry::Occupied(_) => {
-                                               return Err(MsgHandleErrInternal::send_err_msg_no_close(
-                                                       "The funding_created message had the same funding_txid as an existing channel - funding is not possible".to_owned(),
-                                                       chan.context.channel_id()))
+                                               fail_chan!("The funding_created message had the same funding_txid as an existing channel - funding is not possible");
                                        },
                                        hash_map::Entry::Vacant(i_e) => {
                                                let monitor_res = self.chain_monitor.watch_channel(monitor.get_funding_txo().0, monitor);
                                                if let Ok(persist_state) = monitor_res {
                                                        i_e.insert(chan.context.get_counterparty_node_id());
-                                                       mem::drop(id_to_peer_lock);
+                                                       mem::drop(outpoint_to_peer_lock);
 
                                                        // There's no problem signing a counterparty's funding transaction if our monitor
                                                        // hasn't persisted to disk yet - we can't lose money on a transaction that we haven't
@@ -6215,13 +6379,7 @@ where
                                                } else {
                                                        let logger = WithChannelContext::from(&self.logger, &chan.context);
                                                        log_error!(logger, "Persisting initial ChannelMonitor failed, implying the funding outpoint was duplicated");
-                                                       let channel_id = match funding_msg_opt {
-                                                               Some(msg) => msg.channel_id,
-                                                               None => chan.context.channel_id(),
-                                                       };
-                                                       return Err(MsgHandleErrInternal::send_err_msg_no_close(
-                                                               "The funding_created message had the same funding_txid as an existing channel - funding is not possible".to_owned(),
-                                                               channel_id));
+                                                       fail_chan!("Duplicate funding outpoint");
                                                }
                                        }
                                }
@@ -6241,22 +6399,43 @@ where
                let mut peer_state_lock = peer_state_mutex.lock().unwrap();
                let peer_state = &mut *peer_state_lock;
                match peer_state.channel_by_id.entry(msg.channel_id) {
-                       hash_map::Entry::Occupied(mut chan_phase_entry) => {
-                               match chan_phase_entry.get_mut() {
-                                       ChannelPhase::Funded(ref mut chan) => {
-                                               let logger = WithChannelContext::from(&self.logger, &chan.context);
-                                               let monitor = try_chan_phase_entry!(self,
-                                                       chan.funding_signed(&msg, best_block, &self.signer_provider, &&logger), chan_phase_entry);
-                                               if let Ok(persist_status) = self.chain_monitor.watch_channel(chan.context.get_funding_txo().unwrap(), monitor) {
-                                                       handle_new_monitor_update!(self, persist_status, peer_state_lock, peer_state, per_peer_state, chan, INITIAL_MONITOR);
-                                                       Ok(())
-                                               } else {
-                                                       try_chan_phase_entry!(self, Err(ChannelError::Close("Channel funding outpoint was a duplicate".to_owned())), chan_phase_entry)
+                       hash_map::Entry::Occupied(chan_phase_entry) => {
+                               if matches!(chan_phase_entry.get(), ChannelPhase::UnfundedOutboundV1(_)) {
+                                       let chan = if let ChannelPhase::UnfundedOutboundV1(chan) = chan_phase_entry.remove() { chan } else { unreachable!() };
+                                       let logger = WithContext::from(
+                                               &self.logger,
+                                               Some(chan.context.get_counterparty_node_id()),
+                                               Some(chan.context.channel_id())
+                                       );
+                                       let res =
+                                               chan.funding_signed(&msg, best_block, &self.signer_provider, &&logger);
+                                       match res {
+                                               Ok((chan, monitor)) => {
+                                                       if let Ok(persist_status) = self.chain_monitor.watch_channel(chan.context.get_funding_txo().unwrap(), monitor) {
+                                                               // We really should be able to insert here without doing a second
+                                                               // lookup, but sadly rust stdlib doesn't currently allow keeping
+                                                               // the original Entry around with the value removed.
+                                                               let mut chan = peer_state.channel_by_id.entry(msg.channel_id).or_insert(ChannelPhase::Funded(chan));
+                                                               if let ChannelPhase::Funded(ref mut chan) = &mut chan {
+                                                                       handle_new_monitor_update!(self, persist_status, peer_state_lock, peer_state, per_peer_state, chan, INITIAL_MONITOR);
+                                                               } else { unreachable!(); }
+                                                               Ok(())
+                                                       } else {
+                                                               let e = ChannelError::Close("Channel funding outpoint was a duplicate".to_owned());
+                                                               return Err(convert_chan_phase_err!(self, e, &mut ChannelPhase::Funded(chan), &msg.channel_id).1);
+                                                       }
+                                               },
+                                               Err((chan, e)) => {
+                                                       debug_assert!(matches!(e, ChannelError::Close(_)),
+                                                               "We don't have a channel anymore, so the error better have expected close");
+                                                       // We've already removed this outbound channel from the map in
+                                                       // `PeerState` above so at this point we just need to clean up any
+                                                       // lingering entries concerning this channel as it is safe to do so.
+                                                       return Err(convert_chan_phase_err!(self, e, &mut ChannelPhase::UnfundedOutboundV1(chan), &msg.channel_id).1);
                                                }
-                                       },
-                                       _ => {
-                                               return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel".to_owned(), msg.channel_id));
-                                       },
+                                       }
+                               } else {
+                                       return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel".to_owned(), msg.channel_id));
                                }
                        },
                        hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel".to_owned(), msg.channel_id))
@@ -6458,7 +6637,7 @@ where
                // Note that the ChannelManager is NOT re-persisted on disk after this (unless we error
                // closing a channel), so any changes are likely to be lost on restart!
 
-               let decoded_hop_res = self.decode_update_add_htlc_onion(msg);
+               let decoded_hop_res = self.decode_update_add_htlc_onion(msg, counterparty_node_id);
                let per_peer_state = self.per_peer_state.read().unwrap();
                let peer_state_mutex = per_peer_state.get(counterparty_node_id)
                        .ok_or_else(|| {
@@ -6472,11 +6651,23 @@ where
                                if let ChannelPhase::Funded(chan) = chan_phase_entry.get_mut() {
                                        let pending_forward_info = match decoded_hop_res {
                                                Ok((next_hop, shared_secret, next_packet_pk_opt)) =>
-                                                       self.construct_pending_htlc_status(msg, shared_secret, next_hop,
-                                                               chan.context.config().accept_underpaying_htlcs, next_packet_pk_opt),
+                                                       self.construct_pending_htlc_status(
+                                                               msg, counterparty_node_id, shared_secret, next_hop,
+                                                               chan.context.config().accept_underpaying_htlcs, next_packet_pk_opt,
+                                                       ),
                                                Err(e) => PendingHTLCStatus::Fail(e)
                                        };
                                        let create_pending_htlc_status = |chan: &Channel<SP>, pending_forward_info: PendingHTLCStatus, error_code: u16| {
+                                               if msg.blinding_point.is_some() {
+                                                       return PendingHTLCStatus::Fail(HTLCFailureMsg::Malformed(
+                                                                       msgs::UpdateFailMalformedHTLC {
+                                                                               channel_id: msg.channel_id,
+                                                                               htlc_id: msg.htlc_id,
+                                                                               sha256_of_onion: [0; 32],
+                                                                               failure_code: INVALID_ONION_BLINDING,
+                                                                       }
+                                                       ))
+                                               }
                                                // If the update_add is completely bogus, the call will Err and we will close,
                                                // but if we've sent a shutdown and they haven't acknowledged it yet, we just
                                                // want to reject the new HTLC and fail it backwards instead of forwarding.
@@ -6990,7 +7181,7 @@ where
                                },
                                hash_map::Entry::Vacant(_) => {
                                        log_debug!(logger, "Sending bogus ChannelReestablish for unknown channel {} to force channel closure",
-                                               log_bytes!(msg.channel_id.0));
+                                               msg.channel_id);
                                        // Unfortunately, lnd doesn't force close on errors
                                        // (https://github.com/lightningnetwork/lnd/blob/abb1e3463f3a83bbb843d5c399869dbe930ad94f/htlcswitch/link.go#L2119).
                                        // One of the few ways to get an lnd counterparty to force close is by
@@ -7063,9 +7254,9 @@ where
                                                        Some(cp_id) => Some(cp_id),
                                                        None => {
                                                                // TODO: Once we can rely on the counterparty_node_id from the
-                                                               // monitor event, this and the id_to_peer map should be removed.
-                                                               let id_to_peer = self.id_to_peer.lock().unwrap();
-                                                               id_to_peer.get(&funding_outpoint.to_channel_id()).cloned()
+                                                               // monitor event, this and the outpoint_to_peer map should be removed.
+                                                               let outpoint_to_peer = self.outpoint_to_peer.lock().unwrap();
+                                                               outpoint_to_peer.get(&funding_outpoint).cloned()
                                                        }
                                                };
                                                if let Some(counterparty_node_id) = counterparty_node_id_opt {
@@ -7173,36 +7364,40 @@ where
        /// attempted in every channel, or in the specifically provided channel.
        ///
        /// [`ChannelSigner`]: crate::sign::ChannelSigner
-       #[cfg(test)] // This is only implemented for one signer method, and should be private until we
-                    // actually finish implementing it fully.
+       #[cfg(async_signing)]
        pub fn signer_unblocked(&self, channel_opt: Option<(PublicKey, ChannelId)>) {
                let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
 
                let unblock_chan = |phase: &mut ChannelPhase<SP>, pending_msg_events: &mut Vec<MessageSendEvent>| {
                        let node_id = phase.context().get_counterparty_node_id();
-                       if let ChannelPhase::Funded(chan) = phase {
-                               let msgs = chan.signer_maybe_unblocked(&self.logger);
-                               if let Some(updates) = msgs.commitment_update {
-                                       pending_msg_events.push(events::MessageSendEvent::UpdateHTLCs {
-                                               node_id,
-                                               updates,
-                                       });
-                               }
-                               if let Some(msg) = msgs.funding_signed {
-                                       pending_msg_events.push(events::MessageSendEvent::SendFundingSigned {
-                                               node_id,
-                                               msg,
-                                       });
-                               }
-                               if let Some(msg) = msgs.funding_created {
-                                       pending_msg_events.push(events::MessageSendEvent::SendFundingCreated {
-                                               node_id,
-                                               msg,
-                                       });
+                       match phase {
+                               ChannelPhase::Funded(chan) => {
+                                       let msgs = chan.signer_maybe_unblocked(&self.logger);
+                                       if let Some(updates) = msgs.commitment_update {
+                                               pending_msg_events.push(events::MessageSendEvent::UpdateHTLCs {
+                                                       node_id,
+                                                       updates,
+                                               });
+                                       }
+                                       if let Some(msg) = msgs.funding_signed {
+                                               pending_msg_events.push(events::MessageSendEvent::SendFundingSigned {
+                                                       node_id,
+                                                       msg,
+                                               });
+                                       }
+                                       if let Some(msg) = msgs.channel_ready {
+                                               send_channel_ready!(self, pending_msg_events, chan, msg);
+                                       }
                                }
-                               if let Some(msg) = msgs.channel_ready {
-                                       send_channel_ready!(self, pending_msg_events, chan, msg);
+                               ChannelPhase::UnfundedOutboundV1(chan) => {
+                                       if let Some(msg) = chan.signer_maybe_unblocked(&self.logger) {
+                                               pending_msg_events.push(events::MessageSendEvent::SendFundingCreated {
+                                                       node_id,
+                                                       msg,
+                                               });
+                                       }
                                }
+                               ChannelPhase::UnfundedInboundV1(_) => {},
                        }
                };
 
@@ -7330,32 +7525,43 @@ where
        ///
        /// # Privacy
        ///
-       /// Uses a one-hop [`BlindedPath`] for the offer with [`ChannelManager::get_our_node_id`] as the
-       /// introduction node and a derived signing pubkey for recipient privacy. As such, currently,
-       /// the node must be announced. Otherwise, there is no way to find a path to the introduction
-       /// node in order to send the [`InvoiceRequest`].
+       /// Uses [`MessageRouter::create_blinded_paths`] to construct a [`BlindedPath`] for the offer.
+       /// However, if one is not found, uses a one-hop [`BlindedPath`] with
+       /// [`ChannelManager::get_our_node_id`] as the introduction node instead. In the latter case,
+       /// the node must be announced, otherwise, there is no way to find a path to the introduction in
+       /// order to send the [`InvoiceRequest`].
+       ///
+       /// Also, uses a derived signing pubkey in the offer for recipient privacy.
        ///
        /// # Limitations
        ///
        /// Requires a direct connection to the introduction node in the responding [`InvoiceRequest`]'s
        /// reply path.
        ///
+       /// # Errors
+       ///
+       /// Errors if the parameterized [`Router`] is unable to create a blinded path for the offer.
+       ///
        /// This is not exported to bindings users as builder patterns don't map outside of move semantics.
        ///
        /// [`Offer`]: crate::offers::offer::Offer
        /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
        pub fn create_offer_builder(
                &self, description: String
-       ) -> OfferBuilder<DerivedMetadata, secp256k1::All> {
+       ) -> Result<OfferBuilder<DerivedMetadata, secp256k1::All>, Bolt12SemanticError> {
                let node_id = self.get_our_node_id();
                let expanded_key = &self.inbound_payment_key;
                let entropy = &*self.entropy_source;
                let secp_ctx = &self.secp_ctx;
-               let path = self.create_one_hop_blinded_path();
 
-               OfferBuilder::deriving_signing_pubkey(description, node_id, expanded_key, entropy, secp_ctx)
+               let path = self.create_blinded_path().map_err(|_| Bolt12SemanticError::MissingPaths)?;
+               let builder = OfferBuilder::deriving_signing_pubkey(
+                       description, node_id, expanded_key, entropy, secp_ctx
+               )
                        .chain_hash(self.chain_hash)
-                       .path(path)
+                       .path(path);
+
+               Ok(builder)
        }
 
        /// Creates a [`RefundBuilder`] such that the [`Refund`] it builds is recognized by the
@@ -7380,10 +7586,13 @@ where
        ///
        /// # Privacy
        ///
-       /// Uses a one-hop [`BlindedPath`] for the refund with [`ChannelManager::get_our_node_id`] as
-       /// the introduction node and a derived payer id for payer privacy. As such, currently, the
-       /// node must be announced. Otherwise, there is no way to find a path to the introduction node
-       /// in order to send the [`Bolt12Invoice`].
+       /// Uses [`MessageRouter::create_blinded_paths`] to construct a [`BlindedPath`] for the refund.
+       /// However, if one is not found, uses a one-hop [`BlindedPath`] with
+       /// [`ChannelManager::get_our_node_id`] as the introduction node instead. In the latter case,
+       /// the node must be announced, otherwise, there is no way to find a path to the introduction in
+       /// order to send the [`Bolt12Invoice`].
+       ///
+       /// Also, uses a derived payer id in the refund for payer privacy.
        ///
        /// # Limitations
        ///
@@ -7392,14 +7601,17 @@ where
        ///
        /// # Errors
        ///
-       /// Errors if a duplicate `payment_id` is provided given the caveats in the aforementioned link
-       /// or if `amount_msats` is invalid.
+       /// Errors if:
+       /// - a duplicate `payment_id` is provided given the caveats in the aforementioned link,
+       /// - `amount_msats` is invalid, or
+       /// - the parameterized [`Router`] is unable to create a blinded path for the refund.
        ///
        /// This is not exported to bindings users as builder patterns don't map outside of move semantics.
        ///
        /// [`Refund`]: crate::offers::refund::Refund
        /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
        /// [`Bolt12Invoice::payment_paths`]: crate::offers::invoice::Bolt12Invoice::payment_paths
+       /// [Avoiding Duplicate Payments]: #avoiding-duplicate-payments
        pub fn create_refund_builder(
                &self, description: String, amount_msats: u64, absolute_expiry: Duration,
                payment_id: PaymentId, retry_strategy: Retry, max_total_routing_fee_msat: Option<u64>
@@ -7408,8 +7620,8 @@ where
                let expanded_key = &self.inbound_payment_key;
                let entropy = &*self.entropy_source;
                let secp_ctx = &self.secp_ctx;
-               let path = self.create_one_hop_blinded_path();
 
+               let path = self.create_blinded_path().map_err(|_| Bolt12SemanticError::MissingPaths)?;
                let builder = RefundBuilder::deriving_payer_id(
                        description, node_id, expanded_key, entropy, secp_ctx, amount_msats, payment_id
                )?
@@ -7467,8 +7679,11 @@ where
        ///
        /// # Errors
        ///
-       /// Errors if a duplicate `payment_id` is provided given the caveats in the aforementioned link
-       /// or if the provided parameters are invalid for the offer.
+       /// Errors if:
+       /// - a duplicate `payment_id` is provided given the caveats in the aforementioned link,
+       /// - the provided parameters are invalid for the offer,
+       /// - the parameterized [`Router`] is unable to create a blinded reply path for the invoice
+       ///   request.
        ///
        /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
        /// [`InvoiceRequest::quantity`]: crate::offers::invoice_request::InvoiceRequest::quantity
@@ -7501,9 +7716,8 @@ where
                        None => builder,
                        Some(payer_note) => builder.payer_note(payer_note),
                };
-
                let invoice_request = builder.build_and_sign()?;
-               let reply_path = self.create_one_hop_blinded_path();
+               let reply_path = self.create_blinded_path().map_err(|_| Bolt12SemanticError::MissingPaths)?;
 
                let expiration = StaleExpiration::TimerTicks(1);
                self.pending_outbound_payments
@@ -7552,6 +7766,11 @@ where
        /// node meeting the aforementioned criteria, but there's no guarantee that they will be
        /// received and no retries will be made.
        ///
+       /// # Errors
+       ///
+       /// Errors if the parameterized [`Router`] is unable to create a blinded payment path or reply
+       /// path for the invoice.
+       ///
        /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
        pub fn request_refund_payment(&self, refund: &Refund) -> Result<(), Bolt12SemanticError> {
                let expanded_key = &self.inbound_payment_key;
@@ -7563,9 +7782,9 @@ where
 
                match self.create_inbound_payment(Some(amount_msats), relative_expiry, None) {
                        Ok((payment_hash, payment_secret)) => {
-                               let payment_paths = vec![
-                                       self.create_one_hop_blinded_payment_path(payment_secret),
-                               ];
+                               let payment_paths = self.create_blinded_payment_paths(amount_msats, payment_secret)
+                                       .map_err(|_| Bolt12SemanticError::MissingPaths)?;
+
                                #[cfg(not(feature = "no-std"))]
                                let builder = refund.respond_using_derived_keys(
                                        payment_paths, payment_hash, expanded_key, entropy
@@ -7579,7 +7798,8 @@ where
                                        payment_paths, payment_hash, created_at, expanded_key, entropy
                                )?;
                                let invoice = builder.allow_mpp().build_and_sign(secp_ctx)?;
-                               let reply_path = self.create_one_hop_blinded_path();
+                               let reply_path = self.create_blinded_path()
+                                       .map_err(|_| Bolt12SemanticError::MissingPaths)?;
 
                                let mut pending_offers_messages = self.pending_offers_messages.lock().unwrap();
                                if refund.paths().is_empty() {
@@ -7706,24 +7926,37 @@ where
                inbound_payment::get_payment_preimage(payment_hash, payment_secret, &self.inbound_payment_key)
        }
 
-       /// Creates a one-hop blinded path with [`ChannelManager::get_our_node_id`] as the introduction
-       /// node.
-       fn create_one_hop_blinded_path(&self) -> BlindedPath {
+       /// Creates a blinded path by delegating to [`MessageRouter::create_blinded_paths`].
+       ///
+       /// Errors if the `MessageRouter` errors or returns an empty `Vec`.
+       fn create_blinded_path(&self) -> Result<BlindedPath, ()> {
+               let recipient = self.get_our_node_id();
                let entropy_source = self.entropy_source.deref();
                let secp_ctx = &self.secp_ctx;
-               BlindedPath::one_hop_for_message(self.get_our_node_id(), entropy_source, secp_ctx).unwrap()
+
+               let peers = self.per_peer_state.read().unwrap()
+                       .iter()
+                       .filter(|(_, peer)| peer.lock().unwrap().latest_features.supports_onion_messages())
+                       .map(|(node_id, _)| *node_id)
+                       .collect::<Vec<_>>();
+
+               self.router
+                       .create_blinded_paths(recipient, peers, entropy_source, secp_ctx)
+                       .and_then(|paths| paths.into_iter().next().ok_or(()))
        }
 
-       /// Creates a one-hop blinded path with [`ChannelManager::get_our_node_id`] as the introduction
-       /// node.
-       fn create_one_hop_blinded_payment_path(
-               &self, payment_secret: PaymentSecret
-       ) -> (BlindedPayInfo, BlindedPath) {
+       /// Creates multi-hop blinded payment paths for the given `amount_msats` by delegating to
+       /// [`Router::create_blinded_payment_paths`].
+       fn create_blinded_payment_paths(
+               &self, amount_msats: u64, payment_secret: PaymentSecret
+       ) -> Result<Vec<(BlindedPayInfo, BlindedPath)>, ()> {
                let entropy_source = self.entropy_source.deref();
                let secp_ctx = &self.secp_ctx;
 
+               let first_hops = self.list_usable_channels();
                let payee_node_id = self.get_our_node_id();
-               let max_cltv_expiry = self.best_block.read().unwrap().height() + LATENCY_GRACE_PERIOD_BLOCKS;
+               let max_cltv_expiry = self.best_block.read().unwrap().height() + CLTV_FAR_FAR_AWAY
+                       + LATENCY_GRACE_PERIOD_BLOCKS;
                let payee_tlvs = ReceiveTlvs {
                        payment_secret,
                        payment_constraints: PaymentConstraints {
@@ -7731,10 +7964,9 @@ where
                                htlc_minimum_msat: 1,
                        },
                };
-               // TODO: Err for overflow?
-               BlindedPath::one_hop_for_payment(
-                       payee_node_id, payee_tlvs, entropy_source, secp_ctx
-               ).unwrap()
+               self.router.create_blinded_payment_paths(
+                       payee_node_id, first_hops, payee_tlvs, amount_msats, entropy_source, secp_ctx
+               )
        }
 
        /// Gets a fake short channel id for use in receiving [phantom node payments]. These fake scids
@@ -8781,13 +9013,7 @@ where
                                let pending_msg_events = &mut peer_state.pending_msg_events;
 
                                peer_state.channel_by_id.iter_mut().filter_map(|(_, phase)|
-                                       if let ChannelPhase::Funded(chan) = phase { Some(chan) } else {
-                                               // Since unfunded channel maps are cleared upon disconnecting a peer, and they're not persisted
-                                               // (so won't be recovered after a crash), they shouldn't exist here and we would never need to
-                                               // worry about closing and removing them.
-                                               debug_assert!(false);
-                                               None
-                                       }
+                                       if let ChannelPhase::Funded(chan) = phase { Some(chan) } else { None }
                                ).for_each(|chan| {
                                        let logger = WithChannelContext::from(&self.logger, &chan.context);
                                        pending_msg_events.push(events::MessageSendEvent::SendChannelReestablish {
@@ -8974,7 +9200,7 @@ where
                                let amount_msats = match InvoiceBuilder::<DerivedSigningPubkey>::amount_msats(
                                        &invoice_request
                                ) {
-                                       Ok(amount_msats) => Some(amount_msats),
+                                       Ok(amount_msats) => amount_msats,
                                        Err(error) => return Some(OffersMessage::InvoiceError(error.into())),
                                };
                                let invoice_request = match invoice_request.verify(expanded_key, secp_ctx) {
@@ -8984,64 +9210,69 @@ where
                                                return Some(OffersMessage::InvoiceError(error.into()));
                                        },
                                };
-                               let relative_expiry = DEFAULT_RELATIVE_EXPIRY.as_secs() as u32;
 
-                               match self.create_inbound_payment(amount_msats, relative_expiry, None) {
-                                       Ok((payment_hash, payment_secret)) if invoice_request.keys.is_some() => {
-                                               let payment_paths = vec![
-                                                       self.create_one_hop_blinded_payment_path(payment_secret),
-                                               ];
-                                               #[cfg(not(feature = "no-std"))]
-                                               let builder = invoice_request.respond_using_derived_keys(
-                                                       payment_paths, payment_hash
-                                               );
-                                               #[cfg(feature = "no-std")]
-                                               let created_at = Duration::from_secs(
-                                                       self.highest_seen_timestamp.load(Ordering::Acquire) as u64
-                                               );
-                                               #[cfg(feature = "no-std")]
-                                               let builder = invoice_request.respond_using_derived_keys_no_std(
-                                                       payment_paths, payment_hash, created_at
-                                               );
-                                               match builder.and_then(|b| b.allow_mpp().build_and_sign(secp_ctx)) {
-                                                       Ok(invoice) => Some(OffersMessage::Invoice(invoice)),
-                                                       Err(error) => Some(OffersMessage::InvoiceError(error.into())),
-                                               }
-                                       },
-                                       Ok((payment_hash, payment_secret)) => {
-                                               let payment_paths = vec![
-                                                       self.create_one_hop_blinded_payment_path(payment_secret),
-                                               ];
-                                               #[cfg(not(feature = "no-std"))]
-                                               let builder = invoice_request.respond_with(payment_paths, payment_hash);
-                                               #[cfg(feature = "no-std")]
-                                               let created_at = Duration::from_secs(
-                                                       self.highest_seen_timestamp.load(Ordering::Acquire) as u64
-                                               );
-                                               #[cfg(feature = "no-std")]
-                                               let builder = invoice_request.respond_with_no_std(
-                                                       payment_paths, payment_hash, created_at
-                                               );
-                                               let response = builder.and_then(|builder| builder.allow_mpp().build())
-                                                       .map_err(|e| OffersMessage::InvoiceError(e.into()))
-                                                       .and_then(|invoice|
-                                                               match invoice.sign(|invoice| self.node_signer.sign_bolt12_invoice(invoice)) {
-                                                                       Ok(invoice) => Ok(OffersMessage::Invoice(invoice)),
-                                                                       Err(SignError::Signing(())) => Err(OffersMessage::InvoiceError(
-                                                                                       InvoiceError::from_string("Failed signing invoice".to_string())
-                                                                       )),
-                                                                       Err(SignError::Verification(_)) => Err(OffersMessage::InvoiceError(
-                                                                                       InvoiceError::from_string("Failed invoice signature verification".to_string())
-                                                                       )),
-                                                               });
-                                               match response {
-                                                       Ok(invoice) => Some(invoice),
-                                                       Err(error) => Some(error),
-                                               }
+                               let relative_expiry = DEFAULT_RELATIVE_EXPIRY.as_secs() as u32;
+                               let (payment_hash, payment_secret) = match self.create_inbound_payment(
+                                       Some(amount_msats), relative_expiry, None
+                               ) {
+                                       Ok((payment_hash, payment_secret)) => (payment_hash, payment_secret),
+                                       Err(()) => {
+                                               let error = Bolt12SemanticError::InvalidAmount;
+                                               return Some(OffersMessage::InvoiceError(error.into()));
                                        },
+                               };
+
+                               let payment_paths = match self.create_blinded_payment_paths(
+                                       amount_msats, payment_secret
+                               ) {
+                                       Ok(payment_paths) => payment_paths,
                                        Err(()) => {
-                                               Some(OffersMessage::InvoiceError(Bolt12SemanticError::InvalidAmount.into()))
+                                               let error = Bolt12SemanticError::MissingPaths;
+                                               return Some(OffersMessage::InvoiceError(error.into()));
                                        },
+                               };
+
+                               #[cfg(feature = "no-std")]
+                               let created_at = Duration::from_secs(
+                                       self.highest_seen_timestamp.load(Ordering::Acquire) as u64
+                               );
+
+                               if invoice_request.keys.is_some() {
+                                       #[cfg(not(feature = "no-std"))]
+                                       let builder = invoice_request.respond_using_derived_keys(
+                                               payment_paths, payment_hash
+                                       );
+                                       #[cfg(feature = "no-std")]
+                                       let builder = invoice_request.respond_using_derived_keys_no_std(
+                                               payment_paths, payment_hash, created_at
+                                       );
+                                       match builder.and_then(|b| b.allow_mpp().build_and_sign(secp_ctx)) {
+                                               Ok(invoice) => Some(OffersMessage::Invoice(invoice)),
+                                               Err(error) => Some(OffersMessage::InvoiceError(error.into())),
+                                       }
+                               } else {
+                                       #[cfg(not(feature = "no-std"))]
+                                       let builder = invoice_request.respond_with(payment_paths, payment_hash);
+                                       #[cfg(feature = "no-std")]
+                                       let builder = invoice_request.respond_with_no_std(
+                                               payment_paths, payment_hash, created_at
+                                       );
+                                       let response = builder.and_then(|builder| builder.allow_mpp().build())
+                                               .map_err(|e| OffersMessage::InvoiceError(e.into()))
+                                               .and_then(|invoice|
+                                                       match invoice.sign(|invoice| self.node_signer.sign_bolt12_invoice(invoice)) {
+                                                               Ok(invoice) => Ok(OffersMessage::Invoice(invoice)),
+                                                               Err(SignError::Signing(())) => Err(OffersMessage::InvoiceError(
+                                                                               InvoiceError::from_string("Failed signing invoice".to_string())
+                                                               )),
+                                                               Err(SignError::Verification(_)) => Err(OffersMessage::InvoiceError(
+                                                                               InvoiceError::from_string("Failed invoice signature verification".to_string())
+                                                               )),
+                                                       });
+                                       match response {
+                                               Ok(invoice) => Some(invoice),
+                                               Err(error) => Some(error),
+                                       }
                                }
                        },
                        OffersMessage::Invoice(invoice) => {
@@ -9287,6 +9518,7 @@ impl_writeable_tlv_based_enum!(PendingHTLCRouting,
                (2, incoming_cltv_expiry, required),
                (3, payment_metadata, option),
                (5, custom_tlvs, optional_vec),
+               (7, requires_blinded_error, (default_value, false)),
        },
        (2, ReceiveKeysend) => {
                (0, payment_preimage, required),
@@ -9381,7 +9613,8 @@ impl_writeable_tlv_based_enum!(PendingHTLCStatus, ;
 );
 
 impl_writeable_tlv_based_enum!(BlindedFailure,
-       (0, FromIntroductionNode) => {}, ;
+       (0, FromIntroductionNode) => {},
+       (2, FromBlindedNode) => {}, ;
 );
 
 impl_writeable_tlv_based!(HTLCPreviousHopData, {
@@ -9545,13 +9778,68 @@ impl_writeable_tlv_based!(PendingAddHTLCInfo, {
        (6, prev_funding_outpoint, required),
 });
 
-impl_writeable_tlv_based_enum!(HTLCForwardInfo,
-       (1, FailHTLC) => {
-               (0, htlc_id, required),
-               (2, err_packet, required),
-       };
-       (0, AddHTLC)
-);
+impl Writeable for HTLCForwardInfo {
+       fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
+               const FAIL_HTLC_VARIANT_ID: u8 = 1;
+               match self {
+                       Self::AddHTLC(info) => {
+                               0u8.write(w)?;
+                               info.write(w)?;
+                       },
+                       Self::FailHTLC { htlc_id, err_packet } => {
+                               FAIL_HTLC_VARIANT_ID.write(w)?;
+                               write_tlv_fields!(w, {
+                                       (0, htlc_id, required),
+                                       (2, err_packet, required),
+                               });
+                       },
+                       Self::FailMalformedHTLC { htlc_id, failure_code, sha256_of_onion } => {
+                               // Since this variant was added in 0.0.119, write this as `::FailHTLC` with an empty error
+                               // packet so older versions have something to fail back with, but serialize the real data as
+                               // optional TLVs for the benefit of newer versions.
+                               FAIL_HTLC_VARIANT_ID.write(w)?;
+                               let dummy_err_packet = msgs::OnionErrorPacket { data: Vec::new() };
+                               write_tlv_fields!(w, {
+                                       (0, htlc_id, required),
+                                       (1, failure_code, required),
+                                       (2, dummy_err_packet, required),
+                                       (3, sha256_of_onion, required),
+                               });
+                       },
+               }
+               Ok(())
+       }
+}
+
+impl Readable for HTLCForwardInfo {
+       fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
+               let id: u8 = Readable::read(r)?;
+               Ok(match id {
+                       0 => Self::AddHTLC(Readable::read(r)?),
+                       1 => {
+                               _init_and_read_len_prefixed_tlv_fields!(r, {
+                                       (0, htlc_id, required),
+                                       (1, malformed_htlc_failure_code, option),
+                                       (2, err_packet, required),
+                                       (3, sha256_of_onion, option),
+                               });
+                               if let Some(failure_code) = malformed_htlc_failure_code {
+                                       Self::FailMalformedHTLC {
+                                               htlc_id: _init_tlv_based_struct_field!(htlc_id, required),
+                                               failure_code,
+                                               sha256_of_onion: sha256_of_onion.ok_or(DecodeError::InvalidValue)?,
+                                       }
+                               } else {
+                                       Self::FailHTLC {
+                                               htlc_id: _init_tlv_based_struct_field!(htlc_id, required),
+                                               err_packet: _init_tlv_based_struct_field!(err_packet, required),
+                                       }
+                               }
+                       },
+                       _ => return Err(DecodeError::InvalidValue),
+               })
+       }
+}
 
 impl_writeable_tlv_based!(PendingInboundPayment, {
        (0, payment_secret, required),
@@ -9997,7 +10285,7 @@ where
                let channel_count: u64 = Readable::read(reader)?;
                let mut funding_txo_set = HashSet::with_capacity(cmp::min(channel_count as usize, 128));
                let mut funded_peer_channels: HashMap<PublicKey, HashMap<ChannelId, ChannelPhase<SP>>> = HashMap::with_capacity(cmp::min(channel_count as usize, 128));
-               let mut id_to_peer = HashMap::with_capacity(cmp::min(channel_count as usize, 128));
+               let mut outpoint_to_peer = HashMap::with_capacity(cmp::min(channel_count as usize, 128));
                let mut short_to_chan_info = HashMap::with_capacity(cmp::min(channel_count as usize, 128));
                let mut channel_closures = VecDeque::new();
                let mut close_background_events = Vec::new();
@@ -10017,7 +10305,7 @@ where
                                        log_error!(logger, "A ChannelManager is stale compared to the current ChannelMonitor!");
                                        log_error!(logger, " The channel will be force-closed and the latest commitment transaction from the ChannelMonitor broadcast.");
                                        if channel.context.get_latest_monitor_update_id() < monitor.get_latest_update_id() {
-                                               log_error!(args.logger, " The ChannelMonitor for channel {} is at update_id {} but the ChannelManager is at update_id {}.",
+                                               log_error!(logger, " The ChannelMonitor for channel {} is at update_id {} but the ChannelManager is at update_id {}.",
                                                        &channel.context.channel_id(), monitor.get_latest_update_id(), channel.context.get_latest_monitor_update_id());
                                        }
                                        if channel.get_cur_holder_commitment_transaction_number() > monitor.get_cur_holder_commitment_number() {
@@ -10075,8 +10363,8 @@ where
                                        if let Some(short_channel_id) = channel.context.get_short_channel_id() {
                                                short_to_chan_info.insert(short_channel_id, (channel.context.get_counterparty_node_id(), channel.context.channel_id()));
                                        }
-                                       if channel.context.is_funding_broadcast() {
-                                               id_to_peer.insert(channel.context.channel_id(), channel.context.get_counterparty_node_id());
+                                       if let Some(funding_txo) = channel.context.get_funding_txo() {
+                                               outpoint_to_peer.insert(funding_txo, channel.context.get_counterparty_node_id());
                                        }
                                        match funded_peer_channels.entry(channel.context.get_counterparty_node_id()) {
                                                hash_map::Entry::Occupied(mut entry) => {
@@ -10119,6 +10407,7 @@ where
                                        &funding_txo.to_channel_id());
                                let monitor_update = ChannelMonitorUpdate {
                                        update_id: CLOSED_CHANNEL_UPDATE_ID,
+                                       counterparty_node_id: None,
                                        updates: vec![ChannelMonitorUpdateStep::ChannelForceClosed { should_broadcast: true }],
                                };
                                close_background_events.push(BackgroundEvent::ClosedMonitorUpdateRegeneratedOnStartup((*funding_txo, monitor_update)));
@@ -10410,10 +10699,9 @@ where
                        // We only rebuild the pending payments map if we were most recently serialized by
                        // 0.0.102+
                        for (_, monitor) in args.channel_monitors.iter() {
-                               let counterparty_opt = id_to_peer.get(&monitor.get_funding_txo().0.to_channel_id());
-                               let chan_id = monitor.get_funding_txo().0.to_channel_id();
+                               let counterparty_opt = outpoint_to_peer.get(&monitor.get_funding_txo().0);
                                if counterparty_opt.is_none() {
-                                       let logger = WithContext::from(&args.logger, None, Some(chan_id));
+                                       let logger = WithChannelMonitor::from(&args.logger, monitor);
                                        for (htlc_source, (htlc, _)) in monitor.get_pending_or_resolved_outbound_htlcs() {
                                                if let HTLCSource::OutboundRoute { payment_id, session_priv, path, .. } = htlc_source {
                                                        if path.hops.is_empty() {
@@ -10428,7 +10716,7 @@ where
                                                                hash_map::Entry::Occupied(mut entry) => {
                                                                        let newly_added = entry.get_mut().insert(session_priv_bytes, &path);
                                                                        log_info!(logger, "{} a pending payment path for {} msat for session priv {} on an existing pending payment with payment hash {}",
-                                                                               if newly_added { "Added" } else { "Had" }, path_amt, log_bytes!(session_priv_bytes), log_bytes!(htlc.payment_hash.0));
+                                                                               if newly_added { "Added" } else { "Had" }, path_amt, log_bytes!(session_priv_bytes), htlc.payment_hash);
                                                                },
                                                                hash_map::Entry::Vacant(entry) => {
                                                                        let path_fee = path.fee_msat();
@@ -10704,7 +10992,7 @@ where
                                                // without the new monitor persisted - we'll end up right back here on
                                                // restart.
                                                let previous_channel_id = claimable_htlc.prev_hop.outpoint.to_channel_id();
-                                               if let Some(peer_node_id) = id_to_peer.get(&previous_channel_id){
+                                               if let Some(peer_node_id) = outpoint_to_peer.get(&claimable_htlc.prev_hop.outpoint) {
                                                        let peer_state_mutex = per_peer_state.get(peer_node_id).unwrap();
                                                        let mut peer_state_lock = peer_state_mutex.lock().unwrap();
                                                        let peer_state = &mut *peer_state_lock;
@@ -10714,8 +11002,7 @@ where
                                                        }
                                                }
                                                if let Some(previous_hop_monitor) = args.channel_monitors.get(&claimable_htlc.prev_hop.outpoint) {
-                                                       let logger = WithChannelMonitor::from(&args.logger, previous_hop_monitor);
-                                                       previous_hop_monitor.provide_payment_preimage(&payment_hash, &payment_preimage, &args.tx_broadcaster, &bounded_fee_estimator, &&logger);
+                                                       previous_hop_monitor.provide_payment_preimage(&payment_hash, &payment_preimage, &args.tx_broadcaster, &bounded_fee_estimator, &args.logger);
                                                }
                                        }
                                        pending_events_read.push_back((events::Event::PaymentClaimed {
@@ -10783,7 +11070,7 @@ where
                        forward_htlcs: Mutex::new(forward_htlcs),
                        claimable_payments: Mutex::new(ClaimablePayments { claimable_payments, pending_claiming_payments: pending_claiming_payments.unwrap() }),
                        outbound_scid_aliases: Mutex::new(outbound_scid_aliases),
-                       id_to_peer: Mutex::new(id_to_peer),
+                       outpoint_to_peer: Mutex::new(outpoint_to_peer),
                        short_to_chan_info: FairRwLock::new(short_to_chan_info),
                        fake_scid_rand_bytes: fake_scid_rand_bytes.unwrap(),
 
@@ -10848,12 +11135,14 @@ mod tests {
        use crate::events::{Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider, ClosureReason};
        use crate::ln::{PaymentPreimage, PaymentHash, PaymentSecret};
        use crate::ln::ChannelId;
-       use crate::ln::channelmanager::{create_recv_pending_htlc_info, inbound_payment, PaymentId, PaymentSendFailure, RecipientOnionFields, InterceptId};
+       use crate::ln::channelmanager::{create_recv_pending_htlc_info, HTLCForwardInfo, inbound_payment, PaymentId, PaymentSendFailure, RecipientOnionFields, InterceptId};
        use crate::ln::functional_test_utils::*;
        use crate::ln::msgs::{self, ErrorAction};
        use crate::ln::msgs::ChannelMessageHandler;
+       use crate::prelude::*;
        use crate::routing::router::{PaymentParameters, RouteParameters, find_route};
        use crate::util::errors::APIError;
+       use crate::util::ser::Writeable;
        use crate::util::test_utils;
        use crate::util::config::{ChannelConfig, ChannelConfigUpdate};
        use crate::sign::EntropySource;
@@ -11400,8 +11689,8 @@ mod tests {
        }
 
        #[test]
-       fn test_id_to_peer_coverage() {
-               // Test that the `ChannelManager:id_to_peer` contains channels which have been assigned
+       fn test_outpoint_to_peer_coverage() {
+               // Test that the `ChannelManager:outpoint_to_peer` contains channels which have been assigned
                // a `channel_id` (i.e. have had the funding tx created), and that they are removed once
                // the channel is successfully closed.
                let chanmon_cfgs = create_chanmon_cfgs(2);
@@ -11415,42 +11704,42 @@ mod tests {
                let accept_channel = get_event_msg!(nodes[1], MessageSendEvent::SendAcceptChannel, nodes[0].node.get_our_node_id());
                nodes[0].node.handle_accept_channel(&nodes[1].node.get_our_node_id(), &accept_channel);
 
-               let (temporary_channel_id, tx, _funding_output) = create_funding_transaction(&nodes[0], &nodes[1].node.get_our_node_id(), 1_000_000, 42);
+               let (temporary_channel_id, tx, funding_output) = create_funding_transaction(&nodes[0], &nodes[1].node.get_our_node_id(), 1_000_000, 42);
                let channel_id = ChannelId::from_bytes(tx.txid().to_byte_array());
                {
-                       // Ensure that the `id_to_peer` map is empty until either party has received the
+                       // Ensure that the `outpoint_to_peer` map is empty until either party has received the
                        // funding transaction, and have the real `channel_id`.
-                       assert_eq!(nodes[0].node.id_to_peer.lock().unwrap().len(), 0);
-                       assert_eq!(nodes[1].node.id_to_peer.lock().unwrap().len(), 0);
+                       assert_eq!(nodes[0].node.outpoint_to_peer.lock().unwrap().len(), 0);
+                       assert_eq!(nodes[1].node.outpoint_to_peer.lock().unwrap().len(), 0);
                }
 
                nodes[0].node.funding_transaction_generated(&temporary_channel_id, &nodes[1].node.get_our_node_id(), tx.clone()).unwrap();
                {
-                       // Assert that `nodes[0]`'s `id_to_peer` map is populated with the channel as soon as
+                       // Assert that `nodes[0]`'s `outpoint_to_peer` map is populated with the channel as soon as
                        // as it has the funding transaction.
-                       let nodes_0_lock = nodes[0].node.id_to_peer.lock().unwrap();
+                       let nodes_0_lock = nodes[0].node.outpoint_to_peer.lock().unwrap();
                        assert_eq!(nodes_0_lock.len(), 1);
-                       assert!(nodes_0_lock.contains_key(&channel_id));
+                       assert!(nodes_0_lock.contains_key(&funding_output));
                }
 
-               assert_eq!(nodes[1].node.id_to_peer.lock().unwrap().len(), 0);
+               assert_eq!(nodes[1].node.outpoint_to_peer.lock().unwrap().len(), 0);
 
                let funding_created_msg = get_event_msg!(nodes[0], MessageSendEvent::SendFundingCreated, nodes[1].node.get_our_node_id());
 
                nodes[1].node.handle_funding_created(&nodes[0].node.get_our_node_id(), &funding_created_msg);
                {
-                       let nodes_0_lock = nodes[0].node.id_to_peer.lock().unwrap();
+                       let nodes_0_lock = nodes[0].node.outpoint_to_peer.lock().unwrap();
                        assert_eq!(nodes_0_lock.len(), 1);
-                       assert!(nodes_0_lock.contains_key(&channel_id));
+                       assert!(nodes_0_lock.contains_key(&funding_output));
                }
                expect_channel_pending_event(&nodes[1], &nodes[0].node.get_our_node_id());
 
                {
-                       // Assert that `nodes[1]`'s `id_to_peer` map is populated with the channel as soon as
-                       // as it has the funding transaction.
-                       let nodes_1_lock = nodes[1].node.id_to_peer.lock().unwrap();
+                       // Assert that `nodes[1]`'s `outpoint_to_peer` map is populated with the channel as
+                       // soon as it has the funding transaction.
+                       let nodes_1_lock = nodes[1].node.outpoint_to_peer.lock().unwrap();
                        assert_eq!(nodes_1_lock.len(), 1);
-                       assert!(nodes_1_lock.contains_key(&channel_id));
+                       assert!(nodes_1_lock.contains_key(&funding_output));
                }
                check_added_monitors!(nodes[1], 1);
                let funding_signed = get_event_msg!(nodes[1], MessageSendEvent::SendFundingSigned, nodes[0].node.get_our_node_id());
@@ -11469,23 +11758,23 @@ mod tests {
                let closing_signed_node_0 = get_event_msg!(nodes[0], MessageSendEvent::SendClosingSigned, nodes[1].node.get_our_node_id());
                nodes[1].node.handle_closing_signed(&nodes[0].node.get_our_node_id(), &closing_signed_node_0);
                {
-                       // Assert that the channel is kept in the `id_to_peer` map for both nodes until the
+                       // Assert that the channel is kept in the `outpoint_to_peer` map for both nodes until the
                        // channel can be fully closed by both parties (i.e. no outstanding htlcs exists, the
                        // fee for the closing transaction has been negotiated and the parties has the other
                        // party's signature for the fee negotiated closing transaction.)
-                       let nodes_0_lock = nodes[0].node.id_to_peer.lock().unwrap();
+                       let nodes_0_lock = nodes[0].node.outpoint_to_peer.lock().unwrap();
                        assert_eq!(nodes_0_lock.len(), 1);
-                       assert!(nodes_0_lock.contains_key(&channel_id));
+                       assert!(nodes_0_lock.contains_key(&funding_output));
                }
 
                {
                        // At this stage, `nodes[1]` has proposed a fee for the closing transaction in the
                        // `handle_closing_signed` call above. As `nodes[1]` has not yet received the signature
                        // from `nodes[0]` for the closing transaction with the proposed fee, the channel is
-                       // kept in the `nodes[1]`'s `id_to_peer` map.
-                       let nodes_1_lock = nodes[1].node.id_to_peer.lock().unwrap();
+                       // kept in the `nodes[1]`'s `outpoint_to_peer` map.
+                       let nodes_1_lock = nodes[1].node.outpoint_to_peer.lock().unwrap();
                        assert_eq!(nodes_1_lock.len(), 1);
-                       assert!(nodes_1_lock.contains_key(&channel_id));
+                       assert!(nodes_1_lock.contains_key(&funding_output));
                }
 
                nodes[0].node.handle_closing_signed(&nodes[1].node.get_our_node_id(), &get_event_msg!(nodes[1], MessageSendEvent::SendClosingSigned, nodes[0].node.get_our_node_id()));
@@ -11493,24 +11782,24 @@ mod tests {
                        // `nodes[0]` accepts `nodes[1]`'s proposed fee for the closing transaction, and
                        // therefore has all it needs to fully close the channel (both signatures for the
                        // closing transaction).
-                       // Assert that the channel is removed from `nodes[0]`'s `id_to_peer` map as it can be
+                       // Assert that the channel is removed from `nodes[0]`'s `outpoint_to_peer` map as it can be
                        // fully closed by `nodes[0]`.
-                       assert_eq!(nodes[0].node.id_to_peer.lock().unwrap().len(), 0);
+                       assert_eq!(nodes[0].node.outpoint_to_peer.lock().unwrap().len(), 0);
 
-                       // Assert that the channel is still in `nodes[1]`'s  `id_to_peer` map, as `nodes[1]`
+                       // Assert that the channel is still in `nodes[1]`'s  `outpoint_to_peer` map, as `nodes[1]`
                        // doesn't have `nodes[0]`'s signature for the closing transaction yet.
-                       let nodes_1_lock = nodes[1].node.id_to_peer.lock().unwrap();
+                       let nodes_1_lock = nodes[1].node.outpoint_to_peer.lock().unwrap();
                        assert_eq!(nodes_1_lock.len(), 1);
-                       assert!(nodes_1_lock.contains_key(&channel_id));
+                       assert!(nodes_1_lock.contains_key(&funding_output));
                }
 
                let (_nodes_0_update, closing_signed_node_0) = get_closing_signed_broadcast!(nodes[0].node, nodes[1].node.get_our_node_id());
 
                nodes[1].node.handle_closing_signed(&nodes[0].node.get_our_node_id(), &closing_signed_node_0.unwrap());
                {
-                       // Assert that the channel has now been removed from both parties `id_to_peer` map once
+                       // Assert that the channel has now been removed from both parties `outpoint_to_peer` map once
                        // they both have everything required to fully close the channel.
-                       assert_eq!(nodes[1].node.id_to_peer.lock().unwrap().len(), 0);
+                       assert_eq!(nodes[1].node.outpoint_to_peer.lock().unwrap().len(), 0);
                }
                let (_nodes_1_update, _none) = get_closing_signed_broadcast!(nodes[1].node, nodes[0].node.get_our_node_id());
 
@@ -12128,6 +12417,63 @@ mod tests {
                        check_spends!(txn[0], funding_tx);
                }
        }
+
+       #[test]
+       fn test_malformed_forward_htlcs_ser() {
+               // Ensure that `HTLCForwardInfo::FailMalformedHTLC`s are (de)serialized properly.
+               let chanmon_cfg = create_chanmon_cfgs(1);
+               let node_cfg = create_node_cfgs(1, &chanmon_cfg);
+               let persister;
+               let chain_monitor;
+               let chanmgrs = create_node_chanmgrs(1, &node_cfg, &[None]);
+               let deserialized_chanmgr;
+               let mut nodes = create_network(1, &node_cfg, &chanmgrs);
+
+               let dummy_failed_htlc = |htlc_id| {
+                       HTLCForwardInfo::FailHTLC { htlc_id, err_packet: msgs::OnionErrorPacket { data: vec![42] }, }
+               };
+               let dummy_malformed_htlc = |htlc_id| {
+                       HTLCForwardInfo::FailMalformedHTLC { htlc_id, failure_code: 0x4000, sha256_of_onion: [0; 32] }
+               };
+
+               let dummy_htlcs_1: Vec<HTLCForwardInfo> = (1..10).map(|htlc_id| {
+                       if htlc_id % 2 == 0 {
+                               dummy_failed_htlc(htlc_id)
+                       } else {
+                               dummy_malformed_htlc(htlc_id)
+                       }
+               }).collect();
+
+               let dummy_htlcs_2: Vec<HTLCForwardInfo> = (1..10).map(|htlc_id| {
+                       if htlc_id % 2 == 1 {
+                               dummy_failed_htlc(htlc_id)
+                       } else {
+                               dummy_malformed_htlc(htlc_id)
+                       }
+               }).collect();
+
+
+               let (scid_1, scid_2) = (42, 43);
+               let mut forward_htlcs = HashMap::new();
+               forward_htlcs.insert(scid_1, dummy_htlcs_1.clone());
+               forward_htlcs.insert(scid_2, dummy_htlcs_2.clone());
+
+               let mut chanmgr_fwd_htlcs = nodes[0].node.forward_htlcs.lock().unwrap();
+               *chanmgr_fwd_htlcs = forward_htlcs.clone();
+               core::mem::drop(chanmgr_fwd_htlcs);
+
+               reload_node!(nodes[0], nodes[0].node.encode(), &[], persister, chain_monitor, deserialized_chanmgr);
+
+               let mut deserialized_fwd_htlcs = nodes[0].node.forward_htlcs.lock().unwrap();
+               for scid in [scid_1, scid_2].iter() {
+                       let deserialized_htlcs = deserialized_fwd_htlcs.remove(scid).unwrap();
+                       assert_eq!(forward_htlcs.remove(scid).unwrap(), deserialized_htlcs);
+               }
+               assert!(deserialized_fwd_htlcs.is_empty());
+               core::mem::drop(deserialized_fwd_htlcs);
+
+               expect_pending_htlcs_forwardable!(nodes[0]);
+       }
 }
 
 #[cfg(ldk_bench)]