Skip to content

Commit 86b53fa

Browse files
committed
Wait for reorg safety when promoting zero conf funding scopes
If we don't, it's possible that an alternative funding transaction confirms instead of the one that was locked and we're not able to broadcast a holder commitment to recover our funds.
1 parent 3e5f0b0 commit 86b53fa

File tree

1 file changed

+75
-12
lines changed

1 file changed

+75
-12
lines changed

lightning/src/chain/channelmonitor.rs

Lines changed: 75 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,9 @@ enum OnchainEvent {
534534
},
535535
/// An output waiting on [`ANTI_REORG_DELAY`] confirmations before we hand the user the
536536
/// [`SpendableOutputDescriptor`].
537-
MaturingOutput { descriptor: SpendableOutputDescriptor },
537+
MaturingOutput {
538+
descriptor: SpendableOutputDescriptor,
539+
},
538540
/// A spend of the funding output, either a commitment transaction or a cooperative closing
539541
/// transaction.
540542
FundingSpendConfirmation {
@@ -578,6 +580,16 @@ enum OnchainEvent {
578580
// considered locked.
579581
confirmation_depth: u32,
580582
},
583+
// We've negotiated and locked (via a `RenegotiatedFundingLocked` monitor update) a zero conf
584+
// funding transcation. If confirmations were required, we'd remove all alternative funding
585+
// transactions and their associated holder commitment transactions, as we can assume they can
586+
// no longer confirm. However, with zero conf funding transactions, we cannot guarantee this.
587+
// Ultimately an alternative funding transaction could actually end up on chain, and we must
588+
// still be able to claim our funds from it.
589+
//
590+
// This event will only be generated when a `RenegotiatedFundingLocked` monitor update is
591+
// applied for a zero conf funding transaction.
592+
ZeroConfFundingReorgSafety {},
581593
}
582594

583595
impl Writeable for OnchainEventEntry {
@@ -629,6 +641,7 @@ impl_writeable_tlv_based_enum_upgradable!(OnchainEvent,
629641
(0, on_local_output_csv, option),
630642
(1, commitment_tx_to_counterparty_output, option),
631643
},
644+
(4, ZeroConfFundingReorgSafety) => {},
632645
(5, HTLCSpendConfirmation) => {
633646
(0, commitment_tx_output_idx, required),
634647
(2, preimage, option),
@@ -1299,6 +1312,11 @@ pub(crate) struct ChannelMonitorImpl<Signer: EcdsaChannelSigner> {
12991312
// commitment transactions, their ordering with respect to each other must remain the same.
13001313
current_holder_htlc_data: CommitmentHTLCData,
13011314
prev_holder_htlc_data: Option<CommitmentHTLCData>,
1315+
1316+
// If a funding transaction has been renegotiated for the channel and it requires zero
1317+
// confirmations to be considered locked, we wait for `ANTI_REORG_DELAY` confirmations until we
1318+
// no longer track alternative funding candidates.
1319+
wait_for_0conf_funding_reorg_safety: bool,
13021320
}
13031321

13041322
// Macro helper to access holder commitment HTLC data (including both non-dust and dust) while
@@ -1556,6 +1574,8 @@ impl<Signer: EcdsaChannelSigner> Writeable for ChannelMonitorImpl<Signer> {
15561574
_ => self.pending_monitor_events.clone(),
15571575
};
15581576

1577+
let wait_for_0conf_funding_reorg_safety = self.wait_for_0conf_funding_reorg_safety.then(|| ());
1578+
15591579
write_tlv_fields!(writer, {
15601580
(1, self.funding_spend_confirmed, option),
15611581
(3, self.htlcs_resolved_on_chain, required_vec),
@@ -1574,6 +1594,7 @@ impl<Signer: EcdsaChannelSigner> Writeable for ChannelMonitorImpl<Signer> {
15741594
(29, self.initial_counterparty_commitment_tx, option),
15751595
(31, self.funding.channel_parameters, required),
15761596
(32, self.pending_funding, optional_vec),
1597+
(34, wait_for_0conf_funding_reorg_safety, option),
15771598
});
15781599

15791600
Ok(())
@@ -1799,6 +1820,8 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
17991820
// There are never any HTLCs in the initial commitment transaction
18001821
current_holder_htlc_data: CommitmentHTLCData::new(),
18011822
prev_holder_htlc_data: None,
1823+
1824+
wait_for_0conf_funding_reorg_safety: false,
18021825
})
18031826
}
18041827

@@ -3835,23 +3858,29 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
38353858
Ok(())
38363859
}
38373860

3838-
fn promote_funding(&mut self, new_funding_txid: Txid) -> Result<(), ()> {
3861+
fn promote_funding(
3862+
&mut self, new_funding_txid: Txid, from_monitor_update: bool,
3863+
) -> Result<(), ()> {
38393864
let new_funding = self
38403865
.pending_funding
38413866
.iter_mut()
3842-
.map(|pending_funding| &mut pending_funding.0)
3843-
.find(|funding| funding.funding_txid() == new_funding_txid);
3867+
.find(|pending_funding| pending_funding.0.funding_txid() == new_funding_txid);
38443868
if new_funding.is_none() {
38453869
return Err(());
38463870
}
3847-
let mut new_funding = new_funding.unwrap();
3871+
let (new_funding, confirmation_depth) = new_funding.unwrap();
38483872

3849-
mem::swap(&mut self.funding, &mut new_funding);
3873+
mem::swap(&mut self.funding, new_funding);
38503874
self.onchain_tx_handler.update_after_renegotiated_funding_locked(
38513875
self.funding.current_holder_commitment_tx.clone(),
38523876
self.funding.prev_holder_commitment_tx.clone(),
38533877
);
38543878

3879+
if from_monitor_update && *confirmation_depth == 0 {
3880+
self.wait_for_0conf_funding_reorg_safety = true;
3881+
return Ok(());
3882+
}
3883+
38553884
// The swap above places the previous `FundingScope` into `pending_funding`.
38563885
for (funding, _) in self.pending_funding.drain(..) {
38573886
self.outputs_to_watch.remove(&funding.funding_txid());
@@ -3982,7 +4011,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
39824011
},
39834012
ChannelMonitorUpdateStep::RenegotiatedFundingLocked { funding_txid } => {
39844013
log_trace!(logger, "Updating ChannelMonitor with locked renegotiated funding txid {}", funding_txid);
3985-
if let Err(_) = self.promote_funding(*funding_txid) {
4014+
if let Err(_) = self.promote_funding(*funding_txid, true) {
39864015
log_error!(logger, "Unknown funding with txid {} became locked", funding_txid);
39874016
ret = Err(());
39884017
}
@@ -4940,6 +4969,20 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
49404969
}
49414970
}
49424971

4972+
// A zero-conf renegotiated funding transaction has confirmed. We should have already
4973+
// applied the corresponding `RenegotiatedFundingLocked` monitor update for it, but
4974+
// we're still tracking the alternative funding transactions until we're reorg safe.
4975+
if self.wait_for_0conf_funding_reorg_safety && self.funding.funding_txid() == txid {
4976+
debug_assert!(self.funding_spend_confirmed.is_none());
4977+
self.onchain_events_awaiting_threshold_conf.push(OnchainEventEntry {
4978+
txid,
4979+
transaction: Some((*tx).clone()),
4980+
height,
4981+
block_hash: Some(block_hash),
4982+
event: OnchainEvent::ZeroConfFundingReorgSafety {},
4983+
});
4984+
}
4985+
49434986
// A splice transaction has confirmed. We can't promote the splice's scope until we see
49444987
// the corresponding monitor update for it, but we track the txid so we know which
49454988
// holder commitment transaction we may need to broadcast.
@@ -5212,19 +5255,35 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
52125255
},
52135256
OnchainEvent::AlternativeFundingConfirmation { .. } => {
52145257
// An alternative funding transaction has irrevocably confirmed. Locate the
5215-
// corresponding scope and promote it if the monitor is no longer allowing
5216-
// updates. Otherwise, we expect it to be promoted via
5217-
// [`ChannelMonitorUpdateStep::RenegotiatedFundingLocked`].
5218-
if self.no_further_updates_allowed() {
5258+
// corresponding scope and promote it. If we're still allowing monitor updates,
5259+
// we expect it to be promoted via
5260+
// [`ChannelMonitorUpdateStep::RenegotiatedFundingLocked`] instead. However, in
5261+
// the zero conf funding case, while we already promoted the scope, we're still
5262+
// tracking the alternative funding candidates until we're reorg safe.
5263+
if self.no_further_updates_allowed() || self.wait_for_0conf_funding_reorg_safety {
52195264
let funding_txid = entry.transaction
52205265
.expect("Transactions are always present for AlternativeFundingConfirmation entries")
52215266
.compute_txid();
52225267
debug_assert_ne!(self.funding.funding_txid(), funding_txid);
5223-
if let Err(_) = self.promote_funding(funding_txid) {
5268+
if let Err(_) = self.promote_funding(funding_txid, false) {
52245269
log_error!(logger, "Missing scope for alternative funding confirmation with txid {}", entry.txid);
52255270
}
52265271
}
52275272
},
5273+
OnchainEvent::ZeroConfFundingReorgSafety {} => {
5274+
if self.wait_for_0conf_funding_reorg_safety {
5275+
debug_assert_eq!(self.funding.funding_txid(), entry.txid);
5276+
self
5277+
.pending_funding
5278+
.drain(..)
5279+
.for_each(|(funding, _)| {
5280+
self.outputs_to_watch.remove(&funding.funding_txid());
5281+
});
5282+
self.wait_for_0conf_funding_reorg_safety = false;
5283+
} else {
5284+
debug_assert!(false, "These events can only be generated when wait_for_0conf_funding_reorg_safety is set");
5285+
}
5286+
},
52285287
}
52295288
}
52305289

@@ -6073,6 +6132,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
60736132
let mut first_negotiated_funding_txo = RequiredWrapper(None);
60746133
let mut channel_parameters = None;
60756134
let mut pending_funding = None;
6135+
let mut wait_for_0conf_funding_reorg_safety: Option<()> = None;
60766136
read_tlv_fields!(reader, {
60776137
(1, funding_spend_confirmed, option),
60786138
(3, htlcs_resolved_on_chain, optional_vec),
@@ -6091,6 +6151,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
60916151
(29, initial_counterparty_commitment_tx, option),
60926152
(31, channel_parameters, (option: ReadableArgs, None)),
60936153
(32, pending_funding, optional_vec),
6154+
(34, wait_for_0conf_funding_reorg_safety, option),
60946155
});
60956156
if let Some(payment_preimages_with_info) = payment_preimages_with_info {
60966157
if payment_preimages_with_info.len() != payment_preimages.len() {
@@ -6260,6 +6321,8 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
62606321

62616322
current_holder_htlc_data,
62626323
prev_holder_htlc_data,
6324+
6325+
wait_for_0conf_funding_reorg_safety: wait_for_0conf_funding_reorg_safety.is_some(),
62636326
})))
62646327
}
62656328
}

0 commit comments

Comments
 (0)