Skip to content

Commit ebfe6af

Browse files
committed
Detect channel alternative funding transaction confirmation
Whether it's a splice, or a dual-funded RBF, we need to know which funding transaction out of all of the negotiated ones is currently confirmed in case we need to broadcast the holder commitment.
1 parent ecce268 commit ebfe6af

File tree

1 file changed

+85
-3
lines changed

1 file changed

+85
-3
lines changed

lightning/src/chain/channelmonitor.rs

Lines changed: 85 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,13 @@ enum OnchainEvent {
565565
/// output (and generate a SpendableOutput event).
566566
on_to_local_output_csv: Option<u16>,
567567
},
568+
/// An alternative funding transaction (due to a splice/RBF) has confirmed but can no longer be
569+
/// locked now as the monitor is no longer allowing updates. Note that we wait to promote the
570+
/// corresponding `FundingScope` until we see a
571+
/// [`ChannelMonitorUpdateStep::RenegotiatedFundingLocked`], but this event is only applicable
572+
/// once [`ChannelMonitor::no_further_updates_allowed`] returns true. We promote the
573+
/// `FundingScope` once the funding transaction is irrevocably confirmed.
574+
AlternativeFundingConfirmation {},
568575
}
569576

570577
impl Writeable for OnchainEventEntry {
@@ -609,6 +616,7 @@ impl_writeable_tlv_based_enum_upgradable!(OnchainEvent,
609616
(1, MaturingOutput) => {
610617
(0, descriptor, required),
611618
},
619+
(2, AlternativeFundingConfirmation) => {},
612620
(3, FundingSpendConfirmation) => {
613621
(0, on_local_output_csv, option),
614622
(1, commitment_tx_to_counterparty_output, option),
@@ -618,7 +626,6 @@ impl_writeable_tlv_based_enum_upgradable!(OnchainEvent,
618626
(2, preimage, option),
619627
(4, on_to_local_output_csv, option),
620628
},
621-
622629
);
623630

624631
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -1280,6 +1287,10 @@ pub(crate) struct ChannelMonitorImpl<Signer: EcdsaChannelSigner> {
12801287
// commitment transactions, their ordering with respect to each other must remain the same.
12811288
current_holder_htlc_data: CommitmentHTLCData,
12821289
prev_holder_htlc_data: Option<CommitmentHTLCData>,
1290+
1291+
// Tracks the txid and confirmation height of a renegotiated funding transaction upon
1292+
// confirmation. Used to determine which commitment we should broadcast when necessary.
1293+
alternative_funding_confirmed: Option<(Txid, u32)>,
12831294
}
12841295

12851296
// Macro helper to access holder commitment HTLC data (including both non-dust and dust) while
@@ -1555,6 +1566,7 @@ impl<Signer: EcdsaChannelSigner> Writeable for ChannelMonitorImpl<Signer> {
15551566
(29, self.initial_counterparty_commitment_tx, option),
15561567
(31, self.funding.channel_parameters, required),
15571568
(32, self.pending_funding, optional_vec),
1569+
(34, self.alternative_funding_confirmed, option),
15581570
});
15591571

15601572
Ok(())
@@ -1780,6 +1792,8 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
17801792
// There are never any HTLCs in the initial commitment transaction
17811793
current_holder_htlc_data: CommitmentHTLCData::new(),
17821794
prev_holder_htlc_data: None,
1795+
1796+
alternative_funding_confirmed: None,
17831797
})
17841798
}
17851799

@@ -3815,6 +3829,9 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
38153829
for funding in self.pending_funding.drain(..) {
38163830
self.outputs_to_watch.remove(&funding.funding_txid());
38173831
}
3832+
if let Some((alternative_funding_txid, _)) = self.alternative_funding_confirmed.take() {
3833+
debug_assert_eq!(alternative_funding_txid, new_funding_txid);
3834+
}
38183835

38193836
Ok(())
38203837
}
@@ -4873,6 +4890,44 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
48734890
}
48744891
}
48754892

4893+
// A splice/dual-funded RBF transaction has confirmed. We can't promote the
4894+
// `FundingScope` scope until we see the
4895+
// [`ChannelMonitorUpdateStep::RenegotiatedFundingLocked`] for it, but we track the txid
4896+
// so we know which holder commitment transaction we may need to broadcast.
4897+
if let Some(_alternative_funding) = self
4898+
.pending_funding
4899+
.iter()
4900+
.find(|funding| funding.funding_txid() == txid)
4901+
{
4902+
debug_assert!(self.alternative_funding_confirmed.is_none());
4903+
debug_assert!(
4904+
!self.onchain_events_awaiting_threshold_conf.iter()
4905+
.any(|e| matches!(e.event, OnchainEvent::AlternativeFundingConfirmation {}))
4906+
);
4907+
debug_assert!(self.funding_spend_confirmed.is_none());
4908+
debug_assert!(
4909+
!self.onchain_events_awaiting_threshold_conf.iter()
4910+
.any(|e| matches!(e.event, OnchainEvent::FundingSpendConfirmation { .. }))
4911+
);
4912+
4913+
self.alternative_funding_confirmed = Some((txid, height));
4914+
4915+
if self.no_further_updates_allowed() {
4916+
// We can no longer rely on
4917+
// [`ChannelMonitorUpdateStep::RenegotiatedFundingLocked`] to promote the
4918+
// scope; do so when the funding is no longer under reorg risk.
4919+
self.onchain_events_awaiting_threshold_conf.push(OnchainEventEntry {
4920+
txid,
4921+
transaction: Some((*tx).clone()),
4922+
height,
4923+
block_hash: Some(block_hash),
4924+
event: OnchainEvent::AlternativeFundingConfirmation {},
4925+
});
4926+
}
4927+
4928+
continue 'tx_iter;
4929+
}
4930+
48764931
if tx.input.len() == 1 {
48774932
// Assuming our keys were not leaked (in which case we're screwed no matter what),
48784933
// commitment transactions and HTLC transactions will all only ever have one input
@@ -5004,7 +5059,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
50045059
let unmatured_htlcs: Vec<_> = self.onchain_events_awaiting_threshold_conf
50055060
.iter()
50065061
.filter_map(|entry| match &entry.event {
5007-
OnchainEvent::HTLCUpdate { source, .. } => Some(source),
5062+
OnchainEvent::HTLCUpdate { source, .. } => Some(source.clone()),
50085063
_ => None,
50095064
})
50105065
.collect();
@@ -5019,7 +5074,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
50195074
#[cfg(debug_assertions)]
50205075
{
50215076
debug_assert!(
5022-
!unmatured_htlcs.contains(&&source),
5077+
!unmatured_htlcs.contains(&source),
50235078
"An unmature HTLC transaction conflicts with a maturing one; failed to \
50245079
call either transaction_unconfirmed for the conflicting transaction \
50255080
or block_disconnected for a block containing it.");
@@ -5066,6 +5121,15 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
50665121
self.funding_spend_confirmed = Some(entry.txid);
50675122
self.confirmed_commitment_tx_counterparty_output = commitment_tx_to_counterparty_output;
50685123
},
5124+
OnchainEvent::AlternativeFundingConfirmation {} => {
5125+
// An alternative funding transaction has irrevocably confirmed and we're no
5126+
// longer allowing monitor updates, so promote the `FundingScope` now.
5127+
debug_assert!(self.no_further_updates_allowed());
5128+
debug_assert_ne!(self.funding.funding_txid(), entry.txid);
5129+
if let Err(_) = self.promote_funding(entry.txid) {
5130+
log_error!(logger, "Missing scope for alternative funding confirmation with txid {}", entry.txid);
5131+
}
5132+
},
50695133
}
50705134
}
50715135

@@ -5178,6 +5242,13 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
51785242
//- maturing spendable output has transaction paying us has been disconnected
51795243
self.onchain_events_awaiting_threshold_conf.retain(|ref entry| entry.height < height);
51805244

5245+
// TODO: Replace with `take_if` once our MSRV is >= 1.80.
5246+
if let Some((_, conf_height)) = self.alternative_funding_confirmed.as_ref() {
5247+
if *conf_height == height {
5248+
self.alternative_funding_confirmed.take();
5249+
}
5250+
}
5251+
51815252
let bounded_fee_estimator = LowerBoundedFeeEstimator::new(fee_estimator);
51825253
let conf_target = self.closure_conf_target();
51835254
self.onchain_tx_handler.block_disconnected(
@@ -5217,6 +5288,13 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
52175288

52185289
debug_assert!(!self.onchain_events_awaiting_threshold_conf.iter().any(|ref entry| entry.txid == *txid));
52195290

5291+
// TODO: Replace with `take_if` once our MSRV is >= 1.80.
5292+
if let Some((alternative_funding_txid, _)) = self.alternative_funding_confirmed.as_ref() {
5293+
if alternative_funding_txid == txid {
5294+
self.alternative_funding_confirmed.take();
5295+
}
5296+
}
5297+
52205298
let conf_target = self.closure_conf_target();
52215299
self.onchain_tx_handler.transaction_unconfirmed(
52225300
txid, broadcaster, conf_target, &self.destination_script, fee_estimator, logger
@@ -5870,6 +5948,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
58705948
let mut first_negotiated_funding_txo = RequiredWrapper(None);
58715949
let mut channel_parameters = None;
58725950
let mut pending_funding = None;
5951+
let mut alternative_funding_confirmed = None;
58735952
read_tlv_fields!(reader, {
58745953
(1, funding_spend_confirmed, option),
58755954
(3, htlcs_resolved_on_chain, optional_vec),
@@ -5888,6 +5967,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
58885967
(29, initial_counterparty_commitment_tx, option),
58895968
(31, channel_parameters, (option: ReadableArgs, None)),
58905969
(32, pending_funding, optional_vec),
5970+
(34, alternative_funding_confirmed, option),
58915971
});
58925972
if let Some(payment_preimages_with_info) = payment_preimages_with_info {
58935973
if payment_preimages_with_info.len() != payment_preimages.len() {
@@ -6057,6 +6137,8 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
60576137

60586138
current_holder_htlc_data,
60596139
prev_holder_htlc_data,
6140+
6141+
alternative_funding_confirmed,
60606142
})))
60616143
}
60626144
}

0 commit comments

Comments
 (0)