Skip to content

Commit

Permalink
Force close pending channels in internal_shutdown
Browse files Browse the repository at this point in the history
  • Loading branch information
dunxen committed Jul 18, 2023
1 parent ea515da commit b4881d9
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 32 deletions.
9 changes: 7 additions & 2 deletions lightning/src/events/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ impl_writeable_tlv_based_enum_upgradable!(PathFailure,
);

#[derive(Clone, Debug, PartialEq, Eq)]
/// The reason the channel was closed. See individual variants more details.
/// The reason the channel was closed. See individual variants for more details.
pub enum ClosureReason {
/// Closure generated from receiving a peer error message.
///
Expand Down Expand Up @@ -164,7 +164,10 @@ pub enum ClosureReason {
///
/// [`ChannelMonitor`]: crate::chain::channelmonitor::ChannelMonitor
/// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager
OutdatedChannelManager
OutdatedChannelManager,
/// The counterparty requested a cooperative close of a channel that had not been funded yet.
/// The channel has been immediately closed.
CounterpartyCoopClosedUnfundedChannel,
}

impl core::fmt::Display for ClosureReason {
Expand All @@ -184,6 +187,7 @@ impl core::fmt::Display for ClosureReason {
},
ClosureReason::DisconnectedPeer => f.write_str("the peer disconnected prior to the channel being funded"),
ClosureReason::OutdatedChannelManager => f.write_str("the ChannelManager read from disk was stale compared to ChannelMonitor(s)"),
ClosureReason::CounterpartyCoopClosedUnfundedChannel => f.write_str("the peer requested the prefunded channel be closed"),
}
}
}
Expand All @@ -197,6 +201,7 @@ impl_writeable_tlv_based_enum_upgradable!(ClosureReason,
(8, ProcessingError) => { (1, err, required) },
(10, DisconnectedPeer) => {},
(12, OutdatedChannelManager) => {},
(13, CounterpartyCoopClosedUnfundedChannel) => {},
);

/// Intended destination of a failed HTLC as indicated in [`Event::HTLCHandlingFailed`].
Expand Down
72 changes: 42 additions & 30 deletions lightning/src/ln/channelmanager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5530,38 +5530,50 @@ where
})?;
let mut peer_state_lock = peer_state_mutex.lock().unwrap();
let peer_state = &mut *peer_state_lock;
match peer_state.channel_by_id.entry(msg.channel_id.clone()) {
hash_map::Entry::Occupied(mut chan_entry) => {

if !chan_entry.get().received_shutdown() {
log_info!(self.logger, "Received a shutdown message from our counterparty for channel {}{}.",
log_bytes!(msg.channel_id),
if chan_entry.get().sent_shutdown() { " after we initiated shutdown" } else { "" });
}

let funding_txo_opt = chan_entry.get().context.get_funding_txo();
let (shutdown, monitor_update_opt, htlcs) = try_chan_entry!(self,
chan_entry.get_mut().shutdown(&self.signer_provider, &peer_state.latest_features, &msg), chan_entry);
dropped_htlcs = htlcs;
// TODO(dunxen): Fix this duplication when we switch to a single map with enums as per
// https://github.com/lightningdevkit/rust-lightning/issues/2422
if let hash_map::Entry::Occupied(chan_entry) = peer_state.outbound_v1_channel_by_id.entry(msg.channel_id.clone()) {
log_error!(self.logger, "Immediately closing prefunded channel {} as peer asked to cooperatively shut it down (which is unnecessary)", log_bytes!(&msg.channel_id[..]));
self.issue_channel_close_events(&chan_entry.get().context, ClosureReason::CounterpartyCoopClosedUnfundedChannel);
let mut chan = remove_channel!(self, chan_entry);
self.finish_force_close_channel(chan.context.force_shutdown(false));
return Ok(());
} else if let hash_map::Entry::Occupied(chan_entry) = peer_state.inbound_v1_channel_by_id.entry(msg.channel_id.clone()) {
log_error!(self.logger, "Immediately closing prefunded channel {} as peer asked to cooperatively shut it down (which is unnecessary)", log_bytes!(&msg.channel_id[..]));
self.issue_channel_close_events(&chan_entry.get().context, ClosureReason::CounterpartyCoopClosedUnfundedChannel);
let mut chan = remove_channel!(self, chan_entry);
self.finish_force_close_channel(chan.context.force_shutdown(false));
return Ok(());
} else if let hash_map::Entry::Occupied(mut chan_entry) = peer_state.channel_by_id.entry(msg.channel_id.clone()) {
if !chan_entry.get().received_shutdown() {
log_info!(self.logger, "Received a shutdown message from our counterparty for channel {}{}.",
log_bytes!(msg.channel_id),
if chan_entry.get().sent_shutdown() { " after we initiated shutdown" } else { "" });
}

if let Some(msg) = shutdown {
// We can send the `shutdown` message before updating the `ChannelMonitor`
// here as we don't need the monitor update to complete until we send a
// `shutdown_signed`, which we'll delay if we're pending a monitor update.
peer_state.pending_msg_events.push(events::MessageSendEvent::SendShutdown {
node_id: *counterparty_node_id,
msg,
});
}
let funding_txo_opt = chan_entry.get().context.get_funding_txo();
let (shutdown, monitor_update_opt, htlcs) = try_chan_entry!(self,
chan_entry.get_mut().shutdown(&self.signer_provider, &peer_state.latest_features, &msg), chan_entry);
dropped_htlcs = htlcs;

if let Some(msg) = shutdown {
// We can send the `shutdown` message before updating the `ChannelMonitor`
// here as we don't need the monitor update to complete until we send a
// `shutdown_signed`, which we'll delay if we're pending a monitor update.
peer_state.pending_msg_events.push(events::MessageSendEvent::SendShutdown {
node_id: *counterparty_node_id,
msg,
});
}

// Update the monitor with the shutdown script if necessary.
if let Some(monitor_update) = monitor_update_opt {
break handle_new_monitor_update!(self, funding_txo_opt.unwrap(), monitor_update,
peer_state_lock, peer_state, per_peer_state, chan_entry).map(|_| ());
}
break Ok(());
},
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))
// Update the monitor with the shutdown script if necessary.
if let Some(monitor_update) = monitor_update_opt {
break handle_new_monitor_update!(self, funding_txo_opt.unwrap(), monitor_update,
peer_state_lock, peer_state, per_peer_state, chan_entry).map(|_| ());
}
break Ok(());
} 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))
}
};
for htlc_source in dropped_htlcs.drain(..) {
Expand Down

0 comments on commit b4881d9

Please sign in to comment.