@@ -531,7 +531,9 @@ enum OnchainEvent {
531531 } ,
532532 /// An output waiting on [`ANTI_REORG_DELAY`] confirmations before we hand the user the
533533 /// [`SpendableOutputDescriptor`].
534- MaturingOutput { descriptor : SpendableOutputDescriptor } ,
534+ MaturingOutput {
535+ descriptor : SpendableOutputDescriptor ,
536+ } ,
535537 /// A spend of the funding output, either a commitment transaction or a cooperative closing
536538 /// transaction.
537539 FundingSpendConfirmation {
@@ -572,6 +574,17 @@ enum OnchainEvent {
572574 /// once [`ChannelMonitor::no_further_updates_allowed`] returns true. We promote the
573575 /// `FundingScope` once the funding transaction is irrevocably confirmed.
574576 AlternativeFundingConfirmation { } ,
577+ // We've negotiated and locked (via a [`ChannelMonitorUpdateStep::RenegotiatedFundingLocked`]) a
578+ // zero conf funding transcation. If confirmations were required, we'd remove all alternative
579+ // funding transactions and their associated holder commitment transactions, as we assume they
580+ // can no longer confirm. However, with zero conf funding transactions, we cannot guarantee
581+ // this. Ultimately an alternative funding transaction could actually end up on chain, and we
582+ // must still be able to claim our funds from it.
583+ //
584+ // This event will only be generated when a
585+ // [`ChannelMonitorUpdateStep::RenegotiatedFundingLocked`] is applied for a zero conf funding
586+ // transaction.
587+ ZeroConfFundingReorgSafety { } ,
575588}
576589
577590impl Writeable for OnchainEventEntry {
@@ -621,6 +634,7 @@ impl_writeable_tlv_based_enum_upgradable!(OnchainEvent,
621634 ( 0 , on_local_output_csv, option) ,
622635 ( 1 , commitment_tx_to_counterparty_output, option) ,
623636 } ,
637+ ( 4 , ZeroConfFundingReorgSafety ) => { } ,
624638 ( 5 , HTLCSpendConfirmation ) => {
625639 ( 0 , commitment_tx_output_idx, required) ,
626640 ( 2 , preimage, option) ,
@@ -1295,6 +1309,11 @@ pub(crate) struct ChannelMonitorImpl<Signer: EcdsaChannelSigner> {
12951309 prev_holder_htlc_data : Option < CommitmentHTLCData > ,
12961310
12971311 alternative_funding_confirmed : Option < ( Txid , u32 ) > ,
1312+
1313+ // If a funding transaction has been renegotiated for the channel and it requires zero
1314+ // confirmations to be considered locked, we wait for `ANTI_REORG_DELAY` confirmations until we
1315+ // no longer track alternative funding candidates.
1316+ wait_for_0conf_funding_reorg_safety : bool ,
12981317}
12991318
13001319// Macro helper to access holder commitment HTLC data (including both non-dust and dust) while
@@ -1552,6 +1571,8 @@ impl<Signer: EcdsaChannelSigner> Writeable for ChannelMonitorImpl<Signer> {
15521571 _ => self . pending_monitor_events . clone ( ) ,
15531572 } ;
15541573
1574+ let wait_for_0conf_funding_reorg_safety = self . wait_for_0conf_funding_reorg_safety . then ( || ( ) ) ;
1575+
15551576 write_tlv_fields ! ( writer, {
15561577 ( 1 , self . funding_spend_confirmed, option) ,
15571578 ( 3 , self . htlcs_resolved_on_chain, required_vec) ,
@@ -1571,6 +1592,7 @@ impl<Signer: EcdsaChannelSigner> Writeable for ChannelMonitorImpl<Signer> {
15711592 ( 31 , self . funding. channel_parameters, required) ,
15721593 ( 32 , self . pending_funding, optional_vec) ,
15731594 ( 34 , self . alternative_funding_confirmed, option) ,
1595+ ( 36 , wait_for_0conf_funding_reorg_safety, option) ,
15741596 } ) ;
15751597
15761598 Ok ( ( ) )
@@ -1798,6 +1820,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
17981820 prev_holder_htlc_data : None ,
17991821
18001822 alternative_funding_confirmed : None ,
1823+ wait_for_0conf_funding_reorg_safety : false ,
18011824 } )
18021825 }
18031826
@@ -3419,6 +3442,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
34193442 self . update_holder_commitment_data ( vec ! [ holder_commitment_tx] , htlc_data, claimed_htlcs)
34203443 }
34213444
3445+ // TODO(splicing): Consider `wait_for_0conf_funding_reorg_safety`.
34223446 fn verify_matching_commitment_transactions <
34233447 ' a ,
34243448 I : ExactSizeIterator < Item = & ' a CommitmentTransaction > ,
@@ -3795,14 +3819,16 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
37953819 if let Some ( parent_funding_txid) = channel_parameters. splice_parent_funding_txid . as_ref ( ) {
37963820 // Only one splice can be negotiated at a time after we've exchanged `channel_ready`
37973821 // (implying our funding is confirmed) that spends our currently locked funding.
3798- if !self . pending_funding . is_empty ( ) {
3822+ if !self . pending_funding . is_empty ( ) && ! self . wait_for_0conf_funding_reorg_safety {
37993823 log_error ! (
38003824 logger,
38013825 "Negotiated splice while channel is pending channel_ready/splice_locked"
38023826 ) ;
38033827 return Err ( ( ) ) ;
38043828 }
3805- if * parent_funding_txid != self . funding . funding_txid ( ) {
3829+ if * parent_funding_txid != self . funding . funding_txid ( )
3830+ || alternative_funding_outpoint. txid != self . funding . funding_txid ( )
3831+ {
38063832 log_error ! (
38073833 logger,
38083834 "Negotiated splice that does not spend currently locked funding transaction"
@@ -3822,6 +3848,11 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
38223848 ) ;
38233849 self . pending_funding . push ( alternative_funding) ;
38243850
3851+ // FIXME: Wait for anything below `ANTI_REORG_DELAY`, not just zero conf?
3852+ if !self . wait_for_0conf_funding_reorg_safety {
3853+ self . wait_for_0conf_funding_reorg_safety = confirmation_depth == 0 ;
3854+ }
3855+
38253856 Ok ( ( ) )
38263857 }
38273858
@@ -3841,12 +3872,20 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
38413872 self . funding . prev_holder_commitment_tx . clone ( ) ,
38423873 ) ;
38433874
3875+ if self . wait_for_0conf_funding_reorg_safety {
3876+ return Ok ( ( ) ) ;
3877+ }
3878+
38443879 // The swap above places the previous `FundingScope` into `pending_funding`.
38453880 for funding in self . pending_funding . drain ( ..) {
38463881 self . outputs_to_watch . remove ( & funding. funding_txid ( ) ) ;
38473882 }
38483883 self . alternative_funding_confirmed . take ( ) ;
38493884
3885+ // Make sure we're no longer waiting for zero conf funding reorg safety if a non-zero conf
3886+ // splice is negotiated and locked after a zero conf one.
3887+ self . wait_for_0conf_funding_reorg_safety = false ;
3888+
38503889 Ok ( ( ) )
38513890 }
38523891
@@ -4926,20 +4965,33 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
49264965 }
49274966 }
49284967
4929- // A splice/dual-funded RBF transaction has confirmed. We can't promote the
4930- // `FundingScope` scope until we see the
4968+ // A splice/dual-funded RBF transaction has confirmed. If it's not zero conf, we can't
4969+ // promote the `FundingScope` scope until we see the
49314970 // [`ChannelMonitorUpdateStep::RenegotiatedFundingLocked`] for it, but we track the txid
49324971 // so we know which holder commitment transaction we may need to broadcast.
4933- if let Some ( alternative_funding) = self . pending_funding . iter ( )
4972+ if let Some ( ( confirmed_funding, is_funding_locked) ) = self
4973+ . pending_funding
4974+ . iter ( )
49344975 . find ( |funding| funding. funding_txid ( ) == txid)
4976+ . map ( |funding| ( funding, false ) )
4977+ . or_else ( || {
4978+ ( self . funding . funding_txid ( ) == txid) . then ( || & self . funding )
4979+ . filter ( |_| self . wait_for_0conf_funding_reorg_safety )
4980+ . map ( |funding| ( funding, true ) )
4981+ } )
49354982 {
4983+ debug_assert ! ( self . alternative_funding_confirmed. is_none( ) ) ;
4984+ debug_assert ! (
4985+ !self . onchain_events_awaiting_threshold_conf. iter( )
4986+ . any( |e| matches!( e. event, OnchainEvent :: AlternativeFundingConfirmation { } ) )
4987+ ) ;
49364988 debug_assert ! ( self . funding_spend_confirmed. is_none( ) ) ;
49374989 debug_assert ! (
49384990 !self . onchain_events_awaiting_threshold_conf. iter( )
49394991 . any( |e| matches!( e. event, OnchainEvent :: FundingSpendConfirmation { .. } ) )
49404992 ) ;
49414993
4942- let ( desc, msg) = if alternative_funding . channel_parameters . splice_parent_funding_txid . is_some ( ) {
4994+ let ( desc, msg) = if confirmed_funding . is_splice ( ) {
49434995 debug_assert ! ( tx. input. iter( ) . any( |input| {
49444996 let funding_outpoint = self . funding. funding_outpoint( ) . into_bitcoin_outpoint( ) ;
49454997 input. previous_output == funding_outpoint
@@ -4948,36 +5000,77 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
49485000 } else {
49495001 ( "RBF" , "channel_ready" )
49505002 } ;
4951- let action = if self . no_further_updates_allowed ( ) {
4952- if self . holder_tx_signed {
4953- ", broadcasting post-splice holder commitment transaction" . to_string ( )
4954- } else {
4955- "" . to_string ( )
4956- }
4957- } else {
5003+ let action = if self . holder_tx_signed {
5004+ ", broadcasting post-splice holder commitment transaction" . to_string ( )
5005+ } else if !self . no_further_updates_allowed ( ) && !is_funding_locked {
49585006 format ! ( ", waiting for `{msg}` exchange" )
5007+ } else {
5008+ "" . to_string ( )
49595009 } ;
49605010 log_info ! ( logger, "{desc} for channel {} confirmed with txid {txid}{action}" , self . channel_id( ) ) ;
49615011
4962- self . onchain_events_awaiting_threshold_conf . push ( OnchainEventEntry {
4963- txid,
4964- transaction : Some ( ( * tx) . clone ( ) ) ,
4965- height,
4966- block_hash : Some ( block_hash) ,
4967- event : OnchainEvent :: AlternativeFundingConfirmation { } ,
4968- } ) ;
5012+ if is_funding_locked {
5013+ // We can only have already processed the
5014+ // [`ChannelMonitorUpdateStep::RenegotiatedFundingLocked`] if we're zero conf,
5015+ // but we still track the alternative funding transactions until we're no longer
5016+ // under reorg risk.
5017+ debug_assert ! ( self . wait_for_0conf_funding_reorg_safety) ;
49695018
4970- if self . holder_tx_signed {
5019+ let event_entry = OnchainEventEntry {
5020+ txid,
5021+ transaction : Some ( ( * tx) . clone ( ) ) ,
5022+ height,
5023+ block_hash : Some ( block_hash) ,
5024+ event : OnchainEvent :: ZeroConfFundingReorgSafety { } ,
5025+ } ;
5026+
5027+ if let Some ( event) = self . onchain_events_awaiting_threshold_conf
5028+ . iter_mut ( )
5029+ . find ( |e| matches ! ( e. event, OnchainEvent :: ZeroConfFundingReorgSafety { } ) )
5030+ {
5031+ // Since channels may chain multiple zero conf splices, we only want to track
5032+ // one event overall.
5033+ * event = event_entry;
5034+ } else {
5035+ self . onchain_events_awaiting_threshold_conf . push ( event_entry) ;
5036+ }
5037+ } else {
5038+ debug_assert ! (
5039+ !self . onchain_events_awaiting_threshold_conf. iter( )
5040+ . any( |e| matches!( e. event, OnchainEvent :: ZeroConfFundingReorgSafety { } ) )
5041+ ) ;
5042+
5043+ self . alternative_funding_confirmed = Some ( ( txid, height) ) ;
5044+
5045+ if self . no_further_updates_allowed ( ) {
5046+ // We can no longer rely on
5047+ // [`ChannelMonitorUpdateStep::RenegotiatedFundingLocked`] to promote the
5048+ // scope, do so when the funding is no longer under reorg risk.
5049+ self . onchain_events_awaiting_threshold_conf . push ( OnchainEventEntry {
5050+ txid,
5051+ transaction : Some ( ( * tx) . clone ( ) ) ,
5052+ height,
5053+ block_hash : Some ( block_hash) ,
5054+ event : OnchainEvent :: AlternativeFundingConfirmation { } ,
5055+ } ) ;
5056+ }
5057+ }
5058+
5059+ // If we've previously broadcast a holder commitment, queue claims for the
5060+ // alternative holder commitment now that it is the only one that can confirm (until
5061+ // we see a reorg of its funding transaction). We also choose to broadcast if our
5062+ // intended funding transaction was locked with zero confirmations, but another
5063+ // funding transaction has now confirmed, since commitment updates were only made to
5064+ // the locked funding.
5065+ if self . holder_tx_signed ||
5066+ ( self . wait_for_0conf_funding_reorg_safety && !is_funding_locked)
5067+ {
49715068 // Cancel any previous claims that are no longer valid as they stemmed from a
49725069 // different funding transaction.
49735070 let alternative_holder_commitment_txid =
4974- alternative_funding . current_holder_commitment_tx . trust ( ) . txid ( ) ;
5071+ confirmed_funding . current_holder_commitment_tx . trust ( ) . txid ( ) ;
49755072 self . cancel_prev_commitment_claims ( & logger, & alternative_holder_commitment_txid) ;
49765073
4977- // Queue claims for the alternative holder commitment since it is the only one
4978- // that can currently confirm so far (until we see a reorg of its funding
4979- // transaction).
4980- //
49815074 // It's possible we process a counterparty commitment within this same block
49825075 // that would invalidate our holder commitment. If we were to broadcast our
49835076 // holder commitment now, we wouldn't be able to cancel it via our usual
@@ -5204,6 +5297,21 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
52045297 log_error ! ( logger, "Missing scope for alternative funding confirmation with txid {}" , entry. txid) ;
52055298 }
52065299 } ,
5300+ OnchainEvent :: ZeroConfFundingReorgSafety { } => {
5301+ if self . wait_for_0conf_funding_reorg_safety {
5302+ debug_assert_eq ! ( self . funding. funding_txid( ) , entry. txid) ;
5303+ debug_assert ! ( self . alternative_funding_confirmed. is_none( ) ) ;
5304+ self
5305+ . pending_funding
5306+ . drain ( ..)
5307+ . for_each ( |funding| {
5308+ self . outputs_to_watch . remove ( & funding. funding_txid ( ) ) ;
5309+ } ) ;
5310+ self . wait_for_0conf_funding_reorg_safety = false ;
5311+ } else {
5312+ debug_assert ! ( false , "These events can only be generated when wait_for_0conf_funding_reorg_safety is set" ) ;
5313+ }
5314+ } ,
52075315 }
52085316 }
52095317
@@ -6057,6 +6165,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
60576165 let mut channel_parameters = None ;
60586166 let mut pending_funding = None ;
60596167 let mut alternative_funding_confirmed = None ;
6168+ let mut wait_for_0conf_funding_reorg_safety: Option < ( ) > = None ;
60606169 read_tlv_fields ! ( reader, {
60616170 ( 1 , funding_spend_confirmed, option) ,
60626171 ( 3 , htlcs_resolved_on_chain, optional_vec) ,
@@ -6076,6 +6185,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
60766185 ( 31 , channel_parameters, ( option: ReadableArgs , None ) ) ,
60776186 ( 32 , pending_funding, optional_vec) ,
60786187 ( 34 , alternative_funding_confirmed, option) ,
6188+ ( 36 , wait_for_0conf_funding_reorg_safety, option) ,
60796189 } ) ;
60806190 if let Some ( payment_preimages_with_info) = payment_preimages_with_info {
60816191 if payment_preimages_with_info. len ( ) != payment_preimages. len ( ) {
@@ -6247,6 +6357,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
62476357 prev_holder_htlc_data,
62486358
62496359 alternative_funding_confirmed,
6360+ wait_for_0conf_funding_reorg_safety : wait_for_0conf_funding_reorg_safety. is_some ( ) ,
62506361 } ) ) )
62516362 }
62526363}
0 commit comments