Skip to content

Commit 240f24e

Browse files
committed
Force-close if finish closing_signed negotiation takes a full minute
1 parent 47ba1ef commit 240f24e

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
@@ -2687,46 +2687,56 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
26872687
let pending_msg_events = &mut channel_state.pending_msg_events;
26882688
let short_to_id = &mut channel_state.short_to_id;
26892689
channel_state.by_id.retain(|chan_id, chan| {
2690-
match chan.channel_update_status() {
2691-
ChannelUpdateStatus::Enabled if !chan.is_live() => chan.set_channel_update_status(ChannelUpdateStatus::DisabledStaged),
2692-
ChannelUpdateStatus::Disabled if chan.is_live() => chan.set_channel_update_status(ChannelUpdateStatus::EnabledStaged),
2693-
ChannelUpdateStatus::DisabledStaged if chan.is_live() => chan.set_channel_update_status(ChannelUpdateStatus::Enabled),
2694-
ChannelUpdateStatus::EnabledStaged if !chan.is_live() => chan.set_channel_update_status(ChannelUpdateStatus::Disabled),
2695-
ChannelUpdateStatus::DisabledStaged if !chan.is_live() => {
2696-
if let Ok(update) = self.get_channel_update_for_broadcast(&chan) {
2697-
pending_msg_events.push(events::MessageSendEvent::BroadcastChannelUpdate {
2698-
msg: update
2699-
});
2700-
}
2701-
should_persist = NotifyOption::DoPersist;
2702-
chan.set_channel_update_status(ChannelUpdateStatus::Disabled);
2703-
},
2704-
ChannelUpdateStatus::EnabledStaged if chan.is_live() => {
2705-
if let Ok(update) = self.get_channel_update_for_broadcast(&chan) {
2706-
pending_msg_events.push(events::MessageSendEvent::BroadcastChannelUpdate {
2707-
msg: update
2708-
});
2709-
}
2710-
should_persist = NotifyOption::DoPersist;
2711-
chan.set_channel_update_status(ChannelUpdateStatus::Enabled);
2712-
},
2713-
_ => {},
2714-
}
2715-
27162690
let counterparty_node_id = chan.get_counterparty_node_id();
2717-
let (retain_channel, chan_needs_persist, err) = self.update_channel_fee(short_to_id, pending_msg_events, chan_id, chan, new_feerate);
2691+
let (mut retain_channel, chan_needs_persist, err) = self.update_channel_fee(short_to_id, pending_msg_events, chan_id, chan, new_feerate);
27182692
if chan_needs_persist == NotifyOption::DoPersist { should_persist = NotifyOption::DoPersist; }
27192693
if err.is_err() {
27202694
handle_errors.push((err, counterparty_node_id));
27212695
}
2696+
2697+
if retain_channel {
2698+
if let Err(e) = chan.timer_check_closing_negotiation_progress() {
2699+
let (needs_close, err) = convert_chan_err!(self, e, short_to_id, chan, chan_id);
2700+
handle_errors.push((Err(err), chan.get_counterparty_node_id()));
2701+
if needs_close { retain_channel = false; }
2702+
}
2703+
}
2704+
2705+
if retain_channel {
2706+
match chan.channel_update_status() {
2707+
ChannelUpdateStatus::Enabled if !chan.is_live() => chan.set_channel_update_status(ChannelUpdateStatus::DisabledStaged),
2708+
ChannelUpdateStatus::Disabled if chan.is_live() => chan.set_channel_update_status(ChannelUpdateStatus::EnabledStaged),
2709+
ChannelUpdateStatus::DisabledStaged if chan.is_live() => chan.set_channel_update_status(ChannelUpdateStatus::Enabled),
2710+
ChannelUpdateStatus::EnabledStaged if !chan.is_live() => chan.set_channel_update_status(ChannelUpdateStatus::Disabled),
2711+
ChannelUpdateStatus::DisabledStaged 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::Disabled);
2719+
},
2720+
ChannelUpdateStatus::EnabledStaged if chan.is_live() => {
2721+
if let Ok(update) = self.get_channel_update_for_broadcast(&chan) {
2722+
pending_msg_events.push(events::MessageSendEvent::BroadcastChannelUpdate {
2723+
msg: update
2724+
});
2725+
}
2726+
should_persist = NotifyOption::DoPersist;
2727+
chan.set_channel_update_status(ChannelUpdateStatus::Enabled);
2728+
},
2729+
_ => {},
2730+
}
2731+
}
2732+
27222733
retain_channel
27232734
});
27242735
}
27252736

27262737
for (err, counterparty_node_id) in handle_errors.drain(..) {
27272738
let _ = handle_error!(self, err, counterparty_node_id);
27282739
}
2729-
27302740
should_persist
27312741
});
27322742

0 commit comments

Comments
 (0)