Skip to content

Commit 8b97e09

Browse files
committed
f: log resolved HTLC preimage losses
Log when a replayed preimage claim is skipped because the HTLC output reached anti-reorg finality without that preimage.
1 parent 125303a commit 8b97e09

1 file changed

Lines changed: 64 additions & 14 deletions

File tree

lightning/src/chain/channelmonitor.rs

Lines changed: 64 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3813,7 +3813,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
38133813
// First check if a counterparty commitment transaction has been broadcasted:
38143814
macro_rules! claim_htlcs {
38153815
($commitment_number: expr, $txid: expr, $htlcs: expr) => {
3816-
let htlc_claim_reqs = self.get_counterparty_output_claims_for_preimage(*payment_preimage, funding_spent, $commitment_number, $txid, $htlcs, confirmed_spend_height);
3816+
let htlc_claim_reqs = self.get_counterparty_output_claims_for_preimage(*payment_preimage, funding_spent, $commitment_number, $txid, $htlcs, confirmed_spend_height, logger);
38173817
let conf_target = self.closure_conf_target();
38183818
self.onchain_tx_handler.update_claims_view_from_requests(
38193819
htlc_claim_reqs, self.best_block.height, self.best_block.height, broadcaster,
@@ -3862,6 +3862,9 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
38623862
None
38633863
};
38643864
if let Some(holder_commitment_tx) = holder_commitment_tx {
3865+
self.log_holder_preimage_claim_after_htlc_resolved_on_chain(
3866+
logger, holder_commitment_tx, *payment_preimage,
3867+
);
38653868
// Assume that the broadcasted commitment transaction confirmed in the current best
38663869
// block. Even if not, its a reasonable metric for the bump criteria on the HTLC
38673870
// transactions.
@@ -4965,11 +4968,11 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
49654968
}
49664969
}
49674970

4968-
fn get_counterparty_output_claims_for_preimage(
4971+
fn get_counterparty_output_claims_for_preimage<L: Logger>(
49694972
&self, preimage: PaymentPreimage, funding_spent: &FundingScope, commitment_number: u64,
49704973
commitment_txid: Txid,
49714974
per_commitment_option: Option<&Vec<(HTLCOutputInCommitment, Option<Box<HTLCSource>>)>>,
4972-
confirmation_height: Option<u32>,
4975+
confirmation_height: Option<u32>, logger: &L,
49734976
) -> Vec<ClaimRequest> {
49744977
let per_commitment_claimable_data = match per_commitment_option {
49754978
Some(outputs) => outputs,
@@ -4985,10 +4988,17 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
49854988
.iter()
49864989
.filter_map(|(htlc, _)| {
49874990
if let Some(transaction_output_index) = htlc.transaction_output_index {
4988-
if htlc.offered
4989-
&& htlc.payment_hash == matching_payment_hash
4990-
&& !self.is_htlc_output_resolved_on_chain(htlc)
4991-
{
4991+
if htlc.offered && htlc.payment_hash == matching_payment_hash {
4992+
if let Some(resolved_htlc) = self.htlc_output_resolution_on_chain(htlc) {
4993+
self.log_preimage_claim_after_htlc_resolved_on_chain(
4994+
logger,
4995+
commitment_txid,
4996+
htlc,
4997+
preimage,
4998+
resolved_htlc,
4999+
);
5000+
return None;
5001+
}
49925002
let htlc_data = PackageSolvingData::CounterpartyOfferedHTLCOutput(
49935003
CounterpartyOfferedHTLCOutput::build(
49945004
per_commitment_point,
@@ -5014,17 +5024,57 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
50145024
.collect()
50155025
}
50165026

5017-
fn is_htlc_output_resolved_on_chain(&self, htlc: &HTLCOutputInCommitment) -> bool {
5018-
if let Some(transaction_output_index) = htlc.transaction_output_index {
5027+
fn htlc_output_resolution_on_chain(
5028+
&self, htlc: &HTLCOutputInCommitment,
5029+
) -> Option<&IrrevocablyResolvedHTLC> {
5030+
htlc.transaction_output_index.and_then(|transaction_output_index| {
50195031
// Only suppress claims once the commitment HTLC output spend has
50205032
// reached anti-reorg finality. Any output created by that spend may
50215033
// still be CSV-delayed, but the original HTLC outpoint should not be
50225034
// re-claimed.
5023-
self.htlcs_resolved_on_chain.iter().any(|resolved_htlc| {
5035+
self.htlcs_resolved_on_chain.iter().find(|resolved_htlc| {
50245036
resolved_htlc.commitment_tx_output_idx == Some(transaction_output_index)
50255037
})
5026-
} else {
5027-
false
5038+
})
5039+
}
5040+
5041+
fn log_preimage_claim_after_htlc_resolved_on_chain<L: Logger>(
5042+
&self, logger: &L, commitment_txid: Txid, htlc: &HTLCOutputInCommitment,
5043+
preimage: PaymentPreimage, resolved_htlc: &IrrevocablyResolvedHTLC,
5044+
) {
5045+
if resolved_htlc.payment_preimage == Some(preimage) {
5046+
return;
5047+
}
5048+
if let Some(transaction_output_index) = htlc.transaction_output_index {
5049+
let logger = WithContext::from(logger, None, None, Some(htlc.payment_hash));
5050+
if let Some(resolving_txid) = resolved_htlc.resolving_txid.as_ref() {
5051+
log_error!(logger, "WE HAVE LIKELY LOST FUNDS: HTLC output {}:{} was irrevocably resolved on-chain by transaction {} without the payment preimage we now know; not replaying the claim",
5052+
commitment_txid, transaction_output_index, resolving_txid);
5053+
} else {
5054+
log_error!(logger, "WE HAVE LIKELY LOST FUNDS: HTLC output {}:{} was irrevocably resolved on-chain by an unknown transaction without the payment preimage we now know; not replaying the claim",
5055+
commitment_txid, transaction_output_index);
5056+
}
5057+
}
5058+
}
5059+
5060+
fn log_holder_preimage_claim_after_htlc_resolved_on_chain<L: Logger>(
5061+
&self, logger: &L, holder_tx: &HolderCommitmentTransaction, preimage: PaymentPreimage,
5062+
) {
5063+
let matching_payment_hash = PaymentHash::from(preimage);
5064+
let tx = holder_tx.trust();
5065+
for htlc in holder_tx.nondust_htlcs() {
5066+
if htlc.offered || htlc.payment_hash != matching_payment_hash {
5067+
continue;
5068+
}
5069+
if let Some(resolved_htlc) = self.htlc_output_resolution_on_chain(htlc) {
5070+
self.log_preimage_claim_after_htlc_resolved_on_chain(
5071+
logger,
5072+
tx.txid(),
5073+
htlc,
5074+
preimage,
5075+
resolved_htlc,
5076+
);
5077+
}
50285078
}
50295079
}
50305080

@@ -5075,7 +5125,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
50755125
// per_commitment_data is corrupt or our commitment signing key leaked!
50765126
return (claimable_outpoints, to_counterparty_output_info);
50775127
}
5078-
if self.is_htlc_output_resolved_on_chain(htlc) {
5128+
if self.htlc_output_resolution_on_chain(htlc).is_some() {
50795129
continue;
50805130
}
50815131
let preimage = if htlc.offered {
@@ -5179,7 +5229,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
51795229
let mut htlcs = Vec::with_capacity(holder_tx.nondust_htlcs().len());
51805230
debug_assert_eq!(holder_tx.nondust_htlcs().len(), holder_tx.counterparty_htlc_sigs.len());
51815231
for (htlc, counterparty_sig) in holder_tx.nondust_htlcs().iter().zip(holder_tx.counterparty_htlc_sigs.iter()) {
5182-
if self.is_htlc_output_resolved_on_chain(htlc) {
5232+
if self.htlc_output_resolution_on_chain(htlc).is_some() {
51835233
continue;
51845234
}
51855235
assert!(htlc.transaction_output_index.is_some(), "Expected transaction output index for non-dust HTLC");

0 commit comments

Comments
 (0)