Skip to content

Commit 8cce3cc

Browse files
author
Antoine Riard
committed
Add Fast-Close in case of fees spikes
1 parent 1a74367 commit 8cce3cc

File tree

3 files changed

+84
-3
lines changed

3 files changed

+84
-3
lines changed

lightning/src/chain/channelmonitor.rs

+35-3
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,9 @@ pub(crate) enum ChannelMonitorUpdateStep {
479479
ShutdownScript {
480480
scriptpubkey: Script,
481481
},
482+
FeesSpikes {
483+
feerate: u32,
484+
},
482485
}
483486

484487
impl_writeable_tlv_based_enum_upgradable!(ChannelMonitorUpdateStep,
@@ -505,6 +508,9 @@ impl_writeable_tlv_based_enum_upgradable!(ChannelMonitorUpdateStep,
505508
(5, ShutdownScript) => {
506509
(0, scriptpubkey, required),
507510
},
511+
(6, FeesSpikes) => {
512+
(0, feerate, required),
513+
}
508514
);
509515

510516
/// Details about the balance(s) available for spending once the channel appears on chain.
@@ -1925,6 +1931,30 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
19251931
panic!("Attempted to replace shutdown script {} with {}", shutdown_script, scriptpubkey);
19261932
}
19271933
},
1934+
ChannelMonitorUpdateStep::FeesSpikes { feerate } => {
1935+
let should_broadcast = self.should_broadcast_holder_commitment_txn(logger, true);
1936+
let mut claimable_outpoints = Vec::new();
1937+
let mut watch_outputs = Vec::new();
1938+
if should_broadcast { //XXX: do a helper to factor code with block_confirmed()
1939+
let funding_outp = HolderFundingOutput::build(self.funding_redeemscript.clone());
1940+
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());
1941+
claimable_outpoints.push(commitment_package);
1942+
self.pending_monitor_events.push(MonitorEvent::CommitmentTxConfirmed(self.funding_info.0));
1943+
let commitment_tx = self.onchain_tx_handler.get_fully_signed_holder_tx(&self.funding_redeemscript);
1944+
self.holder_tx_signed = true;
1945+
// Because we're broadcasting a commitment transaction, we should construct the package
1946+
// assuming it gets confirmed in the next block. Sadly, we have code which considers
1947+
// "not yet confirmed" things as discardable, so we cannot do that here.
1948+
let (mut new_outpoints, _) = self.get_broadcasted_holder_claims(&self.current_holder_commitment_tx, self.best_block.height());
1949+
let new_outputs = self.get_broadcasted_holder_watch_outputs(&self.current_holder_commitment_tx, &commitment_tx);
1950+
if !new_outputs.is_empty() {
1951+
watch_outputs.push((self.current_holder_commitment_tx.txid.clone(), new_outputs));
1952+
}
1953+
claimable_outpoints.append(&mut new_outpoints);
1954+
};
1955+
self.onchain_tx_handler.update_claims_view(&Vec::new(), claimable_outpoints, self.best_block.height(), self.best_block.height(), broadcaster, fee_estimator, logger);
1956+
//XXX: return watch_outputs to ChainMonitor, factor code from process_chain_data about outputs registration
1957+
}
19281958
}
19291959
}
19301960
self.latest_update_id = updates.update_id;
@@ -2431,7 +2461,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
24312461
log_trace!(logger, "Processing {} matched transactions for block at height {}.", txn_matched.len(), conf_height);
24322462
debug_assert!(self.best_block.height() >= conf_height);
24332463

2434-
let should_broadcast = self.should_broadcast_holder_commitment_txn(logger);
2464+
let should_broadcast = self.should_broadcast_holder_commitment_txn(logger, false);
24352465
if should_broadcast {
24362466
let funding_outp = HolderFundingOutput::build(self.funding_redeemscript.clone());
24372467
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 +2652,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
26222652
false
26232653
}
26242654

2625-
fn should_broadcast_holder_commitment_txn<L: Deref>(&self, logger: &L) -> bool where L::Target: Logger {
2655+
fn should_broadcast_holder_commitment_txn<L: Deref>(&self, logger: &L, spikes: bool) -> bool where L::Target: Logger {
26262656
// We need to consider all HTLCs which are:
26272657
// * in any unrevoked counterparty commitment transaction, as they could broadcast said
26282658
// transactions and we'd end up in a race, or
@@ -2661,8 +2691,10 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
26612691
// The final, above, condition is checked for statically in channelmanager
26622692
// with CHECK_CLTV_EXPIRY_SANITY_2.
26632693
let htlc_outbound = $holder_tx == htlc.offered;
2694+
//XXX: don't go on-chain for inbound payment not worthy the dust threshold ?
2695+
//XXX: scale down the timer in function of feerate ?
26642696
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)) {
2697+
(!htlc_outbound && htlc.cltv_expiry <= height + CLTV_CLAIM_BUFFER / (if spikes { 2 } else { 1 }) && self.payment_preimages.contains_key(&htlc.payment_hash)) {
26662698
log_info!(logger, "Force-closing channel due to {} HTLC timeout, HTLC expiry is {}", if htlc_outbound { "outbound" } else { "inbound "}, htlc.cltv_expiry);
26672699
return true;
26682700
}

lightning/src/ln/channel.rs

+31
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,8 @@ pub(super) struct Channel<Signer: Sign> {
592592

593593
/// This channel's type, as negotiated during channel open
594594
channel_type: ChannelTypeFeatures,
595+
596+
autoclose_timestamp: u64,
595597
}
596598

597599
#[cfg(any(test, feature = "fuzztarget"))]
@@ -837,6 +839,8 @@ impl<Signer: Sign> Channel<Signer> {
837839
// on error messages. When we support more we'll need fallback support (assuming we
838840
// want to support old types).
839841
channel_type: ChannelTypeFeatures::only_static_remote_key(),
842+
843+
autoclose_timestamp: 0,
840844
})
841845
}
842846

@@ -1128,6 +1132,8 @@ impl<Signer: Sign> Channel<Signer> {
11281132
historical_inbound_htlc_fulfills: HashSet::new(),
11291133

11301134
channel_type,
1135+
1136+
autoclose_timestamp: 0
11311137
};
11321138

11331139
Ok(chan)
@@ -3154,6 +3160,26 @@ impl<Signer: Sign> Channel<Signer> {
31543160
})
31553161
}
31563162

3163+
pub fn monitor_fees_spikes(&mut self, new_feerate: u32, highest_seen_timestamp: u64) -> Option<ChannelMonitorUpdate> {
3164+
if new_feerate > self.feerate_per_kw * 1300 / 1000 {
3165+
//XXX: more than 30 ticks ?
3166+
if highest_seen_timestamp + 30 < self.autoclose_timestamp {
3167+
let monitor_update = ChannelMonitorUpdate {
3168+
update_id: self.latest_monitor_update_id,
3169+
updates: vec![ChannelMonitorUpdateStep::FeesSpikes {
3170+
feerate: new_feerate,
3171+
}]
3172+
};
3173+
return Some(monitor_update);
3174+
} else {
3175+
self.autoclose_timestamp += 1;
3176+
}
3177+
} else {
3178+
self.autoclose_timestamp = 0;
3179+
}
3180+
return None;
3181+
}
3182+
31573183
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 {
31583184
match self.send_update_fee(feerate_per_kw, logger) {
31593185
Some(update_fee) => {
@@ -5384,6 +5410,7 @@ impl<Signer: Sign> Writeable for Channel<Signer> {
53845410
(9, self.target_closing_feerate_sats_per_kw, option),
53855411
(11, self.monitor_pending_finalized_fulfills, vec_type),
53865412
(13, self.channel_creation_height, required),
5413+
(15, self.autoclose_timestamp, required),
53875414
});
53885415

53895416
Ok(())
@@ -5623,6 +5650,7 @@ impl<'a, Signer: Sign, K: Deref> ReadableArgs<(&'a K, u32)> for Channel<Signer>
56235650
// only, so we default to that if none was written.
56245651
let mut channel_type = Some(ChannelTypeFeatures::only_static_remote_key());
56255652
let mut channel_creation_height = Some(serialized_height);
5653+
let mut autoclose_timestamp = 0;
56265654
read_tlv_fields!(reader, {
56275655
(0, announcement_sigs, option),
56285656
(1, minimum_depth, option),
@@ -5633,6 +5661,7 @@ impl<'a, Signer: Sign, K: Deref> ReadableArgs<(&'a K, u32)> for Channel<Signer>
56335661
(9, target_closing_feerate_sats_per_kw, option),
56345662
(11, monitor_pending_finalized_fulfills, vec_type),
56355663
(13, channel_creation_height, option),
5664+
(15, autoclose_timestamp, required)
56365665
});
56375666

56385667
let chan_features = channel_type.as_ref().unwrap();
@@ -5742,6 +5771,8 @@ impl<'a, Signer: Sign, K: Deref> ReadableArgs<(&'a K, u32)> for Channel<Signer>
57425771
historical_inbound_htlc_fulfills,
57435772

57445773
channel_type: channel_type.unwrap(),
5774+
5775+
autoclose_timestamp,
57455776
})
57465777
}
57475778
}

lightning/src/ln/channelmanager.rs

+18
Original file line numberDiff line numberDiff line change
@@ -3021,6 +3021,16 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
30213021
(retain_channel, NotifyOption::DoPersist, ret_err)
30223022
}
30233023

3024+
fn monitor_fees_spikes(&self, short_to_id: &mut HashMap<u64, [u8; 32]>, chan_id: &[u8; 32], chan: &mut Channel<Signer>, new_feerate: u32, highest_seen_timestamp: u64) -> (bool, NotifyOption, Result<(), MsgHandleErrInternal>) {
3025+
if let Some(monitor_update) = chan.monitor_fees_spikes(new_feerate, highest_seen_timestamp) {
3026+
if let Err(e) = self.chain_monitor.update_channel(chan.get_funding_txo().unwrap(), monitor_update) {
3027+
let (res, drop) = handle_monitor_err!(self, e, short_to_id, chan, RAACommitmentOrder::CommitmentFirst, false, false, Vec::new(), Vec::new(), Vec::new(), chan_id);
3028+
return (if drop { false } else { true }, NotifyOption::DoPersist, res);
3029+
}
3030+
}
3031+
(true, NotifyOption::SkipPersist, Ok(()))
3032+
}
3033+
30243034
#[cfg(fuzzing)]
30253035
/// In chanmon_consistency we want to sometimes do the channel fee updates done in
30263036
/// timer_tick_occurred, but we can't generate the disabled channel updates as it considers
@@ -3077,6 +3087,14 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
30773087
let short_to_id = &mut channel_state.short_to_id;
30783088
channel_state.by_id.retain(|chan_id, chan| {
30793089
let counterparty_node_id = chan.get_counterparty_node_id();
3090+
3091+
let (retain_channel, chan_needs_persist, err) = self.monitor_fees_spikes(short_to_id, chan_id, chan, new_feerate, self.highest_seen_timestamp.load(Ordering::Acquire) as u64);
3092+
if chan_needs_persist == NotifyOption::DoPersist { should_persist = NotifyOption::DoPersist; }
3093+
if err.is_err() {
3094+
handle_errors.push((err, counterparty_node_id));
3095+
}
3096+
if !retain_channel { return false; }
3097+
30803098
let (retain_channel, chan_needs_persist, err) = self.update_channel_fee(short_to_id, pending_msg_events, chan_id, chan, new_feerate);
30813099
if chan_needs_persist == NotifyOption::DoPersist { should_persist = NotifyOption::DoPersist; }
30823100
if err.is_err() {

0 commit comments

Comments
 (0)