-
Notifications
You must be signed in to change notification settings - Fork 402
Fail HTLC backwards before upstream claims on-chain #2457
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
61d72f8
aa5d7a9
ccbaba9
d2873bc
3d1d830
58e01e2
d96a272
34c48d9
9e6bf86
e71d338
597111b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2100,6 +2100,45 @@ impl<SP: Deref> Channel<SP> where | |
Ok(()) | ||
} | ||
|
||
/// The total fee paid to close the channel and claim an HTLC-success transaction, accounting | ||
/// for an added input (spending a P2WPKH) and P2WPKH output for fee bumping. | ||
fn cost_to_claim_onchain<L: Deref>(&self, feerate_per_kw: u32, logger: &L) -> u64 | ||
where L::Target: Logger | ||
{ | ||
let commitment_weight = if self.context.is_outbound() { | ||
let keys = self.context.build_holder_transaction_keys(self.context.cur_holder_commitment_transaction_number); | ||
let commitment_stats = self.context.build_commitment_transaction(self.context.cur_holder_commitment_transaction_number, &keys, true, true, logger); | ||
commitment_stats.total_fee_sat | ||
} else { 0 }; | ||
|
||
let htlc_weight = htlc_success_tx_weight(self.context.get_channel_type()); | ||
let p2wpkh_weight = 31 * 4; | ||
let input_weight = 272; // Including witness, assuming it spends from a p2wpkh | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FYI some of these are already defined in
Comment on lines
+2115
to
+2116
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These only apply with anchors, otherwise the HTLC success transaction has the same feerate as the commitment transaction. |
||
|
||
let total_weight = commitment_weight + htlc_weight + p2wpkh_weight + input_weight; | ||
let total_fee = feerate_per_kw as u64 * total_weight / 1000; | ||
total_fee | ||
} | ||
Comment on lines
+2118
to
+2121
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh also, for now I’ve only calculated the total fee if we were to claim just the one HTLC tx, but we may also want to consider all the fees we'd pay if we were to claim other pending HTLCs (though it's not clear which ones we'll end up having to claim ourselves vs our counterparty) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Ah because there could be HTLCs that expire much later than the HTLC we're attempting to fail back and the counterparty still has a chance to claim those onchain? |
||
|
||
/// Check whether we should risk letting this channel force-close while waiting | ||
/// for a downstream HTLC resolution on-chain. See [`ChannelConfig::early_fail_multiplier`] | ||
/// for more info. | ||
pub(super) fn check_worth_upstream_closing<F: Deref, L: Deref>( | ||
&self, htlc_id: u64, fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L | ||
) -> Option<bool> | ||
where F::Target: FeeEstimator, L::Target: Logger | ||
{ | ||
let feerate_per_kw = fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::HighPriority); | ||
let htlc = self.context.pending_inbound_htlcs.iter().find(|htlc| htlc.htlc_id == htlc_id)?; | ||
let total_fee = self.cost_to_claim_onchain(feerate_per_kw, logger); | ||
let threshold = total_fee * self.context.config().early_fail_multiplier / 1_000_000; | ||
if htlc.amount_msat / 1000 >= threshold { | ||
Some(true) | ||
} else { | ||
Some(false) | ||
} | ||
} | ||
|
||
#[inline] | ||
fn get_closing_scriptpubkey(&self) -> Script { | ||
// The shutdown scriptpubkey is set on channel opening when option_upfront_shutdown_script | ||
|
@@ -2238,10 +2277,6 @@ impl<SP: Deref> Channel<SP> where | |
} | ||
} | ||
if pending_idx == core::usize::MAX { | ||
#[cfg(any(test, fuzzing))] | ||
// If we failed to find an HTLC to fulfill, make sure it was previously fulfilled and | ||
// this is simply a duplicate claim, not previously failed and we lost funds. | ||
debug_assert!(self.context.historical_inbound_htlc_fulfills.contains(&htlc_id_arg)); | ||
return UpdateFulfillFetch::DuplicateClaim {}; | ||
} | ||
|
||
|
@@ -2410,10 +2445,6 @@ impl<SP: Deref> Channel<SP> where | |
} | ||
} | ||
if pending_idx == core::usize::MAX { | ||
#[cfg(any(test, fuzzing))] | ||
// If we failed to find an HTLC to fail, make sure it was previously fulfilled and this | ||
// is simply a duplicate fail, not previously failed and we failed-back too early. | ||
debug_assert!(self.context.historical_inbound_htlc_fulfills.contains(&htlc_id_arg)); | ||
return Ok(None); | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be returning the weight here, not the fee.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should be able to compute the weight without re-building the transaction, you may need a few more consts than we have defined, and it'll need to depend on the channel type.