Skip to content

Commit dcaf1d4

Browse files
committed
Force-close if finish closing_signed negotiation takes a full minute
1 parent b6fa68c commit dcaf1d4

File tree

2 files changed

+76
-36
lines changed

2 files changed

+76
-36
lines changed

lightning/src/ln/channel.rs

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -246,8 +246,6 @@ enum ChannelState {
246246
RemoteShutdownSent = 1 << 10,
247247
/// Flag which is set on ChannelFunded or FundingSent after sending a shutdown message. At this
248248
/// point, we may not add any new HTLCs to the channel.
249-
/// TODO: Investigate some kind of timeout mechanism by which point the remote end must provide
250-
/// us their shutdown.
251249
LocalShutdownSent = 1 << 11,
252250
/// We've successfully negotiated a closing_signed dance. At this point ChannelManager is about
253251
/// to drop us, but we store this anyway.
@@ -486,6 +484,13 @@ pub(super) struct Channel<Signer: Sign> {
486484
commitment_secrets: CounterpartyCommitmentSecrets,
487485

488486
channel_update_status: ChannelUpdateStatus,
487+
/// Once we reach `closing_negotiation_ready`, we set this, indicating if closing_signed does
488+
/// not complete within a single timer tick (one minute), we should force-close the channel.
489+
/// This prevents us from keeping unusable channels around forever if our counterparty wishes
490+
/// to DoS us.
491+
/// Note that this field is reset to false on deserialization to give us a chance to connect to
492+
/// our peer and start the closing_signed negotiation fresh.
493+
closing_signed_in_flight: bool,
489494

490495
/// Our counterparty's channel_announcement signatures provided in announcement_signatures.
491496
/// This can be used to rebroadcast the channel_announcement message later.
@@ -723,6 +728,7 @@ impl<Signer: Sign> Channel<Signer> {
723728
commitment_secrets: CounterpartyCommitmentSecrets::new(),
724729

725730
channel_update_status: ChannelUpdateStatus::Enabled,
731+
closing_signed_in_flight: false,
726732

727733
announcement_sigs: None,
728734

@@ -984,6 +990,7 @@ impl<Signer: Sign> Channel<Signer> {
984990
commitment_secrets: CounterpartyCommitmentSecrets::new(),
985991

986992
channel_update_status: ChannelUpdateStatus::Enabled,
993+
closing_signed_in_flight: false,
987994

988995
announcement_sigs: None,
989996

@@ -3321,16 +3328,38 @@ impl<Signer: Sign> Channel<Signer> {
33213328
self.closing_fee_limits = Some((proposed_total_fee_satoshis, proposed_max_total_fee_satoshis));
33223329
}
33233330

3331+
/// Returns true if we're ready to commence the closing_signed negotiation phase. This is true
3332+
/// after both sides have exchanged a `shutdown` message and all HTLCs have been drained. At
3333+
/// this point if we're the funder we should send the initial closing_signed, and in any case
3334+
/// shutdown should complete within a reasonable timeframe.
3335+
fn closing_negotiation_ready(&self) -> bool {
3336+
self.pending_inbound_htlcs.is_empty() && self.pending_outbound_htlcs.is_empty() &&
3337+
self.channel_state &
3338+
(BOTH_SIDES_SHUTDOWN_MASK | ChannelState::AwaitingRemoteRevoke as u32 |
3339+
ChannelState::PeerDisconnected as u32 | ChannelState::MonitorUpdateFailed as u32)
3340+
== BOTH_SIDES_SHUTDOWN_MASK &&
3341+
self.pending_update_fee.is_none()
3342+
}
3343+
3344+
/// Checks if the closing_signed negotiation is making appropriate progress, possibly returning
3345+
/// an Err if no progress is being made and the channel should be force-closed instead.
3346+
/// Should be called on a one-minute timer.
3347+
pub fn timer_check_closing_negotiation_progress(&mut self) -> Result<(), ChannelError> {
3348+
if self.closing_negotiation_ready() {
3349+
if self.closing_signed_in_flight {
3350+
return Err(ChannelError::Close("closing_signed negotiation failed to finish within one minute".to_owned()));
3351+
} else {
3352+
self.closing_signed_in_flight = true;
3353+
}
3354+
}
3355+
Ok(())
3356+
}
3357+
33243358
pub fn maybe_propose_first_closing_signed<F: Deref, L: Deref>(&mut self, fee_estimator: &F, logger: &L)
33253359
-> Result<(Option<msgs::ClosingSigned>, Option<Transaction>), ChannelError>
33263360
where F::Target: FeeEstimator, L::Target: Logger
33273361
{
3328-
if !self.pending_inbound_htlcs.is_empty() || !self.pending_outbound_htlcs.is_empty() ||
3329-
self.channel_state &
3330-
(BOTH_SIDES_SHUTDOWN_MASK | ChannelState::AwaitingRemoteRevoke as u32 |
3331-
ChannelState::PeerDisconnected as u32 | ChannelState::MonitorUpdateFailed as u32)
3332-
!= BOTH_SIDES_SHUTDOWN_MASK ||
3333-
self.last_sent_closing_fee.is_some() || self.pending_update_fee.is_some() {
3362+
if self.last_sent_closing_fee.is_some() || !self.closing_negotiation_ready() {
33343363
return Ok((None, None));
33353364
}
33363365

@@ -5338,6 +5367,7 @@ impl<'a, Signer: Sign, K: Deref> ReadableArgs<&'a K> for Channel<Signer>
53385367
commitment_secrets,
53395368

53405369
channel_update_status,
5370+
closing_signed_in_flight: false,
53415371

53425372
announcement_sigs,
53435373

lightning/src/ln/channelmanager.rs

Lines changed: 38 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2694,46 +2694,56 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
26942694
let pending_msg_events = &mut channel_state.pending_msg_events;
26952695
let short_to_id = &mut channel_state.short_to_id;
26962696
channel_state.by_id.retain(|chan_id, chan| {
2697-
match chan.channel_update_status() {
2698-
ChannelUpdateStatus::Enabled if !chan.is_live() => chan.set_channel_update_status(ChannelUpdateStatus::DisabledStaged),
2699-
ChannelUpdateStatus::Disabled if chan.is_live() => chan.set_channel_update_status(ChannelUpdateStatus::EnabledStaged),
2700-
ChannelUpdateStatus::DisabledStaged if chan.is_live() => chan.set_channel_update_status(ChannelUpdateStatus::Enabled),
2701-
ChannelUpdateStatus::EnabledStaged if !chan.is_live() => chan.set_channel_update_status(ChannelUpdateStatus::Disabled),
2702-
ChannelUpdateStatus::DisabledStaged if !chan.is_live() => {
2703-
if let Ok(update) = self.get_channel_update_for_broadcast(&chan) {
2704-
pending_msg_events.push(events::MessageSendEvent::BroadcastChannelUpdate {
2705-
msg: update
2706-
});
2707-
}
2708-
should_persist = NotifyOption::DoPersist;
2709-
chan.set_channel_update_status(ChannelUpdateStatus::Disabled);
2710-
},
2711-
ChannelUpdateStatus::EnabledStaged if chan.is_live() => {
2712-
if let Ok(update) = self.get_channel_update_for_broadcast(&chan) {
2713-
pending_msg_events.push(events::MessageSendEvent::BroadcastChannelUpdate {
2714-
msg: update
2715-
});
2716-
}
2717-
should_persist = NotifyOption::DoPersist;
2718-
chan.set_channel_update_status(ChannelUpdateStatus::Enabled);
2719-
},
2720-
_ => {},
2721-
}
2722-
27232697
let counterparty_node_id = chan.get_counterparty_node_id();
2724-
let (retain_channel, chan_needs_persist, err) = self.update_channel_fee(short_to_id, pending_msg_events, chan_id, chan, new_feerate);
2698+
let (mut retain_channel, chan_needs_persist, err) = self.update_channel_fee(short_to_id, pending_msg_events, chan_id, chan, new_feerate);
27252699
if chan_needs_persist == NotifyOption::DoPersist { should_persist = NotifyOption::DoPersist; }
27262700
if err.is_err() {
27272701
handle_errors.push((err, counterparty_node_id));
27282702
}
2703+
2704+
if retain_channel {
2705+
if let Err(e) = chan.timer_check_closing_negotiation_progress() {
2706+
let (needs_close, err) = convert_chan_err!(self, e, short_to_id, chan, chan_id);
2707+
handle_errors.push((Err(err), chan.get_counterparty_node_id()));
2708+
if needs_close { retain_channel = false; }
2709+
}
2710+
}
2711+
2712+
if retain_channel {
2713+
match chan.channel_update_status() {
2714+
ChannelUpdateStatus::Enabled if !chan.is_live() => chan.set_channel_update_status(ChannelUpdateStatus::DisabledStaged),
2715+
ChannelUpdateStatus::Disabled if chan.is_live() => chan.set_channel_update_status(ChannelUpdateStatus::EnabledStaged),
2716+
ChannelUpdateStatus::DisabledStaged if chan.is_live() => chan.set_channel_update_status(ChannelUpdateStatus::Enabled),
2717+
ChannelUpdateStatus::EnabledStaged if !chan.is_live() => chan.set_channel_update_status(ChannelUpdateStatus::Disabled),
2718+
ChannelUpdateStatus::DisabledStaged if !chan.is_live() => {
2719+
if let Ok(update) = self.get_channel_update_for_broadcast(&chan) {
2720+
pending_msg_events.push(events::MessageSendEvent::BroadcastChannelUpdate {
2721+
msg: update
2722+
});
2723+
}
2724+
should_persist = NotifyOption::DoPersist;
2725+
chan.set_channel_update_status(ChannelUpdateStatus::Disabled);
2726+
},
2727+
ChannelUpdateStatus::EnabledStaged if chan.is_live() => {
2728+
if let Ok(update) = self.get_channel_update_for_broadcast(&chan) {
2729+
pending_msg_events.push(events::MessageSendEvent::BroadcastChannelUpdate {
2730+
msg: update
2731+
});
2732+
}
2733+
should_persist = NotifyOption::DoPersist;
2734+
chan.set_channel_update_status(ChannelUpdateStatus::Enabled);
2735+
},
2736+
_ => {},
2737+
}
2738+
}
2739+
27292740
retain_channel
27302741
});
27312742
}
27322743

27332744
for (err, counterparty_node_id) in handle_errors.drain(..) {
27342745
let _ = handle_error!(self, err, counterparty_node_id);
27352746
}
2736-
27372747
should_persist
27382748
});
27392749

0 commit comments

Comments
 (0)