X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fln%2Fchannelmanager.rs;h=364150645e52b118422f9ea31db888d411c267f8;hb=407762446cd4abaab5ea9c1c96e8c181a0f63b86;hp=9536a9366e18506fa586d96c325b7d725e677de2;hpb=a866ba7ee3a59fc70079bd0aedadf00b6f26d80c;p=rust-lightning diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 9536a936..36415064 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -43,14 +43,12 @@ use crate::events::{Event, EventHandler, EventsProvider, MessageSendEvent, Messa // Since this struct is returned in `list_channels` methods, expose it here in case users want to // construct one themselves. use crate::ln::{inbound_payment, ChannelId, PaymentHash, PaymentPreimage, PaymentSecret}; -use crate::ln::channel::{Channel, ChannelPhase, ChannelContext, ChannelError, ChannelUpdateStatus, ShutdownResult, UnfundedChannelContext, UpdateFulfillCommitFetch, OutboundV1Channel, InboundV1Channel, WithChannelContext}; +use crate::ln::channel::{self, Channel, ChannelPhase, ChannelContext, ChannelError, ChannelUpdateStatus, ShutdownResult, UnfundedChannelContext, UpdateFulfillCommitFetch, OutboundV1Channel, InboundV1Channel, WithChannelContext}; use crate::ln::features::{Bolt12InvoiceFeatures, ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures}; #[cfg(any(feature = "_test_utils", test))] use crate::ln::features::Bolt11InvoiceFeatures; -use crate::routing::gossip::NetworkGraph; -use crate::routing::router::{BlindedTail, DefaultRouter, InFlightHtlcs, Path, Payee, PaymentParameters, Route, RouteParameters, Router}; -use crate::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringFeeParameters}; -use crate::ln::onion_payment::{check_incoming_htlc_cltv, create_recv_pending_htlc_info, create_fwd_pending_htlc_info, decode_incoming_update_add_htlc_onion, InboundOnionErr, NextPacketDetails}; +use crate::routing::router::{BlindedTail, InFlightHtlcs, Path, Payee, PaymentParameters, Route, RouteParameters, Router}; +use crate::ln::onion_payment::{check_incoming_htlc_cltv, create_recv_pending_htlc_info, create_fwd_pending_htlc_info, decode_incoming_update_add_htlc_onion, InboundHTLCErr, NextPacketDetails}; use crate::ln::msgs; use crate::ln::onion_utils; use crate::ln::onion_utils::{HTLCFailReason, INVALID_ONION_BLINDING}; @@ -59,14 +57,16 @@ use crate::ln::msgs::{ChannelMessageHandler, DecodeError, LightningError}; 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::{Destination, OffersMessage, OffersMessageHandler, PendingOnionMessage, new_pending_onion_message}; -use crate::sign::{EntropySource, KeysManager, NodeSigner, Recipient, SignerProvider}; +use crate::onion_message::messenger::{Destination, MessageRouter, PendingOnionMessage, new_pending_onion_message}; +use crate::onion_message::offers::{OffersMessage, OffersMessageHandler}; +use crate::sign::{EntropySource, NodeSigner, Recipient, SignerProvider}; use crate::sign::ecdsa::WriteableEcdsaChannelSigner; use crate::util::config::{UserConfig, ChannelConfig, ChannelConfigUpdate}; use crate::util::wakers::{Future, Notifier}; @@ -75,6 +75,19 @@ use crate::util::string::UntrustedString; use crate::util::ser::{BigSize, FixedLengthReader, Readable, ReadableArgs, MaybeReadable, Writeable, Writer, VecWriter}; use crate::util::logger::{Level, Logger, WithContext}; use crate::util::errors::APIError; +#[cfg(not(c_bindings))] +use { + crate::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}; @@ -196,15 +209,16 @@ pub struct BlindedForward { /// onion payload if we're the introduction node. Useful for calculating the next hop's /// [`msgs::UpdateAddHTLC::blinding_point`]. pub inbound_blinding_point: PublicKey, - // Another field will be added here when we support forwarding as a non-intro node. + /// If needed, this determines how this HTLC should be failed backwards, based on whether we are + /// the introduction node. + pub failure: BlindedFailure, } impl PendingHTLCRouting { // Used to override the onion failure code and data if the HTLC is blinded. fn blinded_failure(&self) -> Option { - // TODO: needs update when we support forwarding blinded HTLCs as non-intro node match self { - Self::Forward { blinded: Some(_), .. } => Some(BlindedFailure::FromIntroductionNode), + Self::Forward { blinded: Some(BlindedForward { failure, .. }), .. } => Some(*failure), Self::Receive { requires_blinded_error: true, .. } => Some(BlindedFailure::FromBlindedNode), _ => None, } @@ -299,10 +313,15 @@ pub(super) enum HTLCForwardInfo { }, } -// Used for failing blinded HTLCs backwards correctly. +/// Whether this blinded HTLC is being failed backwards by the introduction node or a blinded node, +/// which determines the failure message that should be used. #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] -enum BlindedFailure { +pub enum BlindedFailure { + /// This HTLC is being failed backwards by the introduction node, and thus should be failed with + /// [`msgs::UpdateFailHTLC`] and error code `0x8000|0x4000|24`. FromIntroductionNode, + /// This HTLC is being failed backwards by a blinded node within the path, and thus should be + /// failed with [`msgs::UpdateFailMalformedHTLC`] and error code `0x8000|0x4000|24`. FromBlindedNode, } @@ -545,9 +564,8 @@ impl Into for FailureCode { struct MsgHandleErrInternal { err: msgs::LightningError, - chan_id: Option<(ChannelId, u128)>, // If Some a channel of ours has been closed + closes_channel: bool, shutdown_finish: Option<(ShutdownResult, Option)>, - channel_capacity: Option, } impl MsgHandleErrInternal { #[inline] @@ -562,17 +580,16 @@ impl MsgHandleErrInternal { }, }, }, - chan_id: None, + closes_channel: false, shutdown_finish: None, - channel_capacity: None, } } #[inline] fn from_no_close(err: msgs::LightningError) -> Self { - Self { err, chan_id: None, shutdown_finish: None, channel_capacity: None } + Self { err, closes_channel: false, shutdown_finish: None } } #[inline] - fn from_finish_shutdown(err: String, channel_id: ChannelId, user_channel_id: u128, shutdown_res: ShutdownResult, channel_update: Option, channel_capacity: u64) -> Self { + fn from_finish_shutdown(err: String, channel_id: ChannelId, shutdown_res: ShutdownResult, channel_update: Option) -> Self { let err_msg = msgs::ErrorMessage { channel_id, data: err.clone() }; let action = if shutdown_res.monitor_update.is_some() { // We have a closing `ChannelMonitorUpdate`, which means the channel was funded and we @@ -584,9 +601,8 @@ impl MsgHandleErrInternal { }; Self { err: LightningError { err, action }, - chan_id: Some((channel_id, user_channel_id)), + closes_channel: true, shutdown_finish: Some((shutdown_res, channel_update)), - channel_capacity: Some(channel_capacity) } } #[inline] @@ -617,14 +633,13 @@ impl MsgHandleErrInternal { }, }, }, - chan_id: None, + closes_channel: false, shutdown_finish: None, - channel_capacity: None, } } fn closes_channel(&self) -> bool { - self.chan_id.is_some() + self.closes_channel } } @@ -1150,7 +1165,7 @@ where // | // |__`peer_state` // | -// |__`id_to_peer` +// |__`outpoint_to_peer` // | // |__`short_to_chan_info` // | @@ -1244,11 +1259,7 @@ where /// See `ChannelManager` struct-level documentation for lock order requirements. outbound_scid_aliases: Mutex>, - /// `channel_id` -> `counterparty_node_id`. - /// - /// Only `channel_id`s are allowed as keys in this map, and not `temporary_channel_id`s. As - /// multiple channels with the same `temporary_channel_id` to different peers can exist, - /// allowing `temporary_channel_id`s in this map would cause collisions for such channels. + /// Channel funding outpoint -> `counterparty_node_id`. /// /// Note that this map should only be used for `MonitorEvent` handling, to be able to access /// the corresponding channel for the event, as we only have access to the `channel_id` during @@ -1266,7 +1277,10 @@ where /// required to access the channel with the `counterparty_node_id`. /// /// See `ChannelManager` struct-level documentation for lock order requirements. - id_to_peer: Mutex>, + #[cfg(not(test))] + outpoint_to_peer: Mutex>, + #[cfg(test)] + pub(crate) outpoint_to_peer: Mutex>, /// SCIDs (and outbound SCID aliases) -> `counterparty_node_id`s and `channel_id`s. /// @@ -1957,30 +1971,27 @@ macro_rules! handle_error { match $internal { Ok(msg) => Ok(msg), - Err(MsgHandleErrInternal { err, chan_id, shutdown_finish, channel_capacity }) => { + Err(MsgHandleErrInternal { err, shutdown_finish, .. }) => { let mut msg_events = Vec::with_capacity(2); if let Some((shutdown_res, update_option)) = shutdown_finish { + let counterparty_node_id = shutdown_res.counterparty_node_id; + let channel_id = shutdown_res.channel_id; + let logger = WithContext::from( + &$self.logger, Some(counterparty_node_id), Some(channel_id), + ); + log_error!(logger, "Force-closing channel: {}", err.err); + $self.finish_close_channel(shutdown_res); if let Some(update) = update_option { msg_events.push(events::MessageSendEvent::BroadcastChannelUpdate { msg: update }); } - if let Some((channel_id, user_channel_id)) = chan_id { - $self.pending_events.lock().unwrap().push_back((events::Event::ChannelClosed { - channel_id, user_channel_id, - reason: ClosureReason::ProcessingError { err: err.err.clone() }, - counterparty_node_id: Some($counterparty_node_id), - channel_capacity_sats: channel_capacity, - }, None)); - } + } else { + log_error!($self.logger, "Got non-closing error: {}", err.err); } - let logger = WithContext::from( - &$self.logger, Some($counterparty_node_id), chan_id.map(|(chan_id, _)| chan_id) - ); - log_error!(logger, "{}", err.err); if let msgs::ErrorAction::IgnoreError = err.action { } else { msg_events.push(events::MessageSendEvent::HandleError { @@ -2006,7 +2017,9 @@ macro_rules! handle_error { macro_rules! update_maps_on_chan_removal { ($self: expr, $channel_context: expr) => {{ - $self.id_to_peer.lock().unwrap().remove(&$channel_context.channel_id()); + if let Some(outpoint) = $channel_context.get_funding_txo() { + $self.outpoint_to_peer.lock().unwrap().remove(&outpoint); + } let mut short_to_chan_info = $self.short_to_chan_info.write().unwrap(); if let Some(short_id) = $channel_context.get_short_channel_id() { short_to_chan_info.remove(&short_id); @@ -2038,12 +2051,11 @@ macro_rules! convert_chan_phase_err { let logger = WithChannelContext::from(&$self.logger, &$channel.context); log_error!(logger, "Closing channel {} due to close-required error: {}", $channel_id, msg); update_maps_on_chan_removal!($self, $channel.context); - let shutdown_res = $channel.context.force_shutdown(true); - let user_id = $channel.context.get_user_id(); - let channel_capacity_satoshis = $channel.context.get_value_satoshis(); - - (true, MsgHandleErrInternal::from_finish_shutdown(msg, *$channel_id, user_id, - shutdown_res, $channel_update, channel_capacity_satoshis)) + let reason = ClosureReason::ProcessingError { err: msg.clone() }; + let shutdown_res = $channel.context.force_shutdown(true, reason); + let err = + MsgHandleErrInternal::from_finish_shutdown(msg, *$channel_id, shutdown_res, $channel_update); + (true, err) }, } }; @@ -2425,7 +2437,7 @@ where forward_htlcs: Mutex::new(HashMap::new()), claimable_payments: Mutex::new(ClaimablePayments { claimable_payments: HashMap::new(), pending_claiming_payments: HashMap::new() }), pending_intercepted_htlcs: Mutex::new(HashMap::new()), - id_to_peer: Mutex::new(HashMap::new()), + outpoint_to_peer: Mutex::new(HashMap::new()), short_to_chan_info: FairRwLock::new(HashMap::new()), our_network_pubkey: node_signer.get_node_id(Recipient::Node).unwrap(), @@ -2576,7 +2588,7 @@ where fn list_funded_channels_with_filter)) -> bool + Copy>(&self, f: Fn) -> Vec { // Allocate our best estimate of the number of channels we have in the `res` // Vec. Sadly the `short_to_chan_info` map doesn't cover channels without - // a scid or a scid alias, and the `id_to_peer` shouldn't be used outside + // a scid or a scid alias, and the `outpoint_to_peer` shouldn't be used outside // of the ChannelMonitor handling. Therefore reallocations may still occur, but is // unlikely as the `short_to_chan_info` map often contains 2 entries for // the same channel. @@ -2609,7 +2621,7 @@ where pub fn list_channels(&self) -> Vec { // Allocate our best estimate of the number of channels we have in the `res` // Vec. Sadly the `short_to_chan_info` map doesn't cover channels without - // a scid or a scid alias, and the `id_to_peer` shouldn't be used outside + // a scid or a scid alias, and the `outpoint_to_peer` shouldn't be used outside // of the ChannelMonitor handling. Therefore reallocations may still occur, but is // unlikely as the `short_to_chan_info` map often contains 2 entries for // the same channel. @@ -2700,26 +2712,6 @@ where .collect() } - /// Helper function that issues the channel close events - fn issue_channel_close_events(&self, context: &ChannelContext, closure_reason: ClosureReason) { - let mut pending_events_lock = self.pending_events.lock().unwrap(); - match context.unbroadcasted_funding() { - Some(transaction) => { - pending_events_lock.push_back((events::Event::DiscardFunding { - channel_id: context.channel_id(), transaction - }, None)); - }, - None => {}, - } - pending_events_lock.push_back((events::Event::ChannelClosed { - channel_id: context.channel_id(), - user_channel_id: context.get_user_id(), - reason: closure_reason, - counterparty_node_id: Some(context.get_counterparty_node_id()), - channel_capacity_sats: Some(context.get_value_satoshis()), - }, None)); - } - fn close_channel_internal(&self, channel_id: &ChannelId, counterparty_node_id: &PublicKey, target_feerate_sats_per_1000_weight: Option, override_shutdown_script: Option) -> Result<(), APIError> { let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self); @@ -2761,9 +2753,8 @@ where peer_state_lock, peer_state, per_peer_state, chan); } } else { - self.issue_channel_close_events(chan_phase_entry.get().context(), ClosureReason::HolderForceClosed); let mut chan_phase = remove_channel_phase!(self, chan_phase_entry); - shutdown_result = Some(chan_phase.context_mut().force_shutdown(false)); + shutdown_result = Some(chan_phase.context_mut().force_shutdown(false, ClosureReason::HolderForceClosed)); } }, hash_map::Entry::Vacant(_) => { @@ -2860,7 +2851,9 @@ where let logger = WithContext::from( &self.logger, Some(shutdown_res.counterparty_node_id), Some(shutdown_res.channel_id), ); - log_debug!(logger, "Finishing closure of channel with {} HTLCs to fail", shutdown_res.dropped_outbound_htlcs.len()); + + log_debug!(logger, "Finishing closure of channel due to {} with {} HTLCs to fail", + shutdown_res.closure_reason, shutdown_res.dropped_outbound_htlcs.len()); for htlc_source in shutdown_res.dropped_outbound_htlcs.drain(..) { let (source, payment_hash, counterparty_node_id, channel_id) = htlc_source; let reason = HTLCFailReason::from_failure_code(0x4000 | 8); @@ -2885,8 +2878,7 @@ where let mut peer_state = peer_state_mutex.lock().unwrap(); if let Some(mut chan) = peer_state.channel_by_id.remove(&channel_id) { update_maps_on_chan_removal!(self, &chan.context()); - self.issue_channel_close_events(&chan.context(), ClosureReason::FundingBatchClosure); - shutdown_results.push(chan.context_mut().force_shutdown(false)); + shutdown_results.push(chan.context_mut().force_shutdown(false, ClosureReason::FundingBatchClosure)); } } has_uncompleted_channel = Some(has_uncompleted_channel.map_or(!state, |v| v || !state)); @@ -2896,6 +2888,24 @@ where "Closing a batch where all channels have completed initial monitor update", ); } + + { + let mut pending_events = self.pending_events.lock().unwrap(); + pending_events.push_back((events::Event::ChannelClosed { + channel_id: shutdown_res.channel_id, + user_channel_id: shutdown_res.user_channel_id, + reason: shutdown_res.closure_reason, + counterparty_node_id: Some(shutdown_res.counterparty_node_id), + channel_capacity_sats: Some(shutdown_res.channel_capacity_satoshis), + channel_funding_txo: shutdown_res.channel_funding_txo, + }, None)); + + if let Some(transaction) = shutdown_res.unbroadcasted_funding_tx { + pending_events.push_back((events::Event::DiscardFunding { + channel_id: shutdown_res.channel_id, transaction + }, None)); + } + } for shutdown_result in shutdown_results.drain(..) { self.finish_close_channel(shutdown_result); } @@ -2918,17 +2928,16 @@ where let logger = WithContext::from(&self.logger, Some(*peer_node_id), Some(*channel_id)); if let hash_map::Entry::Occupied(chan_phase_entry) = peer_state.channel_by_id.entry(channel_id.clone()) { log_error!(logger, "Force-closing channel {}", channel_id); - self.issue_channel_close_events(&chan_phase_entry.get().context(), closure_reason); let mut chan_phase = remove_channel_phase!(self, chan_phase_entry); mem::drop(peer_state); mem::drop(per_peer_state); match chan_phase { ChannelPhase::Funded(mut chan) => { - self.finish_close_channel(chan.context.force_shutdown(broadcast)); + self.finish_close_channel(chan.context.force_shutdown(broadcast, closure_reason)); (self.get_channel_update_for_broadcast(&chan).ok(), chan.context.get_counterparty_node_id()) }, ChannelPhase::UnfundedOutboundV1(_) | ChannelPhase::UnfundedInboundV1(_) => { - self.finish_close_channel(chan_phase.context_mut().force_shutdown(false)); + 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()) }, @@ -3029,8 +3038,9 @@ where let is_intro_node_forward = match next_hop { onion_utils::Hop::Forward { - // TODO: update this when we support blinded forwarding as non-intro node - next_hop_data: msgs::InboundOnionPayload::BlindedForward { .. }, .. + next_hop_data: msgs::InboundOnionPayload::BlindedForward { + intro_node_blinding_point: Some(_), .. + }, .. } => true, _ => false, }; @@ -3239,14 +3249,14 @@ where // delay) once they've send us a commitment_signed! PendingHTLCStatus::Forward(info) }, - Err(InboundOnionErr { err_code, err_data, msg }) => return_err!(msg, err_code, &err_data) + Err(InboundHTLCErr { err_code, err_data, msg }) => return_err!(msg, err_code, &err_data) } }, onion_utils::Hop::Forward { next_hop_data, next_hop_hmac, new_packet_bytes } => { match create_fwd_pending_htlc_info(msg, next_hop_data, next_hop_hmac, new_packet_bytes, shared_secret, next_packet_pubkey_opt) { Ok(info) => PendingHTLCStatus::Forward(info), - Err(InboundOnionErr { err_code, err_data, msg }) => return_err!(msg, err_code, &err_data) + Err(InboundHTLCErr { err_code, err_data, msg }) => return_err!(msg, err_code, &err_data) } } } @@ -3748,18 +3758,18 @@ where let mut peer_state_lock = peer_state_mutex.lock().unwrap(); let peer_state = &mut *peer_state_lock; - let (chan, msg_opt) = match peer_state.channel_by_id.remove(temporary_channel_id) { + let funding_txo; + let (mut chan, msg_opt) = match peer_state.channel_by_id.remove(temporary_channel_id) { Some(ChannelPhase::UnfundedOutboundV1(mut chan)) => { - let funding_txo = find_funding_output(&chan, &funding_transaction)?; + funding_txo = find_funding_output(&chan, &funding_transaction)?; let logger = WithChannelContext::from(&self.logger, &chan.context); let funding_res = chan.get_funding_created(funding_transaction, funding_txo, is_batch_funding, &&logger) .map_err(|(mut chan, e)| if let ChannelError::Close(msg) = e { let channel_id = chan.context.channel_id(); - let user_id = chan.context.get_user_id(); - let shutdown_res = chan.context.force_shutdown(false); - let channel_capacity = chan.context.get_value_satoshis(); - (chan, MsgHandleErrInternal::from_finish_shutdown(msg, channel_id, user_id, shutdown_res, None, channel_capacity)) + let reason = ClosureReason::ProcessingError { err: msg.clone() }; + let shutdown_res = chan.context.force_shutdown(false, reason); + (chan, MsgHandleErrInternal::from_finish_shutdown(msg, channel_id, shutdown_res, None)) } else { unreachable!(); }); match funding_res { Ok(funding_msg) => (chan, funding_msg), @@ -3798,9 +3808,21 @@ where panic!("Generated duplicate funding txid?"); }, hash_map::Entry::Vacant(e) => { - let mut id_to_peer = self.id_to_peer.lock().unwrap(); - if id_to_peer.insert(chan.context.channel_id(), chan.context.get_counterparty_node_id()).is_some() { - panic!("id_to_peer map already contained funding txid, which shouldn't be possible"); + let mut outpoint_to_peer = self.outpoint_to_peer.lock().unwrap(); + match outpoint_to_peer.entry(funding_txo) { + hash_map::Entry::Vacant(e) => { e.insert(chan.context.get_counterparty_node_id()); }, + hash_map::Entry::Occupied(o) => { + let err = format!( + "An existing channel using outpoint {} is open with peer {}", + funding_txo, o.get() + ); + mem::drop(outpoint_to_peer); + mem::drop(peer_state_lock); + mem::drop(per_peer_state); + let reason = ClosureReason::ProcessingError { err: err.clone() }; + self.finish_close_channel(chan.context.force_shutdown(true, reason)); + return Err(APIError::ChannelUnavailable { err }); + } } e.insert(ChannelPhase::UnfundedOutboundV1(chan)); } @@ -3964,11 +3986,12 @@ where .and_then(|mut peer_state| peer_state.channel_by_id.remove(&channel_id)) .map(|mut chan| { update_maps_on_chan_removal!(self, &chan.context()); - self.issue_channel_close_events(&chan.context(), ClosureReason::ProcessingError { err: e.clone() }); - shutdown_results.push(chan.context_mut().force_shutdown(false)); + let closure_reason = ClosureReason::ProcessingError { err: e.clone() }; + shutdown_results.push(chan.context_mut().force_shutdown(false, closure_reason)); }); } } + mem::drop(funding_batch_states); for shutdown_result in shutdown_results.drain(..) { self.finish_close_channel(shutdown_result); } @@ -4302,7 +4325,7 @@ where 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)])), - Err(InboundOnionErr { err_code, err_data, msg }) => failed_payment!(msg, err_code, err_data, Some(phantom_shared_secret)) + Err(InboundHTLCErr { err_code, err_data, msg }) => failed_payment!(msg, err_code, err_data, Some(phantom_shared_secret)) } }, _ => panic!(), @@ -4344,7 +4367,7 @@ where if let Some(ChannelPhase::Funded(ref mut chan)) = peer_state.channel_by_id.get_mut(&forward_chan_id) { let logger = WithChannelContext::from(&self.logger, &chan.context); for forward_info in pending_forwards.drain(..) { - match forward_info { + 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 { @@ -4363,7 +4386,7 @@ where incoming_packet_shared_secret: incoming_shared_secret, // Phantom payments are only PendingHTLCRouting::Receive. phantom_shared_secret: None, - blinded_failure: blinded.map(|_| BlindedFailure::FromIntroductionNode), + blinded_failure: blinded.map(|b| b.failure), }); let next_blinding_point = blinded.and_then(|b| { let encrypted_tlvs_ss = self.node_signer.ecdh( @@ -4390,40 +4413,35 @@ where )); continue; } + None }, HTLCForwardInfo::AddHTLC { .. } => { panic!("short_channel_id != 0 should imply any pending_forward entries are of type Forward"); }, HTLCForwardInfo::FailHTLC { htlc_id, err_packet } => { log_trace!(logger, "Failing HTLC back to channel with short id {} (backward HTLC ID {}) after delay", short_chan_id, htlc_id); - if let Err(e) = chan.queue_fail_htlc( - htlc_id, err_packet, &&logger - ) { - if let ChannelError::Ignore(msg) = e { - log_trace!(logger, "Failed to fail HTLC with ID {} backwards to short_id {}: {}", htlc_id, short_chan_id, msg); - } else { - panic!("Stated return value requirements in queue_fail_htlc() were not met"); - } - // fail-backs are best-effort, we probably already have one - // pending, and if not that's OK, if not, the channel is on - // the chain and sending the HTLC-Timeout is their problem. - continue; - } + Some((chan.queue_fail_htlc(htlc_id, err_packet, &&logger), htlc_id)) }, HTLCForwardInfo::FailMalformedHTLC { htlc_id, failure_code, sha256_of_onion } => { - log_trace!(self.logger, "Failing malformed HTLC back to channel with short id {} (backward HTLC ID {}) after delay", short_chan_id, htlc_id); - if let Err(e) = chan.queue_fail_malformed_htlc(htlc_id, failure_code, sha256_of_onion, &self.logger) { - if let ChannelError::Ignore(msg) = e { - log_trace!(self.logger, "Failed to fail HTLC with ID {} backwards to short_id {}: {}", htlc_id, short_chan_id, msg); - } else { - panic!("Stated return value requirements in queue_fail_malformed_htlc() were not met"); - } - // fail-backs are best-effort, we probably already have one - // pending, and if not that's OK, if not, the channel is on - // the chain and sending the HTLC-Timeout is their problem. - continue; - } + log_trace!(logger, "Failing malformed HTLC back to channel with short id {} (backward HTLC ID {}) after delay", short_chan_id, htlc_id); + let res = chan.queue_fail_malformed_htlc( + htlc_id, failure_code, sha256_of_onion, &&logger + ); + Some((res, htlc_id)) }, + }; + if let Some((queue_fail_htlc_res, htlc_id)) = queue_fail_htlc_res { + if let Err(e) = queue_fail_htlc_res { + if let ChannelError::Ignore(msg) = e { + log_trace!(logger, "Failed to fail HTLC with ID {} backwards to short_id {}: {}", htlc_id, short_chan_id, msg); + } else { + panic!("Stated return value requirements in queue_fail_{{malformed_}}htlc() were not met"); + } + // fail-backs are best-effort, we probably already have one + // pending, and if not that's OK, if not, the channel is on + // the chain and sending the HTLC-Timeout is their problem. + continue; + } } } } else { @@ -4887,8 +4905,7 @@ where log_error!(logger, "Force-closing pending channel with ID {} for not establishing in a timely manner", chan_id); update_maps_on_chan_removal!(self, &context); - self.issue_channel_close_events(&context, ClosureReason::HolderForceClosed); - shutdown_channels.push(context.force_shutdown(false)); + shutdown_channels.push(context.force_shutdown(false, ClosureReason::HolderForceClosed)); pending_msg_events.push(MessageSendEvent::HandleError { node_id: counterparty_node_id, action: msgs::ErrorAction::SendErrorMessage { @@ -5610,6 +5627,7 @@ where } let preimage_update = ChannelMonitorUpdate { update_id: CLOSED_CHANNEL_UPDATE_ID, + counterparty_node_id: None, updates: vec![ChannelMonitorUpdateStep::PaymentPreimage { payment_preimage, }], @@ -5911,9 +5929,9 @@ where Some(cp_id) => cp_id.clone(), None => { // TODO: Once we can rely on the counterparty_node_id from the - // monitor event, this and the id_to_peer map should be removed. - let id_to_peer = self.id_to_peer.lock().unwrap(); - match id_to_peer.get(&funding_txo.to_channel_id()) { + // monitor event, this and the outpoint_to_peer map should be removed. + let outpoint_to_peer = self.outpoint_to_peer.lock().unwrap(); + match outpoint_to_peer.get(&funding_txo) { Some(cp_id) => cp_id.clone(), None => return, } @@ -5994,13 +6012,20 @@ where } fn do_accept_inbound_channel(&self, temporary_channel_id: &ChannelId, counterparty_node_id: &PublicKey, accept_0conf: bool, user_channel_id: u128) -> Result<(), APIError> { + + let logger = WithContext::from(&self.logger, Some(*counterparty_node_id), Some(*temporary_channel_id)); let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self); let peers_without_funded_channels = self.peers_without_funded_channels(|peer| { peer.total_channel_count() > 0 }); let per_peer_state = self.per_peer_state.read().unwrap(); let peer_state_mutex = per_peer_state.get(counterparty_node_id) - .ok_or_else(|| APIError::ChannelUnavailable { err: format!("Can't find a peer matching the passed counterparty node_id {}", counterparty_node_id) })?; + .ok_or_else(|| { + let err_str = format!("Can't find a peer matching the passed counterparty node_id {}", counterparty_node_id); + log_error!(logger, "{}", err_str); + + APIError::ChannelUnavailable { err: err_str } + })?; let mut peer_state_lock = peer_state_mutex.lock().unwrap(); let peer_state = &mut *peer_state_lock; let is_only_peer_channel = peer_state.total_channel_count() == 1; @@ -6015,9 +6040,19 @@ where InboundV1Channel::new(&self.fee_estimator, &self.entropy_source, &self.signer_provider, counterparty_node_id.clone(), &self.channel_type_features(), &peer_state.latest_features, &unaccepted_channel.open_channel_msg, user_channel_id, &self.default_configuration, best_block_height, - &self.logger, accept_0conf).map_err(|e| APIError::ChannelUnavailable { err: e.to_string() }) + &self.logger, accept_0conf).map_err(|e| { + let err_str = e.to_string(); + log_error!(logger, "{}", err_str); + + APIError::ChannelUnavailable { err: err_str } + }) + } + _ => { + let err_str = "No such channel awaiting to be accepted.".to_owned(); + log_error!(logger, "{}", err_str); + + Err(APIError::APIMisuseError { err: err_str }) } - _ => Err(APIError::APIMisuseError { err: "No such channel awaiting to be accepted.".to_owned() }) }?; if accept_0conf { @@ -6031,7 +6066,10 @@ where } }; peer_state.pending_msg_events.push(send_msg_err_event); - return Err(APIError::APIMisuseError { err: "Please use accept_inbound_channel_from_trusted_peer_0conf to accept channels with zero confirmations.".to_owned() }); + let err_str = "Please use accept_inbound_channel_from_trusted_peer_0conf to accept channels with zero confirmations.".to_owned(); + log_error!(logger, "{}", err_str); + + return Err(APIError::APIMisuseError { err: err_str }); } else { // If this peer already has some channels, a new channel won't increase our number of peers // with unfunded channels, so as long as we aren't over the maximum number of unfunded @@ -6044,7 +6082,10 @@ where } }; peer_state.pending_msg_events.push(send_msg_err_event); - return Err(APIError::APIMisuseError { err: "Too many peers with unfunded channels, refusing to accept new ones".to_owned() }); + let err_str = "Too many peers with unfunded channels, refusing to accept new ones".to_owned(); + log_error!(logger, "{}", err_str); + + return Err(APIError::APIMisuseError { err: err_str }); } } @@ -6167,13 +6208,18 @@ where // If we're doing manual acceptance checks on the channel, then defer creation until we're sure we want to accept. if self.default_configuration.manually_accept_inbound_channels { + let channel_type = channel::channel_type_from_open_channel( + &msg, &peer_state.latest_features, &self.channel_type_features() + ).map_err(|e| + MsgHandleErrInternal::from_chan_no_close(e, msg.temporary_channel_id) + )?; let mut pending_events = self.pending_events.lock().unwrap(); pending_events.push_back((events::Event::OpenChannelRequest { temporary_channel_id: msg.temporary_channel_id.clone(), counterparty_node_id: counterparty_node_id.clone(), funding_satoshis: msg.funding_satoshis, push_msat: msg.push_msat, - channel_type: msg.channel_type.clone().unwrap(), + channel_type, }, None)); peer_state.inbound_channel_request_by_id.insert(channel_id, InboundChannelRequest { open_channel_msg: msg.clone(), @@ -6265,50 +6311,61 @@ where let mut peer_state_lock = peer_state_mutex.lock().unwrap(); let peer_state = &mut *peer_state_lock; - let (chan, funding_msg_opt, monitor) = + let (mut chan, funding_msg_opt, monitor) = match peer_state.channel_by_id.remove(&msg.temporary_channel_id) { Some(ChannelPhase::UnfundedInboundV1(inbound_chan)) => { let logger = WithChannelContext::from(&self.logger, &inbound_chan.context); match inbound_chan.funding_created(msg, best_block, &self.signer_provider, &&logger) { Ok(res) => res, - Err((mut inbound_chan, err)) => { + Err((inbound_chan, err)) => { // We've already removed this inbound channel from the map in `PeerState` // above so at this point we just need to clean up any lingering entries // concerning this channel as it is safe to do so. - update_maps_on_chan_removal!(self, &inbound_chan.context); - let user_id = inbound_chan.context.get_user_id(); - let shutdown_res = inbound_chan.context.force_shutdown(false); - return Err(MsgHandleErrInternal::from_finish_shutdown(format!("{}", err), - msg.temporary_channel_id, user_id, shutdown_res, None, inbound_chan.context.get_value_satoshis())); + debug_assert!(matches!(err, ChannelError::Close(_))); + // Really we should be returning the channel_id the peer expects based + // on their funding info here, but they're horribly confused anyway, so + // there's not a lot we can do to save them. + return Err(convert_chan_phase_err!(self, err, &mut ChannelPhase::UnfundedInboundV1(inbound_chan), &msg.temporary_channel_id).1); }, } }, - Some(ChannelPhase::Funded(_)) | Some(ChannelPhase::UnfundedOutboundV1(_)) => { - return Err(MsgHandleErrInternal::send_err_msg_no_close(format!("Got an unexpected funding_created message from peer with counterparty_node_id {}", counterparty_node_id), msg.temporary_channel_id)); + Some(mut phase) => { + let err_msg = format!("Got an unexpected funding_created message from peer with counterparty_node_id {}", counterparty_node_id); + let err = ChannelError::Close(err_msg); + return Err(convert_chan_phase_err!(self, err, &mut phase, &msg.temporary_channel_id).1); }, None => return Err(MsgHandleErrInternal::send_err_msg_no_close(format!("Got a message for a channel from the wrong node! No such channel for the passed counterparty_node_id {}", counterparty_node_id), msg.temporary_channel_id)) }; - match peer_state.channel_by_id.entry(chan.context.channel_id()) { + let funded_channel_id = chan.context.channel_id(); + + macro_rules! fail_chan { ($err: expr) => { { + // Note that at this point we've filled in the funding outpoint on our + // channel, but its actually in conflict with another channel. Thus, if + // we call `convert_chan_phase_err` immediately (thus calling + // `update_maps_on_chan_removal`), we'll remove the existing channel + // from `outpoint_to_peer`. Thus, we must first unset the funding outpoint + // on the channel. + let err = ChannelError::Close($err.to_owned()); + chan.unset_funding_info(msg.temporary_channel_id); + return Err(convert_chan_phase_err!(self, err, chan, &funded_channel_id, UNFUNDED_CHANNEL).1); + } } } + + match peer_state.channel_by_id.entry(funded_channel_id) { hash_map::Entry::Occupied(_) => { - Err(MsgHandleErrInternal::send_err_msg_no_close( - "Already had channel with the new channel_id".to_owned(), - chan.context.channel_id() - )) + fail_chan!("Already had channel with the new channel_id"); }, hash_map::Entry::Vacant(e) => { - let mut id_to_peer_lock = self.id_to_peer.lock().unwrap(); - match id_to_peer_lock.entry(chan.context.channel_id()) { + let mut outpoint_to_peer_lock = self.outpoint_to_peer.lock().unwrap(); + match outpoint_to_peer_lock.entry(monitor.get_funding_txo().0) { hash_map::Entry::Occupied(_) => { - return Err(MsgHandleErrInternal::send_err_msg_no_close( - "The funding_created message had the same funding_txid as an existing channel - funding is not possible".to_owned(), - chan.context.channel_id())) + fail_chan!("The funding_created message had the same funding_txid as an existing channel - funding is not possible"); }, hash_map::Entry::Vacant(i_e) => { let monitor_res = self.chain_monitor.watch_channel(monitor.get_funding_txo().0, monitor); if let Ok(persist_state) = monitor_res { i_e.insert(chan.context.get_counterparty_node_id()); - mem::drop(id_to_peer_lock); + mem::drop(outpoint_to_peer_lock); // There's no problem signing a counterparty's funding transaction if our monitor // hasn't persisted to disk yet - we can't lose money on a transaction that we haven't @@ -6331,13 +6388,7 @@ where } else { let logger = WithChannelContext::from(&self.logger, &chan.context); log_error!(logger, "Persisting initial ChannelMonitor failed, implying the funding outpoint was duplicated"); - let channel_id = match funding_msg_opt { - Some(msg) => msg.channel_id, - None => chan.context.channel_id(), - }; - return Err(MsgHandleErrInternal::send_err_msg_no_close( - "The funding_created message had the same funding_txid as an existing channel - funding is not possible".to_owned(), - channel_id)); + fail_chan!("Duplicate funding outpoint"); } } } @@ -6368,7 +6419,7 @@ where let res = chan.funding_signed(&msg, best_block, &self.signer_provider, &&logger); match res { - Ok((chan, monitor)) => { + Ok((mut chan, monitor)) => { if let Ok(persist_status) = self.chain_monitor.watch_channel(chan.context.get_funding_txo().unwrap(), monitor) { // We really should be able to insert here without doing a second // lookup, but sadly rust stdlib doesn't currently allow keeping @@ -6380,6 +6431,11 @@ where Ok(()) } else { let e = ChannelError::Close("Channel funding outpoint was a duplicate".to_owned()); + // We weren't able to watch the channel to begin with, so no + // updates should be made on it. Previously, full_stack_target + // found an (unreachable) panic when the monitor update contained + // within `shutdown_finish` was applied. + chan.unset_funding_info(msg.channel_id); return Err(convert_chan_phase_err!(self, e, &mut ChannelPhase::Funded(chan), &msg.channel_id).1); } }, @@ -6502,9 +6558,8 @@ where let context = phase.context_mut(); let logger = WithChannelContext::from(&self.logger, context); log_error!(logger, "Immediately closing unfunded channel {} as peer asked to cooperatively shut it down (which is unnecessary)", &msg.channel_id); - self.issue_channel_close_events(&context, ClosureReason::CounterpartyCoopClosedUnfundedChannel); let mut chan = remove_channel_phase!(self, chan_phase_entry); - finish_shutdown = Some(chan.context_mut().force_shutdown(false)); + finish_shutdown = Some(chan.context_mut().force_shutdown(false, ClosureReason::CounterpartyCoopClosedUnfundedChannel)); }, } } else { @@ -6573,7 +6628,6 @@ where msg: update }); } - self.issue_channel_close_events(&chan.context, ClosureReason::CooperativeClosure); } mem::drop(per_peer_state); if let Some(shutdown_result) = shutdown_result { @@ -7212,9 +7266,9 @@ where Some(cp_id) => Some(cp_id), None => { // TODO: Once we can rely on the counterparty_node_id from the - // monitor event, this and the id_to_peer map should be removed. - let id_to_peer = self.id_to_peer.lock().unwrap(); - id_to_peer.get(&funding_outpoint.to_channel_id()).cloned() + // monitor event, this and the outpoint_to_peer map should be removed. + let outpoint_to_peer = self.outpoint_to_peer.lock().unwrap(); + outpoint_to_peer.get(&funding_outpoint).cloned() } }; if let Some(counterparty_node_id) = counterparty_node_id_opt { @@ -7225,13 +7279,12 @@ where 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 ChannelPhase::Funded(mut chan) = remove_channel_phase!(self, chan_phase_entry) { - failed_channels.push(chan.context.force_shutdown(false)); + failed_channels.push(chan.context.force_shutdown(false, ClosureReason::HolderForceClosed)); if let Ok(update) = self.get_channel_update_for_broadcast(&chan) { pending_msg_events.push(events::MessageSendEvent::BroadcastChannelUpdate { msg: update }); } - self.issue_channel_close_events(&chan.context, ClosureReason::HolderForceClosed); pending_msg_events.push(events::MessageSendEvent::HandleError { node_id: chan.context.get_counterparty_node_id(), action: msgs::ErrorAction::DisconnectPeer { @@ -7418,8 +7471,6 @@ where }); } - self.issue_channel_close_events(&chan.context, ClosureReason::CooperativeClosure); - log_info!(logger, "Broadcasting {}", log_tx!(tx)); self.tx_broadcaster.broadcast_transactions(&[&tx]); update_maps_on_chan_removal!(self, &chan.context); @@ -7476,41 +7527,56 @@ where 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. /// /// # Privacy /// - /// Uses a one-hop [`BlindedPath`] for the offer with [`ChannelManager::get_our_node_id`] as the - /// introduction node and a derived signing pubkey for recipient privacy. As such, currently, - /// the node must be announced. Otherwise, there is no way to find a path to the introduction - /// node in order to send the [`InvoiceRequest`]. + /// Uses [`MessageRouter::create_blinded_paths`] to construct a [`BlindedPath`] for the offer. + /// However, if one is not found, uses a one-hop [`BlindedPath`] with + /// [`ChannelManager::get_our_node_id`] as the introduction node instead. In the latter case, + /// the node must be announced, otherwise, there is no way to find a path to the introduction in + /// order to send the [`InvoiceRequest`]. + /// + /// Also, uses a derived signing pubkey in the offer for recipient privacy. /// /// # Limitations /// /// Requires a direct connection to the introduction node in the responding [`InvoiceRequest`]'s /// reply path. /// + /// # Errors + /// + /// Errors if the parameterized [`Router`] is unable to create a blinded path for the offer. + /// /// This is not exported to bindings users as builder patterns don't map outside of move semantics. /// /// [`Offer`]: crate::offers::offer::Offer /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest pub fn create_offer_builder( - &self, description: String - ) -> OfferBuilder { - let node_id = self.get_our_node_id(); - let expanded_key = &self.inbound_payment_key; - let entropy = &*self.entropy_source; - let secp_ctx = &self.secp_ctx; - let path = self.create_one_hop_blinded_path(); + &$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) + .path(path); - OfferBuilder::deriving_signing_pubkey(description, node_id, expanded_key, entropy, secp_ctx) - .chain_hash(self.chain_hash) - .path(path) + 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. /// @@ -7533,10 +7599,13 @@ where /// /// # Privacy /// - /// Uses a one-hop [`BlindedPath`] for the refund with [`ChannelManager::get_our_node_id`] as - /// the introduction node and a derived payer id for payer privacy. As such, currently, the - /// node must be announced. Otherwise, there is no way to find a path to the introduction node - /// in order to send the [`Bolt12Invoice`]. + /// Uses [`MessageRouter::create_blinded_paths`] to construct a [`BlindedPath`] for the refund. + /// However, if one is not found, uses a one-hop [`BlindedPath`] with + /// [`ChannelManager::get_our_node_id`] as the introduction node instead. In the latter case, + /// the node must be announced, otherwise, there is no way to find a path to the introduction in + /// order to send the [`Bolt12Invoice`]. + /// + /// Also, uses a derived payer id in the refund for payer privacy. /// /// # Limitations /// @@ -7545,40 +7614,65 @@ where /// /// # Errors /// - /// Errors if a duplicate `payment_id` is provided given the caveats in the aforementioned link - /// or if `amount_msats` is invalid. + /// Errors if: + /// - a duplicate `payment_id` is provided given the caveats in the aforementioned link, + /// - `amount_msats` is invalid, or + /// - the parameterized [`Router`] is unable to create a blinded path for the refund. /// /// This is not exported to bindings users as builder patterns don't map outside of move semantics. /// /// [`Refund`]: crate::offers::refund::Refund /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice /// [`Bolt12Invoice::payment_paths`]: crate::offers::invoice::Bolt12Invoice::payment_paths + /// [Avoiding Duplicate Payments]: #avoiding-duplicate-payments pub fn create_refund_builder( - &self, description: String, amount_msats: u64, absolute_expiry: Duration, + &$self, description: String, amount_msats: u64, absolute_expiry: Duration, payment_id: PaymentId, retry_strategy: Retry, max_total_routing_fee_msat: Option - ) -> Result, Bolt12SemanticError> { - let node_id = self.get_our_node_id(); - let expanded_key = &self.inbound_payment_key; - let entropy = &*self.entropy_source; - let secp_ctx = &self.secp_ctx; - let path = self.create_one_hop_blinded_path(); + ) -> 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 = 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 ChannelManager +where + M::Target: chain::Watch<::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); + #[cfg(not(c_bindings))] + create_refund_builder!(self, RefundBuilder); + + #[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 @@ -7620,8 +7714,11 @@ where /// /// # Errors /// - /// Errors if a duplicate `payment_id` is provided given the caveats in the aforementioned link - /// or if the provided parameters are invalid for the offer. + /// Errors if: + /// - a duplicate `payment_id` is provided given the caveats in the aforementioned link, + /// - the provided parameters are invalid for the offer, + /// - the parameterized [`Router`] is unable to create a blinded reply path for the invoice + /// request. /// /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest /// [`InvoiceRequest::quantity`]: crate::offers::invoice_request::InvoiceRequest::quantity @@ -7639,9 +7736,11 @@ where let entropy = &*self.entropy_source; let secp_ctx = &self.secp_ctx; - let builder = offer + let builder: InvoiceRequestBuilder = 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)?, @@ -7654,9 +7753,8 @@ where None => builder, Some(payer_note) => builder.payer_note(payer_note), }; - let invoice_request = builder.build_and_sign()?; - let reply_path = self.create_one_hop_blinded_path(); + let reply_path = self.create_blinded_path().map_err(|_| Bolt12SemanticError::MissingPaths)?; let expiration = StaleExpiration::TimerTicks(1); self.pending_outbound_payments @@ -7705,6 +7803,11 @@ where /// node meeting the aforementioned criteria, but there's no guarantee that they will be /// received and no retries will be made. /// + /// # Errors + /// + /// Errors if the parameterized [`Router`] is unable to create a blinded payment path or reply + /// path for the invoice. + /// /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice pub fn request_refund_payment(&self, refund: &Refund) -> Result<(), Bolt12SemanticError> { let expanded_key = &self.inbound_payment_key; @@ -7716,23 +7819,25 @@ where match self.create_inbound_payment(Some(amount_msats), relative_expiry, None) { Ok((payment_hash, payment_secret)) => { - let payment_paths = vec![ - self.create_one_hop_blinded_payment_path(payment_secret), - ]; - #[cfg(not(feature = "no-std"))] + let payment_paths = self.create_blinded_payment_paths(amount_msats, payment_secret) + .map_err(|_| Bolt12SemanticError::MissingPaths)?; + + #[cfg(feature = "std")] let builder = refund.respond_using_derived_keys( payment_paths, payment_hash, expanded_key, entropy )?; - #[cfg(feature = "no-std")] + #[cfg(not(feature = "std"))] let created_at = Duration::from_secs( self.highest_seen_timestamp.load(Ordering::Acquire) as u64 ); - #[cfg(feature = "no-std")] + #[cfg(not(feature = "std"))] let builder = refund.respond_using_derived_keys_no_std( payment_paths, payment_hash, created_at, expanded_key, entropy )?; + let builder: InvoiceBuilder = builder.into(); let invoice = builder.allow_mpp().build_and_sign(secp_ctx)?; - let reply_path = self.create_one_hop_blinded_path(); + let reply_path = self.create_blinded_path() + .map_err(|_| Bolt12SemanticError::MissingPaths)?; let mut pending_offers_messages = self.pending_offers_messages.lock().unwrap(); if refund.paths().is_empty() { @@ -7859,24 +7964,37 @@ where inbound_payment::get_payment_preimage(payment_hash, payment_secret, &self.inbound_payment_key) } - /// Creates a one-hop blinded path with [`ChannelManager::get_our_node_id`] as the introduction - /// node. - fn create_one_hop_blinded_path(&self) -> BlindedPath { + /// Creates a blinded path by delegating to [`MessageRouter::create_blinded_paths`]. + /// + /// Errors if the `MessageRouter` errors or returns an empty `Vec`. + fn create_blinded_path(&self) -> Result { + let recipient = self.get_our_node_id(); let entropy_source = self.entropy_source.deref(); let secp_ctx = &self.secp_ctx; - BlindedPath::one_hop_for_message(self.get_our_node_id(), entropy_source, secp_ctx).unwrap() + + let peers = self.per_peer_state.read().unwrap() + .iter() + .filter(|(_, peer)| peer.lock().unwrap().latest_features.supports_onion_messages()) + .map(|(node_id, _)| *node_id) + .collect::>(); + + self.router + .create_blinded_paths(recipient, peers, entropy_source, secp_ctx) + .and_then(|paths| paths.into_iter().next().ok_or(())) } - /// Creates a one-hop blinded path with [`ChannelManager::get_our_node_id`] as the introduction - /// node. - fn create_one_hop_blinded_payment_path( - &self, payment_secret: PaymentSecret - ) -> (BlindedPayInfo, BlindedPath) { + /// Creates multi-hop blinded payment paths for the given `amount_msats` by delegating to + /// [`Router::create_blinded_payment_paths`]. + fn create_blinded_payment_paths( + &self, amount_msats: u64, payment_secret: PaymentSecret + ) -> Result, ()> { let entropy_source = self.entropy_source.deref(); let secp_ctx = &self.secp_ctx; + let first_hops = self.list_usable_channels(); let payee_node_id = self.get_our_node_id(); - let max_cltv_expiry = self.best_block.read().unwrap().height() + LATENCY_GRACE_PERIOD_BLOCKS; + let max_cltv_expiry = self.best_block.read().unwrap().height() + CLTV_FAR_FAR_AWAY + + LATENCY_GRACE_PERIOD_BLOCKS; let payee_tlvs = ReceiveTlvs { payment_secret, payment_constraints: PaymentConstraints { @@ -7884,10 +8002,9 @@ where htlc_minimum_msat: 1, }, }; - // TODO: Err for overflow? - BlindedPath::one_hop_for_payment( - payee_node_id, payee_tlvs, entropy_source, secp_ctx - ).unwrap() + self.router.create_blinded_payment_paths( + payee_node_id, first_hops, payee_tlvs, amount_msats, entropy_source, secp_ctx + ) } /// Gets a fake short channel id for use in receiving [phantom node payments]. These fake scids @@ -8395,14 +8512,13 @@ where update_maps_on_chan_removal!(self, &channel.context); // It looks like our counterparty went on-chain or funding transaction was // reorged out of the main chain. Close the channel. - failed_channels.push(channel.context.force_shutdown(true)); + let reason_message = format!("{}", reason); + failed_channels.push(channel.context.force_shutdown(true, reason)); if let Ok(update) = self.get_channel_update_for_broadcast(&channel) { pending_msg_events.push(events::MessageSendEvent::BroadcastChannelUpdate { msg: update }); } - let reason_message = format!("{}", reason); - self.issue_channel_close_events(&channel.context, reason); pending_msg_events.push(events::MessageSendEvent::HandleError { node_id: channel.context.get_counterparty_node_id(), action: msgs::ErrorAction::DisconnectPeer { @@ -8800,8 +8916,7 @@ where }; // Clean up for removal. update_maps_on_chan_removal!(self, &context); - self.issue_channel_close_events(&context, ClosureReason::DisconnectedPeer); - failed_channels.push(context.force_shutdown(false)); + failed_channels.push(context.force_shutdown(false, ClosureReason::DisconnectedPeer)); false }); // Note that we don't bother generating any events for pre-accept channels - @@ -8934,13 +9049,7 @@ where let pending_msg_events = &mut peer_state.pending_msg_events; peer_state.channel_by_id.iter_mut().filter_map(|(_, phase)| - if let ChannelPhase::Funded(chan) = phase { Some(chan) } else { - // Since unfunded channel maps are cleared upon disconnecting a peer, and they're not persisted - // (so won't be recovered after a crash), they shouldn't exist here and we would never need to - // worry about closing and removing them. - debug_assert!(false); - None - } + if let ChannelPhase::Funded(chan) = phase { Some(chan) } else { None } ).for_each(|chan| { let logger = WithChannelContext::from(&self.logger, &chan.context); pending_msg_events.push(events::MessageSendEvent::SendChannelReestablish { @@ -9127,7 +9236,7 @@ where let amount_msats = match InvoiceBuilder::::amount_msats( &invoice_request ) { - Ok(amount_msats) => Some(amount_msats), + Ok(amount_msats) => amount_msats, Err(error) => return Some(OffersMessage::InvoiceError(error.into())), }; let invoice_request = match invoice_request.verify(expanded_key, secp_ctx) { @@ -9137,64 +9246,78 @@ where return Some(OffersMessage::InvoiceError(error.into())); }, }; - let relative_expiry = DEFAULT_RELATIVE_EXPIRY.as_secs() as u32; - match self.create_inbound_payment(amount_msats, relative_expiry, None) { - Ok((payment_hash, payment_secret)) if invoice_request.keys.is_some() => { - let payment_paths = vec![ - self.create_one_hop_blinded_payment_path(payment_secret), - ]; - #[cfg(not(feature = "no-std"))] - let builder = invoice_request.respond_using_derived_keys( - payment_paths, payment_hash - ); - #[cfg(feature = "no-std")] - let created_at = Duration::from_secs( - self.highest_seen_timestamp.load(Ordering::Acquire) as u64 - ); - #[cfg(feature = "no-std")] - let builder = invoice_request.respond_using_derived_keys_no_std( - payment_paths, payment_hash, created_at - ); - match builder.and_then(|b| b.allow_mpp().build_and_sign(secp_ctx)) { - Ok(invoice) => Some(OffersMessage::Invoice(invoice)), - Err(error) => Some(OffersMessage::InvoiceError(error.into())), - } - }, - Ok((payment_hash, payment_secret)) => { - let payment_paths = vec![ - self.create_one_hop_blinded_payment_path(payment_secret), - ]; - #[cfg(not(feature = "no-std"))] - let builder = invoice_request.respond_with(payment_paths, payment_hash); - #[cfg(feature = "no-std")] - let created_at = Duration::from_secs( - self.highest_seen_timestamp.load(Ordering::Acquire) as u64 - ); - #[cfg(feature = "no-std")] - let builder = invoice_request.respond_with_no_std( - payment_paths, payment_hash, created_at - ); - let response = builder.and_then(|builder| builder.allow_mpp().build()) - .map_err(|e| OffersMessage::InvoiceError(e.into())) - .and_then(|invoice| - match invoice.sign(|invoice| self.node_signer.sign_bolt12_invoice(invoice)) { - Ok(invoice) => Ok(OffersMessage::Invoice(invoice)), - Err(SignError::Signing(())) => Err(OffersMessage::InvoiceError( - InvoiceError::from_string("Failed signing invoice".to_string()) - )), - Err(SignError::Verification(_)) => Err(OffersMessage::InvoiceError( - InvoiceError::from_string("Failed invoice signature verification".to_string()) - )), - }); - match response { - Ok(invoice) => Some(invoice), - Err(error) => Some(error), - } + let relative_expiry = DEFAULT_RELATIVE_EXPIRY.as_secs() as u32; + let (payment_hash, payment_secret) = match self.create_inbound_payment( + Some(amount_msats), relative_expiry, None + ) { + Ok((payment_hash, payment_secret)) => (payment_hash, payment_secret), + Err(()) => { + let error = Bolt12SemanticError::InvalidAmount; + return Some(OffersMessage::InvoiceError(error.into())); }, + }; + + let payment_paths = match self.create_blinded_payment_paths( + amount_msats, payment_secret + ) { + Ok(payment_paths) => payment_paths, Err(()) => { - Some(OffersMessage::InvoiceError(Bolt12SemanticError::InvalidAmount.into())) + let error = Bolt12SemanticError::MissingPaths; + return Some(OffersMessage::InvoiceError(error.into())); }, + }; + + #[cfg(not(feature = "std"))] + let created_at = Duration::from_secs( + self.highest_seen_timestamp.load(Ordering::Acquire) as u64 + ); + + if invoice_request.keys.is_some() { + #[cfg(feature = "std")] + let builder = invoice_request.respond_using_derived_keys( + payment_paths, payment_hash + ); + #[cfg(not(feature = "std"))] + let builder = invoice_request.respond_using_derived_keys_no_std( + payment_paths, payment_hash, created_at + ); + let builder: Result, _> = + 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())), + } + } else { + #[cfg(feature = "std")] + let builder = invoice_request.respond_with(payment_paths, payment_hash); + #[cfg(not(feature = "std"))] + let builder = invoice_request.respond_with_no_std( + payment_paths, payment_hash, created_at + ); + let builder: Result, _> = + 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| { + #[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( + InvoiceError::from_string("Failed signing invoice".to_string()) + )), + Err(SignError::Verification(_)) => Err(OffersMessage::InvoiceError( + InvoiceError::from_string("Failed invoice signature verification".to_string()) + )), + } + }); + match response { + Ok(invoice) => Some(invoice), + Err(error) => Some(error), + } } }, OffersMessage::Invoice(invoice) => { @@ -9281,6 +9404,7 @@ pub fn provided_init_features(config: &UserConfig) -> InitFeatures { features.set_channel_type_optional(); features.set_scid_privacy_optional(); features.set_zero_conf_optional(); + features.set_route_blinding_optional(); if config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx { features.set_anchors_zero_fee_htlc_tx_optional(); } @@ -9426,6 +9550,7 @@ impl_writeable_tlv_based!(PhantomRouteHints, { impl_writeable_tlv_based!(BlindedForward, { (0, inbound_blinding_point, required), + (1, failure, (default_value, BlindedFailure::FromIntroductionNode)), }); impl_writeable_tlv_based_enum!(PendingHTLCRouting, @@ -10207,7 +10332,7 @@ where let channel_count: u64 = Readable::read(reader)?; let mut funding_txo_set = HashSet::with_capacity(cmp::min(channel_count as usize, 128)); let mut funded_peer_channels: HashMap>> = HashMap::with_capacity(cmp::min(channel_count as usize, 128)); - let mut id_to_peer = HashMap::with_capacity(cmp::min(channel_count as usize, 128)); + let mut outpoint_to_peer = HashMap::with_capacity(cmp::min(channel_count as usize, 128)); let mut short_to_chan_info = HashMap::with_capacity(cmp::min(channel_count as usize, 128)); let mut channel_closures = VecDeque::new(); let mut close_background_events = Vec::new(); @@ -10242,7 +10367,7 @@ where log_error!(logger, " The ChannelMonitor for channel {} is at counterparty commitment transaction number {} but the ChannelManager is at counterparty commitment transaction number {}.", &channel.context.channel_id(), monitor.get_cur_counterparty_commitment_number(), channel.get_cur_counterparty_commitment_transaction_number()); } - let mut shutdown_result = channel.context.force_shutdown(true); + let mut shutdown_result = channel.context.force_shutdown(true, ClosureReason::OutdatedChannelManager); if shutdown_result.unbroadcasted_batch_funding_txid.is_some() { return Err(DecodeError::InvalidValue); } @@ -10258,6 +10383,7 @@ where reason: ClosureReason::OutdatedChannelManager, counterparty_node_id: Some(channel.context.get_counterparty_node_id()), channel_capacity_sats: Some(channel.context.get_value_satoshis()), + channel_funding_txo: channel.context.get_funding_txo(), }, None)); for (channel_htlc_source, payment_hash) in channel.inflight_htlc_sources() { let mut found_htlc = false; @@ -10285,8 +10411,8 @@ where if let Some(short_channel_id) = channel.context.get_short_channel_id() { short_to_chan_info.insert(short_channel_id, (channel.context.get_counterparty_node_id(), channel.context.channel_id())); } - if channel.context.is_funding_broadcast() { - id_to_peer.insert(channel.context.channel_id(), channel.context.get_counterparty_node_id()); + if let Some(funding_txo) = channel.context.get_funding_txo() { + outpoint_to_peer.insert(funding_txo, channel.context.get_counterparty_node_id()); } match funded_peer_channels.entry(channel.context.get_counterparty_node_id()) { hash_map::Entry::Occupied(mut entry) => { @@ -10304,13 +10430,14 @@ where // If we were persisted and shut down while the initial ChannelMonitor persistence // was in-progress, we never broadcasted the funding transaction and can still // safely discard the channel. - let _ = channel.context.force_shutdown(false); + let _ = channel.context.force_shutdown(false, ClosureReason::DisconnectedPeer); channel_closures.push_back((events::Event::ChannelClosed { channel_id: channel.context.channel_id(), user_channel_id: channel.context.get_user_id(), reason: ClosureReason::DisconnectedPeer, counterparty_node_id: Some(channel.context.get_counterparty_node_id()), channel_capacity_sats: Some(channel.context.get_value_satoshis()), + channel_funding_txo: channel.context.get_funding_txo(), }, None)); } else { log_error!(logger, "Missing ChannelMonitor for channel {} needed by ChannelManager.", &channel.context.channel_id()); @@ -10329,6 +10456,7 @@ where &funding_txo.to_channel_id()); let monitor_update = ChannelMonitorUpdate { update_id: CLOSED_CHANNEL_UPDATE_ID, + counterparty_node_id: None, updates: vec![ChannelMonitorUpdateStep::ChannelForceClosed { should_broadcast: true }], }; close_background_events.push(BackgroundEvent::ClosedMonitorUpdateRegeneratedOnStartup((*funding_txo, monitor_update))); @@ -10620,7 +10748,7 @@ where // We only rebuild the pending payments map if we were most recently serialized by // 0.0.102+ for (_, monitor) in args.channel_monitors.iter() { - let counterparty_opt = id_to_peer.get(&monitor.get_funding_txo().0.to_channel_id()); + let counterparty_opt = outpoint_to_peer.get(&monitor.get_funding_txo().0); if counterparty_opt.is_none() { let logger = WithChannelMonitor::from(&args.logger, monitor); for (htlc_source, (htlc, _)) in monitor.get_pending_or_resolved_outbound_htlcs() { @@ -10913,7 +11041,7 @@ where // without the new monitor persisted - we'll end up right back here on // restart. let previous_channel_id = claimable_htlc.prev_hop.outpoint.to_channel_id(); - if let Some(peer_node_id) = id_to_peer.get(&previous_channel_id){ + if let Some(peer_node_id) = outpoint_to_peer.get(&claimable_htlc.prev_hop.outpoint) { let peer_state_mutex = per_peer_state.get(peer_node_id).unwrap(); let mut peer_state_lock = peer_state_mutex.lock().unwrap(); let peer_state = &mut *peer_state_lock; @@ -10991,7 +11119,7 @@ where forward_htlcs: Mutex::new(forward_htlcs), claimable_payments: Mutex::new(ClaimablePayments { claimable_payments, pending_claiming_payments: pending_claiming_payments.unwrap() }), outbound_scid_aliases: Mutex::new(outbound_scid_aliases), - id_to_peer: Mutex::new(id_to_peer), + outpoint_to_peer: Mutex::new(outpoint_to_peer), short_to_chan_info: FairRwLock::new(short_to_chan_info), fake_scid_rand_bytes: fake_scid_rand_bytes.unwrap(), @@ -11610,8 +11738,8 @@ mod tests { } #[test] - fn test_id_to_peer_coverage() { - // Test that the `ChannelManager:id_to_peer` contains channels which have been assigned + fn test_outpoint_to_peer_coverage() { + // Test that the `ChannelManager:outpoint_to_peer` contains channels which have been assigned // a `channel_id` (i.e. have had the funding tx created), and that they are removed once // the channel is successfully closed. let chanmon_cfgs = create_chanmon_cfgs(2); @@ -11625,42 +11753,42 @@ mod tests { let accept_channel = get_event_msg!(nodes[1], MessageSendEvent::SendAcceptChannel, nodes[0].node.get_our_node_id()); nodes[0].node.handle_accept_channel(&nodes[1].node.get_our_node_id(), &accept_channel); - let (temporary_channel_id, tx, _funding_output) = create_funding_transaction(&nodes[0], &nodes[1].node.get_our_node_id(), 1_000_000, 42); + let (temporary_channel_id, tx, funding_output) = create_funding_transaction(&nodes[0], &nodes[1].node.get_our_node_id(), 1_000_000, 42); let channel_id = ChannelId::from_bytes(tx.txid().to_byte_array()); { - // Ensure that the `id_to_peer` map is empty until either party has received the + // Ensure that the `outpoint_to_peer` map is empty until either party has received the // funding transaction, and have the real `channel_id`. - assert_eq!(nodes[0].node.id_to_peer.lock().unwrap().len(), 0); - assert_eq!(nodes[1].node.id_to_peer.lock().unwrap().len(), 0); + assert_eq!(nodes[0].node.outpoint_to_peer.lock().unwrap().len(), 0); + assert_eq!(nodes[1].node.outpoint_to_peer.lock().unwrap().len(), 0); } nodes[0].node.funding_transaction_generated(&temporary_channel_id, &nodes[1].node.get_our_node_id(), tx.clone()).unwrap(); { - // Assert that `nodes[0]`'s `id_to_peer` map is populated with the channel as soon as + // Assert that `nodes[0]`'s `outpoint_to_peer` map is populated with the channel as soon as // as it has the funding transaction. - let nodes_0_lock = nodes[0].node.id_to_peer.lock().unwrap(); + let nodes_0_lock = nodes[0].node.outpoint_to_peer.lock().unwrap(); assert_eq!(nodes_0_lock.len(), 1); - assert!(nodes_0_lock.contains_key(&channel_id)); + assert!(nodes_0_lock.contains_key(&funding_output)); } - assert_eq!(nodes[1].node.id_to_peer.lock().unwrap().len(), 0); + assert_eq!(nodes[1].node.outpoint_to_peer.lock().unwrap().len(), 0); let funding_created_msg = get_event_msg!(nodes[0], MessageSendEvent::SendFundingCreated, nodes[1].node.get_our_node_id()); nodes[1].node.handle_funding_created(&nodes[0].node.get_our_node_id(), &funding_created_msg); { - let nodes_0_lock = nodes[0].node.id_to_peer.lock().unwrap(); + let nodes_0_lock = nodes[0].node.outpoint_to_peer.lock().unwrap(); assert_eq!(nodes_0_lock.len(), 1); - assert!(nodes_0_lock.contains_key(&channel_id)); + assert!(nodes_0_lock.contains_key(&funding_output)); } expect_channel_pending_event(&nodes[1], &nodes[0].node.get_our_node_id()); { - // Assert that `nodes[1]`'s `id_to_peer` map is populated with the channel as soon as - // as it has the funding transaction. - let nodes_1_lock = nodes[1].node.id_to_peer.lock().unwrap(); + // Assert that `nodes[1]`'s `outpoint_to_peer` map is populated with the channel as + // soon as it has the funding transaction. + let nodes_1_lock = nodes[1].node.outpoint_to_peer.lock().unwrap(); assert_eq!(nodes_1_lock.len(), 1); - assert!(nodes_1_lock.contains_key(&channel_id)); + assert!(nodes_1_lock.contains_key(&funding_output)); } check_added_monitors!(nodes[1], 1); let funding_signed = get_event_msg!(nodes[1], MessageSendEvent::SendFundingSigned, nodes[0].node.get_our_node_id()); @@ -11679,23 +11807,23 @@ mod tests { let closing_signed_node_0 = get_event_msg!(nodes[0], MessageSendEvent::SendClosingSigned, nodes[1].node.get_our_node_id()); nodes[1].node.handle_closing_signed(&nodes[0].node.get_our_node_id(), &closing_signed_node_0); { - // Assert that the channel is kept in the `id_to_peer` map for both nodes until the + // Assert that the channel is kept in the `outpoint_to_peer` map for both nodes until the // channel can be fully closed by both parties (i.e. no outstanding htlcs exists, the // fee for the closing transaction has been negotiated and the parties has the other // party's signature for the fee negotiated closing transaction.) - let nodes_0_lock = nodes[0].node.id_to_peer.lock().unwrap(); + let nodes_0_lock = nodes[0].node.outpoint_to_peer.lock().unwrap(); assert_eq!(nodes_0_lock.len(), 1); - assert!(nodes_0_lock.contains_key(&channel_id)); + assert!(nodes_0_lock.contains_key(&funding_output)); } { // At this stage, `nodes[1]` has proposed a fee for the closing transaction in the // `handle_closing_signed` call above. As `nodes[1]` has not yet received the signature // from `nodes[0]` for the closing transaction with the proposed fee, the channel is - // kept in the `nodes[1]`'s `id_to_peer` map. - let nodes_1_lock = nodes[1].node.id_to_peer.lock().unwrap(); + // kept in the `nodes[1]`'s `outpoint_to_peer` map. + let nodes_1_lock = nodes[1].node.outpoint_to_peer.lock().unwrap(); assert_eq!(nodes_1_lock.len(), 1); - assert!(nodes_1_lock.contains_key(&channel_id)); + assert!(nodes_1_lock.contains_key(&funding_output)); } nodes[0].node.handle_closing_signed(&nodes[1].node.get_our_node_id(), &get_event_msg!(nodes[1], MessageSendEvent::SendClosingSigned, nodes[0].node.get_our_node_id())); @@ -11703,24 +11831,24 @@ mod tests { // `nodes[0]` accepts `nodes[1]`'s proposed fee for the closing transaction, and // therefore has all it needs to fully close the channel (both signatures for the // closing transaction). - // Assert that the channel is removed from `nodes[0]`'s `id_to_peer` map as it can be + // Assert that the channel is removed from `nodes[0]`'s `outpoint_to_peer` map as it can be // fully closed by `nodes[0]`. - assert_eq!(nodes[0].node.id_to_peer.lock().unwrap().len(), 0); + assert_eq!(nodes[0].node.outpoint_to_peer.lock().unwrap().len(), 0); - // Assert that the channel is still in `nodes[1]`'s `id_to_peer` map, as `nodes[1]` + // Assert that the channel is still in `nodes[1]`'s `outpoint_to_peer` map, as `nodes[1]` // doesn't have `nodes[0]`'s signature for the closing transaction yet. - let nodes_1_lock = nodes[1].node.id_to_peer.lock().unwrap(); + let nodes_1_lock = nodes[1].node.outpoint_to_peer.lock().unwrap(); assert_eq!(nodes_1_lock.len(), 1); - assert!(nodes_1_lock.contains_key(&channel_id)); + assert!(nodes_1_lock.contains_key(&funding_output)); } let (_nodes_0_update, closing_signed_node_0) = get_closing_signed_broadcast!(nodes[0].node, nodes[1].node.get_our_node_id()); nodes[1].node.handle_closing_signed(&nodes[0].node.get_our_node_id(), &closing_signed_node_0.unwrap()); { - // Assert that the channel has now been removed from both parties `id_to_peer` map once + // Assert that the channel has now been removed from both parties `outpoint_to_peer` map once // they both have everything required to fully close the channel. - assert_eq!(nodes[1].node.id_to_peer.lock().unwrap().len(), 0); + assert_eq!(nodes[1].node.outpoint_to_peer.lock().unwrap().len(), 0); } let (_nodes_1_update, _none) = get_closing_signed_broadcast!(nodes[1].node, nodes[0].node.get_our_node_id()); @@ -12049,8 +12177,8 @@ mod tests { let sender_intended_amt_msat = 100; let extra_fee_msat = 10; let hop_data = msgs::InboundOnionPayload::Receive { - amt_msat: 100, - outgoing_cltv_value: 42, + sender_intended_htlc_amt_msat: 100, + cltv_expiry_height: 42, payment_metadata: None, keysend_preimage: None, payment_data: Some(msgs::FinalOnionHopData { @@ -12061,7 +12189,7 @@ mod tests { // 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(); - if let Err(crate::ln::channelmanager::InboundOnionErr { err_code, .. }) = + 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), current_height, node[0].node.default_configuration.accept_mpp_keysend) @@ -12071,8 +12199,8 @@ mod tests { // If amt_received + extra_fee is equal to the sender intended amount, we're fine. let hop_data = msgs::InboundOnionPayload::Receive { // This is the same payload as above, InboundOnionPayload doesn't implement Clone - amt_msat: 100, - outgoing_cltv_value: 42, + sender_intended_htlc_amt_msat: 100, + cltv_expiry_height: 42, payment_metadata: None, keysend_preimage: None, payment_data: Some(msgs::FinalOnionHopData { @@ -12095,8 +12223,8 @@ mod tests { let current_height: u32 = node[0].node.best_block.read().unwrap().height(); let result = create_recv_pending_htlc_info(msgs::InboundOnionPayload::Receive { - amt_msat: 100, - outgoing_cltv_value: 22, + sender_intended_htlc_amt_msat: 100, + cltv_expiry_height: 22, payment_metadata: None, keysend_preimage: None, payment_data: Some(msgs::FinalOnionHopData { @@ -12414,7 +12542,7 @@ pub mod bench { use bitcoin::blockdata::locktime::absolute::LockTime; use bitcoin::hashes::Hash; use bitcoin::hashes::sha256::Hash as Sha256; - use bitcoin::{Block, Transaction, TxOut}; + use bitcoin::{Transaction, TxOut}; use crate::sync::{Arc, Mutex, RwLock}; @@ -12454,7 +12582,7 @@ pub mod bench { let fee_estimator = test_utils::TestFeeEstimator { sat_per_kw: Mutex::new(253) }; let logger_a = test_utils::TestLogger::with_id("node a".to_owned()); let scorer = RwLock::new(test_utils::TestScorer::new()); - let router = test_utils::TestRouter::new(Arc::new(NetworkGraph::new(network, &logger_a)), &scorer); + let router = test_utils::TestRouter::new(Arc::new(NetworkGraph::new(network, &logger_a)), &logger_a, &scorer); let mut config: UserConfig = Default::default(); config.channel_config.max_dust_htlc_exposure = MaxDustHTLCExposure::FeeRateMultiplier(5_000_000 / 253);