Skip to content

Commit 37ef2e5

Browse files
author
Antoine Riard
committed
Add auto-close if outbound feerate don't complete
According to the lightning security model, commitment transactions must offer a compelling feerate at anytime to maximize odds of block inclusion, and thus claim on-chain time-sensitive HTLCs. This fee efficiency is currently guarantee through the update_fee mechanism, a cooperative update of the channel feerate. However, if the counterparty is malicious or offline, this update might not succeed, therefore exposing our balance at risk. To lighten this risk, we add a new "auto-close" mechanism. If we detect that a update_fee should have been committed but never effectively finalized, we start a timer. At expiration, the channel is force-close, if they're pending *included HTLCs. We choose 60 ticks as "auto-close" timer duration. The mechanism can be disabled through `should_autoclose` (default: true)
1 parent 1a74367 commit 37ef2e5

File tree

3 files changed

+130
-15
lines changed

3 files changed

+130
-15
lines changed

lightning/src/ln/channel.rs

+93-11
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,7 @@ struct HTLCStats {
295295
on_holder_tx_dust_exposure_msat: u64,
296296
holding_cell_msat: u64,
297297
on_holder_tx_holding_cell_htlcs_count: u32, // dust HTLCs *non*-included
298+
on_holder_tx_included_htlcs: u32,
298299
}
299300

300301
/// An enum gathering stats on commitment transaction, either local or remote.
@@ -413,6 +414,12 @@ pub(crate) const CONCURRENT_INBOUND_HTLC_FEE_BUFFER: u32 = 2;
413414
/// transaction (not counting the value of the HTLCs themselves).
414415
pub(crate) const MIN_AFFORDABLE_HTLC_COUNT: usize = 4;
415416

417+
/// If after this value tick periods, the channel feerate isn't satisfying, we auto-close the
418+
/// channel and goes on-chain to avoid unsafe situations where a commitment transaction with
419+
/// time-sensitive outputs won't confirm due to staling feerate too far away from the upper feerate
420+
/// groups of network mempools.
421+
const AUTOCLOSE_TIMEOUT: u16 = 60;
422+
416423
// TODO: We should refactor this to be an Inbound/OutboundChannel until initial setup handshaking
417424
// has been completed, and then turn into a Channel to get compiler-time enforcement of things like
418425
// calling channel_id() before we're set up or things like get_outbound_funding_signed on an
@@ -435,6 +442,14 @@ pub(super) struct Channel<Signer: Sign> {
435442

436443
latest_monitor_update_id: u64,
437444

445+
// Auto-close timer, if the channel is outbound, we sent a `update_fee`, and we didn't
446+
// receive a RAA from counterparty committing this state after `AUTOCLOSE_TIMEOUT` periods,
447+
// this channel must be force-closed.
448+
// If the channel is inbound, it has been observed the channel feerate should increase,
449+
// and we didn't receive a RAA from counterparty committing an `update_fee` after
450+
// `AUTOCLOSE_TIMEOUT` periods, this channel must be force-closed.
451+
autoclose_timer: u16,
452+
438453
holder_signer: Signer,
439454
shutdown_scriptpubkey: Option<ShutdownScript>,
440455
destination_script: Script,
@@ -747,6 +762,8 @@ impl<Signer: Sign> Channel<Signer> {
747762

748763
latest_monitor_update_id: 0,
749764

765+
autoclose_timer: 0,
766+
750767
holder_signer,
751768
shutdown_scriptpubkey,
752769
destination_script: keys_provider.get_destination_script(),
@@ -1037,6 +1054,8 @@ impl<Signer: Sign> Channel<Signer> {
10371054

10381055
latest_monitor_update_id: 0,
10391056

1057+
autoclose_timer: 0,
1058+
10401059
holder_signer,
10411060
shutdown_scriptpubkey,
10421061
destination_script: keys_provider.get_destination_script(),
@@ -2042,6 +2061,7 @@ impl<Signer: Sign> Channel<Signer> {
20422061
on_holder_tx_dust_exposure_msat: 0,
20432062
holding_cell_msat: 0,
20442063
on_holder_tx_holding_cell_htlcs_count: 0,
2064+
on_holder_tx_included_htlcs: 0,
20452065
};
20462066

20472067
let counterparty_dust_limit_timeout_sat = (self.get_dust_buffer_feerate(outbound_feerate_update) as u64 * HTLC_TIMEOUT_TX_WEIGHT / 1000) + self.counterparty_dust_limit_satoshis;
@@ -2053,6 +2073,8 @@ impl<Signer: Sign> Channel<Signer> {
20532073
}
20542074
if htlc.amount_msat / 1000 < holder_dust_limit_success_sat {
20552075
stats.on_holder_tx_dust_exposure_msat += htlc.amount_msat;
2076+
} else {
2077+
stats.on_holder_tx_included_htlcs += 1;
20562078
}
20572079
}
20582080
stats
@@ -2067,6 +2089,7 @@ impl<Signer: Sign> Channel<Signer> {
20672089
on_holder_tx_dust_exposure_msat: 0,
20682090
holding_cell_msat: 0,
20692091
on_holder_tx_holding_cell_htlcs_count: 0,
2092+
on_holder_tx_included_htlcs: 0,
20702093
};
20712094

20722095
let counterparty_dust_limit_success_sat = (self.get_dust_buffer_feerate(outbound_feerate_update) as u64 * HTLC_SUCCESS_TX_WEIGHT / 1000) + self.counterparty_dust_limit_satoshis;
@@ -2078,6 +2101,8 @@ impl<Signer: Sign> Channel<Signer> {
20782101
}
20792102
if htlc.amount_msat / 1000 < holder_dust_limit_timeout_sat {
20802103
stats.on_holder_tx_dust_exposure_msat += htlc.amount_msat;
2104+
} else {
2105+
stats.on_holder_tx_included_htlcs += 1;
20812106
}
20822107
}
20832108

@@ -2711,16 +2736,16 @@ impl<Signer: Sign> Channel<Signer> {
27112736
/// Public version of the below, checking relevant preconditions first.
27122737
/// If we're not in a state where freeing the holding cell makes sense, this is a no-op and
27132738
/// returns `(None, Vec::new())`.
2714-
pub fn maybe_free_holding_cell_htlcs<L: Deref>(&mut self, logger: &L) -> Result<(Option<(msgs::CommitmentUpdate, ChannelMonitorUpdate)>, Vec<(HTLCSource, PaymentHash)>), ChannelError> where L::Target: Logger {
2739+
pub fn maybe_free_holding_cell_htlcs<L: Deref>(&mut self, background_feerate: u32, logger: &L) -> Result<(Option<(msgs::CommitmentUpdate, ChannelMonitorUpdate)>, Vec<(HTLCSource, PaymentHash)>), ChannelError> where L::Target: Logger {
27152740
if self.channel_state >= ChannelState::ChannelFunded as u32 &&
27162741
(self.channel_state & (ChannelState::AwaitingRemoteRevoke as u32 | ChannelState::PeerDisconnected as u32 | ChannelState::MonitorUpdateFailed as u32)) == 0 {
2717-
self.free_holding_cell_htlcs(logger)
2742+
self.free_holding_cell_htlcs(background_feerate, logger)
27182743
} else { Ok((None, Vec::new())) }
27192744
}
27202745

27212746
/// Used to fulfill holding_cell_htlcs when we get a remote ack (or implicitly get it by them
27222747
/// fulfilling or failing the last pending HTLC)
2723-
fn free_holding_cell_htlcs<L: Deref>(&mut self, logger: &L) -> Result<(Option<(msgs::CommitmentUpdate, ChannelMonitorUpdate)>, Vec<(HTLCSource, PaymentHash)>), ChannelError> where L::Target: Logger {
2748+
fn free_holding_cell_htlcs<L: Deref>(&mut self, background_feerate: u32, logger: &L) -> Result<(Option<(msgs::CommitmentUpdate, ChannelMonitorUpdate)>, Vec<(HTLCSource, PaymentHash)>), ChannelError> where L::Target: Logger {
27242749
assert_eq!(self.channel_state & ChannelState::MonitorUpdateFailed as u32, 0);
27252750
if self.holding_cell_htlc_updates.len() != 0 || self.holding_cell_update_fee.is_some() {
27262751
log_trace!(logger, "Freeing holding cell with {} HTLC updates{} in channel {}", self.holding_cell_htlc_updates.len(),
@@ -2804,7 +2829,7 @@ impl<Signer: Sign> Channel<Signer> {
28042829
return Ok((None, htlcs_to_fail));
28052830
}
28062831
let update_fee = if let Some(feerate) = self.holding_cell_update_fee.take() {
2807-
self.send_update_fee(feerate, logger)
2832+
self.send_update_fee(feerate, background_feerate, logger)
28082833
} else {
28092834
None
28102835
};
@@ -2832,13 +2857,35 @@ impl<Signer: Sign> Channel<Signer> {
28322857
}
28332858
}
28342859

2860+
/// Trigger the autoclose timer if it's in the starting position
2861+
pub fn maybe_trigger_autoclose_timer(&mut self, current_feerate: u32, new_feerate: u32, background_feerate: u32) -> bool {
2862+
2863+
// Verify the new feerate is at least superior by 30% of current feerate before to
2864+
// start a autoclose timer.
2865+
if new_feerate < current_feerate * 1300 / 1000 && new_feerate < background_feerate {
2866+
return false;
2867+
}
2868+
2869+
// Start an auto-close timer, if the channel feerate doesn't increase before its
2870+
// expiration (i.e this outbound feerate update has been committed on both sides),
2871+
// the channel will be marked as unsafe and force-closed.
2872+
// If a timer is already pending, no-op, as a higher-feerate `update_fee` will
2873+
// implicitly override a lower-feerate `update_fee` part of the same update sequence.
2874+
if self.autoclose_timer == 0 {
2875+
self.autoclose_timer = 1;
2876+
return true;
2877+
}
2878+
return false;
2879+
}
2880+
28352881
/// Handles receiving a remote's revoke_and_ack. Note that we may return a new
28362882
/// commitment_signed message here in case we had pending outbound HTLCs to add which were
28372883
/// waiting on this revoke_and_ack. The generation of this new commitment_signed may also fail,
28382884
/// generating an appropriate error *after* the channel state has been updated based on the
28392885
/// revoke_and_ack message.
2840-
pub fn revoke_and_ack<L: Deref>(&mut self, msg: &msgs::RevokeAndACK, logger: &L) -> Result<RAAUpdates, ChannelError>
2886+
pub fn revoke_and_ack<L: Deref, F: Deref>(&mut self, msg: &msgs::RevokeAndACK, logger: &L, fee_estimator: &F) -> Result<RAAUpdates, ChannelError>
28412887
where L::Target: Logger,
2888+
F::Target: FeeEstimator
28422889
{
28432890
if (self.channel_state & (ChannelState::ChannelFunded as u32)) != (ChannelState::ChannelFunded as u32) {
28442891
return Err(ChannelError::Close("Got revoke/ACK message when channel was not in an operational state".to_owned()));
@@ -2999,6 +3046,7 @@ impl<Signer: Sign> Channel<Signer> {
29993046
log_trace!(logger, " ...promoting outbound fee update {} to Committed", feerate);
30003047
self.feerate_per_kw = feerate;
30013048
self.pending_update_fee = None;
3049+
self.autoclose_timer = 0;
30023050
},
30033051
FeeUpdateState::RemoteAnnounced => { debug_assert!(!self.is_outbound()); },
30043052
FeeUpdateState::AwaitingRemoteRevokeToAnnounce => {
@@ -3007,6 +3055,7 @@ impl<Signer: Sign> Channel<Signer> {
30073055
require_commitment = true;
30083056
self.feerate_per_kw = feerate;
30093057
self.pending_update_fee = None;
3058+
self.autoclose_timer = 0;
30103059
},
30113060
}
30123061
}
@@ -3037,7 +3086,8 @@ impl<Signer: Sign> Channel<Signer> {
30373086
});
30383087
}
30393088

3040-
match self.free_holding_cell_htlcs(logger)? {
3089+
let background_feerate = fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Background);
3090+
match self.free_holding_cell_htlcs(background_feerate, logger)? {
30413091
(Some((mut commitment_update, mut additional_update)), htlcs_to_fail) => {
30423092
commitment_update.update_fail_htlcs.reserve(update_fail_htlcs.len());
30433093
for fail_msg in update_fail_htlcs.drain(..) {
@@ -3104,7 +3154,7 @@ impl<Signer: Sign> Channel<Signer> {
31043154
/// If our balance is too low to cover the cost of the next commitment transaction at the
31053155
/// new feerate, the update is cancelled.
31063156
/// You MUST call send_commitment prior to any other calls on this Channel
3107-
fn send_update_fee<L: Deref>(&mut self, feerate_per_kw: u32, logger: &L) -> Option<msgs::UpdateFee> where L::Target: Logger {
3157+
fn send_update_fee<L: Deref>(&mut self, feerate_per_kw: u32, background_feerate: u32, logger: &L) -> Option<msgs::UpdateFee> where L::Target: Logger {
31083158
if !self.is_outbound() {
31093159
panic!("Cannot send fee from inbound channel");
31103160
}
@@ -3145,6 +3195,8 @@ impl<Signer: Sign> Channel<Signer> {
31453195
return None;
31463196
}
31473197

3198+
self.maybe_trigger_autoclose_timer(self.get_feerate(), feerate_per_kw, background_feerate);
3199+
31483200
debug_assert!(self.pending_update_fee.is_none());
31493201
self.pending_update_fee = Some((feerate_per_kw, FeeUpdateState::Outbound));
31503202

@@ -3154,8 +3206,8 @@ impl<Signer: Sign> Channel<Signer> {
31543206
})
31553207
}
31563208

3157-
pub fn send_update_fee_and_commit<L: Deref>(&mut self, feerate_per_kw: u32, logger: &L) -> Result<Option<(msgs::UpdateFee, msgs::CommitmentSigned, ChannelMonitorUpdate)>, ChannelError> where L::Target: Logger {
3158-
match self.send_update_fee(feerate_per_kw, logger) {
3209+
pub fn send_update_fee_and_commit<L: Deref>(&mut self, feerate_per_kw: u32, background_feerate: u32, logger: &L) -> Result<Option<(msgs::UpdateFee, msgs::CommitmentSigned, ChannelMonitorUpdate)>, ChannelError> where L::Target: Logger {
3210+
match self.send_update_fee(feerate_per_kw, background_feerate, logger) {
31593211
Some(update_fee) => {
31603212
let (commitment_signed, monitor_update) = self.send_commitment_no_status_check(logger)?;
31613213
Ok(Some((update_fee, commitment_signed, monitor_update)))
@@ -3343,6 +3395,26 @@ impl<Signer: Sign> Channel<Signer> {
33433395
Ok(())
33443396
}
33453397

3398+
/// If the auto-close timer is reached following the triggering of a auto-close condition
3399+
/// (i.e a non-satisfying feerate to ensure efficient confirmation), we force-close
3400+
/// channel, hopefully narrowing the safety risks for the user funds.
3401+
pub fn check_autoclose(&mut self) -> Result<(), ChannelError> {
3402+
if self.autoclose_timer > 0 && self.autoclose_timer < AUTOCLOSE_TIMEOUT {
3403+
self.autoclose_timer += 1;
3404+
}
3405+
if self.autoclose_timer == AUTOCLOSE_TIMEOUT {
3406+
let inbound_stats = self.get_inbound_pending_htlc_stats(None);
3407+
let outbound_stats = self.get_outbound_pending_htlc_stats(None);
3408+
// If the channel has pending *included* HTLCs to claim on-chain and `should_autoclose`=y, close the channel
3409+
if inbound_stats.on_holder_tx_included_htlcs + outbound_stats.on_holder_tx_included_htlcs > 0 && self.config.should_autoclose {
3410+
return Err(ChannelError::Close("Channel has time-sensitive outputs and the auto-close timer has been reached".to_owned()));
3411+
}
3412+
// Otherwise, do not reset the autoclose timer as a substantial HTLC can be committed
3413+
// at anytime on the still low-feerate channel.
3414+
}
3415+
Ok(())
3416+
}
3417+
33463418
fn get_last_revoke_and_ack(&self) -> msgs::RevokeAndACK {
33473419
let next_per_commitment_point = self.holder_signer.get_per_commitment_point(self.cur_holder_commitment_transaction_number, &self.secp_ctx);
33483420
let per_commitment_secret = self.holder_signer.release_commitment_secret(self.cur_holder_commitment_transaction_number + 2);
@@ -3419,7 +3491,10 @@ impl<Signer: Sign> Channel<Signer> {
34193491

34203492
/// May panic if some calls other than message-handling calls (which will all Err immediately)
34213493
/// have been called between remove_uncommitted_htlcs_and_mark_paused and this call.
3422-
pub fn channel_reestablish<L: Deref>(&mut self, msg: &msgs::ChannelReestablish, logger: &L) -> Result<(Option<msgs::FundingLocked>, Option<msgs::RevokeAndACK>, Option<msgs::CommitmentUpdate>, Option<ChannelMonitorUpdate>, RAACommitmentOrder, Vec<(HTLCSource, PaymentHash)>, Option<msgs::Shutdown>), ChannelError> where L::Target: Logger {
3494+
pub fn channel_reestablish<L: Deref, F: Deref>(&mut self, msg: &msgs::ChannelReestablish, logger: &L, fee_estimator: &F) -> Result<(Option<msgs::FundingLocked>, Option<msgs::RevokeAndACK>, Option<msgs::CommitmentUpdate>, Option<ChannelMonitorUpdate>, RAACommitmentOrder, Vec<(HTLCSource, PaymentHash)>, Option<msgs::Shutdown>), ChannelError>
3495+
where L::Target: Logger,
3496+
F::Target: FeeEstimator
3497+
{
34233498
if self.channel_state & (ChannelState::PeerDisconnected as u32) == 0 {
34243499
// While BOLT 2 doesn't indicate explicitly we should error this channel here, it
34253500
// almost certainly indicates we are going to end up out-of-sync in some way, so we
@@ -3524,7 +3599,8 @@ impl<Signer: Sign> Channel<Signer> {
35243599
// channel_reestablish should result in them sending a revoke_and_ack), but we may
35253600
// have received some updates while we were disconnected. Free the holding cell
35263601
// now!
3527-
match self.free_holding_cell_htlcs(logger) {
3602+
let background_feerate = fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Background);
3603+
match self.free_holding_cell_htlcs(background_feerate, logger) {
35283604
Err(ChannelError::Close(msg)) => return Err(ChannelError::Close(msg)),
35293605
Err(ChannelError::Warn(_)) | Err(ChannelError::Ignore(_)) | Err(ChannelError::CloseDelayBroadcast(_)) =>
35303606
panic!("Got non-channel-failing result from free_holding_cell_htlcs"),
@@ -4865,6 +4941,7 @@ impl<Signer: Sign> Channel<Signer> {
48654941
log_trace!(logger, " ...promoting inbound AwaitingRemoteRevokeToAnnounce fee update {} to Committed", feerate);
48664942
self.feerate_per_kw = feerate;
48674943
self.pending_update_fee = None;
4944+
self.autoclose_timer = 0;
48684945
}
48694946
}
48704947
self.resend_order = RAACommitmentOrder::RevokeAndACKFirst;
@@ -5384,6 +5461,7 @@ impl<Signer: Sign> Writeable for Channel<Signer> {
53845461
(9, self.target_closing_feerate_sats_per_kw, option),
53855462
(11, self.monitor_pending_finalized_fulfills, vec_type),
53865463
(13, self.channel_creation_height, required),
5464+
(15, self.autoclose_timer, required),
53875465
});
53885466

53895467
Ok(())
@@ -5623,6 +5701,7 @@ impl<'a, Signer: Sign, K: Deref> ReadableArgs<(&'a K, u32)> for Channel<Signer>
56235701
// only, so we default to that if none was written.
56245702
let mut channel_type = Some(ChannelTypeFeatures::only_static_remote_key());
56255703
let mut channel_creation_height = Some(serialized_height);
5704+
let mut autoclose_timer = 0;
56265705
read_tlv_fields!(reader, {
56275706
(0, announcement_sigs, option),
56285707
(1, minimum_depth, option),
@@ -5633,6 +5712,7 @@ impl<'a, Signer: Sign, K: Deref> ReadableArgs<(&'a K, u32)> for Channel<Signer>
56335712
(9, target_closing_feerate_sats_per_kw, option),
56345713
(11, monitor_pending_finalized_fulfills, vec_type),
56355714
(13, channel_creation_height, option),
5715+
(15, autoclose_timer, required),
56365716
});
56375717

56385718
let chan_features = channel_type.as_ref().unwrap();
@@ -5661,6 +5741,8 @@ impl<'a, Signer: Sign, K: Deref> ReadableArgs<(&'a K, u32)> for Channel<Signer>
56615741

56625742
latest_monitor_update_id,
56635743

5744+
autoclose_timer,
5745+
56645746
holder_signer,
56655747
shutdown_scriptpubkey,
56665748
destination_script,

0 commit comments

Comments
 (0)