Skip to content

Commit c6ca774

Browse files
author
Antoine Riard
committed
Add force-close inbound HTLC on fee spikes detection
1 parent 1a74367 commit c6ca774

File tree

3 files changed

+41
-8
lines changed

3 files changed

+41
-8
lines changed

lightning/src/chain/channelmonitor.rs

+28-6
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ use ln::chan_utils::{CounterpartyCommitmentSecrets, HTLCOutputInCommitment, HTLC
4040
use ln::channelmanager::HTLCSource;
4141
use chain;
4242
use chain::{BestBlock, WatchedOutput};
43-
use chain::chaininterface::{BroadcasterInterface, FeeEstimator};
43+
use chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator};
4444
use chain::transaction::{OutPoint, TransactionData};
4545
use chain::keysinterface::{SpendableOutputDescriptor, StaticPaymentOutputDescriptor, DelayedPaymentOutputDescriptor, Sign, KeysInterface};
4646
use chain::onchaintx::OnchainTxHandler;
@@ -701,6 +701,8 @@ pub(crate) struct ChannelMonitorImpl<Signer: Sign> {
701701
/// spending CSV for revocable outputs).
702702
htlcs_resolved_on_chain: Vec<IrrevocablyResolvedHTLC>,
703703

704+
spikes_force_close_rate: u32,
705+
704706
// We simply modify best_block in Channel's block_connected so that serialization is
705707
// consistent but hopefully the users' copy handles block_connected in a consistent way.
706708
// (we do *not*, however, update them in update_monitor to ensure any local user copies keep
@@ -935,6 +937,7 @@ impl<Signer: Sign> Writeable for ChannelMonitorImpl<Signer> {
935937
(1, self.funding_spend_confirmed, option),
936938
(3, self.htlcs_resolved_on_chain, vec_type),
937939
(5, self.pending_monitor_events, vec_type),
940+
(7, self.spikes_force_close_rate, required),
938941
});
939942

940943
Ok(())
@@ -948,7 +951,8 @@ impl<Signer: Sign> ChannelMonitor<Signer> {
948951
funding_redeemscript: Script, channel_value_satoshis: u64,
949952
commitment_transaction_number_obscure_factor: u64,
950953
initial_holder_commitment_tx: HolderCommitmentTransaction,
951-
best_block: BestBlock) -> ChannelMonitor<Signer> {
954+
best_block: BestBlock,
955+
spikes_force_close_rate: u32) -> ChannelMonitor<Signer> {
952956

953957
assert!(commitment_transaction_number_obscure_factor <= (1 << 48));
954958
let payment_key_hash = WPubkeyHash::hash(&keys.pubkeys().payment_point.serialize());
@@ -1036,6 +1040,8 @@ impl<Signer: Sign> ChannelMonitor<Signer> {
10361040
funding_spend_confirmed: None,
10371041
htlcs_resolved_on_chain: Vec::new(),
10381042

1043+
spikes_force_close_rate,
1044+
10391045
best_block,
10401046

10411047
secp_ctx,
@@ -2431,7 +2437,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
24312437
log_trace!(logger, "Processing {} matched transactions for block at height {}.", txn_matched.len(), conf_height);
24322438
debug_assert!(self.best_block.height() >= conf_height);
24332439

2434-
let should_broadcast = self.should_broadcast_holder_commitment_txn(logger);
2440+
let should_broadcast = self.should_broadcast_holder_commitment_txn(if self.should_force_close_fee_spikes(fee_estimator, self.current_holder_commitment_tx.feerate_per_kw) { true } else { false }, logger);
24352441
if should_broadcast {
24362442
let funding_outp = HolderFundingOutput::build(self.funding_redeemscript.clone());
24372443
let commitment_package = PackageTemplate::build_package(self.funding_info.0.txid.clone(), self.funding_info.0.index as u32, PackageSolvingData::HolderFundingOutput(funding_outp), self.best_block.height(), false, self.best_block.height());
@@ -2622,7 +2628,18 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
26222628
false
26232629
}
26242630

2625-
fn should_broadcast_holder_commitment_txn<L: Deref>(&self, logger: &L) -> bool where L::Target: Logger {
2631+
fn should_force_close_fee_spikes<F: Deref>(&self, fee_estimator: &F, current_feerate: u32) -> bool where F::Target: FeeEstimator {
2632+
2633+
let new_feerate = fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Normal);
2634+
// Verify the new feerate is at least superior by 30% of current feerate before to start
2635+
// a autoclose timer.
2636+
if new_feerate < current_feerate * self.spikes_force_close_rate / 1000 {
2637+
return true;
2638+
}
2639+
false
2640+
}
2641+
2642+
fn should_broadcast_holder_commitment_txn<L: Deref>(&self, spikes_force_close: bool, logger: &L) -> bool where L::Target: Logger {
26262643
// We need to consider all HTLCs which are:
26272644
// * in any unrevoked counterparty commitment transaction, as they could broadcast said
26282645
// transactions and we'd end up in a race, or
@@ -2662,7 +2679,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
26622679
// with CHECK_CLTV_EXPIRY_SANITY_2.
26632680
let htlc_outbound = $holder_tx == htlc.offered;
26642681
if ( htlc_outbound && htlc.cltv_expiry + LATENCY_GRACE_PERIOD_BLOCKS <= height) ||
2665-
(!htlc_outbound && htlc.cltv_expiry <= height + CLTV_CLAIM_BUFFER && self.payment_preimages.contains_key(&htlc.payment_hash)) {
2682+
((!htlc_outbound && (htlc.cltv_expiry <= height + CLTV_CLAIM_BUFFER || spikes_force_close)) && self.payment_preimages.contains_key(&htlc.payment_hash)) { //TODO: don't force-close if dust satoshis
26662683
log_info!(logger, "Force-closing channel due to {} HTLC timeout, HTLC expiry is {}", if htlc_outbound { "outbound" } else { "inbound "}, htlc.cltv_expiry);
26672684
return true;
26682685
}
@@ -3206,10 +3223,12 @@ impl<'a, Signer: Sign, K: KeysInterface<Signer = Signer>> ReadableArgs<&'a K>
32063223

32073224
let mut funding_spend_confirmed = None;
32083225
let mut htlcs_resolved_on_chain = Some(Vec::new());
3226+
let mut spikes_force_close_rate = 0;
32093227
read_tlv_fields!(reader, {
32103228
(1, funding_spend_confirmed, option),
32113229
(3, htlcs_resolved_on_chain, vec_type),
32123230
(5, pending_monitor_events, vec_type),
3231+
(7, spikes_force_close_rate, required),
32133232
});
32143233

32153234
let mut secp_ctx = Secp256k1::new();
@@ -3262,6 +3281,8 @@ impl<'a, Signer: Sign, K: KeysInterface<Signer = Signer>> ReadableArgs<&'a K>
32623281
funding_spend_confirmed,
32633282
htlcs_resolved_on_chain: htlcs_resolved_on_chain.unwrap(),
32643283

3284+
spikes_force_close_rate,
3285+
32653286
best_block,
32663287

32673288
secp_ctx,
@@ -3392,7 +3413,8 @@ mod tests {
33923413
(OutPoint { txid: Txid::from_slice(&[43; 32]).unwrap(), index: 0 }, Script::new()),
33933414
&channel_parameters,
33943415
Script::new(), 46, 0,
3395-
HolderCommitmentTransaction::dummy(), best_block);
3416+
HolderCommitmentTransaction::dummy(), best_block,
3417+
1300);
33963418

33973419
monitor.provide_latest_holder_commitment_tx(HolderCommitmentTransaction::dummy(), preimages_to_holder_htlcs!(preimages[0..10])).unwrap();
33983420
let dummy_txid = dummy_tx.txid();

lightning/src/ln/channel.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -1896,7 +1896,8 @@ impl<Signer: Sign> Channel<Signer> {
18961896
&self.channel_transaction_parameters,
18971897
funding_redeemscript.clone(), self.channel_value_satoshis,
18981898
obscure_factor,
1899-
holder_commitment_tx, best_block);
1899+
holder_commitment_tx, best_block,
1900+
self.config.spikes_force_close_rate);
19001901

19011902
channel_monitor.provide_latest_counterparty_commitment_tx(counterparty_initial_commitment_txid, Vec::new(), self.cur_counterparty_commitment_transaction_number, self.counterparty_cur_commitment_point.unwrap(), logger);
19021903

@@ -1973,7 +1974,8 @@ impl<Signer: Sign> Channel<Signer> {
19731974
&self.channel_transaction_parameters,
19741975
funding_redeemscript.clone(), self.channel_value_satoshis,
19751976
obscure_factor,
1976-
holder_commitment_tx, best_block);
1977+
holder_commitment_tx, best_block,
1978+
self.config.spikes_force_close_rate);
19771979

19781980
channel_monitor.provide_latest_counterparty_commitment_tx(counterparty_initial_bitcoin_tx.txid, Vec::new(), self.cur_counterparty_commitment_transaction_number, self.counterparty_cur_commitment_point.unwrap(), logger);
19791981

lightning/src/util/config.rs

+9
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,13 @@ pub struct ChannelConfig {
246246
/// [`Normal`]: crate::chain::chaininterface::ConfirmationTarget::Normal
247247
/// [`Background`]: crate::chain::chaininterface::ConfirmationTarget::Background
248248
pub force_close_avoidance_max_fee_satoshis: u64,
249+
/// When we detect a fee spikes and receive a block, we force-close immediately channel
250+
/// with pending non-dust inbound HTLCs, of which the preimage is known.
251+
///
252+
/// To disable the force-close on fee spikes mechanism, select 0 as a rate.
253+
///
254+
/// Default value: 30%
255+
pub spikes_force_close_rate: u32,
249256
}
250257

251258
impl Default for ChannelConfig {
@@ -259,6 +266,7 @@ impl Default for ChannelConfig {
259266
commit_upfront_shutdown_pubkey: true,
260267
max_dust_htlc_exposure_msat: 5_000_000,
261268
force_close_avoidance_max_fee_satoshis: 1000,
269+
spikes_force_close_rate: 1300,
262270
}
263271
}
264272
}
@@ -269,6 +277,7 @@ impl_writeable_tlv_based!(ChannelConfig, {
269277
(2, cltv_expiry_delta, required),
270278
(3, force_close_avoidance_max_fee_satoshis, (default_value, 1000)),
271279
(4, announced_channel, required),
280+
(5, spikes_force_close_rate, (default_value, 1300)),
272281
(6, commit_upfront_shutdown_pubkey, required),
273282
(8, forwarding_fee_base_msat, required),
274283
});

0 commit comments

Comments
 (0)