Skip to content

Commit 5a1cc28

Browse files
committed
Force-close channels if their feerate gets stale without any update
For quite some time, LDK has force-closed channels if the peer sends us a feerate update which is below our `FeeEstimator`'s concept of a channel lower-bound. This is intended to ensure that channel feerates are always sufficient to get our commitment transaction confirmed on-chain if we do need to force-close. However, we've never checked our channel feerate regularly - if a peer is offline (or just uninterested in updating the channel feerate) and the prevailing feerates on-chain go up, we'll simply ignore it and allow our commitment transaction to sit around with a feerate too low to get confirmed. Here we rectify this oversight by force-closing channels with stale feerates, checking after each block. However, because fee estimators are often buggy and force-closures piss off users, we only do so rather conservatively. Specifically, we only force-close if a channel's feerate is below the minimum `FeeEstimator`-provided minimum across the last day. Further, because fee estimators are often especially buggy on startup (and because peers haven't had a chance to update the channel feerates yet), we don't force-close channels until we have a full day of feerate lower-bound history. This should reduce the incidence of force-closures substantially, but it is expected this will still increase force-closures somewhat substantially depending on the users' `FeeEstimator`. Fixes #993
1 parent 66e6ee5 commit 5a1cc28

File tree

3 files changed

+96
-1
lines changed

3 files changed

+96
-1
lines changed

fuzz/src/full_stack.rs

+20
Original file line numberDiff line numberDiff line change
@@ -921,19 +921,32 @@ mod tests {
921921
ext_from_hex("0c005e", &mut test);
922922
// the funding transaction
923923
ext_from_hex("020000000100000000000000000000000000000000000000000000000000000000000000000000000000ffffffff0150c3000000000000220020ae0000000000000000000000000000000000000000000000000000000000000000000000", &mut test);
924+
ext_from_hex("00fd00fd", &mut test); // Two feerate requests during block connection
924925
// connect a block with no transactions, one per line
925926
ext_from_hex("0c0000", &mut test);
927+
ext_from_hex("00fd00fd", &mut test); // Two feerate requests during block connection
926928
ext_from_hex("0c0000", &mut test);
929+
ext_from_hex("00fd00fd", &mut test); // Two feerate requests during block connection
927930
ext_from_hex("0c0000", &mut test);
931+
ext_from_hex("00fd00fd", &mut test); // Two feerate requests during block connection
928932
ext_from_hex("0c0000", &mut test);
933+
ext_from_hex("00fd00fd", &mut test); // Two feerate requests during block connection
929934
ext_from_hex("0c0000", &mut test);
935+
ext_from_hex("00fd00fd", &mut test); // Two feerate requests during block connection
930936
ext_from_hex("0c0000", &mut test);
937+
ext_from_hex("00fd00fd", &mut test); // Two feerate requests during block connection
931938
ext_from_hex("0c0000", &mut test);
939+
ext_from_hex("00fd00fd", &mut test); // Two feerate requests during block connection
932940
ext_from_hex("0c0000", &mut test);
941+
ext_from_hex("00fd00fd", &mut test); // Two feerate requests during block connection
933942
ext_from_hex("0c0000", &mut test);
943+
ext_from_hex("00fd00fd", &mut test); // Two feerate requests during block connection
934944
ext_from_hex("0c0000", &mut test);
945+
ext_from_hex("00fd00fd", &mut test); // Two feerate requests during block connection
935946
ext_from_hex("0c0000", &mut test);
947+
ext_from_hex("00fd00fd", &mut test); // Two feerate requests during block connection
936948
ext_from_hex("0c0000", &mut test);
949+
ext_from_hex("00fd00fd", &mut test); // Two feerate requests during block connection
937950
// by now client should have sent a channel_ready (CHECK 3: SendChannelReady to 03000000 for chan 3d000000)
938951

939952
// inbound read from peer id 0 of len 18
@@ -1303,21 +1316,28 @@ mod tests {
13031316
ext_from_hex("0c007d", &mut test);
13041317
// the commitment transaction for channel 3f00000000000000000000000000000000000000000000000000000000000000
13051318
ext_from_hex("02000000013a000000000000000000000000000000000000000000000000000000000000000000000000000000800258020000000000002200204b0000000000000000000000000000000000000000000000000000000000000014c0000000000000160014280000000000000000000000000000000000000005000020", &mut test);
1319+
ext_from_hex("00fd00fd", &mut test); // Two feerate requests during block connection
13061320
//
13071321
// connect a block with one transaction of len 94
13081322
ext_from_hex("0c005e", &mut test);
13091323
// the HTLC timeout transaction
13101324
ext_from_hex("0200000001730000000000000000000000000000000000000000000000000000000000000000000000000000000001a701000000000000220020b20000000000000000000000000000000000000000000000000000000000000000000000", &mut test);
1325+
ext_from_hex("00fd00fd", &mut test); // Two feerate requests during block connection
13111326
// connect a block with no transactions
13121327
ext_from_hex("0c0000", &mut test);
1328+
ext_from_hex("00fd00fd", &mut test); // Two feerate requests during block connection
13131329
// connect a block with no transactions
13141330
ext_from_hex("0c0000", &mut test);
1331+
ext_from_hex("00fd00fd", &mut test); // Two feerate requests during block connection
13151332
// connect a block with no transactions
13161333
ext_from_hex("0c0000", &mut test);
1334+
ext_from_hex("00fd00fd", &mut test); // Two feerate requests during block connection
13171335
// connect a block with no transactions
13181336
ext_from_hex("0c0000", &mut test);
1337+
ext_from_hex("00fd00fd", &mut test); // Two feerate requests during block connection
13191338
// connect a block with no transactions
13201339
ext_from_hex("0c0000", &mut test);
1340+
ext_from_hex("00fd00fd", &mut test); // Two feerate requests during block connection
13211341

13221342
// process the now-pending HTLC forward
13231343
ext_from_hex("07", &mut test);

lightning/src/ln/channel.rs

+20
Original file line numberDiff line numberDiff line change
@@ -5117,6 +5117,26 @@ impl<SP: Deref> Channel<SP> where
51175117
}
51185118
}
51195119

5120+
pub fn check_for_stale_feerate<L: Logger>(&mut self, logger: &L, min_feerate: u32) -> Result<(), ClosureReason> {
5121+
if self.context.is_outbound() {
5122+
// While its possible our fee is too low for an outbound channel because we've been
5123+
// unable to increase the fee, we don't try to force-close directly here.
5124+
return Ok(());
5125+
}
5126+
if self.context.feerate_per_kw < min_feerate {
5127+
log_info!(logger,
5128+
"Closing channel as feerate of {} is below required {} (the minimum required rate over the past day)",
5129+
self.context.feerate_per_kw, min_feerate
5130+
);
5131+
Err(ClosureReason::PeerFeerateTooLow {
5132+
peer_feerate_sat_per_kw: self.context.feerate_per_kw,
5133+
required_feerate_sat_per_kw: min_feerate,
5134+
})
5135+
} else {
5136+
Ok(())
5137+
}
5138+
}
5139+
51205140
pub fn update_fee<F: Deref, L: Deref>(&mut self, fee_estimator: &LowerBoundedFeeEstimator<F>, msg: &msgs::UpdateFee, logger: &L) -> Result<(), ChannelError>
51215141
where F::Target: FeeEstimator, L::Target: Logger
51225142
{

lightning/src/ln/channelmanager.rs

+56-1
Original file line numberDiff line numberDiff line change
@@ -962,6 +962,11 @@ pub(super) struct InboundChannelRequest {
962962
/// accepted. An unaccepted channel that exceeds this limit will be abandoned.
963963
const UNACCEPTED_INBOUND_CHANNEL_AGE_LIMIT_TICKS: i32 = 2;
964964

965+
/// The number of blocks of historical feerate estimates we keep around and consider when deciding
966+
/// to force-close a channel for having too-low fees. Also the number of blocks we have to see
967+
/// after startup before we consider force-closing channels for having too-low fees.
968+
const FEERATE_TRACKING_BLOCKS: usize = 144;
969+
965970
/// Stores a PaymentSecret and any other data we may need to validate an inbound payment is
966971
/// actually ours and not some duplicate HTLC sent to us by a node along the route.
967972
///
@@ -2098,6 +2103,21 @@ where
20982103
/// Tracks the message events that are to be broadcasted when we are connected to some peer.
20992104
pending_broadcast_messages: Mutex<Vec<MessageSendEvent>>,
21002105

2106+
/// We only want to force-close our channels on peers based on stale feerates when we're
2107+
/// confident the feerate on the channel is *really* stale, not just became stale recently.
2108+
/// Thus, we store the fee estimates we had as of the last [`FEERATE_TRACKING_BLOCKS`] blocks
2109+
/// (after startup completed) here, and only force-close when channels have a lower feerate
2110+
/// than we predicted any time in the last [`FEERATE_TRACKING_BLOCKS`] blocks.
2111+
///
2112+
/// We only keep this in memory as we assume any feerates we receive immediately after startup
2113+
/// may be bunk (as they often are if Bitcoin Core crashes) and want to delay taking any
2114+
/// actions for a day anyway.
2115+
///
2116+
/// The first element in the pair is the
2117+
/// [`ConfirmationTarget::MinAllowedAnchorChannelRemoteFee`] estimate, the second the
2118+
/// [`ConfirmationTarget::MinAllowedNonAnchorChannelRemoteFee`] estimate.
2119+
last_days_feerates: Mutex<VecDeque<(u32, u32)>>,
2120+
21012121
entropy_source: ES,
21022122
node_signer: NS,
21032123
signer_provider: SP,
@@ -2875,6 +2895,8 @@ where
28752895
pending_offers_messages: Mutex::new(Vec::new()),
28762896
pending_broadcast_messages: Mutex::new(Vec::new()),
28772897

2898+
last_days_feerates: Mutex::new(VecDeque::new()),
2899+
28782900
entropy_source,
28792901
node_signer,
28802902
signer_provider,
@@ -9173,7 +9195,38 @@ where
91739195
self, || -> NotifyOption { NotifyOption::DoPersist });
91749196
*self.best_block.write().unwrap() = BestBlock::new(block_hash, height);
91759197

9176-
self.do_chain_event(Some(height), |channel| channel.best_block_updated(height, header.time, self.chain_hash, &self.node_signer, &self.default_configuration, &&WithChannelContext::from(&self.logger, &channel.context, None)));
9198+
let mut min_anchor_feerate = None;
9199+
let mut min_non_anchor_feerate = None;
9200+
if self.background_events_processed_since_startup.load(Ordering::Relaxed) {
9201+
// If we're past the startup phase, update our feerate cache
9202+
let mut last_days_feerates = self.last_days_feerates.lock().unwrap();
9203+
if last_days_feerates.len() >= FEERATE_TRACKING_BLOCKS {
9204+
last_days_feerates.pop_front();
9205+
}
9206+
let anchor_feerate = self.fee_estimator
9207+
.bounded_sat_per_1000_weight(ConfirmationTarget::MinAllowedAnchorChannelRemoteFee);
9208+
let non_anchor_feerate = self.fee_estimator
9209+
.bounded_sat_per_1000_weight(ConfirmationTarget::MinAllowedNonAnchorChannelRemoteFee);
9210+
last_days_feerates.push_back((anchor_feerate, non_anchor_feerate));
9211+
if last_days_feerates.len() >= FEERATE_TRACKING_BLOCKS {
9212+
min_anchor_feerate = last_days_feerates.iter().map(|(f, _)| f).min().copied();
9213+
min_non_anchor_feerate = last_days_feerates.iter().map(|(_, f)| f).min().copied();
9214+
}
9215+
}
9216+
9217+
self.do_chain_event(Some(height), |channel| {
9218+
let logger = WithChannelContext::from(&self.logger, &channel.context, None);
9219+
if channel.context.get_channel_type().supports_anchors_zero_fee_htlc_tx() {
9220+
if let Some(feerate) = min_anchor_feerate {
9221+
channel.check_for_stale_feerate(&logger, feerate)?;
9222+
}
9223+
} else {
9224+
if let Some(feerate) = min_non_anchor_feerate {
9225+
channel.check_for_stale_feerate(&logger, feerate)?;
9226+
}
9227+
}
9228+
channel.best_block_updated(height, header.time, self.chain_hash, &self.node_signer, &self.default_configuration, &&WithChannelContext::from(&self.logger, &channel.context, None))
9229+
});
91779230

91789231
macro_rules! max_time {
91799232
($timestamp: expr) => {
@@ -11992,6 +12045,8 @@ where
1199212045
node_signer: args.node_signer,
1199312046
signer_provider: args.signer_provider,
1199412047

12048+
last_days_feerates: Mutex::new(VecDeque::new()),
12049+
1199512050
logger: args.logger,
1199612051
default_configuration: args.default_config,
1199712052
};

0 commit comments

Comments
 (0)