@@ -531,7 +531,9 @@ enum OnchainEvent {
531
531
} ,
532
532
/// An output waiting on [`ANTI_REORG_DELAY`] confirmations before we hand the user the
533
533
/// [`SpendableOutputDescriptor`].
534
- MaturingOutput { descriptor : SpendableOutputDescriptor } ,
534
+ MaturingOutput {
535
+ descriptor : SpendableOutputDescriptor ,
536
+ } ,
535
537
/// A spend of the funding output, either a commitment transaction or a cooperative closing
536
538
/// transaction.
537
539
FundingSpendConfirmation {
@@ -572,6 +574,17 @@ enum OnchainEvent {
572
574
/// once [`ChannelMonitor::no_further_updates_allowed`] returns true. We promote the
573
575
/// `FundingScope` once the funding transaction is irrevocably confirmed.
574
576
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 { } ,
575
588
}
576
589
577
590
impl Writeable for OnchainEventEntry {
@@ -621,6 +634,7 @@ impl_writeable_tlv_based_enum_upgradable!(OnchainEvent,
621
634
( 0 , on_local_output_csv, option) ,
622
635
( 1 , commitment_tx_to_counterparty_output, option) ,
623
636
} ,
637
+ ( 4 , ZeroConfFundingReorgSafety ) => { } ,
624
638
( 5 , HTLCSpendConfirmation ) => {
625
639
( 0 , commitment_tx_output_idx, required) ,
626
640
( 2 , preimage, option) ,
@@ -1295,6 +1309,11 @@ pub(crate) struct ChannelMonitorImpl<Signer: EcdsaChannelSigner> {
1295
1309
prev_holder_htlc_data : Option < CommitmentHTLCData > ,
1296
1310
1297
1311
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 ,
1298
1317
}
1299
1318
1300
1319
// 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> {
1552
1571
_ => self . pending_monitor_events . clone ( ) ,
1553
1572
} ;
1554
1573
1574
+ let wait_for_0conf_funding_reorg_safety = self . wait_for_0conf_funding_reorg_safety . then ( || ( ) ) ;
1575
+
1555
1576
write_tlv_fields ! ( writer, {
1556
1577
( 1 , self . funding_spend_confirmed, option) ,
1557
1578
( 3 , self . htlcs_resolved_on_chain, required_vec) ,
@@ -1571,6 +1592,7 @@ impl<Signer: EcdsaChannelSigner> Writeable for ChannelMonitorImpl<Signer> {
1571
1592
( 31 , self . funding. channel_parameters, required) ,
1572
1593
( 32 , self . pending_funding, optional_vec) ,
1573
1594
( 34 , self . alternative_funding_confirmed, option) ,
1595
+ ( 36 , wait_for_0conf_funding_reorg_safety, option) ,
1574
1596
} ) ;
1575
1597
1576
1598
Ok ( ( ) )
@@ -1798,6 +1820,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
1798
1820
prev_holder_htlc_data : None ,
1799
1821
1800
1822
alternative_funding_confirmed : None ,
1823
+ wait_for_0conf_funding_reorg_safety : false ,
1801
1824
} )
1802
1825
}
1803
1826
@@ -3419,6 +3442,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
3419
3442
self . update_holder_commitment_data ( vec ! [ holder_commitment_tx] , htlc_data, claimed_htlcs)
3420
3443
}
3421
3444
3445
+ // TODO(splicing): Consider `wait_for_0conf_funding_reorg_safety`.
3422
3446
fn verify_matching_commitment_transactions <
3423
3447
' a ,
3424
3448
I : ExactSizeIterator < Item = & ' a CommitmentTransaction > ,
@@ -3795,14 +3819,16 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
3795
3819
if let Some ( parent_funding_txid) = channel_parameters. splice_parent_funding_txid . as_ref ( ) {
3796
3820
// Only one splice can be negotiated at a time after we've exchanged `channel_ready`
3797
3821
// (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 {
3799
3823
log_error ! (
3800
3824
logger,
3801
3825
"Negotiated splice while channel is pending channel_ready/splice_locked"
3802
3826
) ;
3803
3827
return Err ( ( ) ) ;
3804
3828
}
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
+ {
3806
3832
log_error ! (
3807
3833
logger,
3808
3834
"Negotiated splice that does not spend currently locked funding transaction"
@@ -3822,6 +3848,11 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
3822
3848
) ;
3823
3849
self . pending_funding . push ( alternative_funding) ;
3824
3850
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
+
3825
3856
Ok ( ( ) )
3826
3857
}
3827
3858
@@ -3841,12 +3872,20 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
3841
3872
self . funding . prev_holder_commitment_tx . clone ( ) ,
3842
3873
) ;
3843
3874
3875
+ if self . wait_for_0conf_funding_reorg_safety {
3876
+ return Ok ( ( ) ) ;
3877
+ }
3878
+
3844
3879
// The swap above places the previous `FundingScope` into `pending_funding`.
3845
3880
for funding in self . pending_funding . drain ( ..) {
3846
3881
self . outputs_to_watch . remove ( & funding. funding_txid ( ) ) ;
3847
3882
}
3848
3883
self . alternative_funding_confirmed . take ( ) ;
3849
3884
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
+
3850
3889
Ok ( ( ) )
3851
3890
}
3852
3891
@@ -4926,20 +4965,33 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
4926
4965
}
4927
4966
}
4928
4967
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
4931
4970
// [`ChannelMonitorUpdateStep::RenegotiatedFundingLocked`] for it, but we track the txid
4932
4971
// 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 ( )
4934
4975
. 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
+ } )
4935
4982
{
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
+ ) ;
4936
4988
debug_assert ! ( self . funding_spend_confirmed. is_none( ) ) ;
4937
4989
debug_assert ! (
4938
4990
!self . onchain_events_awaiting_threshold_conf. iter( )
4939
4991
. any( |e| matches!( e. event, OnchainEvent :: FundingSpendConfirmation { .. } ) )
4940
4992
) ;
4941
4993
4942
- let ( desc, msg) = if alternative_funding . channel_parameters . splice_parent_funding_txid . is_some ( ) {
4994
+ let ( desc, msg) = if confirmed_funding . is_splice ( ) {
4943
4995
debug_assert ! ( tx. input. iter( ) . any( |input| {
4944
4996
let funding_outpoint = self . funding. funding_outpoint( ) . into_bitcoin_outpoint( ) ;
4945
4997
input. previous_output == funding_outpoint
@@ -4948,36 +5000,77 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
4948
5000
} else {
4949
5001
( "RBF" , "channel_ready" )
4950
5002
} ;
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 {
4958
5006
format ! ( ", waiting for `{msg}` exchange" )
5007
+ } else {
5008
+ "" . to_string ( )
4959
5009
} ;
4960
5010
log_info ! ( logger, "{desc} for channel {} confirmed with txid {txid}{action}" , self . channel_id( ) ) ;
4961
5011
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) ;
4969
5018
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
+ {
4971
5068
// Cancel any previous claims that are no longer valid as they stemmed from a
4972
5069
// different funding transaction.
4973
5070
let alternative_holder_commitment_txid =
4974
- alternative_funding . current_holder_commitment_tx . trust ( ) . txid ( ) ;
5071
+ confirmed_funding . current_holder_commitment_tx . trust ( ) . txid ( ) ;
4975
5072
self . cancel_prev_commitment_claims ( & logger, & alternative_holder_commitment_txid) ;
4976
5073
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
- //
4981
5074
// It's possible we process a counterparty commitment within this same block
4982
5075
// that would invalidate our holder commitment. If we were to broadcast our
4983
5076
// holder commitment now, we wouldn't be able to cancel it via our usual
@@ -5204,6 +5297,21 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
5204
5297
log_error ! ( logger, "Missing scope for alternative funding confirmation with txid {}" , entry. txid) ;
5205
5298
}
5206
5299
} ,
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
+ } ,
5207
5315
}
5208
5316
}
5209
5317
@@ -6057,6 +6165,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
6057
6165
let mut channel_parameters = None ;
6058
6166
let mut pending_funding = None ;
6059
6167
let mut alternative_funding_confirmed = None ;
6168
+ let mut wait_for_0conf_funding_reorg_safety: Option < ( ) > = None ;
6060
6169
read_tlv_fields ! ( reader, {
6061
6170
( 1 , funding_spend_confirmed, option) ,
6062
6171
( 3 , htlcs_resolved_on_chain, optional_vec) ,
@@ -6076,6 +6185,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
6076
6185
( 31 , channel_parameters, ( option: ReadableArgs , None ) ) ,
6077
6186
( 32 , pending_funding, optional_vec) ,
6078
6187
( 34 , alternative_funding_confirmed, option) ,
6188
+ ( 36 , wait_for_0conf_funding_reorg_safety, option) ,
6079
6189
} ) ;
6080
6190
if let Some ( payment_preimages_with_info) = payment_preimages_with_info {
6081
6191
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
6247
6357
prev_holder_htlc_data,
6248
6358
6249
6359
alternative_funding_confirmed,
6360
+ wait_for_0conf_funding_reorg_safety : wait_for_0conf_funding_reorg_safety. is_some ( ) ,
6250
6361
} ) ) )
6251
6362
}
6252
6363
}
0 commit comments