Skip to content

Commit ccbaba9

Browse files
committed
Check if HTLC is worth claiming onchain when failing from monitor event
In previous commits, we added a config flag to allow a user to control a multiplier on a threshold for whether to fail back an HTLC early if we're waiting on a downstream confirmation, depending on how much we'd save if we ended up successfully claimed on-chain. This commit adds the logic to calculate this threshold, and adds the check when failing HTLCs back from monitor events (although this won't do anything until we actually generate new monitor events for this situation in upcoming commits).
1 parent aa5d7a9 commit ccbaba9

File tree

2 files changed

+76
-4
lines changed

2 files changed

+76
-4
lines changed

lightning/src/ln/channel.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2100,6 +2100,45 @@ impl<SP: Deref> Channel<SP> where
21002100
Ok(())
21012101
}
21022102

2103+
/// The total fee paid to close the channel and claim an HTLC-success transaction, accounting
2104+
/// for an added input (spending a P2WPKH) and P2WPKH output for fee bumping.
2105+
fn cost_to_claim_onchain<L: Deref>(&self, feerate_per_kw: u32, logger: &L) -> u64
2106+
where L::Target: Logger
2107+
{
2108+
let commitment_weight = if self.context.is_outbound() {
2109+
let keys = self.context.build_holder_transaction_keys(self.context.cur_holder_commitment_transaction_number);
2110+
let commitment_stats = self.context.build_commitment_transaction(self.context.cur_holder_commitment_transaction_number, &keys, true, true, logger);
2111+
commitment_stats.total_fee_sat
2112+
} else { 0 };
2113+
2114+
let htlc_weight = htlc_success_tx_weight(self.context.get_channel_type());
2115+
let p2wpkh_weight = 31 * 4;
2116+
let input_weight = 272; // Including witness, assuming it spends from a p2wpkh
2117+
2118+
let total_weight = commitment_weight + htlc_weight + p2wpkh_weight + input_weight;
2119+
let total_fee = feerate_per_kw as u64 * total_weight / 1000;
2120+
total_fee
2121+
}
2122+
2123+
/// Check whether we should risk letting this channel force-close while waiting
2124+
/// for a downstream HTLC resolution on-chain. See [`ChannelConfig::early_fail_multiplier`]
2125+
/// for more info.
2126+
pub(super) fn check_worth_upstream_closing<F: Deref, L: Deref>(
2127+
&self, htlc_id: u64, fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L
2128+
) -> Option<bool>
2129+
where F::Target: FeeEstimator, L::Target: Logger
2130+
{
2131+
let feerate_per_kw = fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::HighPriority);
2132+
let htlc = self.context.pending_inbound_htlcs.iter().find(|htlc| htlc.htlc_id == htlc_id)?;
2133+
let total_fee = self.cost_to_claim_onchain(feerate_per_kw, logger);
2134+
let threshold = total_fee * self.context.config().early_fail_multiplier / 1_000_000;
2135+
if htlc.amount_msat / 1000 >= threshold {
2136+
Some(true)
2137+
} else {
2138+
Some(false)
2139+
}
2140+
}
2141+
21032142
#[inline]
21042143
fn get_closing_scriptpubkey(&self) -> Script {
21052144
// The shutdown scriptpubkey is set on channel opening when option_upfront_shutdown_script

lightning/src/ln/channelmanager.rs

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4791,6 +4791,37 @@ where
47914791
}
47924792
}
47934793

4794+
/// Check whether we should risk letting an upstream channel force-close while waiting
4795+
/// for a downstream HTLC resolution on-chain. See [`ChannelConfig::early_fail_multiplier`]
4796+
/// for more info.
4797+
fn check_worth_upstream_closing(&self, source: &HTLCSource) -> bool {
4798+
let (short_channel_id, htlc_id) = match source {
4799+
HTLCSource::OutboundRoute { .. } => {
4800+
debug_assert!(false, "This should not be called on outbound HTLCs");
4801+
return false;
4802+
},
4803+
HTLCSource::PreviousHopData(HTLCPreviousHopData { ref short_channel_id, ref htlc_id, .. }) => {
4804+
(short_channel_id, htlc_id)
4805+
},
4806+
};
4807+
let (counterparty_node_id, channel_id) = match self.short_to_chan_info.read().unwrap().get(short_channel_id) {
4808+
Some((cp_id, chan_id)) => (cp_id.clone(), chan_id.clone()),
4809+
None => return false,
4810+
};
4811+
let per_peer_state = self.per_peer_state.read().unwrap();
4812+
let mut peer_state_lock = match per_peer_state.get(&counterparty_node_id) {
4813+
Some(peer_state_mutex) => peer_state_mutex.lock().unwrap(),
4814+
None => return false,
4815+
};
4816+
let peer_state = &mut *peer_state_lock;
4817+
let chan = match peer_state.channel_by_id.get(&channel_id) {
4818+
Some(chan) => chan,
4819+
None => return false,
4820+
};
4821+
chan.check_worth_upstream_closing(
4822+
*htlc_id, &self.fee_estimator, &self.logger).unwrap_or(false)
4823+
}
4824+
47944825
/// Fails an HTLC backwards to the sender of it to us.
47954826
/// Note that we do not assume that channels corresponding to failed HTLCs are still available.
47964827
fn fail_htlc_backwards_internal(&self, source: &HTLCSource, payment_hash: &PaymentHash, onion_error: &HTLCFailReason, destination: HTLCDestination) {
@@ -6338,10 +6369,12 @@ where
63386369
log_trace!(self.logger, "Claiming HTLC with preimage {} from our monitor", &preimage);
63396370
self.claim_funds_internal(htlc_update.source, preimage, htlc_update.htlc_value_satoshis.map(|v| v * 1000), true, funding_outpoint);
63406371
} else {
6341-
log_trace!(self.logger, "Failing HTLC with hash {} from our monitor", &htlc_update.payment_hash);
6342-
let receiver = HTLCDestination::NextHopChannel { node_id: counterparty_node_id, channel_id: funding_outpoint.to_channel_id() };
6343-
let reason = HTLCFailReason::from_failure_code(0x4000 | 8);
6344-
self.fail_htlc_backwards_internal(&htlc_update.source, &htlc_update.payment_hash, &reason, receiver);
6372+
if !htlc_update.awaiting_downstream_confirmation || !self.check_worth_upstream_closing(&htlc_update.source) {
6373+
log_trace!(self.logger, "Failing HTLC with hash {} from our monitor", &htlc_update.payment_hash);
6374+
let receiver = HTLCDestination::NextHopChannel { node_id: counterparty_node_id, channel_id: funding_outpoint.to_channel_id() };
6375+
let reason = HTLCFailReason::from_failure_code(0x4000 | 8);
6376+
self.fail_htlc_backwards_internal(&htlc_update.source, &htlc_update.payment_hash, &reason, receiver);
6377+
}
63456378
}
63466379
},
63476380
MonitorEvent::CommitmentTxConfirmed(funding_outpoint) |

0 commit comments

Comments
 (0)