Merge pull request #2903 from jkczyz/2024-02-bindings-builders
authorvalentinewallace <valentinewallace@users.noreply.github.com>
Fri, 8 Mar 2024 21:07:10 +0000 (16:07 -0500)
committerGitHub <noreply@github.com>
Fri, 8 Mar 2024 21:07:10 +0000 (16:07 -0500)
Offers builders for C-bindings

1  2 
lightning/src/ln/channelmanager.rs
lightning/src/offers/offer.rs
lightning/src/offers/refund.rs

index 11413db612ab6fa11bbcdad31cf2cad2d57ccb51,00e281adf438512305b9796a24a052c2b0d52866..8dfd0c8fcaa32538de27b5f255a8b013e3939e26
@@@ -44,7 -44,6 +44,7 @@@ use crate::events::{Event, EventHandler
  // construct one themselves.
  use crate::ln::{inbound_payment, ChannelId, PaymentHash, PaymentPreimage, PaymentSecret};
  use crate::ln::channel::{self, Channel, ChannelPhase, ChannelContext, ChannelError, ChannelUpdateStatus, ShutdownResult, UnfundedChannelContext, UpdateFulfillCommitFetch, OutboundV1Channel, InboundV1Channel, WithChannelContext};
 +pub use crate::ln::channel::{InboundHTLCDetails, InboundHTLCStateDetails, OutboundHTLCDetails, OutboundHTLCStateDetails};
  use crate::ln::features::{Bolt12InvoiceFeatures, ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures};
  #[cfg(any(feature = "_test_utils", test))]
  use crate::ln::features::Bolt11InvoiceFeatures;
@@@ -58,10 -57,11 +58,11 @@@ use crate::ln::msgs::{ChannelMessageHan
  use crate::ln::outbound_payment;
  use crate::ln::outbound_payment::{Bolt12PaymentError, OutboundPayments, PaymentAttempts, PendingOutboundPayment, SendAlongPathArgs, StaleExpiration};
  use crate::ln::wire::Encode;
- use crate::offers::invoice::{BlindedPayInfo, Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, DerivedSigningPubkey, InvoiceBuilder};
+ use crate::offers::invoice::{BlindedPayInfo, Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, DerivedSigningPubkey, ExplicitSigningPubkey, InvoiceBuilder, UnsignedBolt12Invoice};
  use crate::offers::invoice_error::InvoiceError;
+ use crate::offers::invoice_request::{DerivedPayerId, InvoiceRequestBuilder};
  use crate::offers::merkle::SignError;
- use crate::offers::offer::{DerivedMetadata, Offer, OfferBuilder};
+ use crate::offers::offer::{Offer, OfferBuilder};
  use crate::offers::parse::Bolt12SemanticError;
  use crate::offers::refund::{Refund, RefundBuilder};
  use crate::onion_message::messenger::{Destination, MessageRouter, PendingOnionMessage, new_pending_onion_message};
@@@ -77,11 -77,17 +78,17 @@@ use crate::util::logger::{Level, Logger
  use crate::util::errors::APIError;
  #[cfg(not(c_bindings))]
  use {
+       crate::offers::offer::DerivedMetadata,
        crate::routing::router::DefaultRouter,
        crate::routing::gossip::NetworkGraph,
        crate::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringFeeParameters},
        crate::sign::KeysManager,
  };
+ #[cfg(c_bindings)]
+ use {
+       crate::offers::offer::OfferWithDerivedMetadataBuilder,
+       crate::offers::refund::RefundMaybeWithDerivedMetadataBuilder,
+ };
  
  use alloc::collections::{btree_map, BTreeMap};
  
@@@ -289,7 -295,6 +296,7 @@@ pub(super) struct PendingAddHTLCInfo 
        // Note that this may be an outbound SCID alias for the associated channel.
        prev_short_channel_id: u64,
        prev_htlc_id: u64,
 +      prev_channel_id: ChannelId,
        prev_funding_outpoint: OutPoint,
        prev_user_channel_id: u128,
  }
@@@ -330,7 -335,6 +337,7 @@@ pub(crate) struct HTLCPreviousHopData 
        incoming_packet_shared_secret: [u8; 32],
        phantom_shared_secret: Option<[u8; 32]>,
        blinded_failure: Option<BlindedFailure>,
 +      channel_id: ChannelId,
  
        // This field is consumed by `claim_funds_from_hop()` when updating a force-closed backwards
        // channel with a preimage provided by the forward channel.
@@@ -371,7 -375,7 +378,7 @@@ struct ClaimableHTLC 
  impl From<&ClaimableHTLC> for events::ClaimedHTLC {
        fn from(val: &ClaimableHTLC) -> Self {
                events::ClaimedHTLC {
 -                      channel_id: val.prev_hop.outpoint.to_channel_id(),
 +                      channel_id: val.prev_hop.channel_id,
                        user_channel_id: val.prev_hop.user_channel_id.unwrap_or(0),
                        cltv_expiry: val.cltv_expiry,
                        value_msat: val.value,
@@@ -710,7 -714,7 +717,7 @@@ enum BackgroundEvent 
        ///
        /// Note that any such events are lost on shutdown, so in general they must be updates which
        /// are regenerated on startup.
 -      ClosedMonitorUpdateRegeneratedOnStartup((OutPoint, ChannelMonitorUpdate)),
 +      ClosedMonitorUpdateRegeneratedOnStartup((OutPoint, ChannelId, ChannelMonitorUpdate)),
        /// Handle a ChannelMonitorUpdate which may or may not close the channel and may unblock the
        /// channel to continue normal operation.
        ///
        MonitorUpdateRegeneratedOnStartup {
                counterparty_node_id: PublicKey,
                funding_txo: OutPoint,
 +              channel_id: ChannelId,
                update: ChannelMonitorUpdate
        },
        /// Some [`ChannelMonitorUpdate`] (s) completed before we were serialized but we still have
@@@ -753,7 -756,7 +760,7 @@@ pub(crate) enum MonitorUpdateCompletion
        /// outbound edge.
        EmitEventAndFreeOtherChannel {
                event: events::Event,
 -              downstream_counterparty_and_funding_outpoint: Option<(PublicKey, OutPoint, RAAMonitorUpdateBlockingAction)>,
 +              downstream_counterparty_and_funding_outpoint: Option<(PublicKey, OutPoint, ChannelId, RAAMonitorUpdateBlockingAction)>,
        },
        /// Indicates we should immediately resume the operation of another channel, unless there is
        /// some other reason why the channel is blocked. In practice this simply means immediately
                downstream_counterparty_node_id: PublicKey,
                downstream_funding_outpoint: OutPoint,
                blocking_action: RAAMonitorUpdateBlockingAction,
 +              downstream_channel_id: ChannelId,
        },
  }
  
@@@ -783,9 -785,6 +790,9 @@@ impl_writeable_tlv_based_enum_upgradabl
                (0, downstream_counterparty_node_id, required),
                (2, downstream_funding_outpoint, required),
                (4, blocking_action, required),
 +              // Note that by the time we get past the required read above, downstream_funding_outpoint will be
 +              // filled in, so we can safely unwrap it here.
 +              (5, downstream_channel_id, (default_value, ChannelId::v1_from_funding_outpoint(downstream_funding_outpoint.0.unwrap()))),
        },
        (2, EmitEventAndFreeOtherChannel) => {
                (0, event, upgradable_required),
@@@ -803,16 -802,12 +810,16 @@@ pub(crate) enum EventCompletionAction 
        ReleaseRAAChannelMonitorUpdate {
                counterparty_node_id: PublicKey,
                channel_funding_outpoint: OutPoint,
 +              channel_id: ChannelId,
        },
  }
  impl_writeable_tlv_based_enum!(EventCompletionAction,
        (0, ReleaseRAAChannelMonitorUpdate) => {
                (0, channel_funding_outpoint, required),
                (2, counterparty_node_id, required),
 +              // Note that by the time we get past the required read above, channel_funding_outpoint will be
 +              // filled in, so we can safely unwrap it here.
 +              (3, channel_id, (default_value, ChannelId::v1_from_funding_outpoint(channel_funding_outpoint.0.unwrap()))),
        };
  );
  
@@@ -834,7 -829,7 +841,7 @@@ pub(crate) enum RAAMonitorUpdateBlockin
  impl RAAMonitorUpdateBlockingAction {
        fn from_prev_hop_data(prev_hop: &HTLCPreviousHopData) -> Self {
                Self::ForwardedPaymentInboundClaim {
 -                      channel_id: prev_hop.outpoint.to_channel_id(),
 +                      channel_id: prev_hop.channel_id,
                        htlc_id: prev_hop.htlc_id,
                }
        }
@@@ -904,16 -899,7 +911,16 @@@ impl <SP: Deref> PeerState<SP> where SP
                if require_disconnected && self.is_connected {
                        return false
                }
 -              self.channel_by_id.iter().filter(|(_, phase)| matches!(phase, ChannelPhase::Funded(_))).count() == 0
 +              !self.channel_by_id.iter().any(|(_, phase)|
 +                      match phase {
 +                              ChannelPhase::Funded(_) | ChannelPhase::UnfundedOutboundV1(_) => true,
 +                              ChannelPhase::UnfundedInboundV1(_) => false,
 +                              #[cfg(dual_funding)]
 +                              ChannelPhase::UnfundedOutboundV2(_) => true,
 +                              #[cfg(dual_funding)]
 +                              ChannelPhase::UnfundedInboundV2(_) => false,
 +                      }
 +              )
                        && self.monitor_update_blocked_actions.is_empty()
                        && self.in_flight_monitor_updates.is_empty()
        }
@@@ -985,7 -971,6 +992,7 @@@ pub type SimpleArcChannelManager<M, T, 
        Arc<DefaultRouter<
                Arc<NetworkGraph<Arc<L>>>,
                Arc<L>,
 +              Arc<KeysManager>,
                Arc<RwLock<ProbabilisticScorer<Arc<NetworkGraph<Arc<L>>>, Arc<L>>>>,
                ProbabilisticScoringFeeParameters,
                ProbabilisticScorer<Arc<NetworkGraph<Arc<L>>>, Arc<L>>,
@@@ -1016,7 -1001,6 +1023,7 @@@ pub type SimpleRefChannelManager<'a, 'b
                &'e DefaultRouter<
                        &'f NetworkGraph<&'g L>,
                        &'g L,
 +                      &'c KeysManager,
                        &'h RwLock<ProbabilisticScorer<&'f NetworkGraph<&'g L>, &'g L>>,
                        ProbabilisticScoringFeeParameters,
                        ProbabilisticScorer<&'f NetworkGraph<&'g L>, &'g L>
@@@ -1648,6 -1632,9 +1655,6 @@@ pub struct ChannelDetails 
        pub counterparty: ChannelCounterparty,
        /// The Channel's funding transaction output, if we've negotiated the funding transaction with
        /// our counterparty already.
 -      ///
 -      /// Note that, if this has been set, `channel_id` will be equivalent to
 -      /// `funding_txo.unwrap().to_channel_id()`.
        pub funding_txo: Option<OutPoint>,
        /// The features which this channel operates with. See individual features for more info.
        ///
        ///
        /// This field is only `None` for `ChannelDetails` objects serialized prior to LDK 0.0.109.
        pub config: Option<ChannelConfig>,
 +      /// Pending inbound HTLCs.
 +      ///
 +      /// This field is empty for objects serialized with LDK versions prior to 0.0.122.
 +      pub pending_inbound_htlcs: Vec<InboundHTLCDetails>,
 +      /// Pending outbound HTLCs.
 +      ///
 +      /// This field is empty for objects serialized with LDK versions prior to 0.0.122.
 +      pub pending_outbound_htlcs: Vec<OutboundHTLCDetails>,
  }
  
  impl ChannelDetails {
                        inbound_htlc_maximum_msat: context.get_holder_htlc_maximum_msat(),
                        config: Some(context.config()),
                        channel_shutdown_state: Some(context.shutdown_state()),
 +                      pending_inbound_htlcs: context.get_pending_inbound_htlc_details(),
 +                      pending_outbound_htlcs: context.get_pending_outbound_htlc_details(),
                }
        }
  }
@@@ -2099,14 -2076,6 +2106,14 @@@ macro_rules! convert_chan_phase_err 
                        ChannelPhase::UnfundedInboundV1(channel) => {
                                convert_chan_phase_err!($self, $err, channel, $channel_id, UNFUNDED_CHANNEL)
                        },
 +                      #[cfg(dual_funding)]
 +                      ChannelPhase::UnfundedOutboundV2(channel) => {
 +                              convert_chan_phase_err!($self, $err, channel, $channel_id, UNFUNDED_CHANNEL)
 +                      },
 +                      #[cfg(dual_funding)]
 +                      ChannelPhase::UnfundedInboundV2(channel) => {
 +                              convert_chan_phase_err!($self, $err, channel, $channel_id, UNFUNDED_CHANNEL)
 +                      },
                }
        };
  }
@@@ -2182,7 -2151,6 +2189,7 @@@ macro_rules! emit_channel_pending_even
                                counterparty_node_id: $channel.context.get_counterparty_node_id(),
                                user_channel_id: $channel.context.get_user_id(),
                                funding_txo: $channel.context.get_funding_txo().unwrap().into_bitcoin_outpoint(),
 +                              channel_type: Some($channel.context.get_channel_type().clone()),
                        }, None));
                        $channel.context.set_channel_pending_event_emitted();
                }
@@@ -2209,7 -2177,7 +2216,7 @@@ macro_rules! handle_monitor_update_comp
                let logger = WithChannelContext::from(&$self.logger, &$chan.context);
                let mut updates = $chan.monitor_updating_restored(&&logger,
                        &$self.node_signer, $self.chain_hash, &$self.default_configuration,
 -                      $self.best_block.read().unwrap().height());
 +                      $self.best_block.read().unwrap().height);
                let counterparty_node_id = $chan.context.get_counterparty_node_id();
                let channel_update = if updates.channel_ready.is_some() && $chan.context.is_usable() {
                        // We only send a channel_update in the case where we are just now sending a
@@@ -2463,14 -2431,14 +2470,14 @@@ wher
  
                        best_block: RwLock::new(params.best_block),
  
 -                      outbound_scid_aliases: Mutex::new(HashSet::new()),
 -                      pending_inbound_payments: Mutex::new(HashMap::new()),
 +                      outbound_scid_aliases: Mutex::new(new_hash_set()),
 +                      pending_inbound_payments: Mutex::new(new_hash_map()),
                        pending_outbound_payments: OutboundPayments::new(),
 -                      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()),
 -                      outpoint_to_peer: Mutex::new(HashMap::new()),
 -                      short_to_chan_info: FairRwLock::new(HashMap::new()),
 +                      forward_htlcs: Mutex::new(new_hash_map()),
 +                      claimable_payments: Mutex::new(ClaimablePayments { claimable_payments: new_hash_map(), pending_claiming_payments: new_hash_map() }),
 +                      pending_intercepted_htlcs: Mutex::new(new_hash_map()),
 +                      outpoint_to_peer: Mutex::new(new_hash_map()),
 +                      short_to_chan_info: FairRwLock::new(new_hash_map()),
  
                        our_network_pubkey: node_signer.get_node_id(Recipient::Node).unwrap(),
                        secp_ctx,
  
                        highest_seen_timestamp: AtomicUsize::new(current_timestamp as usize),
  
 -                      per_peer_state: FairRwLock::new(HashMap::new()),
 +                      per_peer_state: FairRwLock::new(new_hash_map()),
  
                        pending_events: Mutex::new(VecDeque::new()),
                        pending_events_processor: AtomicBool::new(false),
        }
  
        fn create_and_insert_outbound_scid_alias(&self) -> u64 {
 -              let height = self.best_block.read().unwrap().height();
 +              let height = self.best_block.read().unwrap().height;
                let mut outbound_scid_alias = 0;
                let mut i = 0;
                loop {
                        let config = if override_config.is_some() { override_config.as_ref().unwrap() } else { &self.default_configuration };
                        match OutboundV1Channel::new(&self.fee_estimator, &self.entropy_source, &self.signer_provider, their_network_key,
                                their_features, channel_value_satoshis, push_msat, user_channel_id, config,
 -                              self.best_block.read().unwrap().height(), outbound_scid_alias, temporary_channel_id)
 +                              self.best_block.read().unwrap().height, outbound_scid_alias, temporary_channel_id)
                        {
                                Ok(res) => res,
                                Err(e) => {
                // the same channel.
                let mut res = Vec::with_capacity(self.short_to_chan_info.read().unwrap().len());
                {
 -                      let best_block_height = self.best_block.read().unwrap().height();
 +                      let best_block_height = self.best_block.read().unwrap().height;
                        let per_peer_state = self.per_peer_state.read().unwrap();
                        for (_cp_id, peer_state_mutex) in per_peer_state.iter() {
                                let mut peer_state_lock = peer_state_mutex.lock().unwrap();
                // the same channel.
                let mut res = Vec::with_capacity(self.short_to_chan_info.read().unwrap().len());
                {
 -                      let best_block_height = self.best_block.read().unwrap().height();
 +                      let best_block_height = self.best_block.read().unwrap().height;
                        let per_peer_state = self.per_peer_state.read().unwrap();
                        for (_cp_id, peer_state_mutex) in per_peer_state.iter() {
                                let mut peer_state_lock = peer_state_mutex.lock().unwrap();
  
        /// Gets the list of channels we have with a given counterparty, in random order.
        pub fn list_channels_with_counterparty(&self, counterparty_node_id: &PublicKey) -> Vec<ChannelDetails> {
 -              let best_block_height = self.best_block.read().unwrap().height();
 +              let best_block_height = self.best_block.read().unwrap().height;
                let per_peer_state = self.per_peer_state.read().unwrap();
  
                if let Some(peer_state_mutex) = per_peer_state.get(counterparty_node_id) {
                        let receiver = HTLCDestination::NextHopChannel { node_id: Some(counterparty_node_id), channel_id };
                        self.fail_htlc_backwards_internal(&source, &payment_hash, &reason, receiver);
                }
 -              if let Some((_, funding_txo, monitor_update)) = shutdown_res.monitor_update {
 +              if let Some((_, funding_txo, _channel_id, monitor_update)) = shutdown_res.monitor_update {
                        // There isn't anything we can do if we get an update failure - we're already
                        // force-closing. The monitor update on the required in-memory copy should broadcast
                        // the latest local state, which is the best we can do anyway. Thus, it is safe to
                                                // Unfunded channel has no update
                                                (None, chan_phase.context().get_counterparty_node_id())
                                        },
 +                                      // TODO(dual_funding): Combine this match arm with above once #[cfg(dual_funding)] is removed.
 +                                      #[cfg(dual_funding)]
 +                                      ChannelPhase::UnfundedOutboundV2(_) | ChannelPhase::UnfundedInboundV2(_) => {
 +                                              self.finish_close_channel(chan_phase.context_mut().force_shutdown(false, closure_reason));
 +                                              // Unfunded channel has no update
 +                                              (None, chan_phase.context().get_counterparty_node_id())
 +                                      },
                                }
                        } else if peer_state.inbound_channel_request_by_id.remove(channel_id).is_some() {
                                log_error!(logger, "Force-closing channel {}", &channel_id);
        /// the latest local transaction(s). Fails if `channel_id` is unknown to the manager, or if the
        /// `counterparty_node_id` isn't the counterparty of the corresponding channel.
        ///
 -      /// You can always get the latest local transaction(s) to broadcast from
 -      /// [`ChannelMonitor::get_latest_holder_commitment_txn`].
 +      /// You can always broadcast the latest local transaction(s) via
 +      /// [`ChannelMonitor::broadcast_latest_holder_commitment_txn`].
        pub fn force_close_without_broadcasting_txn(&self, channel_id: &ChannelId, counterparty_node_id: &PublicKey)
        -> Result<(), APIError> {
                self.force_close_sending_error(channel_id, counterparty_node_id, false)
                                None
                        };
  
 -                      let cur_height = self.best_block.read().unwrap().height() + 1;
 +                      let cur_height = self.best_block.read().unwrap().height + 1;
  
                        if let Err((err_msg, code)) = check_incoming_htlc_cltv(
                                cur_height, outgoing_cltv_value, msg.cltv_expiry
                match decoded_hop {
                        onion_utils::Hop::Receive(next_hop_data) => {
                                // OUR PAYMENT!
 -                              let current_height: u32 = self.best_block.read().unwrap().height();
 +                              let current_height: u32 = self.best_block.read().unwrap().height;
                                match create_recv_pending_htlc_info(next_hop_data, shared_secret, msg.payment_hash,
                                        msg.amount_msat, msg.cltv_expiry, None, allow_underpay, msg.skimmed_fee_msat,
                                        current_height, self.default_configuration.accept_mpp_keysend)
        /// [`PeerManager::process_events`]: crate::ln::peer_handler::PeerManager::process_events
        /// [`ChannelMonitorUpdateStatus::InProgress`]: crate::chain::ChannelMonitorUpdateStatus::InProgress
        pub fn send_payment_with_route(&self, route: &Route, payment_hash: PaymentHash, recipient_onion: RecipientOnionFields, payment_id: PaymentId) -> Result<(), PaymentSendFailure> {
 -              let best_block_height = self.best_block.read().unwrap().height();
 +              let best_block_height = self.best_block.read().unwrap().height;
                let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
                self.pending_outbound_payments
                        .send_payment_with_route(route, payment_hash, recipient_onion, payment_id,
        /// Similar to [`ChannelManager::send_payment_with_route`], but will automatically find a route based on
        /// `route_params` and retry failed payment paths based on `retry_strategy`.
        pub fn send_payment(&self, payment_hash: PaymentHash, recipient_onion: RecipientOnionFields, payment_id: PaymentId, route_params: RouteParameters, retry_strategy: Retry) -> Result<(), RetryableSendFailure> {
 -              let best_block_height = self.best_block.read().unwrap().height();
 +              let best_block_height = self.best_block.read().unwrap().height;
                let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
                self.pending_outbound_payments
                        .send_payment(payment_hash, recipient_onion, payment_id, retry_strategy, route_params,
  
        #[cfg(test)]
        pub(super) fn test_send_payment_internal(&self, route: &Route, payment_hash: PaymentHash, recipient_onion: RecipientOnionFields, keysend_preimage: Option<PaymentPreimage>, payment_id: PaymentId, recv_value_msat: Option<u64>, onion_session_privs: Vec<[u8; 32]>) -> Result<(), PaymentSendFailure> {
 -              let best_block_height = self.best_block.read().unwrap().height();
 +              let best_block_height = self.best_block.read().unwrap().height;
                let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
                self.pending_outbound_payments.test_send_payment_internal(route, payment_hash, recipient_onion,
                        keysend_preimage, payment_id, recv_value_msat, onion_session_privs, &self.node_signer,
  
        #[cfg(test)]
        pub(crate) fn test_add_new_pending_payment(&self, payment_hash: PaymentHash, recipient_onion: RecipientOnionFields, payment_id: PaymentId, route: &Route) -> Result<Vec<[u8; 32]>, PaymentSendFailure> {
 -              let best_block_height = self.best_block.read().unwrap().height();
 +              let best_block_height = self.best_block.read().unwrap().height;
                self.pending_outbound_payments.test_add_new_pending_payment(payment_hash, recipient_onion, payment_id, route, None, &self.entropy_source, best_block_height)
        }
  
        }
  
        pub(super) fn send_payment_for_bolt12_invoice(&self, invoice: &Bolt12Invoice, payment_id: PaymentId) -> Result<(), Bolt12PaymentError> {
 -              let best_block_height = self.best_block.read().unwrap().height();
 +              let best_block_height = self.best_block.read().unwrap().height;
                let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
                self.pending_outbound_payments
                        .send_payment_for_bolt12_invoice(
        ///
        /// [`send_payment`]: Self::send_payment
        pub fn send_spontaneous_payment(&self, route: &Route, payment_preimage: Option<PaymentPreimage>, recipient_onion: RecipientOnionFields, payment_id: PaymentId) -> Result<PaymentHash, PaymentSendFailure> {
 -              let best_block_height = self.best_block.read().unwrap().height();
 +              let best_block_height = self.best_block.read().unwrap().height;
                let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
                self.pending_outbound_payments.send_spontaneous_payment_with_route(
                        route, payment_preimage, recipient_onion, payment_id, &self.entropy_source,
        ///
        /// [`PaymentParameters::for_keysend`]: crate::routing::router::PaymentParameters::for_keysend
        pub fn send_spontaneous_payment_with_retry(&self, payment_preimage: Option<PaymentPreimage>, recipient_onion: RecipientOnionFields, payment_id: PaymentId, route_params: RouteParameters, retry_strategy: Retry) -> Result<PaymentHash, RetryableSendFailure> {
 -              let best_block_height = self.best_block.read().unwrap().height();
 +              let best_block_height = self.best_block.read().unwrap().height;
                let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
                self.pending_outbound_payments.send_spontaneous_payment(payment_preimage, recipient_onion,
                        payment_id, retry_strategy, route_params, &self.router, self.list_usable_channels(),
        /// [`PaymentHash`] of probes based on a static secret and a random [`PaymentId`], which allows
        /// us to easily discern them from real payments.
        pub fn send_probe(&self, path: Path) -> Result<(PaymentHash, PaymentId), PaymentSendFailure> {
 -              let best_block_height = self.best_block.read().unwrap().height();
 +              let best_block_height = self.best_block.read().unwrap().height;
                let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
                self.pending_outbound_payments.send_probe(path, self.probing_cookie_secret,
                        &self.entropy_source, &self.node_signer, best_block_height,
                                ProbeSendFailure::RouteNotFound
                        })?;
  
 -              let mut used_liquidity_map = HashMap::with_capacity(first_hops.len());
 +              let mut used_liquidity_map = hash_map_with_capacity(first_hops.len());
  
                let mut res = Vec::new();
  
                        }));
                }
                {
 -                      let height = self.best_block.read().unwrap().height();
 +                      let height = self.best_block.read().unwrap().height;
                        // Transactions are evaluated as final by network mempools if their locktime is strictly
                        // lower than the next block height. However, the modules constituting our Lightning
                        // node might not have perfect sync about their blockchain views. Thus, if the wallet
                                        }
                                        let outpoint = OutPoint { txid: tx.txid(), index: output_index.unwrap() };
                                        if let Some(funding_batch_state) = funding_batch_state.as_mut() {
 -                                              funding_batch_state.push((outpoint.to_channel_id(), *counterparty_node_id, false));
 +                                              // TODO(dual_funding): We only do batch funding for V1 channels at the moment, but we'll probably
 +                                              // need to fix this somehow to not rely on using the outpoint for the channel ID if we
 +                                              // want to support V2 batching here as well.
 +                                              funding_batch_state.push((ChannelId::v1_from_funding_outpoint(outpoint), *counterparty_node_id, false));
                                        }
                                        Ok(outpoint)
                                })
                let mut per_source_pending_forward = [(
                        payment.prev_short_channel_id,
                        payment.prev_funding_outpoint,
 +                      payment.prev_channel_id,
                        payment.prev_user_channel_id,
                        vec![(pending_htlc_info, payment.prev_htlc_id)]
                )];
                                short_channel_id: payment.prev_short_channel_id,
                                user_channel_id: Some(payment.prev_user_channel_id),
                                outpoint: payment.prev_funding_outpoint,
 +                              channel_id: payment.prev_channel_id,
                                htlc_id: payment.prev_htlc_id,
                                incoming_packet_shared_secret: payment.forward_info.incoming_shared_secret,
                                phantom_shared_secret: None,
  
                let mut new_events = VecDeque::new();
                let mut failed_forwards = Vec::new();
 -              let mut phantom_receives: Vec<(u64, OutPoint, u128, Vec<(PendingHTLCInfo, u64)>)> = Vec::new();
 +              let mut phantom_receives: Vec<(u64, OutPoint, ChannelId, u128, Vec<(PendingHTLCInfo, u64)>)> = Vec::new();
                {
 -                      let mut forward_htlcs = HashMap::new();
 +                      let mut forward_htlcs = new_hash_map();
                        mem::swap(&mut forward_htlcs, &mut self.forward_htlcs.lock().unwrap());
  
                        for (short_chan_id, mut pending_forwards) in forward_htlcs {
                                                        for forward_info in pending_forwards.drain(..) {
                                                                match forward_info {
                                                                        HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo {
 -                                                                              prev_short_channel_id, prev_htlc_id, prev_funding_outpoint, prev_user_channel_id,
 -                                                                              forward_info: PendingHTLCInfo {
 +                                                                              prev_short_channel_id, prev_htlc_id, prev_channel_id, prev_funding_outpoint,
 +                                                                              prev_user_channel_id, forward_info: PendingHTLCInfo {
                                                                                        routing, incoming_shared_secret, payment_hash, outgoing_amt_msat,
                                                                                        outgoing_cltv_value, ..
                                                                                }
                                                                        }) => {
                                                                                macro_rules! failure_handler {
                                                                                        ($msg: expr, $err_code: expr, $err_data: expr, $phantom_ss: expr, $next_hop_unknown: expr) => {
 -                                                                                              let logger = WithContext::from(&self.logger, forwarding_counterparty, Some(prev_funding_outpoint.to_channel_id()));
 +                                                                                              let logger = WithContext::from(&self.logger, forwarding_counterparty, Some(prev_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,
                                                                                                        user_channel_id: Some(prev_user_channel_id),
 +                                                                                                      channel_id: prev_channel_id,
                                                                                                        outpoint: prev_funding_outpoint,
                                                                                                        htlc_id: prev_htlc_id,
                                                                                                        incoming_packet_shared_secret: incoming_shared_secret,
                                                                                                };
                                                                                                match next_hop {
                                                                                                        onion_utils::Hop::Receive(hop_data) => {
 -                                                                                                              let current_height: u32 = self.best_block.read().unwrap().height();
 +                                                                                                              let current_height: u32 = self.best_block.read().unwrap().height;
                                                                                                                match create_recv_pending_htlc_info(hop_data,
                                                                                                                        incoming_shared_secret, payment_hash, outgoing_amt_msat,
                                                                                                                        outgoing_cltv_value, Some(phantom_shared_secret), false, None,
                                                                                                                        current_height, self.default_configuration.accept_mpp_keysend)
                                                                                                                {
 -                                                                                                                      Ok(info) => phantom_receives.push((prev_short_channel_id, prev_funding_outpoint, prev_user_channel_id, vec![(info, prev_htlc_id)])),
 +                                                                                                                      Ok(info) => phantom_receives.push((prev_short_channel_id, prev_funding_outpoint, prev_channel_id, prev_user_channel_id, vec![(info, prev_htlc_id)])),
                                                                                                                        Err(InboundHTLCErr { err_code, err_data, msg }) => failed_payment!(msg, err_code, err_data, Some(phantom_shared_secret))
                                                                                                                }
                                                                                                        },
                                                for forward_info in pending_forwards.drain(..) {
                                                        let queue_fail_htlc_res = match forward_info {
                                                                HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo {
 -                                                                      prev_short_channel_id, prev_htlc_id, prev_funding_outpoint, prev_user_channel_id,
 -                                                                      forward_info: PendingHTLCInfo {
 +                                                                      prev_short_channel_id, prev_htlc_id, prev_channel_id, prev_funding_outpoint,
 +                                                                      prev_user_channel_id, forward_info: PendingHTLCInfo {
                                                                                incoming_shared_secret, payment_hash, outgoing_amt_msat, outgoing_cltv_value,
                                                                                routing: PendingHTLCRouting::Forward {
                                                                                        onion_packet, blinded, ..
                                                                        let htlc_source = HTLCSource::PreviousHopData(HTLCPreviousHopData {
                                                                                short_channel_id: prev_short_channel_id,
                                                                                user_channel_id: Some(prev_user_channel_id),
 +                                                                              channel_id: prev_channel_id,
                                                                                outpoint: prev_funding_outpoint,
                                                                                htlc_id: prev_htlc_id,
                                                                                incoming_packet_shared_secret: incoming_shared_secret,
                                        'next_forwardable_htlc: for forward_info in pending_forwards.drain(..) {
                                                match forward_info {
                                                        HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo {
 -                                                              prev_short_channel_id, prev_htlc_id, prev_funding_outpoint, prev_user_channel_id,
 -                                                              forward_info: PendingHTLCInfo {
 +                                                              prev_short_channel_id, prev_htlc_id, prev_channel_id, prev_funding_outpoint,
 +                                                              prev_user_channel_id, forward_info: PendingHTLCInfo {
                                                                        routing, incoming_shared_secret, payment_hash, incoming_amt_msat, outgoing_amt_msat,
                                                                        skimmed_fee_msat, ..
                                                                }
                                                                        prev_hop: HTLCPreviousHopData {
                                                                                short_channel_id: prev_short_channel_id,
                                                                                user_channel_id: Some(prev_user_channel_id),
 +                                                                              channel_id: prev_channel_id,
                                                                                outpoint: prev_funding_outpoint,
                                                                                htlc_id: prev_htlc_id,
                                                                                incoming_packet_shared_secret: incoming_shared_secret,
                                                                                debug_assert!(!committed_to_claimable);
                                                                                let mut htlc_msat_height_data = $htlc.value.to_be_bytes().to_vec();
                                                                                htlc_msat_height_data.extend_from_slice(
 -                                                                                      &self.best_block.read().unwrap().height().to_be_bytes(),
 +                                                                                      &self.best_block.read().unwrap().height.to_be_bytes(),
                                                                                );
                                                                                failed_forwards.push((HTLCSource::PreviousHopData(HTLCPreviousHopData {
                                                                                                short_channel_id: $htlc.prev_hop.short_channel_id,
                                                                                                user_channel_id: $htlc.prev_hop.user_channel_id,
 +                                                                                              channel_id: prev_channel_id,
                                                                                                outpoint: prev_funding_outpoint,
                                                                                                htlc_id: $htlc.prev_hop.htlc_id,
                                                                                                incoming_packet_shared_secret: $htlc.prev_hop.incoming_packet_shared_secret,
                                                                                        #[allow(unused_assignments)] {
                                                                                                committed_to_claimable = true;
                                                                                        }
 -                                                                                      let prev_channel_id = prev_funding_outpoint.to_channel_id();
                                                                                        htlcs.push(claimable_htlc);
                                                                                        let amount_msat = htlcs.iter().map(|htlc| htlc.value).sum();
                                                                                        htlcs.iter_mut().for_each(|htlc| htlc.total_value_received = Some(amount_msat));
                                                                                                        }
                                                                                                };
                                                                                                if let Some(min_final_cltv_expiry_delta) = min_final_cltv_expiry_delta {
 -                                                                                                      let expected_min_expiry_height = (self.current_best_block().height() + min_final_cltv_expiry_delta as u32) as u64;
 +                                                                                                      let expected_min_expiry_height = (self.current_best_block().height + min_final_cltv_expiry_delta as u32) as u64;
                                                                                                        if (cltv_expiry as u64) < expected_min_expiry_height {
                                                                                                                log_trace!(self.logger, "Failing new HTLC with payment_hash {} as its CLTV expiry was too soon (had {}, earliest expected {})",
                                                                                                                        &payment_hash, cltv_expiry, expected_min_expiry_height);
                        }
                }
  
 -              let best_block_height = self.best_block.read().unwrap().height();
 +              let best_block_height = self.best_block.read().unwrap().height;
                self.pending_outbound_payments.check_retry_payments(&self.router, || self.list_usable_channels(),
                        || self.compute_inflight_htlcs(), &self.entropy_source, &self.node_signer, best_block_height,
                        &self.pending_events, &self.logger, |args| self.send_payment_along_path(args));
  
                for event in background_events.drain(..) {
                        match event {
 -                              BackgroundEvent::ClosedMonitorUpdateRegeneratedOnStartup((funding_txo, update)) => {
 +                              BackgroundEvent::ClosedMonitorUpdateRegeneratedOnStartup((funding_txo, _channel_id, update)) => {
                                        // The channel has already been closed, so no use bothering to care about the
                                        // monitor updating completing.
                                        let _ = self.chain_monitor.update_channel(funding_txo, &update);
                                },
 -                              BackgroundEvent::MonitorUpdateRegeneratedOnStartup { counterparty_node_id, funding_txo, update } => {
 +                              BackgroundEvent::MonitorUpdateRegeneratedOnStartup { counterparty_node_id, funding_txo, channel_id, update } => {
                                        let mut updated_chan = false;
                                        {
                                                let per_peer_state = self.per_peer_state.read().unwrap();
                                                if let Some(peer_state_mutex) = per_peer_state.get(&counterparty_node_id) {
                                                        let mut peer_state_lock = peer_state_mutex.lock().unwrap();
                                                        let peer_state = &mut *peer_state_lock;
 -                                                      match peer_state.channel_by_id.entry(funding_txo.to_channel_id()) {
 +                                                      match peer_state.channel_by_id.entry(channel_id) {
                                                                hash_map::Entry::Occupied(mut chan_phase) => {
                                                                        if let ChannelPhase::Funded(chan) = chan_phase.get_mut() {
                                                                                updated_chan = true;
  
                // If the feerate has decreased by less than half, don't bother
                if new_feerate <= chan.context.get_feerate_sat_per_1000_weight() && new_feerate * 2 > chan.context.get_feerate_sat_per_1000_weight() {
 -                      if new_feerate != chan.context.get_feerate_sat_per_1000_weight() {
 -                              log_trace!(logger, "Channel {} does not qualify for a feerate change from {} to {}.",
 -                              chan_id, chan.context.get_feerate_sat_per_1000_weight(), new_feerate);
 -                      }
                        return NotifyOption::SkipPersistNoEvents;
                }
                if !chan.context.is_live() {
                                                                process_unfunded_channel_tick(chan_id, &mut chan.context, &mut chan.unfunded_context,
                                                                        pending_msg_events, counterparty_node_id)
                                                        },
 +                                                      #[cfg(dual_funding)]
 +                                                      ChannelPhase::UnfundedInboundV2(chan) => {
 +                                                              process_unfunded_channel_tick(chan_id, &mut chan.context, &mut chan.unfunded_context,
 +                                                                      pending_msg_events, counterparty_node_id)
 +                                                      },
 +                                                      #[cfg(dual_funding)]
 +                                                      ChannelPhase::UnfundedOutboundV2(chan) => {
 +                                                              process_unfunded_channel_tick(chan_id, &mut chan.context, &mut chan.unfunded_context,
 +                                                                      pending_msg_events, counterparty_node_id)
 +                                                      },
                                                }
                                        });
  
                        FailureCode::RequiredNodeFeatureMissing => HTLCFailReason::from_failure_code(failure_code.into()),
                        FailureCode::IncorrectOrUnknownPaymentDetails => {
                                let mut htlc_msat_height_data = htlc.value.to_be_bytes().to_vec();
 -                              htlc_msat_height_data.extend_from_slice(&self.best_block.read().unwrap().height().to_be_bytes());
 +                              htlc_msat_height_data.extend_from_slice(&self.best_block.read().unwrap().height.to_be_bytes());
                                HTLCFailReason::reason(failure_code.into(), htlc_msat_height_data)
                        },
                        FailureCode::InvalidOnionPayload(data) => {
                        },
                        HTLCSource::PreviousHopData(HTLCPreviousHopData {
                                ref short_channel_id, ref htlc_id, ref incoming_packet_shared_secret,
 -                              ref phantom_shared_secret, ref outpoint, ref blinded_failure, ..
 +                              ref phantom_shared_secret, outpoint: _, ref blinded_failure, ref channel_id, ..
                        }) => {
                                log_trace!(
 -                                      WithContext::from(&self.logger, None, Some(outpoint.to_channel_id())),
 +                                      WithContext::from(&self.logger, None, Some(*channel_id)),
                                        "Failing {}HTLC with payment_hash {} backwards from us: {:?}",
                                        if blinded_failure.is_some() { "blinded " } else { "" }, &payment_hash, onion_error
                                );
                                if push_forward_ev { self.push_pending_forwards_ev(); }
                                let mut pending_events = self.pending_events.lock().unwrap();
                                pending_events.push_back((events::Event::HTLCHandlingFailed {
 -                                      prev_channel_id: outpoint.to_channel_id(),
 +                                      prev_channel_id: *channel_id,
                                        failed_next_destination: destination,
                                }, None));
                        },
                }
                if valid_mpp {
                        for htlc in sources.drain(..) {
 -                              let prev_hop_chan_id = htlc.prev_hop.outpoint.to_channel_id();
 +                              let prev_hop_chan_id = htlc.prev_hop.channel_id;
                                if let Err((pk, err)) = self.claim_funds_from_hop(
                                        htlc.prev_hop, payment_preimage,
                                        |_, definitely_duplicate| {
                if !valid_mpp {
                        for htlc in sources.drain(..) {
                                let mut htlc_msat_height_data = htlc.value.to_be_bytes().to_vec();
 -                              htlc_msat_height_data.extend_from_slice(&self.best_block.read().unwrap().height().to_be_bytes());
 +                              htlc_msat_height_data.extend_from_slice(&self.best_block.read().unwrap().height.to_be_bytes());
                                let source = HTLCSource::PreviousHopData(htlc.prev_hop);
                                let reason = HTLCFailReason::reason(0x4000 | 15, htlc_msat_height_data);
                                let receiver = HTLCDestination::FailedPayment { payment_hash };
  
                {
                        let per_peer_state = self.per_peer_state.read().unwrap();
 -                      let chan_id = prev_hop.outpoint.to_channel_id();
 +                      let chan_id = prev_hop.channel_id;
                        let counterparty_node_id_opt = match self.short_to_chan_info.read().unwrap().get(&prev_hop.short_channel_id) {
                                Some((cp_id, _dup_chan_id)) => Some(cp_id.clone()),
                                None => None
                                                                                BackgroundEvent::MonitorUpdateRegeneratedOnStartup {
                                                                                        counterparty_node_id,
                                                                                        funding_txo: prev_hop.outpoint,
 +                                                                                      channel_id: prev_hop.channel_id,
                                                                                        update: monitor_update.clone(),
                                                                                });
                                                                }
  
                                                                log_trace!(logger, "Completing monitor update completion action for channel {} as claim was redundant: {:?}",
                                                                        chan_id, action);
 -                                                              let (node_id, funding_outpoint, blocker) =
 +                                                              let (node_id, _funding_outpoint, channel_id, blocker) =
                                                                if let MonitorUpdateCompletionAction::FreeOtherChannelImmediately {
                                                                        downstream_counterparty_node_id: node_id,
                                                                        downstream_funding_outpoint: funding_outpoint,
 -                                                                      blocking_action: blocker,
 +                                                                      blocking_action: blocker, downstream_channel_id: channel_id,
                                                                } = action {
 -                                                                      (node_id, funding_outpoint, blocker)
 +                                                                      (node_id, funding_outpoint, channel_id, blocker)
                                                                } else {
                                                                        debug_assert!(false,
                                                                                "Duplicate claims should always free another channel immediately");
                                                                        let mut peer_state = peer_state_mtx.lock().unwrap();
                                                                        if let Some(blockers) = peer_state
                                                                                .actions_blocking_raa_monitor_updates
 -                                                                              .get_mut(&funding_outpoint.to_channel_id())
 +                                                                              .get_mut(&channel_id)
                                                                        {
                                                                                let mut found_blocker = false;
                                                                                blockers.retain(|iter| {
                        updates: vec![ChannelMonitorUpdateStep::PaymentPreimage {
                                payment_preimage,
                        }],
 +                      channel_id: Some(prev_hop.channel_id),
                };
  
                if !during_init {
                                // with a preimage we *must* somehow manage to propagate it to the upstream
                                // channel, or we must have an ability to receive the same event and try
                                // again on restart.
 -                              log_error!(WithContext::from(&self.logger, None, Some(prev_hop.outpoint.to_channel_id())), "Critical error: failed to update channel monitor with preimage {:?}: {:?}",
 +                              log_error!(WithContext::from(&self.logger, None, Some(prev_hop.channel_id)),
 +                                      "Critical error: failed to update channel monitor with preimage {:?}: {:?}",
                                        payment_preimage, update_res);
                        }
                } else {
                        // complete the monitor update completion action from `completion_action`.
                        self.pending_background_events.lock().unwrap().push(
                                BackgroundEvent::ClosedMonitorUpdateRegeneratedOnStartup((
 -                                      prev_hop.outpoint, preimage_update,
 +                                      prev_hop.outpoint, prev_hop.channel_id, preimage_update,
                                )));
                }
                // Note that we do process the completion action here. This totally could be a
        }
  
        fn claim_funds_internal(&self, source: HTLCSource, payment_preimage: PaymentPreimage,
 -              forwarded_htlc_value_msat: Option<u64>, from_onchain: bool, startup_replay: bool,
 -              next_channel_counterparty_node_id: Option<PublicKey>, next_channel_outpoint: OutPoint
 +              forwarded_htlc_value_msat: Option<u64>, skimmed_fee_msat: Option<u64>, from_onchain: bool,
 +              startup_replay: bool, next_channel_counterparty_node_id: Option<PublicKey>,
 +              next_channel_outpoint: OutPoint, next_channel_id: ChannelId,
        ) {
                match source {
                        HTLCSource::OutboundRoute { session_priv, payment_id, path, .. } => {
                                        debug_assert_eq!(pubkey, path.hops[0].pubkey);
                                }
                                let ev_completion_action = EventCompletionAction::ReleaseRAAChannelMonitorUpdate {
 -                                      channel_funding_outpoint: next_channel_outpoint,
 +                                      channel_funding_outpoint: next_channel_outpoint, channel_id: next_channel_id,
                                        counterparty_node_id: path.hops[0].pubkey,
                                };
                                self.pending_outbound_payments.claim_htlc(payment_id, payment_preimage,
                                        &self.logger);
                        },
                        HTLCSource::PreviousHopData(hop_data) => {
 -                              let prev_outpoint = hop_data.outpoint;
 +                              let prev_channel_id = hop_data.channel_id;
                                let completed_blocker = RAAMonitorUpdateBlockingAction::from_prev_hop_data(&hop_data);
                                #[cfg(debug_assertions)]
                                let claiming_chan_funding_outpoint = hop_data.outpoint;
 +                              #[cfg(debug_assertions)]
 +                              let claiming_channel_id = hop_data.channel_id;
                                let res = self.claim_funds_from_hop(hop_data, payment_preimage,
                                        |htlc_claim_value_msat, definitely_duplicate| {
                                                let chan_to_release =
                                                        if let Some(node_id) = next_channel_counterparty_node_id {
 -                                                              Some((node_id, next_channel_outpoint, completed_blocker))
 +                                                              Some((node_id, next_channel_outpoint, next_channel_id, completed_blocker))
                                                        } else {
                                                                // We can only get `None` here if we are processing a
                                                                // `ChannelMonitor`-originated event, in which case we
                                                                                },
                                                                                // or the channel we'd unblock is already closed,
                                                                                BackgroundEvent::ClosedMonitorUpdateRegeneratedOnStartup(
 -                                                                                      (funding_txo, monitor_update)
 +                                                                                      (funding_txo, _channel_id, monitor_update)
                                                                                ) => {
                                                                                        if *funding_txo == next_channel_outpoint {
                                                                                                assert_eq!(monitor_update.updates.len(), 1);
                                                                                BackgroundEvent::MonitorUpdatesComplete {
                                                                                        channel_id, ..
                                                                                } =>
 -                                                                                      *channel_id == claiming_chan_funding_outpoint.to_channel_id(),
 +                                                                                      *channel_id == claiming_channel_id,
                                                                        }
                                                                }), "{:?}", *background_events);
                                                        }
                                                                Some(MonitorUpdateCompletionAction::FreeOtherChannelImmediately {
                                                                        downstream_counterparty_node_id: other_chan.0,
                                                                        downstream_funding_outpoint: other_chan.1,
 -                                                                      blocking_action: other_chan.2,
 +                                                                      downstream_channel_id: other_chan.2,
 +                                                                      blocking_action: other_chan.3,
                                                                })
                                                        } else { None }
                                                } else {
 -                                                      let fee_earned_msat = if let Some(forwarded_htlc_value) = forwarded_htlc_value_msat {
 +                                                      let total_fee_earned_msat = if let Some(forwarded_htlc_value) = forwarded_htlc_value_msat {
                                                                if let Some(claimed_htlc_value) = htlc_claim_value_msat {
                                                                        Some(claimed_htlc_value - forwarded_htlc_value)
                                                                } else { None }
                                                        } else { None };
 +                                                      debug_assert!(skimmed_fee_msat <= total_fee_earned_msat,
 +                                                              "skimmed_fee_msat must always be included in total_fee_earned_msat");
                                                        Some(MonitorUpdateCompletionAction::EmitEventAndFreeOtherChannel {
                                                                event: events::Event::PaymentForwarded {
 -                                                                      fee_earned_msat,
 +                                                                      total_fee_earned_msat,
                                                                        claim_from_onchain_tx: from_onchain,
 -                                                                      prev_channel_id: Some(prev_outpoint.to_channel_id()),
 -                                                                      next_channel_id: Some(next_channel_outpoint.to_channel_id()),
 +                                                                      prev_channel_id: Some(prev_channel_id),
 +                                                                      next_channel_id: Some(next_channel_id),
                                                                        outbound_amount_forwarded_msat: forwarded_htlc_value_msat,
 +                                                                      skimmed_fee_msat,
                                                                },
                                                                downstream_counterparty_and_funding_outpoint: chan_to_release,
                                                        })
                                        event, downstream_counterparty_and_funding_outpoint
                                } => {
                                        self.pending_events.lock().unwrap().push_back((event, None));
 -                                      if let Some((node_id, funding_outpoint, blocker)) = downstream_counterparty_and_funding_outpoint {
 -                                              self.handle_monitor_update_release(node_id, funding_outpoint, Some(blocker));
 +                                      if let Some((node_id, funding_outpoint, channel_id, blocker)) = downstream_counterparty_and_funding_outpoint {
 +                                              self.handle_monitor_update_release(node_id, funding_outpoint, channel_id, Some(blocker));
                                        }
                                },
                                MonitorUpdateCompletionAction::FreeOtherChannelImmediately {
 -                                      downstream_counterparty_node_id, downstream_funding_outpoint, blocking_action,
 +                                      downstream_counterparty_node_id, downstream_funding_outpoint, downstream_channel_id, blocking_action,
                                } => {
                                        self.handle_monitor_update_release(
                                                downstream_counterparty_node_id,
                                                downstream_funding_outpoint,
 +                                              downstream_channel_id,
                                                Some(blocking_action),
                                        );
                                },
                commitment_update: Option<msgs::CommitmentUpdate>, order: RAACommitmentOrder,
                pending_forwards: Vec<(PendingHTLCInfo, u64)>, funding_broadcastable: Option<Transaction>,
                channel_ready: Option<msgs::ChannelReady>, announcement_sigs: Option<msgs::AnnouncementSignatures>)
 -      -> Option<(u64, OutPoint, u128, Vec<(PendingHTLCInfo, u64)>)> {
 +      -> Option<(u64, OutPoint, ChannelId, u128, Vec<(PendingHTLCInfo, u64)>)> {
                let logger = WithChannelContext::from(&self.logger, &channel.context);
                log_trace!(logger, "Handling channel resumption for channel {} with {} RAA, {} commitment update, {} pending forwards, {}broadcasting funding, {} channel ready, {} announcement",
                        &channel.context.channel_id(),
                let counterparty_node_id = channel.context.get_counterparty_node_id();
                if !pending_forwards.is_empty() {
                        htlc_forwards = Some((channel.context.get_short_channel_id().unwrap_or(channel.context.outbound_scid_alias()),
 -                              channel.context.get_funding_txo().unwrap(), channel.context.get_user_id(), pending_forwards));
 +                              channel.context.get_funding_txo().unwrap(), channel.context.channel_id(), channel.context.get_user_id(), pending_forwards));
                }
  
                if let Some(msg) = channel_ready {
                htlc_forwards
        }
  
 -      fn channel_monitor_updated(&self, funding_txo: &OutPoint, highest_applied_update_id: u64, counterparty_node_id: Option<&PublicKey>) {
 +      fn channel_monitor_updated(&self, funding_txo: &OutPoint, channel_id: &ChannelId, highest_applied_update_id: u64, counterparty_node_id: Option<&PublicKey>) {
                debug_assert!(self.total_consistency_lock.try_write().is_err()); // Caller holds read lock
  
                let counterparty_node_id = match counterparty_node_id {
                                // TODO: Once we can rely on the counterparty_node_id from the
                                // 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) {
 +                              match outpoint_to_peer.get(funding_txo) {
                                        Some(cp_id) => cp_id.clone(),
                                        None => return,
                                }
                peer_state_lock = peer_state_mutex_opt.unwrap().lock().unwrap();
                let peer_state = &mut *peer_state_lock;
                let channel =
 -                      if let Some(ChannelPhase::Funded(chan)) = peer_state.channel_by_id.get_mut(&funding_txo.to_channel_id()) {
 +                      if let Some(ChannelPhase::Funded(chan)) = peer_state.channel_by_id.get_mut(channel_id) {
                                chan
                        } else {
                                let update_actions = peer_state.monitor_update_blocked_actions
 -                                      .remove(&funding_txo.to_channel_id()).unwrap_or(Vec::new());
 +                                      .remove(&channel_id).unwrap_or(Vec::new());
                                mem::drop(peer_state_lock);
                                mem::drop(per_peer_state);
                                self.handle_monitor_update_completion_actions(update_actions);
                // succeed.
                let mut channel = match peer_state.inbound_channel_request_by_id.remove(temporary_channel_id) {
                        Some(unaccepted_channel) => {
 -                              let best_block_height = self.best_block.read().unwrap().height();
 +                              let best_block_height = self.best_block.read().unwrap().height;
                                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,
        fn peers_without_funded_channels<Filter>(&self, maybe_count_peer: Filter) -> usize
        where Filter: Fn(&PeerState<SP>) -> bool {
                let mut peers_without_funded_channels = 0;
 -              let best_block_height = self.best_block.read().unwrap().height();
 +              let best_block_height = self.best_block.read().unwrap().height;
                {
                        let peer_state_lock = self.per_peer_state.read().unwrap();
                        for (_, peer_mtx) in peer_state_lock.iter() {
                                                num_unfunded_channels += 1;
                                        }
                                },
 +                              // TODO(dual_funding): Combine this match arm with above once #[cfg(dual_funding)] is removed.
 +                              #[cfg(dual_funding)]
 +                              ChannelPhase::UnfundedInboundV2(chan) => {
 +                                      // Only inbound V2 channels that are not 0conf and that we do not contribute to will be
 +                                      // included in the unfunded count.
 +                                      if chan.context.minimum_depth().unwrap_or(1) != 0 &&
 +                                              chan.dual_funding_context.our_funding_satoshis == 0 {
 +                                              num_unfunded_channels += 1;
 +                                      }
 +                              },
                                ChannelPhase::UnfundedOutboundV1(_) => {
                                        // Outbound channels don't contribute to the unfunded count in the DoS context.
                                        continue;
 +                              },
 +                              // TODO(dual_funding): Combine this match arm with above once #[cfg(dual_funding)] is removed.
 +                              #[cfg(dual_funding)]
 +                              ChannelPhase::UnfundedOutboundV2(_) => {
 +                                      // Outbound channels don't contribute to the unfunded count in the DoS context.
 +                                      continue;
                                }
                        }
                }
        fn internal_open_channel(&self, counterparty_node_id: &PublicKey, msg: &msgs::OpenChannel) -> Result<(), MsgHandleErrInternal> {
                // Note that the ChannelManager is NOT re-persisted on disk after this, so any changes are
                // likely to be lost on restart!
 -              if msg.chain_hash != self.chain_hash {
 -                      return Err(MsgHandleErrInternal::send_err_msg_no_close("Unknown genesis block hash".to_owned(), msg.temporary_channel_id.clone()));
 +              if msg.common_fields.chain_hash != self.chain_hash {
 +                      return Err(MsgHandleErrInternal::send_err_msg_no_close("Unknown genesis block hash".to_owned(),
 +                               msg.common_fields.temporary_channel_id.clone()));
                }
  
                if !self.default_configuration.accept_inbound_channels {
 -                      return Err(MsgHandleErrInternal::send_err_msg_no_close("No inbound channels accepted".to_owned(), msg.temporary_channel_id.clone()));
 +                      return Err(MsgHandleErrInternal::send_err_msg_no_close("No inbound channels accepted".to_owned(),
 +                               msg.common_fields.temporary_channel_id.clone()));
                }
  
                // Get the number of peers with channels, but without funded ones. We don't care too much
                let peer_state_mutex = per_peer_state.get(counterparty_node_id)
                    .ok_or_else(|| {
                                debug_assert!(false);
 -                              MsgHandleErrInternal::send_err_msg_no_close(format!("Can't find a peer matching the passed counterparty node_id {}", counterparty_node_id), msg.temporary_channel_id.clone())
 +                              MsgHandleErrInternal::send_err_msg_no_close(
 +                                      format!("Can't find a peer matching the passed counterparty node_id {}", counterparty_node_id),
 +                                      msg.common_fields.temporary_channel_id.clone())
                        })?;
                let mut peer_state_lock = peer_state_mutex.lock().unwrap();
                let peer_state = &mut *peer_state_lock;
                {
                        return Err(MsgHandleErrInternal::send_err_msg_no_close(
                                "Have too many peers with unfunded channels, not accepting new ones".to_owned(),
 -                              msg.temporary_channel_id.clone()));
 +                              msg.common_fields.temporary_channel_id.clone()));
                }
  
 -              let best_block_height = self.best_block.read().unwrap().height();
 +              let best_block_height = self.best_block.read().unwrap().height;
                if Self::unfunded_channel_count(peer_state, best_block_height) >= MAX_UNFUNDED_CHANS_PER_PEER {
                        return Err(MsgHandleErrInternal::send_err_msg_no_close(
                                format!("Refusing more than {} unfunded channels.", MAX_UNFUNDED_CHANS_PER_PEER),
 -                              msg.temporary_channel_id.clone()));
 +                              msg.common_fields.temporary_channel_id.clone()));
                }
  
 -              let channel_id = msg.temporary_channel_id;
 +              let channel_id = msg.common_fields.temporary_channel_id;
                let channel_exists = peer_state.has_channel(&channel_id);
                if channel_exists {
 -                      return Err(MsgHandleErrInternal::send_err_msg_no_close("temporary_channel_id collision for the same peer!".to_owned(), msg.temporary_channel_id.clone()));
 +                      return Err(MsgHandleErrInternal::send_err_msg_no_close(
 +                              "temporary_channel_id collision for the same peer!".to_owned(),
 +                              msg.common_fields.temporary_channel_id.clone()));
                }
  
                // 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()
 +                                      &msg.common_fields, &peer_state.latest_features, &self.channel_type_features()
                                ).map_err(|e|
 -                                      MsgHandleErrInternal::from_chan_no_close(e, msg.temporary_channel_id)
 +                                      MsgHandleErrInternal::from_chan_no_close(e, msg.common_fields.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(),
 +                              temporary_channel_id: msg.common_fields.temporary_channel_id.clone(),
                                counterparty_node_id: counterparty_node_id.clone(),
 -                              funding_satoshis: msg.funding_satoshis,
 +                              funding_satoshis: msg.common_fields.funding_satoshis,
                                push_msat: msg.push_msat,
                                channel_type,
                        }, None));
                        &self.default_configuration, best_block_height, &self.logger, /*is_0conf=*/false)
                {
                        Err(e) => {
 -                              return Err(MsgHandleErrInternal::from_chan_no_close(e, msg.temporary_channel_id));
 +                              return Err(MsgHandleErrInternal::from_chan_no_close(e, msg.common_fields.temporary_channel_id));
                        },
                        Ok(res) => res
                };
  
                let channel_type = channel.context.get_channel_type();
                if channel_type.requires_zero_conf() {
 -                      return Err(MsgHandleErrInternal::send_err_msg_no_close("No zero confirmation channels accepted".to_owned(), msg.temporary_channel_id.clone()));
 +                      return Err(MsgHandleErrInternal::send_err_msg_no_close(
 +                              "No zero confirmation channels accepted".to_owned(),
 +                              msg.common_fields.temporary_channel_id.clone()));
                }
                if channel_type.requires_anchors_zero_fee_htlc_tx() {
 -                      return Err(MsgHandleErrInternal::send_err_msg_no_close("No channels with anchor outputs accepted".to_owned(), msg.temporary_channel_id.clone()));
 +                      return Err(MsgHandleErrInternal::send_err_msg_no_close(
 +                              "No channels with anchor outputs accepted".to_owned(),
 +                              msg.common_fields.temporary_channel_id.clone()));
                }
  
                let outbound_scid_alias = self.create_and_insert_outbound_scid_alias();
                        let peer_state_mutex = per_peer_state.get(counterparty_node_id)
                                .ok_or_else(|| {
                                        debug_assert!(false);
 -                                      MsgHandleErrInternal::send_err_msg_no_close(format!("Can't find a peer matching the passed counterparty node_id {}", counterparty_node_id), msg.temporary_channel_id)
 +                                      MsgHandleErrInternal::send_err_msg_no_close(format!("Can't find a peer matching the passed counterparty node_id {}", counterparty_node_id), msg.common_fields.temporary_channel_id)
                                })?;
                        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.temporary_channel_id) {
 +                      match peer_state.channel_by_id.entry(msg.common_fields.temporary_channel_id) {
                                hash_map::Entry::Occupied(mut phase) => {
                                        match phase.get_mut() {
                                                ChannelPhase::UnfundedOutboundV1(chan) => {
                                                        (chan.context.get_value_satoshis(), chan.context.get_funding_redeemscript().to_v0_p2wsh(), chan.context.get_user_id())
                                                },
                                                _ => {
 -                                                      return Err(MsgHandleErrInternal::send_err_msg_no_close(format!("Got an unexpected accept_channel message from peer with counterparty_node_id {}", counterparty_node_id), msg.temporary_channel_id));
 +                                                      return Err(MsgHandleErrInternal::send_err_msg_no_close(format!("Got an unexpected accept_channel message from peer with counterparty_node_id {}", counterparty_node_id), msg.common_fields.temporary_channel_id));
                                                }
                                        }
                                },
 -                              hash_map::Entry::Vacant(_) => 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))
 +                              hash_map::Entry::Vacant(_) => 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.common_fields.temporary_channel_id))
                        }
                };
                let mut pending_events = self.pending_events.lock().unwrap();
                pending_events.push_back((events::Event::FundingGenerationReady {
 -                      temporary_channel_id: msg.temporary_channel_id,
 +                      temporary_channel_id: msg.common_fields.temporary_channel_id,
                        counterparty_node_id: *counterparty_node_id,
                        channel_value_satoshis: value,
                        output_script,
                                                let mut chan = remove_channel_phase!(self, chan_phase_entry);
                                                finish_shutdown = Some(chan.context_mut().force_shutdown(false, ClosureReason::CounterpartyCoopClosedUnfundedChannel));
                                        },
 +                                      // TODO(dual_funding): Combine this match arm with above.
 +                                      #[cfg(dual_funding)]
 +                                      ChannelPhase::UnfundedInboundV2(_) | ChannelPhase::UnfundedOutboundV2(_) => {
 +                                              let context = phase.context_mut();
 +                                              log_error!(self.logger, "Immediately closing unfunded channel {} as peer asked to cooperatively shut it down (which is unnecessary)", &msg.channel_id);
 +                                              let mut chan = remove_channel_phase!(self, chan_phase_entry);
 +                                              finish_shutdown = Some(chan.context_mut().force_shutdown(false, ClosureReason::CounterpartyCoopClosedUnfundedChannel));
 +                                      },
                                }
                        } else {
                                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.channel_id))
  
        fn internal_update_fulfill_htlc(&self, counterparty_node_id: &PublicKey, msg: &msgs::UpdateFulfillHTLC) -> Result<(), MsgHandleErrInternal> {
                let funding_txo;
 -              let (htlc_source, forwarded_htlc_value) = {
 +              let (htlc_source, forwarded_htlc_value, skimmed_fee_msat) = {
                        let per_peer_state = self.per_peer_state.read().unwrap();
                        let peer_state_mutex = per_peer_state.get(counterparty_node_id)
                                .ok_or_else(|| {
                                hash_map::Entry::Vacant(_) => 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.channel_id))
                        }
                };
 -              self.claim_funds_internal(htlc_source, msg.payment_preimage.clone(), Some(forwarded_htlc_value), false, false, Some(*counterparty_node_id), funding_txo);
 +              self.claim_funds_internal(htlc_source, msg.payment_preimage.clone(),
 +                      Some(forwarded_htlc_value), skimmed_fee_msat, false, false, Some(*counterparty_node_id),
 +                      funding_txo, msg.channel_id
 +              );
 +
                Ok(())
        }
  
        }
  
        #[inline]
 -      fn forward_htlcs(&self, per_source_pending_forwards: &mut [(u64, OutPoint, u128, Vec<(PendingHTLCInfo, u64)>)]) {
 -              for &mut (prev_short_channel_id, prev_funding_outpoint, prev_user_channel_id, ref mut pending_forwards) in per_source_pending_forwards {
 +      fn forward_htlcs(&self, per_source_pending_forwards: &mut [(u64, OutPoint, ChannelId, u128, Vec<(PendingHTLCInfo, u64)>)]) {
 +              for &mut (prev_short_channel_id, prev_funding_outpoint, prev_channel_id, prev_user_channel_id, ref mut pending_forwards) in per_source_pending_forwards {
                        let mut push_forward_event = false;
                        let mut new_intercept_events = VecDeque::new();
                        let mut failed_intercept_forwards = Vec::new();
                                        match forward_htlcs.entry(scid) {
                                                hash_map::Entry::Occupied(mut entry) => {
                                                        entry.get_mut().push(HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo {
 -                                                              prev_short_channel_id, prev_funding_outpoint, prev_htlc_id, prev_user_channel_id, forward_info }));
 +                                                              prev_short_channel_id, prev_funding_outpoint, prev_channel_id, prev_htlc_id, prev_user_channel_id, forward_info }));
                                                },
                                                hash_map::Entry::Vacant(entry) => {
                                                        if !is_our_scid && forward_info.incoming_amt_msat.is_some() &&
                                                                                        intercept_id
                                                                                }, None));
                                                                                entry.insert(PendingAddHTLCInfo {
 -                                                                                      prev_short_channel_id, prev_funding_outpoint, prev_htlc_id, prev_user_channel_id, forward_info });
 +                                                                                      prev_short_channel_id, prev_funding_outpoint, prev_channel_id, prev_htlc_id, prev_user_channel_id, forward_info });
                                                                        },
                                                                        hash_map::Entry::Occupied(_) => {
 -                                                                              let logger = WithContext::from(&self.logger, None, Some(prev_funding_outpoint.to_channel_id()));
 +                                                                              let logger = WithContext::from(&self.logger, None, Some(prev_channel_id));
                                                                                log_info!(logger, "Failed to forward incoming HTLC: detected duplicate intercepted payment over short channel id {}", scid);
                                                                                let htlc_source = HTLCSource::PreviousHopData(HTLCPreviousHopData {
                                                                                        short_channel_id: prev_short_channel_id,
                                                                                        user_channel_id: Some(prev_user_channel_id),
                                                                                        outpoint: prev_funding_outpoint,
 +                                                                                      channel_id: prev_channel_id,
                                                                                        htlc_id: prev_htlc_id,
                                                                                        incoming_packet_shared_secret: forward_info.incoming_shared_secret,
                                                                                        phantom_shared_secret: None,
                                                                        push_forward_event = true;
                                                                }
                                                                entry.insert(vec!(HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo {
 -                                                                      prev_short_channel_id, prev_funding_outpoint, prev_htlc_id, prev_user_channel_id, forward_info })));
 +                                                                      prev_short_channel_id, prev_funding_outpoint, prev_channel_id, prev_htlc_id, prev_user_channel_id, forward_info })));
                                                        }
                                                }
                                        }
        /// the [`ChannelMonitorUpdate`] in question.
        fn raa_monitor_updates_held(&self,
                actions_blocking_raa_monitor_updates: &BTreeMap<ChannelId, Vec<RAAMonitorUpdateBlockingAction>>,
 -              channel_funding_outpoint: OutPoint, counterparty_node_id: PublicKey
 +              channel_funding_outpoint: OutPoint, channel_id: ChannelId, counterparty_node_id: PublicKey
        ) -> bool {
                actions_blocking_raa_monitor_updates
 -                      .get(&channel_funding_outpoint.to_channel_id()).map(|v| !v.is_empty()).unwrap_or(false)
 +                      .get(&channel_id).map(|v| !v.is_empty()).unwrap_or(false)
                || self.pending_events.lock().unwrap().iter().any(|(_, action)| {
                        action == &Some(EventCompletionAction::ReleaseRAAChannelMonitorUpdate {
                                channel_funding_outpoint,
 +                              channel_id,
                                counterparty_node_id,
                        })
                })
  
                        if let Some(chan) = peer_state.channel_by_id.get(&channel_id) {
                                return self.raa_monitor_updates_held(&peer_state.actions_blocking_raa_monitor_updates,
 -                                      chan.context().get_funding_txo().unwrap(), counterparty_node_id);
 +                                      chan.context().get_funding_txo().unwrap(), channel_id, counterparty_node_id);
                        }
                }
                false
                                                let funding_txo_opt = chan.context.get_funding_txo();
                                                let mon_update_blocked = if let Some(funding_txo) = funding_txo_opt {
                                                        self.raa_monitor_updates_held(
 -                                                              &peer_state.actions_blocking_raa_monitor_updates, funding_txo,
 +                                                              &peer_state.actions_blocking_raa_monitor_updates, funding_txo, msg.channel_id,
                                                                *counterparty_node_id)
                                                } else { false };
                                                let (htlcs_to_fail, monitor_update_opt) = try_chan_phase_entry!(self,
  
                                        peer_state.pending_msg_events.push(events::MessageSendEvent::BroadcastChannelAnnouncement {
                                                msg: try_chan_phase_entry!(self, chan.announcement_signatures(
 -                                                      &self.node_signer, self.chain_hash, self.best_block.read().unwrap().height(),
 +                                                      &self.node_signer, self.chain_hash, self.best_block.read().unwrap().height,
                                                        msg, &self.default_configuration
                                                ), chan_phase_entry),
                                                // Note that announcement_signatures fails if the channel cannot be announced,
                let mut failed_channels = Vec::new();
                let mut pending_monitor_events = self.chain_monitor.release_pending_monitor_events();
                let has_pending_monitor_events = !pending_monitor_events.is_empty();
 -              for (funding_outpoint, mut monitor_events, counterparty_node_id) in pending_monitor_events.drain(..) {
 +              for (funding_outpoint, channel_id, mut monitor_events, counterparty_node_id) in pending_monitor_events.drain(..) {
                        for monitor_event in monitor_events.drain(..) {
                                match monitor_event {
                                        MonitorEvent::HTLCEvent(htlc_update) => {
 -                                              let logger = WithContext::from(&self.logger, counterparty_node_id, Some(funding_outpoint.to_channel_id()));
 +                                              let logger = WithContext::from(&self.logger, counterparty_node_id, Some(channel_id));
                                                if let Some(preimage) = htlc_update.payment_preimage {
                                                        log_trace!(logger, "Claiming HTLC with preimage {} from our monitor", preimage);
 -                                                      self.claim_funds_internal(htlc_update.source, preimage, htlc_update.htlc_value_satoshis.map(|v| v * 1000), true, false, counterparty_node_id, funding_outpoint);
 +                                                      self.claim_funds_internal(htlc_update.source, preimage,
 +                                                              htlc_update.htlc_value_satoshis.map(|v| v * 1000), None, true,
 +                                                              false, counterparty_node_id, funding_outpoint, channel_id);
                                                } else {
                                                        log_trace!(logger, "Failing HTLC with hash {} from our monitor", &htlc_update.payment_hash);
 -                                                      let receiver = HTLCDestination::NextHopChannel { node_id: counterparty_node_id, channel_id: funding_outpoint.to_channel_id() };
 +                                                      let receiver = HTLCDestination::NextHopChannel { node_id: counterparty_node_id, channel_id };
                                                        let reason = HTLCFailReason::from_failure_code(0x4000 | 8);
                                                        self.fail_htlc_backwards_internal(&htlc_update.source, &htlc_update.payment_hash, &reason, receiver);
                                                }
                                        },
 -                                      MonitorEvent::HolderForceClosed(funding_outpoint) => {
 +                                      MonitorEvent::HolderForceClosed(_funding_outpoint) => {
                                                let counterparty_node_id_opt = match counterparty_node_id {
                                                        Some(cp_id) => Some(cp_id),
                                                        None => {
                                                                let mut peer_state_lock = peer_state_mutex.lock().unwrap();
                                                                let peer_state = &mut *peer_state_lock;
                                                                let pending_msg_events = &mut peer_state.pending_msg_events;
 -                                                              if let hash_map::Entry::Occupied(chan_phase_entry) = peer_state.channel_by_id.entry(funding_outpoint.to_channel_id()) {
 +                                                              if let hash_map::Entry::Occupied(chan_phase_entry) = peer_state.channel_by_id.entry(channel_id) {
                                                                        if let ChannelPhase::Funded(mut chan) = remove_channel_phase!(self, chan_phase_entry) {
                                                                                failed_channels.push(chan.context.force_shutdown(false, ClosureReason::HolderForceClosed));
                                                                                if let Ok(update) = self.get_channel_update_for_broadcast(&chan) {
                                                        }
                                                }
                                        },
 -                                      MonitorEvent::Completed { funding_txo, monitor_update_id } => {
 -                                              self.channel_monitor_updated(&funding_txo, monitor_update_id, counterparty_node_id.as_ref());
 +                                      MonitorEvent::Completed { funding_txo, channel_id, monitor_update_id } => {
 +                                              self.channel_monitor_updated(&funding_txo, &channel_id, monitor_update_id, counterparty_node_id.as_ref());
                                        },
                                }
                        }
                        // Channel::force_shutdown tries to make us do) as we may still be in initialization,
                        // so we track the update internally and handle it when the user next calls
                        // timer_tick_occurred, guaranteeing we're running normally.
 -                      if let Some((counterparty_node_id, funding_txo, update)) = failure.monitor_update.take() {
 +                      if let Some((counterparty_node_id, funding_txo, channel_id, update)) = failure.monitor_update.take() {
                                assert_eq!(update.updates.len(), 1);
                                if let ChannelMonitorUpdateStep::ChannelForceClosed { should_broadcast } = update.updates[0] {
                                        assert!(should_broadcast);
                                } else { unreachable!(); }
                                self.pending_background_events.lock().unwrap().push(
                                        BackgroundEvent::MonitorUpdateRegeneratedOnStartup {
 -                                              counterparty_node_id, funding_txo, update
 +                                              counterparty_node_id, funding_txo, update, channel_id,
                                        });
                        }
                        self.finish_close_channel(failure);
                }
        }
+ }
  
+ macro_rules! create_offer_builder { ($self: ident, $builder: ty) => {
        /// Creates an [`OfferBuilder`] such that the [`Offer`] it builds is recognized by the
        /// [`ChannelManager`] when handling [`InvoiceRequest`] messages for the offer. The offer will
        /// not have an expiration unless otherwise set on the builder.
        /// [`Offer`]: crate::offers::offer::Offer
        /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
        pub fn create_offer_builder(
-               &self, description: String
-       ) -> 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_blinded_path().map_err(|_| Bolt12SemanticError::MissingPaths)?;
+               &$self, description: String
+       ) -> Result<$builder, 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_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)
+                       .chain_hash($self.chain_hash)
                        .path(path);
  
-               Ok(builder)
+               Ok(builder.into())
        }
+ } }
  
+ macro_rules! create_refund_builder { ($self: ident, $builder: ty) => {
        /// Creates a [`RefundBuilder`] such that the [`Refund`] it builds is recognized by the
        /// [`ChannelManager`] when handling [`Bolt12Invoice`] messages for the refund.
        ///
        /// [`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,
+               &$self, description: String, amount_msats: u64, absolute_expiry: Duration,
                payment_id: PaymentId, retry_strategy: Retry, max_total_routing_fee_msat: Option<u64>
-       ) -> Result<RefundBuilder<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;
+       ) -> Result<$builder, 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_blinded_path().map_err(|_| Bolt12SemanticError::MissingPaths)?;
+               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
                )?
-                       .chain_hash(self.chain_hash)
+                       .chain_hash($self.chain_hash)
                        .absolute_expiry(absolute_expiry)
                        .path(path);
  
                let expiration = StaleExpiration::AbsoluteTimeout(absolute_expiry);
-               self.pending_outbound_payments
+               $self.pending_outbound_payments
                        .add_new_awaiting_invoice(
                                payment_id, expiration, retry_strategy, max_total_routing_fee_msat,
                        )
                        .map_err(|_| Bolt12SemanticError::DuplicatePaymentId)?;
  
-               Ok(builder)
+               Ok(builder.into())
        }
+ } }
+ impl<M: Deref, T: Deref, ES: Deref, NS: Deref, SP: Deref, F: Deref, R: Deref, L: Deref> ChannelManager<M, T, ES, NS, SP, F, R, L>
+ where
+       M::Target: chain::Watch<<SP::Target as SignerProvider>::EcdsaSigner>,
+       T::Target: BroadcasterInterface,
+       ES::Target: EntropySource,
+       NS::Target: NodeSigner,
+       SP::Target: SignerProvider,
+       F::Target: FeeEstimator,
+       R::Target: Router,
+       L::Target: Logger,
+ {
+       #[cfg(not(c_bindings))]
+       create_offer_builder!(self, OfferBuilder<DerivedMetadata, secp256k1::All>);
+       #[cfg(not(c_bindings))]
+       create_refund_builder!(self, RefundBuilder<secp256k1::All>);
+       #[cfg(c_bindings)]
+       create_offer_builder!(self, OfferWithDerivedMetadataBuilder);
+       #[cfg(c_bindings)]
+       create_refund_builder!(self, RefundMaybeWithDerivedMetadataBuilder);
  
        /// Pays for an [`Offer`] using the given parameters by creating an [`InvoiceRequest`] and
        /// enqueuing it to be sent via an onion message. [`ChannelManager`] will pay the actual
                let entropy = &*self.entropy_source;
                let secp_ctx = &self.secp_ctx;
  
-               let builder = offer
+               let builder: InvoiceRequestBuilder<DerivedPayerId, secp256k1::All> = offer
                        .request_invoice_deriving_payer_id(expanded_key, entropy, secp_ctx, payment_id)?
-                       .chain_hash(self.chain_hash)?;
+                       .into();
+               let builder = builder.chain_hash(self.chain_hash)?;
                let builder = match quantity {
                        None => builder,
                        Some(quantity) => builder.quantity(quantity)?,
                                let builder = refund.respond_using_derived_keys_no_std(
                                        payment_paths, payment_hash, created_at, expanded_key, entropy
                                )?;
+                               let builder: InvoiceBuilder<DerivedSigningPubkey> = builder.into();
                                let invoice = builder.allow_mpp().build_and_sign(secp_ctx)?;
                                let reply_path = self.create_blinded_path()
                                        .map_err(|_| Bolt12SemanticError::MissingPaths)?;
        /// 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;
  
                let peers = self.per_peer_state.read().unwrap()
                        .collect::<Vec<_>>();
  
                self.router
 -                      .create_blinded_paths(recipient, peers, entropy_source, secp_ctx)
 +                      .create_blinded_paths(recipient, peers, secp_ctx)
                        .and_then(|paths| paths.into_iter().next().ok_or(()))
        }
  
        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() + CLTV_FAR_FAR_AWAY
 +              let max_cltv_expiry = self.best_block.read().unwrap().height + CLTV_FAR_FAR_AWAY
                        + LATENCY_GRACE_PERIOD_BLOCKS;
                let payee_tlvs = ReceiveTlvs {
                        payment_secret,
                        },
                };
                self.router.create_blinded_payment_paths(
 -                      payee_node_id, first_hops, payee_tlvs, amount_msats, entropy_source, secp_ctx
 +                      payee_node_id, first_hops, payee_tlvs, amount_msats, secp_ctx
                )
        }
  
        ///
        /// [phantom node payments]: crate::sign::PhantomKeysManager
        pub fn get_phantom_scid(&self) -> u64 {
 -              let best_block_height = self.best_block.read().unwrap().height();
 +              let best_block_height = self.best_block.read().unwrap().height;
                let short_to_chan_info = self.short_to_chan_info.read().unwrap();
                loop {
                        let scid_candidate = fake_scid::Namespace::Phantom.get_fake_scid(best_block_height, &self.chain_hash, &self.fake_scid_rand_bytes, &self.entropy_source);
        /// Note that this method is not guaranteed to return unique values, you may need to call it a few
        /// times to get a unique scid.
        pub fn get_intercept_scid(&self) -> u64 {
 -              let best_block_height = self.best_block.read().unwrap().height();
 +              let best_block_height = self.best_block.read().unwrap().height;
                let short_to_chan_info = self.short_to_chan_info.read().unwrap();
                loop {
                        let scid_candidate = fake_scid::Namespace::Intercept.get_fake_scid(best_block_height, &self.chain_hash, &self.fake_scid_rand_bytes, &self.entropy_source);
        /// [`Event`] being handled) completes, this should be called to restore the channel to normal
        /// operation. It will double-check that nothing *else* is also blocking the same channel from
        /// making progress and then let any blocked [`ChannelMonitorUpdate`]s fly.
 -      fn handle_monitor_update_release(&self, counterparty_node_id: PublicKey, channel_funding_outpoint: OutPoint, mut completed_blocker: Option<RAAMonitorUpdateBlockingAction>) {
 +      fn handle_monitor_update_release(&self, counterparty_node_id: PublicKey,
 +              channel_funding_outpoint: OutPoint, channel_id: ChannelId,
 +              mut completed_blocker: Option<RAAMonitorUpdateBlockingAction>) {
 +
                let logger = WithContext::from(
 -                      &self.logger, Some(counterparty_node_id), Some(channel_funding_outpoint.to_channel_id())
 +                      &self.logger, Some(counterparty_node_id), Some(channel_id),
                );
                loop {
                        let per_peer_state = self.per_peer_state.read().unwrap();
                                if let Some(blocker) = completed_blocker.take() {
                                        // Only do this on the first iteration of the loop.
                                        if let Some(blockers) = peer_state.actions_blocking_raa_monitor_updates
 -                                              .get_mut(&channel_funding_outpoint.to_channel_id())
 +                                              .get_mut(&channel_id)
                                        {
                                                blockers.retain(|iter| iter != &blocker);
                                        }
                                }
  
                                if self.raa_monitor_updates_held(&peer_state.actions_blocking_raa_monitor_updates,
 -                                      channel_funding_outpoint, counterparty_node_id) {
 +                                      channel_funding_outpoint, channel_id, counterparty_node_id) {
                                        // Check that, while holding the peer lock, we don't have anything else
                                        // blocking monitor updates for this channel. If we do, release the monitor
                                        // update(s) when those blockers complete.
                                        log_trace!(logger, "Delaying monitor unlock for channel {} as another channel's mon update needs to complete first",
 -                                              &channel_funding_outpoint.to_channel_id());
 +                                              &channel_id);
                                        break;
                                }
  
 -                              if let hash_map::Entry::Occupied(mut chan_phase_entry) = peer_state.channel_by_id.entry(channel_funding_outpoint.to_channel_id()) {
 +                              if let hash_map::Entry::Occupied(mut chan_phase_entry) = peer_state.channel_by_id.entry(
 +                                      channel_id) {
                                        if let ChannelPhase::Funded(chan) = chan_phase_entry.get_mut() {
                                                debug_assert_eq!(chan.context.get_funding_txo().unwrap(), channel_funding_outpoint);
                                                if let Some((monitor_update, further_update_exists)) = chan.unblock_next_blocked_monitor_update() {
                                                        log_debug!(logger, "Unlocking monitor updating for channel {} and updating monitor",
 -                                                              channel_funding_outpoint.to_channel_id());
 +                                                              channel_id);
                                                        handle_new_monitor_update!(self, channel_funding_outpoint, monitor_update,
                                                                peer_state_lck, peer_state, per_peer_state, chan);
                                                        if further_update_exists {
                                                        }
                                                } else {
                                                        log_trace!(logger, "Unlocked monitor updating for channel {} without monitors to update",
 -                                                              channel_funding_outpoint.to_channel_id());
 +                                                              channel_id);
                                                }
                                        }
                                }
                for action in actions {
                        match action {
                                EventCompletionAction::ReleaseRAAChannelMonitorUpdate {
 -                                      channel_funding_outpoint, counterparty_node_id
 +                                      channel_funding_outpoint, channel_id, counterparty_node_id
                                } => {
 -                                      self.handle_monitor_update_release(counterparty_node_id, channel_funding_outpoint, None);
 +                                      self.handle_monitor_update_release(counterparty_node_id, channel_funding_outpoint, channel_id, None);
                                }
                        }
                }
@@@ -8365,9 -8286,9 +8401,9 @@@ wher
        fn filtered_block_connected(&self, header: &Header, txdata: &TransactionData, height: u32) {
                {
                        let best_block = self.best_block.read().unwrap();
 -                      assert_eq!(best_block.block_hash(), header.prev_blockhash,
 +                      assert_eq!(best_block.block_hash, header.prev_blockhash,
                                "Blocks must be connected in chain-order - the connected header must build on the last connected header");
 -                      assert_eq!(best_block.height(), height - 1,
 +                      assert_eq!(best_block.height, height - 1,
                                "Blocks must be connected in chain-order - the connected block height must be one greater than the previous height");
                }
  
                let new_height = height - 1;
                {
                        let mut best_block = self.best_block.write().unwrap();
 -                      assert_eq!(best_block.block_hash(), header.block_hash(),
 +                      assert_eq!(best_block.block_hash, header.block_hash(),
                                "Blocks must be disconnected in chain-order - the disconnected header must be the last connected header");
 -                      assert_eq!(best_block.height(), height,
 +                      assert_eq!(best_block.height, height,
                                "Blocks must be disconnected in chain-order - the disconnected block must have the correct height");
                        *best_block = BestBlock::new(header.prev_blockhash, new_height)
                }
@@@ -8418,7 -8339,7 +8454,7 @@@ wher
                self.do_chain_event(Some(height), |channel| channel.transactions_confirmed(&block_hash, height, txdata, self.chain_hash, &self.node_signer, &self.default_configuration, &&WithChannelContext::from(&self.logger, &channel.context))
                        .map(|(a, b)| (a, Vec::new(), b)));
  
 -              let last_best_block_height = self.best_block.read().unwrap().height();
 +              let last_best_block_height = self.best_block.read().unwrap().height;
                if height < last_best_block_height {
                        let timestamp = self.highest_seen_timestamp.load(Ordering::Acquire);
                        self.do_chain_event(Some(last_best_block_height), |channel| channel.best_block_updated(last_best_block_height, timestamp as u32, self.chain_hash, &self.node_signer, &self.default_configuration, &&WithChannelContext::from(&self.logger, &channel.context)));
@@@ -8526,9 -8447,6 +8562,9 @@@ wher
                                        match phase {
                                                // Retain unfunded channels.
                                                ChannelPhase::UnfundedOutboundV1(_) | ChannelPhase::UnfundedInboundV1(_) => true,
 +                                              // TODO(dual_funding): Combine this match arm with above.
 +                                              #[cfg(dual_funding)]
 +                                              ChannelPhase::UnfundedOutboundV2(_) | ChannelPhase::UnfundedInboundV2(_) => true,
                                                ChannelPhase::Funded(channel) => {
                                                        let res = f(channel);
                                                        if let Ok((channel_ready_opt, mut timed_out_pending_htlcs, announcement_sigs)) = res {
                                                incoming_packet_shared_secret: htlc.forward_info.incoming_shared_secret,
                                                phantom_shared_secret: None,
                                                outpoint: htlc.prev_funding_outpoint,
 +                                              channel_id: htlc.prev_channel_id,
                                                blinded_failure: htlc.forward_info.routing.blinded_failure(),
                                        });
  
                                                        HTLCFailReason::from_failure_code(0x2000 | 2),
                                                        HTLCDestination::InvalidForward { requested_forward_scid }));
                                        let logger = WithContext::from(
 -                                              &self.logger, None, Some(htlc.prev_funding_outpoint.to_channel_id())
 +                                              &self.logger, None, Some(htlc.prev_channel_id)
                                        );
                                        log_trace!(logger, "Timing out intercepted HTLC with requested forward scid {}", requested_forward_scid);
                                        false
@@@ -8778,7 -8695,7 +8814,7 @@@ wher
        fn handle_open_channel_v2(&self, counterparty_node_id: &PublicKey, msg: &msgs::OpenChannelV2) {
                let _: Result<(), _> = handle_error!(self, Err(MsgHandleErrInternal::send_err_msg_no_close(
                        "Dual-funded channels not supported".to_owned(),
 -                       msg.temporary_channel_id.clone())), *counterparty_node_id);
 +                       msg.common_fields.temporary_channel_id.clone())), *counterparty_node_id);
        }
  
        fn handle_accept_channel(&self, counterparty_node_id: &PublicKey, msg: &msgs::AcceptChannel) {
        fn handle_accept_channel_v2(&self, counterparty_node_id: &PublicKey, msg: &msgs::AcceptChannelV2) {
                let _: Result<(), _> = handle_error!(self, Err(MsgHandleErrInternal::send_err_msg_no_close(
                        "Dual-funded channels not supported".to_owned(),
 -                       msg.temporary_channel_id.clone())), *counterparty_node_id);
 +                       msg.common_fields.temporary_channel_id.clone())), *counterparty_node_id);
        }
  
        fn handle_funding_created(&self, counterparty_node_id: &PublicKey, msg: &msgs::FundingCreated) {
                                                        }
                                                        &mut chan.context
                                                },
 -                                              // Unfunded channels will always be removed.
 -                                              ChannelPhase::UnfundedOutboundV1(chan) => {
 -                                                      &mut chan.context
 +                                              // We retain UnfundedOutboundV1 channel for some time in case
 +                                              // peer unexpectedly disconnects, and intends to reconnect again.
 +                                              ChannelPhase::UnfundedOutboundV1(_) => {
 +                                                      return true;
                                                },
 +                                              // Unfunded inbound channels will always be removed.
                                                ChannelPhase::UnfundedInboundV1(chan) => {
                                                        &mut chan.context
                                                },
 +                                              #[cfg(dual_funding)]
 +                                              ChannelPhase::UnfundedOutboundV2(chan) => {
 +                                                      &mut chan.context
 +                                              },
 +                                              #[cfg(dual_funding)]
 +                                              ChannelPhase::UnfundedInboundV2(chan) => {
 +                                                      &mut chan.context
 +                                              },
                                        };
                                        // Clean up for removal.
                                        update_maps_on_chan_removal!(self, &context);
                                                        return NotifyOption::SkipPersistNoEvents;
                                                }
                                                e.insert(Mutex::new(PeerState {
 -                                                      channel_by_id: HashMap::new(),
 -                                                      inbound_channel_request_by_id: HashMap::new(),
 +                                                      channel_by_id: new_hash_map(),
 +                                                      inbound_channel_request_by_id: new_hash_map(),
                                                        latest_features: init_msg.features.clone(),
                                                        pending_msg_events: Vec::new(),
                                                        in_flight_monitor_updates: BTreeMap::new(),
                                                let mut peer_state = e.get().lock().unwrap();
                                                peer_state.latest_features = init_msg.features.clone();
  
 -                                              let best_block_height = self.best_block.read().unwrap().height();
 +                                              let best_block_height = self.best_block.read().unwrap().height;
                                                if inbound_peer_limited &&
                                                        Self::unfunded_channel_count(&*peer_state, best_block_height) ==
                                                        peer_state.channel_by_id.len()
                                let peer_state = &mut *peer_state_lock;
                                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 { None }
 -                              ).for_each(|chan| {
 -                                      let logger = WithChannelContext::from(&self.logger, &chan.context);
 -                                      pending_msg_events.push(events::MessageSendEvent::SendChannelReestablish {
 -                                              node_id: chan.context.get_counterparty_node_id(),
 -                                              msg: chan.get_channel_reestablish(&&logger),
 -                                      });
 -                              });
 +                              for (_, phase) in peer_state.channel_by_id.iter_mut() {
 +                                      match phase {
 +                                              ChannelPhase::Funded(chan) => {
 +                                                      let logger = WithChannelContext::from(&self.logger, &chan.context);
 +                                                      pending_msg_events.push(events::MessageSendEvent::SendChannelReestablish {
 +                                                              node_id: chan.context.get_counterparty_node_id(),
 +                                                              msg: chan.get_channel_reestablish(&&logger),
 +                                                      });
 +                                              }
 +
 +                                              ChannelPhase::UnfundedOutboundV1(chan) => {
 +                                                      pending_msg_events.push(events::MessageSendEvent::SendOpenChannel {
 +                                                              node_id: chan.context.get_counterparty_node_id(),
 +                                                              msg: chan.get_open_channel(self.chain_hash),
 +                                                      });
 +                                              }
 +
 +                                              // TODO(dual_funding): Combine this match arm with above once #[cfg(dual_funding)] is removed.
 +                                              #[cfg(dual_funding)]
 +                                              ChannelPhase::UnfundedOutboundV2(chan) => {
 +                                                      pending_msg_events.push(events::MessageSendEvent::SendOpenChannelV2 {
 +                                                              node_id: chan.context.get_counterparty_node_id(),
 +                                                              msg: chan.get_open_channel_v2(self.chain_hash),
 +                                                      });
 +                                              },
 +
 +                                              ChannelPhase::UnfundedInboundV1(_) => {
 +                                                      // Since unfunded inbound channel maps are cleared upon disconnecting a peer,
 +                                                      // they are not persisted and won't be recovered after a crash.
 +                                                      // Therefore, they shouldn't exist at this point.
 +                                                      debug_assert!(false);
 +                                              }
 +
 +                                              // TODO(dual_funding): Combine this match arm with above once #[cfg(dual_funding)] is removed.
 +                                              #[cfg(dual_funding)]
 +                                              ChannelPhase::UnfundedInboundV2(channel) => {
 +                                                      // Since unfunded inbound channel maps are cleared upon disconnecting a peer,
 +                                                      // they are not persisted and won't be recovered after a crash.
 +                                                      // Therefore, they shouldn't exist at this point.
 +                                                      debug_assert!(false);
 +                                              },
 +                                      }
 +                              }
                        }
  
                        return NotifyOption::SkipPersistHandleEvents;
                                if peer_state_mutex_opt.is_none() { return; }
                                let mut peer_state_lock = peer_state_mutex_opt.unwrap().lock().unwrap();
                                let peer_state = &mut *peer_state_lock;
 -                              if let Some(ChannelPhase::UnfundedOutboundV1(chan)) = peer_state.channel_by_id.get_mut(&msg.channel_id) {
 -                                      if let Ok(msg) = chan.maybe_handle_error_without_close(self.chain_hash, &self.fee_estimator) {
 -                                              peer_state.pending_msg_events.push(events::MessageSendEvent::SendOpenChannel {
 -                                                      node_id: *counterparty_node_id,
 -                                                      msg,
 -                                              });
 -                                              return;
 -                                      }
 +                              match peer_state.channel_by_id.get_mut(&msg.channel_id) {
 +                                      Some(ChannelPhase::UnfundedOutboundV1(ref mut chan)) => {
 +                                              if let Ok(msg) = chan.maybe_handle_error_without_close(self.chain_hash, &self.fee_estimator) {
 +                                                      peer_state.pending_msg_events.push(events::MessageSendEvent::SendOpenChannel {
 +                                                              node_id: *counterparty_node_id,
 +                                                              msg,
 +                                                      });
 +                                                      return;
 +                                              }
 +                                      },
 +                                      #[cfg(dual_funding)]
 +                                      Some(ChannelPhase::UnfundedOutboundV2(ref mut chan)) => {
 +                                              if let Ok(msg) = chan.maybe_handle_error_without_close(self.chain_hash, &self.fee_estimator) {
 +                                                      peer_state.pending_msg_events.push(events::MessageSendEvent::SendOpenChannelV2 {
 +                                                              node_id: *counterparty_node_id,
 +                                                              msg,
 +                                                      });
 +                                                      return;
 +                                              }
 +                                      },
 +                                      None | Some(ChannelPhase::UnfundedInboundV1(_) | ChannelPhase::Funded(_)) => (),
 +                                      #[cfg(dual_funding)]
 +                                      Some(ChannelPhase::UnfundedInboundV2(_)) => (),
                                }
                        }
  
@@@ -9424,6 -9282,8 +9460,8 @@@ wher
                                        let builder = invoice_request.respond_using_derived_keys_no_std(
                                                payment_paths, payment_hash, created_at
                                        );
+                                       let builder: Result<InvoiceBuilder<DerivedSigningPubkey>, _> =
+                                               builder.map(|b| b.into());
                                        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())),
                                        let builder = invoice_request.respond_with_no_std(
                                                payment_paths, payment_hash, created_at
                                        );
+                                       let builder: Result<InvoiceBuilder<ExplicitSigningPubkey>, _> =
+                                               builder.map(|b| b.into());
                                        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)) {
+                                               .and_then(|invoice| {
+                                                       #[cfg(c_bindings)]
+                                                       let mut invoice = invoice;
+                                                       match invoice.sign(|invoice: &UnsignedBolt12Invoice|
+                                                               self.node_signer.sign_bolt12_invoice(invoice)
+                                                       ) {
                                                                Ok(invoice) => Ok(OffersMessage::Invoice(invoice)),
-                                                               Err(SignError::Signing(())) => Err(OffersMessage::InvoiceError(
+                                                               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),
@@@ -9597,8 -9464,6 +9642,8 @@@ impl Writeable for ChannelDetails 
                        (37, user_channel_id_high_opt, option),
                        (39, self.feerate_sat_per_1000_weight, option),
                        (41, self.channel_shutdown_state, option),
 +                      (43, self.pending_inbound_htlcs, optional_vec),
 +                      (45, self.pending_outbound_htlcs, optional_vec),
                });
                Ok(())
        }
@@@ -9637,8 -9502,6 +9682,8 @@@ impl Readable for ChannelDetails 
                        (37, user_channel_id_high_opt, option),
                        (39, feerate_sat_per_1000_weight, option),
                        (41, channel_shutdown_state, option),
 +                      (43, pending_inbound_htlcs, optional_vec),
 +                      (45, pending_outbound_htlcs, optional_vec),
                });
  
                // `user_channel_id` used to be a single u64 value. In order to remain backwards compatible with
                        inbound_htlc_maximum_msat,
                        feerate_sat_per_1000_weight,
                        channel_shutdown_state,
 +                      pending_inbound_htlcs: pending_inbound_htlcs.unwrap_or(Vec::new()),
 +                      pending_outbound_htlcs: pending_outbound_htlcs.unwrap_or(Vec::new()),
                })
        }
  }
@@@ -9811,9 -9672,6 +9856,9 @@@ impl_writeable_tlv_based!(HTLCPreviousH
        (4, htlc_id, required),
        (6, incoming_packet_shared_secret, required),
        (7, user_channel_id, option),
 +      // Note that by the time we get past the required read for type 2 above, outpoint will be
 +      // filled in, so we can safely unwrap it here.
 +      (9, channel_id, (default_value, ChannelId::v1_from_funding_outpoint(outpoint.0.unwrap()))),
  });
  
  impl Writeable for ClaimableHTLC {
@@@ -9965,9 -9823,6 +10010,9 @@@ impl_writeable_tlv_based!(PendingAddHTL
        (2, prev_short_channel_id, required),
        (4, prev_htlc_id, required),
        (6, prev_funding_outpoint, required),
 +      // Note that by the time we get past the required read for type 6 above, prev_funding_outpoint will be
 +      // filled in, so we can safely unwrap it here.
 +      (7, prev_channel_id, (default_value, ChannelId::v1_from_funding_outpoint(prev_funding_outpoint.0.unwrap()))),
  });
  
  impl Writeable for HTLCForwardInfo {
@@@ -10060,8 -9915,8 +10105,8 @@@ wher
                self.chain_hash.write(writer)?;
                {
                        let best_block = self.best_block.read().unwrap();
 -                      best_block.height().write(writer)?;
 -                      best_block.block_hash().write(writer)?;
 +                      best_block.height.write(writer)?;
 +                      best_block.block_hash.write(writer)?;
                }
  
                let mut serializable_peer_count: u64 = 0;
                }
  
                // Encode without retry info for 0.0.101 compatibility.
 -              let mut pending_outbound_payments_no_retry: HashMap<PaymentId, HashSet<[u8; 32]>> = HashMap::new();
 +              let mut pending_outbound_payments_no_retry: HashMap<PaymentId, HashSet<[u8; 32]>> = new_hash_map();
                for (id, outbound) in pending_outbound_payments.iter() {
                        match outbound {
                                PendingOutboundPayment::Legacy { session_privs } |
                for ((counterparty_id, _), peer_state) in per_peer_state.iter().zip(peer_states.iter()) {
                        for (funding_outpoint, updates) in peer_state.in_flight_monitor_updates.iter() {
                                if !updates.is_empty() {
 -                                      if in_flight_monitor_updates.is_none() { in_flight_monitor_updates = Some(HashMap::new()); }
 +                                      if in_flight_monitor_updates.is_none() { in_flight_monitor_updates = Some(new_hash_map()); }
                                        in_flight_monitor_updates.as_mut().unwrap().insert((counterparty_id, funding_outpoint), updates);
                                }
                        }
@@@ -10428,9 -10283,7 +10473,9 @@@ wher
                        mut channel_monitors: Vec<&'a mut ChannelMonitor<<SP::Target as SignerProvider>::EcdsaSigner>>) -> Self {
                Self {
                        entropy_source, node_signer, signer_provider, fee_estimator, chain_monitor, tx_broadcaster, router, logger, default_config,
 -                      channel_monitors: channel_monitors.drain(..).map(|monitor| { (monitor.get_funding_txo().0, monitor) }).collect()
 +                      channel_monitors: hash_map_from_iter(
 +                              channel_monitors.drain(..).map(|monitor| { (monitor.get_funding_txo().0, monitor) })
 +                      ),
                }
        }
  }
                let mut failed_htlcs = Vec::new();
  
                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 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 funding_txo_set = hash_set_with_capacity(cmp::min(channel_count as usize, 128));
 +              let mut funded_peer_channels: HashMap<PublicKey, HashMap<ChannelId, ChannelPhase<SP>>> = hash_map_with_capacity(cmp::min(channel_count as usize, 128));
 +              let mut outpoint_to_peer = hash_map_with_capacity(cmp::min(channel_count as usize, 128));
 +              let mut short_to_chan_info = hash_map_with_capacity(cmp::min(channel_count as usize, 128));
                let mut channel_closures = VecDeque::new();
                let mut close_background_events = Vec::new();
 +              let mut funding_txo_to_channel_id = hash_map_with_capacity(channel_count as usize);
                for _ in 0..channel_count {
                        let mut channel: Channel<SP> = Channel::read(reader, (
                                &args.entropy_source, &args.signer_provider, best_block_height, &provided_channel_type_features(&args.default_config)
                        ))?;
                        let logger = WithChannelContext::from(&args.logger, &channel.context);
                        let funding_txo = channel.context.get_funding_txo().ok_or(DecodeError::InvalidValue)?;
 +                      funding_txo_to_channel_id.insert(funding_txo, channel.context.channel_id());
                        funding_txo_set.insert(funding_txo.clone());
                        if let Some(ref mut monitor) = args.channel_monitors.get_mut(&funding_txo) {
                                if channel.get_cur_holder_commitment_transaction_number() > monitor.get_cur_holder_commitment_number() ||
                                        if shutdown_result.unbroadcasted_batch_funding_txid.is_some() {
                                                return Err(DecodeError::InvalidValue);
                                        }
 -                                      if let Some((counterparty_node_id, funding_txo, update)) = shutdown_result.monitor_update {
 +                                      if let Some((counterparty_node_id, funding_txo, channel_id, update)) = shutdown_result.monitor_update {
                                                close_background_events.push(BackgroundEvent::MonitorUpdateRegeneratedOnStartup {
 -                                                      counterparty_node_id, funding_txo, update
 +                                                      counterparty_node_id, funding_txo, channel_id, update
                                                });
                                        }
                                        failed_htlcs.append(&mut shutdown_result.dropped_outbound_htlcs);
                                                        by_id_map.insert(channel.context.channel_id(), ChannelPhase::Funded(channel));
                                                },
                                                hash_map::Entry::Vacant(entry) => {
 -                                                      let mut by_id_map = HashMap::new();
 +                                                      let mut by_id_map = new_hash_map();
                                                        by_id_map.insert(channel.context.channel_id(), ChannelPhase::Funded(channel));
                                                        entry.insert(by_id_map);
                                                }
                for (funding_txo, monitor) in args.channel_monitors.iter() {
                        if !funding_txo_set.contains(funding_txo) {
                                let logger = WithChannelMonitor::from(&args.logger, monitor);
 +                              let channel_id = monitor.channel_id();
                                log_info!(logger, "Queueing monitor update to ensure missing channel {} is force closed",
 -                                      &funding_txo.to_channel_id());
 +                                      &channel_id);
                                let monitor_update = ChannelMonitorUpdate {
                                        update_id: CLOSED_CHANNEL_UPDATE_ID,
                                        counterparty_node_id: None,
                                        updates: vec![ChannelMonitorUpdateStep::ChannelForceClosed { should_broadcast: true }],
 +                                      channel_id: Some(monitor.channel_id()),
                                };
 -                              close_background_events.push(BackgroundEvent::ClosedMonitorUpdateRegeneratedOnStartup((*funding_txo, monitor_update)));
 +                              close_background_events.push(BackgroundEvent::ClosedMonitorUpdateRegeneratedOnStartup((*funding_txo, channel_id, monitor_update)));
                        }
                }
  
                const MAX_ALLOC_SIZE: usize = 1024 * 64;
                let forward_htlcs_count: u64 = Readable::read(reader)?;
 -              let mut forward_htlcs = HashMap::with_capacity(cmp::min(forward_htlcs_count as usize, 128));
 +              let mut forward_htlcs = hash_map_with_capacity(cmp::min(forward_htlcs_count as usize, 128));
                for _ in 0..forward_htlcs_count {
                        let short_channel_id = Readable::read(reader)?;
                        let pending_forwards_count: u64 = Readable::read(reader)?;
                let peer_state_from_chans = |channel_by_id| {
                        PeerState {
                                channel_by_id,
 -                              inbound_channel_request_by_id: HashMap::new(),
 +                              inbound_channel_request_by_id: new_hash_map(),
                                latest_features: InitFeatures::empty(),
                                pending_msg_events: Vec::new(),
                                in_flight_monitor_updates: BTreeMap::new(),
                };
  
                let peer_count: u64 = Readable::read(reader)?;
 -              let mut per_peer_state = HashMap::with_capacity(cmp::min(peer_count as usize, MAX_ALLOC_SIZE/mem::size_of::<(PublicKey, Mutex<PeerState<SP>>)>()));
 +              let mut per_peer_state = hash_map_with_capacity(cmp::min(peer_count as usize, MAX_ALLOC_SIZE/mem::size_of::<(PublicKey, Mutex<PeerState<SP>>)>()));
                for _ in 0..peer_count {
                        let peer_pubkey = Readable::read(reader)?;
 -                      let peer_chans = funded_peer_channels.remove(&peer_pubkey).unwrap_or(HashMap::new());
 +                      let peer_chans = funded_peer_channels.remove(&peer_pubkey).unwrap_or(new_hash_map());
                        let mut peer_state = peer_state_from_chans(peer_chans);
                        peer_state.latest_features = Readable::read(reader)?;
                        per_peer_state.insert(peer_pubkey, Mutex::new(peer_state));
                let highest_seen_timestamp: u32 = Readable::read(reader)?;
  
                let pending_inbound_payment_count: u64 = Readable::read(reader)?;
 -              let mut pending_inbound_payments: HashMap<PaymentHash, PendingInboundPayment> = HashMap::with_capacity(cmp::min(pending_inbound_payment_count as usize, MAX_ALLOC_SIZE/(3*32)));
 +              let mut pending_inbound_payments: HashMap<PaymentHash, PendingInboundPayment> = hash_map_with_capacity(cmp::min(pending_inbound_payment_count as usize, MAX_ALLOC_SIZE/(3*32)));
                for _ in 0..pending_inbound_payment_count {
                        if pending_inbound_payments.insert(Readable::read(reader)?, Readable::read(reader)?).is_some() {
                                return Err(DecodeError::InvalidValue);
  
                let pending_outbound_payments_count_compat: u64 = Readable::read(reader)?;
                let mut pending_outbound_payments_compat: HashMap<PaymentId, PendingOutboundPayment> =
 -                      HashMap::with_capacity(cmp::min(pending_outbound_payments_count_compat as usize, MAX_ALLOC_SIZE/32));
 +                      hash_map_with_capacity(cmp::min(pending_outbound_payments_count_compat as usize, MAX_ALLOC_SIZE/32));
                for _ in 0..pending_outbound_payments_count_compat {
                        let session_priv = Readable::read(reader)?;
                        let payment = PendingOutboundPayment::Legacy {
 -                              session_privs: [session_priv].iter().cloned().collect()
 +                              session_privs: hash_set_from_iter([session_priv]),
                        };
                        if pending_outbound_payments_compat.insert(PaymentId(session_priv), payment).is_some() {
                                return Err(DecodeError::InvalidValue)
                // pending_outbound_payments_no_retry is for compatibility with 0.0.101 clients.
                let mut pending_outbound_payments_no_retry: Option<HashMap<PaymentId, HashSet<[u8; 32]>>> = None;
                let mut pending_outbound_payments = None;
 -              let mut pending_intercepted_htlcs: Option<HashMap<InterceptId, PendingAddHTLCInfo>> = Some(HashMap::new());
 +              let mut pending_intercepted_htlcs: Option<HashMap<InterceptId, PendingAddHTLCInfo>> = Some(new_hash_map());
                let mut received_network_pubkey: Option<PublicKey> = None;
                let mut fake_scid_rand_bytes: Option<[u8; 32]> = None;
                let mut probing_cookie_secret: Option<[u8; 32]> = None;
                let mut claimable_htlc_purposes = None;
                let mut claimable_htlc_onion_fields = None;
 -              let mut pending_claiming_payments = Some(HashMap::new());
 +              let mut pending_claiming_payments = Some(new_hash_map());
                let mut monitor_update_blocked_actions_per_peer: Option<Vec<(_, BTreeMap<_, Vec<_>>)>> = Some(Vec::new());
                let mut events_override = None;
                let mut in_flight_monitor_updates: Option<HashMap<(PublicKey, OutPoint), Vec<ChannelMonitorUpdate>>> = None;
                if pending_outbound_payments.is_none() && pending_outbound_payments_no_retry.is_none() {
                        pending_outbound_payments = Some(pending_outbound_payments_compat);
                } else if pending_outbound_payments.is_none() {
 -                      let mut outbounds = HashMap::new();
 +                      let mut outbounds = new_hash_map();
                        for (id, session_privs) in pending_outbound_payments_no_retry.unwrap().drain() {
                                outbounds.insert(id, PendingOutboundPayment::Legacy { session_privs });
                        }
                                $chan_in_flight_upds.retain(|upd| upd.update_id > $monitor.get_latest_update_id());
                                for update in $chan_in_flight_upds.iter() {
                                        log_trace!($logger, "Replaying ChannelMonitorUpdate {} for {}channel {}",
 -                                              update.update_id, $channel_info_log, &$funding_txo.to_channel_id());
 +                                              update.update_id, $channel_info_log, &$monitor.channel_id());
                                        max_in_flight_update_id = cmp::max(max_in_flight_update_id, update.update_id);
                                        pending_background_events.push(
                                                BackgroundEvent::MonitorUpdateRegeneratedOnStartup {
                                                        counterparty_node_id: $counterparty_node_id,
                                                        funding_txo: $funding_txo,
 +                                                      channel_id: $monitor.channel_id(),
                                                        update: update.clone(),
                                                });
                                }
                                        pending_background_events.push(
                                                BackgroundEvent::MonitorUpdatesComplete {
                                                        counterparty_node_id: $counterparty_node_id,
 -                                                      channel_id: $funding_txo.to_channel_id(),
 +                                                      channel_id: $monitor.channel_id(),
                                                });
                                }
                                if $peer_state.in_flight_monitor_updates.insert($funding_txo, $chan_in_flight_upds).is_some() {
  
                if let Some(in_flight_upds) = in_flight_monitor_updates {
                        for ((counterparty_id, funding_txo), mut chan_in_flight_updates) in in_flight_upds {
 -                              let logger = WithContext::from(&args.logger, Some(counterparty_id), Some(funding_txo.to_channel_id()));
 +                              let channel_id = funding_txo_to_channel_id.get(&funding_txo).copied();
 +                              let logger = WithContext::from(&args.logger, Some(counterparty_id), channel_id);
                                if let Some(monitor) = args.channel_monitors.get(&funding_txo) {
                                        // Now that we've removed all the in-flight monitor updates for channels that are
                                        // still open, we need to replay any monitor updates that are for closed channels,
                                        // creating the neccessary peer_state entries as we go.
                                        let peer_state_mutex = per_peer_state.entry(counterparty_id).or_insert_with(|| {
 -                                              Mutex::new(peer_state_from_chans(HashMap::new()))
 +                                              Mutex::new(peer_state_from_chans(new_hash_map()))
                                        });
                                        let mut peer_state = peer_state_mutex.lock().unwrap();
                                        handle_in_flight_updates!(counterparty_id, chan_in_flight_updates,
                                                funding_txo, monitor, peer_state, logger, "closed ");
                                } else {
                                        log_error!(logger, "A ChannelMonitor is missing even though we have in-flight updates for it! This indicates a potentially-critical violation of the chain::Watch API!");
 -                                      log_error!(logger, " The ChannelMonitor for channel {} is missing.",
 -                                              &funding_txo.to_channel_id());
 +                                      log_error!(logger, " The ChannelMonitor for channel {} is missing.", if let Some(channel_id) =
 +                                              channel_id { channel_id.to_string() } else { format!("with outpoint {}", funding_txo) } );
                                        log_error!(logger, " The chain::Watch API *requires* that monitors are persisted durably before returning,");
                                        log_error!(logger, " client applications must ensure that ChannelMonitor data is always available and the latest to avoid funds loss!");
                                        log_error!(logger, " Without the latest ChannelMonitor we cannot continue without risking funds.");
                                                                                retry_strategy: None,
                                                                                attempts: PaymentAttempts::new(),
                                                                                payment_params: None,
 -                                                                              session_privs: [session_priv_bytes].iter().map(|a| *a).collect(),
 +                                                                              session_privs: hash_set_from_iter([session_priv_bytes]),
                                                                                payment_hash: htlc.payment_hash,
                                                                                payment_secret: None, // only used for retries, and we'll never retry on startup
                                                                                payment_metadata: None, // only used for retries, and we'll never retry on startup
                                                                                if let HTLCForwardInfo::AddHTLC(htlc_info) = forward {
                                                                                        if pending_forward_matches_htlc(&htlc_info) {
                                                                                                log_info!(logger, "Removing pending to-forward HTLC with hash {} as it was forwarded to the closed channel {}",
 -                                                                                                      &htlc.payment_hash, &monitor.get_funding_txo().0.to_channel_id());
 +                                                                                                      &htlc.payment_hash, &monitor.channel_id());
                                                                                                false
                                                                                        } else { true }
                                                                                } else { true }
                                                                pending_intercepted_htlcs.as_mut().unwrap().retain(|intercepted_id, htlc_info| {
                                                                        if pending_forward_matches_htlc(&htlc_info) {
                                                                                log_info!(logger, "Removing pending intercepted HTLC with hash {} as it was forwarded to the closed channel {}",
 -                                                                                      &htlc.payment_hash, &monitor.get_funding_txo().0.to_channel_id());
 +                                                                                      &htlc.payment_hash, &monitor.channel_id());
                                                                                pending_events_read.retain(|(event, _)| {
                                                                                        if let Event::HTLCIntercepted { intercept_id: ev_id, .. } = event {
                                                                                                intercepted_id != ev_id
                                                                        let compl_action =
                                                                                EventCompletionAction::ReleaseRAAChannelMonitorUpdate {
                                                                                        channel_funding_outpoint: monitor.get_funding_txo().0,
 +                                                                                      channel_id: monitor.channel_id(),
                                                                                        counterparty_node_id: path.hops[0].pubkey,
                                                                                };
                                                                        pending_outbounds.claim_htlc(payment_id, preimage, session_priv,
                                                                        // channel_id -> peer map entry).
                                                                        counterparty_opt.is_none(),
                                                                        counterparty_opt.cloned().or(monitor.get_counterparty_node_id()),
 -                                                                      monitor.get_funding_txo().0))
 +                                                                      monitor.get_funding_txo().0, monitor.channel_id()))
                                                        } else { None }
                                                } else {
                                                        // If it was an outbound payment, we've handled it above - if a preimage
                let inbound_pmt_key_material = args.node_signer.get_inbound_payment_key_material();
                let expanded_inbound_key = inbound_payment::ExpandedKey::new(&inbound_pmt_key_material);
  
 -              let mut claimable_payments = HashMap::with_capacity(claimable_htlcs_list.len());
 +              let mut claimable_payments = hash_map_with_capacity(claimable_htlcs_list.len());
                if let Some(purposes) = claimable_htlc_purposes {
                        if purposes.len() != claimable_htlcs_list.len() {
                                return Err(DecodeError::InvalidValue);
                        }
                }
  
 -              let mut outbound_scid_aliases = HashSet::new();
 +              let mut outbound_scid_aliases = new_hash_set();
                for (_peer_node_id, peer_state_mutex) in per_peer_state.iter_mut() {
                        let mut peer_state_lock = peer_state_mutex.lock().unwrap();
                        let peer_state = &mut *peer_state_lock;
                                                // this channel as well. On the flip side, there's no harm in restarting
                                                // 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();
 +                                              let previous_channel_id = claimable_htlc.prev_hop.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();
                                        for action in actions.iter() {
                                                if let MonitorUpdateCompletionAction::EmitEventAndFreeOtherChannel {
                                                        downstream_counterparty_and_funding_outpoint:
 -                                                              Some((blocked_node_id, blocked_channel_outpoint, blocking_action)), ..
 +                                                              Some((blocked_node_id, _blocked_channel_outpoint, blocked_channel_id, blocking_action)), ..
                                                } = action {
 -                                                      if let Some(blocked_peer_state) = per_peer_state.get(&blocked_node_id) {
 +                                                      if let Some(blocked_peer_state) = per_peer_state.get(blocked_node_id) {
                                                                log_trace!(logger,
                                                                        "Holding the next revoke_and_ack from {} until the preimage is durably persisted in the inbound edge's ChannelMonitor",
 -                                                                      blocked_channel_outpoint.to_channel_id());
 +                                                                      blocked_channel_id);
                                                                blocked_peer_state.lock().unwrap().actions_blocking_raa_monitor_updates
 -                                                                      .entry(blocked_channel_outpoint.to_channel_id())
 +                                                                      .entry(*blocked_channel_id)
                                                                        .or_insert_with(Vec::new).push(blocking_action.clone());
                                                        } else {
                                                                // If the channel we were blocking has closed, we don't need to
                        channel_manager.fail_htlc_backwards_internal(&source, &payment_hash, &reason, receiver);
                }
  
 -              for (source, preimage, downstream_value, downstream_closed, downstream_node_id, downstream_funding) in pending_claims_to_replay {
 +              for (source, preimage, downstream_value, downstream_closed, downstream_node_id, downstream_funding, downstream_channel_id) in pending_claims_to_replay {
                        // We use `downstream_closed` in place of `from_onchain` here just as a guess - we
                        // don't remember in the `ChannelMonitor` where we got a preimage from, but if the
                        // channel is closed we just assume that it probably came from an on-chain claim.
 -                      channel_manager.claim_funds_internal(source, preimage, Some(downstream_value),
 -                              downstream_closed, true, downstream_node_id, downstream_funding);
 +                      channel_manager.claim_funds_internal(source, preimage, Some(downstream_value), None,
 +                              downstream_closed, true, downstream_node_id, downstream_funding, downstream_channel_id);
                }
  
                //TODO: Broadcast channel update for closed channels, but only after we've made a
@@@ -12006,8 -11852,8 +12051,8 @@@ mod tests 
                }
                let (_nodes_1_update, _none) = get_closing_signed_broadcast!(nodes[1].node, nodes[0].node.get_our_node_id());
  
 -              check_closed_event!(nodes[0], 1, ClosureReason::CooperativeClosure, [nodes[1].node.get_our_node_id()], 1000000);
 -              check_closed_event!(nodes[1], 1, ClosureReason::CooperativeClosure, [nodes[0].node.get_our_node_id()], 1000000);
 +              check_closed_event!(nodes[0], 1, ClosureReason::LocallyInitiatedCooperativeClosure, [nodes[1].node.get_our_node_id()], 1000000);
 +              check_closed_event!(nodes[1], 1, ClosureReason::CounterpartyInitiatedCooperativeClosure, [nodes[0].node.get_our_node_id()], 1000000);
        }
  
        fn check_not_connected_to_peer_error<T>(res_err: Result<T, APIError>, expected_public_key: PublicKey) {
                                check_added_monitors!(nodes[0], 1);
                                expect_channel_pending_event(&nodes[0], &nodes[1].node.get_our_node_id());
                        }
 -                      open_channel_msg.temporary_channel_id = ChannelId::temporary_from_entropy_source(&nodes[0].keys_manager);
 +                      open_channel_msg.common_fields.temporary_channel_id = ChannelId::temporary_from_entropy_source(&nodes[0].keys_manager);
                }
  
                // A MAX_UNFUNDED_CHANS_PER_PEER + 1 channel will be summarily rejected
 -              open_channel_msg.temporary_channel_id = ChannelId::temporary_from_entropy_source(&nodes[0].keys_manager);
 +              open_channel_msg.common_fields.temporary_channel_id = ChannelId::temporary_from_entropy_source(
 +                      &nodes[0].keys_manager);
                nodes[1].node.handle_open_channel(&nodes[0].node.get_our_node_id(), &open_channel_msg);
                assert_eq!(get_err_msg(&nodes[1], &nodes[0].node.get_our_node_id()).channel_id,
 -                      open_channel_msg.temporary_channel_id);
 +                      open_channel_msg.common_fields.temporary_channel_id);
  
                // Further, because all of our channels with nodes[0] are inbound, and none of them funded,
                // it doesn't count as a "protected" peer, i.e. it counts towards the MAX_NO_CHANNEL_PEERS
                for i in 0..super::MAX_UNFUNDED_CHANNEL_PEERS - 1 {
                        nodes[1].node.handle_open_channel(&peer_pks[i], &open_channel_msg);
                        get_event_msg!(nodes[1], MessageSendEvent::SendAcceptChannel, peer_pks[i]);
 -                      open_channel_msg.temporary_channel_id = ChannelId::temporary_from_entropy_source(&nodes[0].keys_manager);
 +                      open_channel_msg.common_fields.temporary_channel_id = ChannelId::temporary_from_entropy_source(&nodes[0].keys_manager);
                }
                nodes[1].node.handle_open_channel(&last_random_pk, &open_channel_msg);
                assert_eq!(get_err_msg(&nodes[1], &last_random_pk).channel_id,
 -                      open_channel_msg.temporary_channel_id);
 +                      open_channel_msg.common_fields.temporary_channel_id);
  
                // Of course, however, outbound channels are always allowed
                nodes[1].node.create_channel(last_random_pk, 100_000, 0, 42, None, None).unwrap();
                for _ in 0..super::MAX_UNFUNDED_CHANS_PER_PEER {
                        nodes[1].node.handle_open_channel(&nodes[0].node.get_our_node_id(), &open_channel_msg);
                        get_event_msg!(nodes[1], MessageSendEvent::SendAcceptChannel, nodes[0].node.get_our_node_id());
 -                      open_channel_msg.temporary_channel_id = ChannelId::temporary_from_entropy_source(&nodes[0].keys_manager);
 +                      open_channel_msg.common_fields.temporary_channel_id = ChannelId::temporary_from_entropy_source(&nodes[0].keys_manager);
                }
  
                // Once we have MAX_UNFUNDED_CHANS_PER_PEER unfunded channels, new inbound channels will be
                // rejected.
                nodes[1].node.handle_open_channel(&nodes[0].node.get_our_node_id(), &open_channel_msg);
                assert_eq!(get_err_msg(&nodes[1], &nodes[0].node.get_our_node_id()).channel_id,
 -                      open_channel_msg.temporary_channel_id);
 +                      open_channel_msg.common_fields.temporary_channel_id);
  
                // but we can still open an outbound channel.
                nodes[1].node.create_channel(nodes[0].node.get_our_node_id(), 100_000, 0, 42, None, None).unwrap();
                // but even with such an outbound channel, additional inbound channels will still fail.
                nodes[1].node.handle_open_channel(&nodes[0].node.get_our_node_id(), &open_channel_msg);
                assert_eq!(get_err_msg(&nodes[1], &nodes[0].node.get_our_node_id()).channel_id,
 -                      open_channel_msg.temporary_channel_id);
 +                      open_channel_msg.common_fields.temporary_channel_id);
        }
  
        #[test]
                                _ => panic!("Unexpected event"),
                        }
                        get_event_msg!(nodes[1], MessageSendEvent::SendAcceptChannel, random_pk);
 -                      open_channel_msg.temporary_channel_id = ChannelId::temporary_from_entropy_source(&nodes[0].keys_manager);
 +                      open_channel_msg.common_fields.temporary_channel_id = ChannelId::temporary_from_entropy_source(&nodes[0].keys_manager);
                }
  
                // If we try to accept a channel from another peer non-0conf it will fail.
                        _ => panic!("Unexpected event"),
                }
                assert_eq!(get_err_msg(&nodes[1], &last_random_pk).channel_id,
 -                      open_channel_msg.temporary_channel_id);
 +                      open_channel_msg.common_fields.temporary_channel_id);
  
                // ...however if we accept the same channel 0conf it should work just fine.
                nodes[1].node.handle_open_channel(&last_random_pk, &open_channel_msg);
                };
                // Check that if the amount we received + the penultimate hop extra fee is less than the sender
                // intended amount, we fail the payment.
 -              let current_height: u32 = node[0].node.best_block.read().unwrap().height();
 +              let current_height: u32 = node[0].node.best_block.read().unwrap().height;
                if let Err(crate::ln::channelmanager::InboundHTLCErr { err_code, .. }) =
                        create_recv_pending_htlc_info(hop_data, [0; 32], PaymentHash([0; 32]),
                                sender_intended_amt_msat - extra_fee_msat - 1, 42, None, true, Some(extra_fee_msat),
                        }),
                        custom_tlvs: Vec::new(),
                };
 -              let current_height: u32 = node[0].node.best_block.read().unwrap().height();
 +              let current_height: u32 = node[0].node.best_block.read().unwrap().height;
                assert!(create_recv_pending_htlc_info(hop_data, [0; 32], PaymentHash([0; 32]),
                        sender_intended_amt_msat - extra_fee_msat, 42, None, true, Some(extra_fee_msat),
                        current_height, node[0].node.default_configuration.accept_mpp_keysend).is_ok());
                let node_chanmgr = create_node_chanmgrs(1, &node_cfg, &[None]);
                let node = create_network(1, &node_cfg, &node_chanmgr);
  
 -              let current_height: u32 = node[0].node.best_block.read().unwrap().height();
 +              let current_height: u32 = node[0].node.best_block.read().unwrap().height;
                let result = create_recv_pending_htlc_info(msgs::InboundOnionPayload::Receive {
                        sender_intended_htlc_amt_msat: 100,
                        cltv_expiry_height: 22,
  
                nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), 100_000, 0, 0, None, None).unwrap();
                let open_channel_msg = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel, nodes[1].node.get_our_node_id());
 -              assert!(open_channel_msg.channel_type.as_ref().unwrap().supports_anchors_zero_fee_htlc_tx());
 +              assert!(open_channel_msg.common_fields.channel_type.as_ref().unwrap().supports_anchors_zero_fee_htlc_tx());
  
                nodes[1].node.handle_open_channel(&nodes[0].node.get_our_node_id(), &open_channel_msg);
                let events = nodes[1].node.get_and_clear_pending_events();
                nodes[0].node.handle_error(&nodes[1].node.get_our_node_id(), &error_msg);
  
                let open_channel_msg = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel, nodes[1].node.get_our_node_id());
 -              assert!(!open_channel_msg.channel_type.unwrap().supports_anchors_zero_fee_htlc_tx());
 +              assert!(!open_channel_msg.common_fields.channel_type.unwrap().supports_anchors_zero_fee_htlc_tx());
  
                // Since nodes[1] should not have accepted the channel, it should
                // not have generated any events.
  
  
                let (scid_1, scid_2) = (42, 43);
 -              let mut forward_htlcs = HashMap::new();
 +              let mut forward_htlcs = new_hash_map();
                forward_htlcs.insert(scid_1, dummy_htlcs_1.clone());
                forward_htlcs.insert(scid_2, dummy_htlcs_2.clone());
  
@@@ -12802,7 -12647,7 +12847,7 @@@ pub mod bench 
  
                assert_eq!(&tx_broadcaster.txn_broadcasted.lock().unwrap()[..], &[tx.clone()]);
  
 -              let block = create_dummy_block(BestBlock::from_network(network).block_hash(), 42, vec![tx]);
 +              let block = create_dummy_block(BestBlock::from_network(network).block_hash, 42, vec![tx]);
                Listen::block_connected(&node_a, &block, 1);
                Listen::block_connected(&node_b, &block, 1);
  
index d71ec2afd04e9642c11d164e4a8c0f6a02e49dda,8f6cac75d60fffb822a2622f9b818a776c424bc3..e9e35836a96e2bec19b5bbe73c390ad78481e4bf
@@@ -80,7 -80,6 +80,7 @@@ use bitcoin::blockdata::constants::Chai
  use bitcoin::network::constants::Network;
  use bitcoin::secp256k1::{KeyPair, PublicKey, Secp256k1, self};
  use core::convert::TryFrom;
 +use core::hash::{Hash, Hasher};
  use core::num::NonZeroU64;
  use core::ops::Deref;
  use core::str::FromStr;
@@@ -92,13 -91,21 +92,21 @@@ use crate::ln::channelmanager::PaymentI
  use crate::ln::features::OfferFeatures;
  use crate::ln::inbound_payment::{ExpandedKey, IV_LEN, Nonce};
  use crate::ln::msgs::MAX_VALUE_MSAT;
- use crate::offers::invoice_request::{DerivedPayerId, ExplicitPayerId, InvoiceRequestBuilder};
  use crate::offers::merkle::TlvStream;
  use crate::offers::parse::{Bech32Encode, Bolt12ParseError, Bolt12SemanticError, ParsedMessage};
  use crate::offers::signer::{Metadata, MetadataMaterial, self};
  use crate::util::ser::{HighZeroBytesDroppedBigSize, WithoutLength, Writeable, Writer};
  use crate::util::string::PrintableString;
  
+ #[cfg(not(c_bindings))]
+ use {
+       crate::offers::invoice_request::{DerivedPayerId, ExplicitPayerId, InvoiceRequestBuilder},
+ };
+ #[cfg(c_bindings)]
+ use {
+       crate::offers::invoice_request::{InvoiceRequestWithDerivedPayerIdBuilder, InvoiceRequestWithExplicitPayerIdBuilder},
+ };
  use crate::prelude::*;
  
  #[cfg(feature = "std")]
@@@ -119,6 -126,34 +127,34 @@@ pub struct OfferBuilder<'a, M: Metadata
        secp_ctx: Option<&'a Secp256k1<T>>,
  }
  
+ /// Builds an [`Offer`] for the "offer to be paid" flow.
+ ///
+ /// See [module-level documentation] for usage.
+ ///
+ /// This is not exported to bindings users as builder patterns don't map outside of move semantics.
+ ///
+ /// [module-level documentation]: self
+ #[cfg(c_bindings)]
+ pub struct OfferWithExplicitMetadataBuilder<'a> {
+       offer: OfferContents,
+       metadata_strategy: core::marker::PhantomData<ExplicitMetadata>,
+       secp_ctx: Option<&'a Secp256k1<secp256k1::All>>,
+ }
+ /// Builds an [`Offer`] for the "offer to be paid" flow.
+ ///
+ /// See [module-level documentation] for usage.
+ ///
+ /// This is not exported to bindings users as builder patterns don't map outside of move semantics.
+ ///
+ /// [module-level documentation]: self
+ #[cfg(c_bindings)]
+ pub struct OfferWithDerivedMetadataBuilder<'a> {
+       offer: OfferContents,
+       metadata_strategy: core::marker::PhantomData<DerivedMetadata>,
+       secp_ctx: Option<&'a Secp256k1<secp256k1::All>>,
+ }
  /// Indicates how [`Offer::metadata`] may be set.
  ///
  /// This is not exported to bindings users as builder patterns don't map outside of move semantics.
@@@ -135,9 -170,12 +171,12 @@@ pub struct ExplicitMetadata {
  pub struct DerivedMetadata {}
  
  impl MetadataStrategy for ExplicitMetadata {}
  impl MetadataStrategy for DerivedMetadata {}
  
- impl<'a> OfferBuilder<'a, ExplicitMetadata, secp256k1::SignOnly> {
+ macro_rules! offer_explicit_metadata_builder_methods { (
+       $self: ident, $self_type: ty, $return_type: ty, $return_value: expr
+ ) => {
        /// Creates a new builder for an offer setting the [`Offer::description`] and using the
        /// [`Offer::signing_pubkey`] for signing invoices. The associated secret key must be remembered
        /// while the offer is valid.
        /// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager
        /// [`ChannelManager::create_offer_builder`]: crate::ln::channelmanager::ChannelManager::create_offer_builder
        pub fn new(description: String, signing_pubkey: PublicKey) -> Self {
-               OfferBuilder {
+               Self {
                        offer: OfferContents {
                                chains: None, metadata: None, amount: None, description,
                                features: OfferFeatures::empty(), absolute_expiry: None, issuer: None, paths: None,
        /// Sets the [`Offer::metadata`] to the given bytes.
        ///
        /// Successive calls to this method will override the previous setting.
-       pub fn metadata(mut self, metadata: Vec<u8>) -> Result<Self, Bolt12SemanticError> {
-               self.offer.metadata = Some(Metadata::Bytes(metadata));
-               Ok(self)
+       pub fn metadata(mut $self: $self_type, metadata: Vec<u8>) -> Result<$return_type, Bolt12SemanticError> {
+               $self.offer.metadata = Some(Metadata::Bytes(metadata));
+               Ok($return_value)
        }
- }
+ } }
  
impl<'a, T: secp256k1::Signing> OfferBuilder<'a, DerivedMetadata, T> {
macro_rules! offer_derived_metadata_builder_methods { ($secp_context: ty) => {
        /// Similar to [`OfferBuilder::new`] except, if [`OfferBuilder::path`] is called, the signing
        /// pubkey is derived from the given [`ExpandedKey`] and [`EntropySource`]. This provides
        /// recipient privacy by using a different signing pubkey for each offer. Otherwise, the
        /// [`ExpandedKey`]: crate::ln::inbound_payment::ExpandedKey
        pub fn deriving_signing_pubkey<ES: Deref>(
                description: String, node_id: PublicKey, expanded_key: &ExpandedKey, entropy_source: ES,
-               secp_ctx: &'a Secp256k1<T>
+               secp_ctx: &'a Secp256k1<$secp_context>
        ) -> Self where ES::Target: EntropySource {
                let nonce = Nonce::from_entropy_source(entropy_source);
                let derivation_material = MetadataMaterial::new(nonce, expanded_key, IV_BYTES, None);
                let metadata = Metadata::DerivedSigningPubkey(derivation_material);
-               OfferBuilder {
+               Self {
                        offer: OfferContents {
                                chains: None, metadata: Some(metadata), amount: None, description,
                                features: OfferFeatures::empty(), absolute_expiry: None, issuer: None, paths: None,
                        secp_ctx: Some(secp_ctx),
                }
        }
- }
+ } }
  
- impl<'a, M: MetadataStrategy, T: secp256k1::Signing> OfferBuilder<'a, M, T> {
+ macro_rules! offer_builder_methods { (
+       $self: ident, $self_type: ty, $return_type: ty, $return_value: expr $(, $self_mut: tt)?
+ ) => {
        /// Adds the chain hash of the given [`Network`] to [`Offer::chains`]. If not called,
        /// the chain hash of [`Network::Bitcoin`] is assumed to be the only one supported.
        ///
        /// See [`Offer::chains`] on how this relates to the payment currency.
        ///
        /// Successive calls to this method will add another chain hash.
-       pub fn chain(self, network: Network) -> Self {
-               self.chain_hash(ChainHash::using_genesis_block(network))
+       pub fn chain($self: $self_type, network: Network) -> $return_type {
+               $self.chain_hash(ChainHash::using_genesis_block(network))
        }
  
        /// Adds the [`ChainHash`] to [`Offer::chains`]. If not called, the chain hash of
        /// See [`Offer::chains`] on how this relates to the payment currency.
        ///
        /// Successive calls to this method will add another chain hash.
-       pub(crate) fn chain_hash(mut self, chain: ChainHash) -> Self {
-               let chains = self.offer.chains.get_or_insert_with(Vec::new);
+       pub(crate) fn chain_hash($($self_mut)* $self: $self_type, chain: ChainHash) -> $return_type {
+               let chains = $self.offer.chains.get_or_insert_with(Vec::new);
                if !chains.contains(&chain) {
                        chains.push(chain);
                }
  
-               self
+               $return_value
        }
  
        /// Sets the [`Offer::amount`] as an [`Amount::Bitcoin`].
        ///
        /// Successive calls to this method will override the previous setting.
-       pub fn amount_msats(self, amount_msats: u64) -> Self {
-               self.amount(Amount::Bitcoin { amount_msats })
+       pub fn amount_msats($self: $self_type, amount_msats: u64) -> $return_type {
+               $self.amount(Amount::Bitcoin { amount_msats })
        }
  
        /// Sets the [`Offer::amount`].
        ///
        /// Successive calls to this method will override the previous setting.
-       pub(super) fn amount(mut self, amount: Amount) -> Self {
-               self.offer.amount = Some(amount);
-               self
+       pub(super) fn amount($($self_mut)* $self: $self_type, amount: Amount) -> $return_type {
+               $self.offer.amount = Some(amount);
+               $return_value
        }
  
        /// Sets the [`Offer::absolute_expiry`] as seconds since the Unix epoch. Any expiry that has
        /// already passed is valid and can be checked for using [`Offer::is_expired`].
        ///
        /// Successive calls to this method will override the previous setting.
-       pub fn absolute_expiry(mut self, absolute_expiry: Duration) -> Self {
-               self.offer.absolute_expiry = Some(absolute_expiry);
-               self
+       pub fn absolute_expiry($($self_mut)* $self: $self_type, absolute_expiry: Duration) -> $return_type {
+               $self.offer.absolute_expiry = Some(absolute_expiry);
+               $return_value
        }
  
        /// Sets the [`Offer::issuer`].
        ///
        /// Successive calls to this method will override the previous setting.
-       pub fn issuer(mut self, issuer: String) -> Self {
-               self.offer.issuer = Some(issuer);
-               self
+       pub fn issuer($($self_mut)* $self: $self_type, issuer: String) -> $return_type {
+               $self.offer.issuer = Some(issuer);
+               $return_value
        }
  
        /// Adds a blinded path to [`Offer::paths`]. Must include at least one path if only connected by
        ///
        /// Successive calls to this method will add another blinded path. Caller is responsible for not
        /// adding duplicate paths.
-       pub fn path(mut self, path: BlindedPath) -> Self {
-               self.offer.paths.get_or_insert_with(Vec::new).push(path);
-               self
+       pub fn path($($self_mut)* $self: $self_type, path: BlindedPath) -> $return_type {
+               $self.offer.paths.get_or_insert_with(Vec::new).push(path);
+               $return_value
        }
  
        /// Sets the quantity of items for [`Offer::supported_quantity`]. If not called, defaults to
        /// [`Quantity::One`].
        ///
        /// Successive calls to this method will override the previous setting.
-       pub fn supported_quantity(mut self, quantity: Quantity) -> Self {
-               self.offer.supported_quantity = quantity;
-               self
+       pub fn supported_quantity($($self_mut)* $self: $self_type, quantity: Quantity) -> $return_type {
+               $self.offer.supported_quantity = quantity;
+               $return_value
        }
  
        /// Builds an [`Offer`] from the builder's settings.
-       pub fn build(mut self) -> Result<Offer, Bolt12SemanticError> {
-               match self.offer.amount {
+       pub fn build($($self_mut)* $self: $self_type) -> Result<Offer, Bolt12SemanticError> {
+               match $self.offer.amount {
                        Some(Amount::Bitcoin { amount_msats }) => {
                                if amount_msats > MAX_VALUE_MSAT {
                                        return Err(Bolt12SemanticError::InvalidAmount);
                        None => {},
                }
  
-               if let Some(chains) = &self.offer.chains {
-                       if chains.len() == 1 && chains[0] == self.offer.implied_chain() {
-                               self.offer.chains = None;
+               if let Some(chains) = &$self.offer.chains {
+                       if chains.len() == 1 && chains[0] == $self.offer.implied_chain() {
+                               $self.offer.chains = None;
                        }
                }
  
-               Ok(self.build_without_checks())
+               Ok($self.build_without_checks())
        }
  
-       fn build_without_checks(mut self) -> Offer {
+       fn build_without_checks($($self_mut)* $self: $self_type) -> Offer {
                // Create the metadata for stateless verification of an InvoiceRequest.
-               if let Some(mut metadata) = self.offer.metadata.take() {
+               if let Some(mut metadata) = $self.offer.metadata.take() {
                        if metadata.has_derivation_material() {
-                               if self.offer.paths.is_none() {
+                               if $self.offer.paths.is_none() {
                                        metadata = metadata.without_keys();
                                }
  
-                               let mut tlv_stream = self.offer.as_tlv_stream();
+                               let mut tlv_stream = $self.offer.as_tlv_stream();
                                debug_assert_eq!(tlv_stream.metadata, None);
                                tlv_stream.metadata = None;
                                if metadata.derives_recipient_keys() {
                                        tlv_stream.node_id = None;
                                }
  
-                               let (derived_metadata, keys) = metadata.derive_from(tlv_stream, self.secp_ctx);
+                               let (derived_metadata, keys) = metadata.derive_from(tlv_stream, $self.secp_ctx);
                                metadata = derived_metadata;
                                if let Some(keys) = keys {
-                                       self.offer.signing_pubkey = keys.public_key();
+                                       $self.offer.signing_pubkey = keys.public_key();
                                }
                        }
  
-                       self.offer.metadata = Some(metadata);
+                       $self.offer.metadata = Some(metadata);
                }
  
                let mut bytes = Vec::new();
-               self.offer.write(&mut bytes).unwrap();
-               Offer { bytes, contents: self.offer }
+               $self.offer.write(&mut bytes).unwrap();
+               Offer {
+                       bytes,
+                       #[cfg(not(c_bindings))]
+                       contents: $self.offer,
+                       #[cfg(c_bindings)]
+                       contents: $self.offer.clone()
+               }
        }
- }
+ } }
  
  #[cfg(test)]
- impl<'a, M: MetadataStrategy, T: secp256k1::Signing> OfferBuilder<'a, M, T> {
-       fn features_unchecked(mut self, features: OfferFeatures) -> Self {
-               self.offer.features = features;
-               self
+ macro_rules! offer_builder_test_methods { (
+       $self: ident, $self_type: ty, $return_type: ty, $return_value: expr $(, $self_mut: tt)?
+ ) => {
+       #[cfg_attr(c_bindings, allow(dead_code))]
+       fn features_unchecked($($self_mut)* $self: $self_type, features: OfferFeatures) -> $return_type {
+               $self.offer.features = features;
+               $return_value
+       }
+       #[cfg_attr(c_bindings, allow(dead_code))]
+       pub(crate) fn clear_paths($($self_mut)* $self: $self_type) -> $return_type {
+               $self.offer.paths = None;
+               $return_value
        }
  
-       pub(crate) fn clear_paths(mut self) -> Self {
-               self.offer.paths = None;
-               self
+       #[cfg_attr(c_bindings, allow(dead_code))]
+       pub(super) fn build_unchecked($self: $self_type) -> Offer {
+               $self.build_without_checks()
        }
+ } }
+ impl<'a, M: MetadataStrategy, T: secp256k1::Signing> OfferBuilder<'a, M, T> {
+       offer_builder_methods!(self, Self, Self, self, mut);
+       #[cfg(test)]
+       offer_builder_test_methods!(self, Self, Self, self, mut);
+ }
+ impl<'a> OfferBuilder<'a, ExplicitMetadata, secp256k1::SignOnly> {
+       offer_explicit_metadata_builder_methods!(self, Self, Self, self);
+ }
+ impl<'a, T: secp256k1::Signing> OfferBuilder<'a, DerivedMetadata, T> {
+       offer_derived_metadata_builder_methods!(T);
+ }
+ #[cfg(all(c_bindings, not(test)))]
+ impl<'a> OfferWithExplicitMetadataBuilder<'a> {
+       offer_explicit_metadata_builder_methods!(self, &mut Self, (), ());
+       offer_builder_methods!(self, &mut Self, (), ());
+ }
+ #[cfg(all(c_bindings, test))]
+ impl<'a> OfferWithExplicitMetadataBuilder<'a> {
+       offer_explicit_metadata_builder_methods!(self, &mut Self, &mut Self, self);
+       offer_builder_methods!(self, &mut Self, &mut Self, self);
+       offer_builder_test_methods!(self, &mut Self, &mut Self, self);
+ }
+ #[cfg(all(c_bindings, not(test)))]
+ impl<'a> OfferWithDerivedMetadataBuilder<'a> {
+       offer_derived_metadata_builder_methods!(secp256k1::All);
+       offer_builder_methods!(self, &mut Self, (), ());
+ }
  
-       pub(super) fn build_unchecked(self) -> Offer {
-               self.build_without_checks()
+ #[cfg(all(c_bindings, test))]
+ impl<'a> OfferWithDerivedMetadataBuilder<'a> {
+       offer_derived_metadata_builder_methods!(secp256k1::All);
+       offer_builder_methods!(self, &mut Self, &mut Self, self);
+       offer_builder_test_methods!(self, &mut Self, &mut Self, self);
+ }
+ #[cfg(c_bindings)]
+ impl<'a> From<OfferBuilder<'a, DerivedMetadata, secp256k1::All>>
+ for OfferWithDerivedMetadataBuilder<'a> {
+       fn from(builder: OfferBuilder<'a, DerivedMetadata, secp256k1::All>) -> Self {
+               let OfferBuilder { offer, metadata_strategy, secp_ctx } = builder;
+               Self { offer, metadata_strategy, secp_ctx }
        }
  }
  
  /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
  /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
  #[derive(Clone, Debug)]
 -#[cfg_attr(test, derive(PartialEq))]
  pub struct Offer {
        // The serialized offer. Needed when creating an `InvoiceRequest` if the offer contains unknown
        // fields.
@@@ -489,7 -592,9 +592,9 @@@ impl Offer 
        pub fn expects_quantity(&self) -> bool {
                self.contents.expects_quantity()
        }
+ }
  
+ macro_rules! request_invoice_derived_payer_id { ($self: ident, $builder: ty) => {
        /// Similar to [`Offer::request_invoice`] except it:
        /// - derives the [`InvoiceRequest::payer_id`] such that a different key can be used for each
        ///   request,
        ///
        /// Useful to protect the sender's privacy.
        ///
-       /// This is not exported to bindings users as builder patterns don't map outside of move semantics.
-       ///
        /// [`InvoiceRequest::payer_id`]: crate::offers::invoice_request::InvoiceRequest::payer_id
        /// [`InvoiceRequest::payer_metadata`]: crate::offers::invoice_request::InvoiceRequest::payer_metadata
        /// [`Bolt12Invoice::verify`]: crate::offers::invoice::Bolt12Invoice::verify
        /// [`ExpandedKey`]: crate::ln::inbound_payment::ExpandedKey
-       pub fn request_invoice_deriving_payer_id<'a, 'b, ES: Deref, T: secp256k1::Signing>(
-               &'a self, expanded_key: &ExpandedKey, entropy_source: ES, secp_ctx: &'b Secp256k1<T>,
+       pub fn request_invoice_deriving_payer_id<
+               'a, 'b, ES: Deref,
+               #[cfg(not(c_bindings))]
+               T: secp256k1::Signing
+       >(
+               &'a $self, expanded_key: &ExpandedKey, entropy_source: ES,
+               #[cfg(not(c_bindings))]
+               secp_ctx: &'b Secp256k1<T>,
+               #[cfg(c_bindings)]
+               secp_ctx: &'b Secp256k1<secp256k1::All>,
                payment_id: PaymentId
-       ) -> Result<InvoiceRequestBuilder<'a, 'b, DerivedPayerId, T>, Bolt12SemanticError>
+       ) -> Result<$builder, Bolt12SemanticError>
        where
                ES::Target: EntropySource,
        {
-               if self.offer_features().requires_unknown_bits() {
+               if $self.offer_features().requires_unknown_bits() {
                        return Err(Bolt12SemanticError::UnknownRequiredFeatures);
                }
  
-               Ok(InvoiceRequestBuilder::deriving_payer_id(
-                       self, expanded_key, entropy_source, secp_ctx, payment_id
-               ))
+               Ok(<$builder>::deriving_payer_id($self, expanded_key, entropy_source, secp_ctx, payment_id))
        }
+ } }
  
+ macro_rules! request_invoice_explicit_payer_id { ($self: ident, $builder: ty) => {
        /// Similar to [`Offer::request_invoice_deriving_payer_id`] except uses `payer_id` for the
        /// [`InvoiceRequest::payer_id`] instead of deriving a different key for each request.
        ///
        /// Useful for recurring payments using the same `payer_id` with different invoices.
        ///
-       /// This is not exported to bindings users as builder patterns don't map outside of move semantics.
-       ///
        /// [`InvoiceRequest::payer_id`]: crate::offers::invoice_request::InvoiceRequest::payer_id
        pub fn request_invoice_deriving_metadata<ES: Deref>(
-               &self, payer_id: PublicKey, expanded_key: &ExpandedKey, entropy_source: ES,
+               &$self, payer_id: PublicKey, expanded_key: &ExpandedKey, entropy_source: ES,
                payment_id: PaymentId
-       ) -> Result<InvoiceRequestBuilder<ExplicitPayerId, secp256k1::SignOnly>, Bolt12SemanticError>
+       ) -> Result<$builder, Bolt12SemanticError>
        where
                ES::Target: EntropySource,
        {
-               if self.offer_features().requires_unknown_bits() {
+               if $self.offer_features().requires_unknown_bits() {
                        return Err(Bolt12SemanticError::UnknownRequiredFeatures);
                }
  
-               Ok(InvoiceRequestBuilder::deriving_metadata(
-                       self, payer_id, expanded_key, entropy_source, payment_id
-               ))
+               Ok(<$builder>::deriving_metadata($self, payer_id, expanded_key, entropy_source, payment_id))
        }
  
        /// Creates an [`InvoiceRequestBuilder`] for the offer with the given `metadata` and `payer_id`,
        ///
        /// Errors if the offer contains unknown required features.
        ///
-       /// This is not exported to bindings users as builder patterns don't map outside of move semantics.
-       ///
        /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
        pub fn request_invoice(
-               &self, metadata: Vec<u8>, payer_id: PublicKey
-       ) -> Result<InvoiceRequestBuilder<ExplicitPayerId, secp256k1::SignOnly>, Bolt12SemanticError> {
-               if self.offer_features().requires_unknown_bits() {
+               &$self, metadata: Vec<u8>, payer_id: PublicKey
+       ) -> Result<$builder, Bolt12SemanticError> {
+               if $self.offer_features().requires_unknown_bits() {
                        return Err(Bolt12SemanticError::UnknownRequiredFeatures);
                }
  
-               Ok(InvoiceRequestBuilder::new(self, metadata, payer_id))
+               Ok(<$builder>::new($self, metadata, payer_id))
        }
+ } }
  
-       #[cfg(test)]
+ #[cfg(not(c_bindings))]
+ impl Offer {
+       request_invoice_derived_payer_id!(self, InvoiceRequestBuilder<'a, 'b, DerivedPayerId, T>);
+       request_invoice_explicit_payer_id!(self, InvoiceRequestBuilder<ExplicitPayerId, secp256k1::SignOnly>);
+ }
+ #[cfg(c_bindings)]
+ impl Offer {
+       request_invoice_derived_payer_id!(self, InvoiceRequestWithDerivedPayerIdBuilder<'a, 'b>);
+       request_invoice_explicit_payer_id!(self, InvoiceRequestWithExplicitPayerIdBuilder);
+ }
+ #[cfg(test)]
+ impl Offer {
        pub(super) fn as_tlv_stream(&self) -> OfferTlvStreamRef {
                self.contents.as_tlv_stream()
        }
@@@ -584,20 -703,6 +703,20 @@@ impl AsRef<[u8]> for Offer 
        }
  }
  
 +impl PartialEq for Offer {
 +      fn eq(&self, other: &Self) -> bool {
 +              self.bytes.eq(&other.bytes)
 +      }
 +}
 +
 +impl Eq for Offer {}
 +
 +impl Hash for Offer {
 +      fn hash<H: Hasher>(&self, state: &mut H) {
 +              self.bytes.hash(state);
 +      }
 +}
 +
  impl OfferContents {
        pub fn chains(&self) -> Vec<ChainHash> {
                self.chains.as_ref().cloned().unwrap_or_else(|| vec![self.implied_chain()])
@@@ -928,7 -1033,15 +1047,15 @@@ impl core::fmt::Display for Offer 
  
  #[cfg(test)]
  mod tests {
-       use super::{Amount, Offer, OfferBuilder, OfferTlvStreamRef, Quantity};
+       use super::{Amount, Offer, OfferTlvStreamRef, Quantity};
+       #[cfg(not(c_bindings))]
+       use {
+               super::OfferBuilder,
+       };
+       #[cfg(c_bindings)]
+       use {
+               super::OfferWithExplicitMetadataBuilder as OfferBuilder,
+       };
  
        use bitcoin::blockdata::constants::ChainHash;
        use bitcoin::network::constants::Network;
                let entropy = FixedEntropy {};
                let secp_ctx = Secp256k1::new();
  
+               #[cfg(c_bindings)]
+               use super::OfferWithDerivedMetadataBuilder as OfferBuilder;
                let offer = OfferBuilder
                        ::deriving_signing_pubkey(desc, node_id, &expanded_key, &entropy, &secp_ctx)
                        .amount_msats(1000)
                        ],
                };
  
+               #[cfg(c_bindings)]
+               use super::OfferWithDerivedMetadataBuilder as OfferBuilder;
                let offer = OfferBuilder
                        ::deriving_signing_pubkey(desc, node_id, &expanded_key, &entropy, &secp_ctx)
                        .amount_msats(1000)
                assert_eq!(tlv_stream.amount, Some(1000));
                assert_eq!(tlv_stream.currency, None);
  
+               #[cfg(not(c_bindings))]
                let builder = OfferBuilder::new("foo".into(), pubkey(42))
                        .amount(currency_amount.clone());
+               #[cfg(c_bindings)]
+               let mut builder = OfferBuilder::new("foo".into(), pubkey(42));
+               #[cfg(c_bindings)]
+               builder.amount(currency_amount.clone());
                let tlv_stream = builder.offer.as_tlv_stream();
                assert_eq!(builder.offer.amount, Some(currency_amount.clone()));
                assert_eq!(tlv_stream.amount, Some(10));
index 5107e31d8d0a07e6527d7838d7a19439c86e54c1,1ae7637b09101741deb2f04cbbacf660618dd1ac..42177960868ab553ee1385f2569146dc8209e2bf
@@@ -85,7 -85,6 +85,7 @@@ use bitcoin::blockdata::constants::Chai
  use bitcoin::network::constants::Network;
  use bitcoin::secp256k1::{PublicKey, Secp256k1, self};
  use core::convert::TryFrom;
 +use core::hash::{Hash, Hasher};
  use core::ops::Deref;
  use core::str::FromStr;
  use core::time::Duration;
@@@ -97,7 -96,7 +97,7 @@@ use crate::ln::channelmanager::PaymentI
  use crate::ln::features::InvoiceRequestFeatures;
  use crate::ln::inbound_payment::{ExpandedKey, IV_LEN, Nonce};
  use crate::ln::msgs::{DecodeError, MAX_VALUE_MSAT};
- use crate::offers::invoice::{BlindedPayInfo, DerivedSigningPubkey, ExplicitSigningPubkey, InvoiceBuilder};
+ use crate::offers::invoice::BlindedPayInfo;
  use crate::offers::invoice_request::{InvoiceRequestTlvStream, InvoiceRequestTlvStreamRef};
  use crate::offers::offer::{OfferTlvStream, OfferTlvStreamRef};
  use crate::offers::parse::{Bech32Encode, Bolt12ParseError, Bolt12SemanticError, ParsedMessage};
@@@ -106,6 -105,15 +106,15 @@@ use crate::offers::signer::{Metadata, M
  use crate::util::ser::{SeekReadable, WithoutLength, Writeable, Writer};
  use crate::util::string::PrintableString;
  
+ #[cfg(not(c_bindings))]
+ use {
+       crate::offers::invoice::{DerivedSigningPubkey, ExplicitSigningPubkey, InvoiceBuilder},
+ };
+ #[cfg(c_bindings)]
+ use {
+       crate::offers::invoice::{InvoiceWithDerivedSigningPubkeyBuilder, InvoiceWithExplicitSigningPubkeyBuilder},
+ };
  use crate::prelude::*;
  
  #[cfg(feature = "std")]
@@@ -125,7 -133,18 +134,18 @@@ pub struct RefundBuilder<'a, T: secp256
        secp_ctx: Option<&'a Secp256k1<T>>,
  }
  
- impl<'a> RefundBuilder<'a, secp256k1::SignOnly> {
+ /// Builds a [`Refund`] for the "offer for money" flow.
+ ///
+ /// See [module-level documentation] for usage.
+ ///
+ /// [module-level documentation]: self
+ #[cfg(c_bindings)]
+ pub struct RefundMaybeWithDerivedMetadataBuilder<'a> {
+       refund: RefundContents,
+       secp_ctx: Option<&'a Secp256k1<secp256k1::All>>,
+ }
+ macro_rules! refund_explicit_metadata_builder_methods { () => {
        /// Creates a new builder for a refund using the [`Refund::payer_id`] for the public node id to
        /// send to if no [`Refund::paths`] are set. Otherwise, it may be a transient pubkey.
        ///
                        secp_ctx: None,
                })
        }
- }
+ } }
  
- impl<'a, T: secp256k1::Signing> RefundBuilder<'a, T> {
+ macro_rules! refund_builder_methods { (
+       $self: ident, $self_type: ty, $return_type: ty, $return_value: expr, $secp_context: ty $(, $self_mut: tt)?
+ ) => {
        /// Similar to [`RefundBuilder::new`] except, if [`RefundBuilder::path`] is called, the payer id
        /// is derived from the given [`ExpandedKey`] and nonce. This provides sender privacy by using a
        /// different payer id for each refund, assuming a different nonce is used.  Otherwise, the
        /// [`ExpandedKey`]: crate::ln::inbound_payment::ExpandedKey
        pub fn deriving_payer_id<ES: Deref>(
                description: String, node_id: PublicKey, expanded_key: &ExpandedKey, entropy_source: ES,
-               secp_ctx: &'a Secp256k1<T>, amount_msats: u64, payment_id: PaymentId
+               secp_ctx: &'a Secp256k1<$secp_context>, amount_msats: u64, payment_id: PaymentId
        ) -> Result<Self, Bolt12SemanticError> where ES::Target: EntropySource {
                if amount_msats > MAX_VALUE_MSAT {
                        return Err(Bolt12SemanticError::InvalidAmount);
        /// already passed is valid and can be checked for using [`Refund::is_expired`].
        ///
        /// Successive calls to this method will override the previous setting.
-       pub fn absolute_expiry(mut self, absolute_expiry: Duration) -> Self {
-               self.refund.absolute_expiry = Some(absolute_expiry);
-               self
+       pub fn absolute_expiry($($self_mut)* $self: $self_type, absolute_expiry: Duration) -> $return_type {
+               $self.refund.absolute_expiry = Some(absolute_expiry);
+               $return_value
        }
  
        /// Sets the [`Refund::issuer`].
        ///
        /// Successive calls to this method will override the previous setting.
-       pub fn issuer(mut self, issuer: String) -> Self {
-               self.refund.issuer = Some(issuer);
-               self
+       pub fn issuer($($self_mut)* $self: $self_type, issuer: String) -> $return_type {
+               $self.refund.issuer = Some(issuer);
+               $return_value
        }
  
        /// Adds a blinded path to [`Refund::paths`]. Must include at least one path if only connected
        ///
        /// Successive calls to this method will add another blinded path. Caller is responsible for not
        /// adding duplicate paths.
-       pub fn path(mut self, path: BlindedPath) -> Self {
-               self.refund.paths.get_or_insert_with(Vec::new).push(path);
-               self
+       pub fn path($($self_mut)* $self: $self_type, path: BlindedPath) -> $return_type {
+               $self.refund.paths.get_or_insert_with(Vec::new).push(path);
+               $return_value
        }
  
        /// Sets the [`Refund::chain`] of the given [`Network`] for paying an invoice. If not
        /// called, [`Network::Bitcoin`] is assumed.
        ///
        /// Successive calls to this method will override the previous setting.
-       pub fn chain(self, network: Network) -> Self {
-               self.chain_hash(ChainHash::using_genesis_block(network))
+       pub fn chain($self: $self_type, network: Network) -> $return_type {
+               $self.chain_hash(ChainHash::using_genesis_block(network))
        }
  
        /// Sets the [`Refund::chain`] of the given [`ChainHash`] for paying an invoice. If not called,
        /// [`Network::Bitcoin`] is assumed.
        ///
        /// Successive calls to this method will override the previous setting.
-       pub(crate) fn chain_hash(mut self, chain: ChainHash) -> Self {
-               self.refund.chain = Some(chain);
-               self
+       pub(crate) fn chain_hash($($self_mut)* $self: $self_type, chain: ChainHash) -> $return_type {
+               $self.refund.chain = Some(chain);
+               $return_value
        }
  
        /// Sets [`Refund::quantity`] of items. This is purely for informational purposes. It is useful
        /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
        /// [`InvoiceRequest::quantity`]: crate::offers::invoice_request::InvoiceRequest::quantity
        /// [`Offer`]: crate::offers::offer::Offer
-       pub fn quantity(mut self, quantity: u64) -> Self {
-               self.refund.quantity = Some(quantity);
-               self
+       pub fn quantity($($self_mut)* $self: $self_type, quantity: u64) -> $return_type {
+               $self.refund.quantity = Some(quantity);
+               $return_value
        }
  
        /// Sets the [`Refund::payer_note`].
        ///
        /// Successive calls to this method will override the previous setting.
-       pub fn payer_note(mut self, payer_note: String) -> Self {
-               self.refund.payer_note = Some(payer_note);
-               self
+       pub fn payer_note($($self_mut)* $self: $self_type, payer_note: String) -> $return_type {
+               $self.refund.payer_note = Some(payer_note);
+               $return_value
        }
  
        /// Builds a [`Refund`] after checking for valid semantics.
-       pub fn build(mut self) -> Result<Refund, Bolt12SemanticError> {
-               if self.refund.chain() == self.refund.implied_chain() {
-                       self.refund.chain = None;
+       pub fn build($($self_mut)* $self: $self_type) -> Result<Refund, Bolt12SemanticError> {
+               if $self.refund.chain() == $self.refund.implied_chain() {
+                       $self.refund.chain = None;
                }
  
                // Create the metadata for stateless verification of a Bolt12Invoice.
-               if self.refund.payer.0.has_derivation_material() {
-                       let mut metadata = core::mem::take(&mut self.refund.payer.0);
+               if $self.refund.payer.0.has_derivation_material() {
+                       let mut metadata = core::mem::take(&mut $self.refund.payer.0);
  
-                       if self.refund.paths.is_none() {
+                       if $self.refund.paths.is_none() {
                                metadata = metadata.without_keys();
                        }
  
-                       let mut tlv_stream = self.refund.as_tlv_stream();
+                       let mut tlv_stream = $self.refund.as_tlv_stream();
                        tlv_stream.0.metadata = None;
                        if metadata.derives_payer_keys() {
                                tlv_stream.2.payer_id = None;
                        }
  
-                       let (derived_metadata, keys) = metadata.derive_from(tlv_stream, self.secp_ctx);
+                       let (derived_metadata, keys) = metadata.derive_from(tlv_stream, $self.secp_ctx);
                        metadata = derived_metadata;
                        if let Some(keys) = keys {
-                               self.refund.payer_id = keys.public_key();
+                               $self.refund.payer_id = keys.public_key();
                        }
  
-                       self.refund.payer.0 = metadata;
+                       $self.refund.payer.0 = metadata;
                }
  
                let mut bytes = Vec::new();
-               self.refund.write(&mut bytes).unwrap();
+               $self.refund.write(&mut bytes).unwrap();
+               Ok(Refund {
+                       bytes,
+                       #[cfg(not(c_bindings))]
+                       contents: $self.refund,
+                       #[cfg(c_bindings)]
+                       contents: $self.refund.clone(),
+               })
+       }
+ } }
+ #[cfg(test)]
+ macro_rules! refund_builder_test_methods { (
+       $self: ident, $self_type: ty, $return_type: ty, $return_value: expr $(, $self_mut: tt)?
+ ) => {
+       #[cfg_attr(c_bindings, allow(dead_code))]
+       pub(crate) fn clear_paths($($self_mut)* $self: $self_type) -> $return_type {
+               $self.refund.paths = None;
+               $return_value
+       }
  
-               Ok(Refund { bytes, contents: self.refund })
+       #[cfg_attr(c_bindings, allow(dead_code))]
+       fn features_unchecked($($self_mut)* $self: $self_type, features: InvoiceRequestFeatures) -> $return_type {
+               $self.refund.features = features;
+               $return_value
        }
+ } }
+ impl<'a> RefundBuilder<'a, secp256k1::SignOnly> {
+       refund_explicit_metadata_builder_methods!();
  }
  
- #[cfg(test)]
  impl<'a, T: secp256k1::Signing> RefundBuilder<'a, T> {
-       pub(crate) fn clear_paths(mut self) -> Self {
-               self.refund.paths = None;
-               self
-       }
+       refund_builder_methods!(self, Self, Self, self, T, mut);
+       #[cfg(test)]
+       refund_builder_test_methods!(self, Self, Self, self, mut);
+ }
+ #[cfg(all(c_bindings, not(test)))]
+ impl<'a> RefundMaybeWithDerivedMetadataBuilder<'a> {
+       refund_explicit_metadata_builder_methods!();
+       refund_builder_methods!(self, &mut Self, (), (), secp256k1::All);
+ }
+ #[cfg(all(c_bindings, test))]
+ impl<'a> RefundMaybeWithDerivedMetadataBuilder<'a> {
+       refund_explicit_metadata_builder_methods!();
+       refund_builder_methods!(self, &mut Self, &mut Self, self, secp256k1::All);
+       refund_builder_test_methods!(self, &mut Self, &mut Self, self);
+ }
  
-       fn features_unchecked(mut self, features: InvoiceRequestFeatures) -> Self {
-               self.refund.features = features;
-               self
+ #[cfg(c_bindings)]
+ impl<'a> From<RefundBuilder<'a, secp256k1::All>>
+ for RefundMaybeWithDerivedMetadataBuilder<'a> {
+       fn from(builder: RefundBuilder<'a, secp256k1::All>) -> Self {
+               let RefundBuilder { refund, secp_ctx } = builder;
+               Self { refund, secp_ctx }
        }
  }
  
  /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
  /// [`Offer`]: crate::offers::offer::Offer
  #[derive(Clone, Debug)]
 -#[cfg_attr(test, derive(PartialEq))]
  pub struct Refund {
        pub(super) bytes: Vec<u8>,
        pub(super) contents: RefundContents,
@@@ -423,7 -489,9 +489,9 @@@ impl Refund 
        pub fn payer_note(&self) -> Option<PrintableString> {
                self.contents.payer_note()
        }
+ }
  
+ macro_rules! respond_with_explicit_signing_pubkey_methods { ($self: ident, $builder: ty) => {
        /// Creates an [`InvoiceBuilder`] for the refund with the given required fields and using the
        /// [`Duration`] since [`std::time::SystemTime::UNIX_EPOCH`] as the creation time.
        ///
        /// [`Duration`]: core::time::Duration
        #[cfg(feature = "std")]
        pub fn respond_with(
-               &self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash,
+               &$self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash,
                signing_pubkey: PublicKey,
-       ) -> Result<InvoiceBuilder<ExplicitSigningPubkey>, Bolt12SemanticError> {
+       ) -> Result<$builder, Bolt12SemanticError> {
                let created_at = std::time::SystemTime::now()
                        .duration_since(std::time::SystemTime::UNIX_EPOCH)
                        .expect("SystemTime::now() should come after SystemTime::UNIX_EPOCH");
  
-               self.respond_with_no_std(payment_paths, payment_hash, signing_pubkey, created_at)
+               $self.respond_with_no_std(payment_paths, payment_hash, signing_pubkey, created_at)
        }
  
        /// Creates an [`InvoiceBuilder`] for the refund with the given required fields.
        ///
        /// [`Bolt12Invoice::created_at`]: crate::offers::invoice::Bolt12Invoice::created_at
        pub fn respond_with_no_std(
-               &self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash,
+               &$self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash,
                signing_pubkey: PublicKey, created_at: Duration
-       ) -> Result<InvoiceBuilder<ExplicitSigningPubkey>, Bolt12SemanticError> {
-               if self.features().requires_unknown_bits() {
+       ) -> Result<$builder, Bolt12SemanticError> {
+               if $self.features().requires_unknown_bits() {
                        return Err(Bolt12SemanticError::UnknownRequiredFeatures);
                }
  
-               InvoiceBuilder::for_refund(self, payment_paths, created_at, payment_hash, signing_pubkey)
+               <$builder>::for_refund($self, payment_paths, created_at, payment_hash, signing_pubkey)
        }
+ } }
  
+ macro_rules! respond_with_derived_signing_pubkey_methods { ($self: ident, $builder: ty) => {
        /// Creates an [`InvoiceBuilder`] for the refund using the given required fields and that uses
        /// derived signing keys to sign the [`Bolt12Invoice`].
        ///
        /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
        #[cfg(feature = "std")]
        pub fn respond_using_derived_keys<ES: Deref>(
-               &self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash,
+               &$self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash,
                expanded_key: &ExpandedKey, entropy_source: ES
-       ) -> Result<InvoiceBuilder<DerivedSigningPubkey>, Bolt12SemanticError>
+       ) -> Result<$builder, Bolt12SemanticError>
        where
                ES::Target: EntropySource,
        {
                        .duration_since(std::time::SystemTime::UNIX_EPOCH)
                        .expect("SystemTime::now() should come after SystemTime::UNIX_EPOCH");
  
-               self.respond_using_derived_keys_no_std(
+               $self.respond_using_derived_keys_no_std(
                        payment_paths, payment_hash, created_at, expanded_key, entropy_source
                )
        }
        ///
        /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
        pub fn respond_using_derived_keys_no_std<ES: Deref>(
-               &self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash,
+               &$self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash,
                created_at: core::time::Duration, expanded_key: &ExpandedKey, entropy_source: ES
-       ) -> Result<InvoiceBuilder<DerivedSigningPubkey>, Bolt12SemanticError>
+       ) -> Result<$builder, Bolt12SemanticError>
        where
                ES::Target: EntropySource,
        {
-               if self.features().requires_unknown_bits() {
+               if $self.features().requires_unknown_bits() {
                        return Err(Bolt12SemanticError::UnknownRequiredFeatures);
                }
  
                let nonce = Nonce::from_entropy_source(entropy_source);
                let keys = signer::derive_keys(nonce, expanded_key);
-               InvoiceBuilder::for_refund_using_keys(self, payment_paths, created_at, payment_hash, keys)
+               <$builder>::for_refund_using_keys($self, payment_paths, created_at, payment_hash, keys)
        }
+ } }
  
-       #[cfg(test)]
+ #[cfg(not(c_bindings))]
+ impl Refund {
+       respond_with_explicit_signing_pubkey_methods!(self, InvoiceBuilder<ExplicitSigningPubkey>);
+       respond_with_derived_signing_pubkey_methods!(self, InvoiceBuilder<DerivedSigningPubkey>);
+ }
+ #[cfg(c_bindings)]
+ impl Refund {
+       respond_with_explicit_signing_pubkey_methods!(self, InvoiceWithExplicitSigningPubkeyBuilder);
+       respond_with_derived_signing_pubkey_methods!(self, InvoiceWithDerivedSigningPubkeyBuilder);
+ }
+ #[cfg(test)]
+ impl Refund {
        fn as_tlv_stream(&self) -> RefundTlvStreamRef {
                self.contents.as_tlv_stream()
        }
@@@ -539,20 -623,6 +623,20 @@@ impl AsRef<[u8]> for Refund 
        }
  }
  
 +impl PartialEq for Refund {
 +      fn eq(&self, other: &Self) -> bool {
 +              self.bytes.eq(&other.bytes)
 +      }
 +}
 +
 +impl Eq for Refund {}
 +
 +impl Hash for Refund {
 +      fn hash<H: Hasher>(&self, state: &mut H) {
 +              self.bytes.hash(state);
 +      }
 +}
 +
  impl RefundContents {
        pub fn description(&self) -> PrintableString {
                PrintableString(&self.description)
@@@ -797,7 -867,15 +881,15 @@@ impl core::fmt::Display for Refund 
  
  #[cfg(test)]
  mod tests {
-       use super::{Refund, RefundBuilder, RefundTlvStreamRef};
+       use super::{Refund, RefundTlvStreamRef};
+       #[cfg(not(c_bindings))]
+       use {
+               super::RefundBuilder,
+       };
+       #[cfg(c_bindings)]
+       use {
+               super::RefundMaybeWithDerivedMetadataBuilder as RefundBuilder,
+       };
  
        use bitcoin::blockdata::constants::ChainHash;
        use bitcoin::network::constants::Network;