Skip to content

Commit 48629c7

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 1802a6b commit 48629c7

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),
@@ -1301,6 +1314,11 @@ pub(crate) struct ChannelMonitorImpl<Signer: EcdsaChannelSigner> {
13011314
// commitment transactions, their ordering with respect to each other must remain the same.
13021315
current_holder_htlc_data: CommitmentHTLCData,
13031316
prev_holder_htlc_data: Option<CommitmentHTLCData>,
1317+
1318+
// If a funding transaction has been renegotiated for the channel and it requires zero
1319+
// confirmations to be considered locked, we wait for `ANTI_REORG_DELAY` confirmations until we
1320+
// no longer track alternative funding candidates.
1321+
wait_for_0conf_funding_reorg_safety: bool,
13041322
}
13051323

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

1579+
let wait_for_0conf_funding_reorg_safety = self.wait_for_0conf_funding_reorg_safety.then(|| ());
1580+
15611581
write_tlv_fields!(writer, {
15621582
(1, self.funding_spend_confirmed, option),
15631583
(3, self.htlcs_resolved_on_chain, required_vec),
@@ -1576,6 +1596,7 @@ impl<Signer: EcdsaChannelSigner> Writeable for ChannelMonitorImpl<Signer> {
15761596
(29, self.initial_counterparty_commitment_tx, option),
15771597
(31, self.funding.channel_parameters, required),
15781598
(32, self.pending_funding, optional_vec),
1599+
(34, wait_for_0conf_funding_reorg_safety, option),
15791600
});
15801601

15811602
Ok(())
@@ -1801,6 +1822,8 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
18011822
// There are never any HTLCs in the initial commitment transaction
18021823
current_holder_htlc_data: CommitmentHTLCData::new(),
18031824
prev_holder_htlc_data: None,
1825+
1826+
wait_for_0conf_funding_reorg_safety: false,
18041827
})
18051828
}
18061829

@@ -3837,23 +3860,29 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
38373860
Ok(())
38383861
}
38393862

3840-
fn promote_funding(&mut self, new_funding_txid: Txid) -> Result<(), ()> {
3863+
fn promote_funding(
3864+
&mut self, new_funding_txid: Txid, from_monitor_update: bool,
3865+
) -> Result<(), ()> {
38413866
let new_funding = self
38423867
.pending_funding
38433868
.iter_mut()
3844-
.map(|pending_funding| &mut pending_funding.0)
3845-
.find(|funding| funding.funding_txid() == new_funding_txid);
3869+
.find(|pending_funding| pending_funding.0.funding_txid() == new_funding_txid);
38463870
if new_funding.is_none() {
38473871
return Err(());
38483872
}
3849-
let mut new_funding = new_funding.unwrap();
3873+
let (new_funding, confirmation_depth) = new_funding.unwrap();
38503874

3851-
mem::swap(&mut self.funding, &mut new_funding);
3875+
mem::swap(&mut self.funding, new_funding);
38523876
self.onchain_tx_handler.update_after_renegotiated_funding_locked(
38533877
self.funding.current_holder_commitment_tx.clone(),
38543878
self.funding.prev_holder_commitment_tx.clone(),
38553879
);
38563880

3881+
if from_monitor_update && *confirmation_depth == 0 {
3882+
self.wait_for_0conf_funding_reorg_safety = true;
3883+
return Ok(());
3884+
}
3885+
38573886
// The swap above places the previous `FundingScope` into `pending_funding`.
38583887
for (funding, _) in self.pending_funding.drain(..) {
38593888
self.outputs_to_watch.remove(&funding.funding_txid());
@@ -3984,7 +4013,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
39844013
},
39854014
ChannelMonitorUpdateStep::RenegotiatedFundingLocked { funding_txid } => {
39864015
log_trace!(logger, "Updating ChannelMonitor with locked renegotiated funding txid {}", funding_txid);
3987-
if let Err(_) = self.promote_funding(*funding_txid) {
4016+
if let Err(_) = self.promote_funding(*funding_txid, true) {
39884017
log_error!(logger, "Unknown funding with txid {} became locked", funding_txid);
39894018
ret = Err(());
39904019
}
@@ -4942,6 +4971,20 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
49424971
}
49434972
}
49444973

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

@@ -6075,6 +6134,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
60756134
let mut first_negotiated_funding_txo = RequiredWrapper(None);
60766135
let mut channel_parameters = None;
60776136
let mut pending_funding = None;
6137+
let mut wait_for_0conf_funding_reorg_safety: Option<()> = None;
60786138
read_tlv_fields!(reader, {
60796139
(1, funding_spend_confirmed, option),
60806140
(3, htlcs_resolved_on_chain, optional_vec),
@@ -6093,6 +6153,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
60936153
(29, initial_counterparty_commitment_tx, option),
60946154
(31, channel_parameters, (option: ReadableArgs, None)),
60956155
(32, pending_funding, optional_vec),
6156+
(34, wait_for_0conf_funding_reorg_safety, option),
60966157
});
60976158
if let Some(payment_preimages_with_info) = payment_preimages_with_info {
60986159
if payment_preimages_with_info.len() != payment_preimages.len() {
@@ -6262,6 +6323,8 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
62626323

62636324
current_holder_htlc_data,
62646325
prev_holder_htlc_data,
6326+
6327+
wait_for_0conf_funding_reorg_safety: wait_for_0conf_funding_reorg_safety.is_some(),
62656328
})))
62666329
}
62676330
}

0 commit comments

Comments
 (0)