Skip to content

Commit 5b32677

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 5b32677

File tree

1 file changed

+98
-3
lines changed

1 file changed

+98
-3
lines changed

lightning/src/chain/channelmonitor.rs

Lines changed: 98 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 not 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,57 @@ 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.pending_funding.iter()
4898+
.find(|funding| funding.funding_txid() == txid)
4899+
{
4900+
debug_assert!(self.funding_spend_confirmed.is_none());
4901+
debug_assert!(
4902+
!self.onchain_events_awaiting_threshold_conf.iter()
4903+
.any(|e| matches!(e.event, OnchainEvent::FundingSpendConfirmation { .. }))
4904+
);
4905+
4906+
let (desc, msg) = if alternative_funding.channel_parameters.splice_parent_funding_txid.is_some() {
4907+
debug_assert!(tx.input.iter().any(|input| {
4908+
let funding_outpoint = self.funding.funding_outpoint().into_bitcoin_outpoint();
4909+
input.previous_output == funding_outpoint
4910+
}));
4911+
("Splice", "splice_locked")
4912+
} else {
4913+
("RBF", "channel_ready")
4914+
};
4915+
let action = if self.no_further_updates_allowed() {
4916+
if self.holder_tx_signed {
4917+
", broadcasting post-splice holder commitment transaction".to_string()
4918+
} else {
4919+
"".to_string()
4920+
}
4921+
} else {
4922+
format!(", waiting for `{msg}` exchange")
4923+
};
4924+
log_info!(logger, "{desc} for channel {} confirmed with txid {txid}{action}", self.channel_id());
4925+
4926+
self.alternative_funding_confirmed = Some((txid, height));
4927+
4928+
if self.no_further_updates_allowed() {
4929+
// We can no longer rely on
4930+
// [`ChannelMonitorUpdateStep::RenegotiatedFundingLocked`] to promote the
4931+
// scope, do so when the funding is no longer under reorg risk.
4932+
self.onchain_events_awaiting_threshold_conf.push(OnchainEventEntry {
4933+
txid,
4934+
transaction: Some((*tx).clone()),
4935+
height,
4936+
block_hash: Some(block_hash),
4937+
event: OnchainEvent::AlternativeFundingConfirmation {},
4938+
});
4939+
}
4940+
4941+
continue 'tx_iter;
4942+
}
4943+
48764944
if tx.input.len() == 1 {
48774945
// Assuming our keys were not leaked (in which case we're screwed no matter what),
48784946
// commitment transactions and HTLC transactions will all only ever have one input
@@ -5004,7 +5072,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
50045072
let unmatured_htlcs: Vec<_> = self.onchain_events_awaiting_threshold_conf
50055073
.iter()
50065074
.filter_map(|entry| match &entry.event {
5007-
OnchainEvent::HTLCUpdate { source, .. } => Some(source),
5075+
OnchainEvent::HTLCUpdate { source, .. } => Some(source.clone()),
50085076
_ => None,
50095077
})
50105078
.collect();
@@ -5019,7 +5087,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
50195087
#[cfg(debug_assertions)]
50205088
{
50215089
debug_assert!(
5022-
!unmatured_htlcs.contains(&&source),
5090+
!unmatured_htlcs.contains(&source),
50235091
"An unmature HTLC transaction conflicts with a maturing one; failed to \
50245092
call either transaction_unconfirmed for the conflicting transaction \
50255093
or block_disconnected for a block containing it.");
@@ -5066,6 +5134,15 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
50665134
self.funding_spend_confirmed = Some(entry.txid);
50675135
self.confirmed_commitment_tx_counterparty_output = commitment_tx_to_counterparty_output;
50685136
},
5137+
OnchainEvent::AlternativeFundingConfirmation {} => {
5138+
// An alternative funding transaction has irrevocably confirmed and we're no
5139+
// longer allowing monitor updates, so promote the `FundingScope` now.
5140+
debug_assert!(self.no_further_updates_allowed());
5141+
debug_assert_ne!(self.funding.funding_txid(), entry.txid);
5142+
if let Err(_) = self.promote_funding(entry.txid) {
5143+
log_error!(logger, "Missing scope for alternative funding confirmation with txid {}", entry.txid);
5144+
}
5145+
},
50695146
}
50705147
}
50715148

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

5258+
// TODO: Replace with `take_if` once our MSRV is >= 1.80.
5259+
if let Some((_, conf_height)) = self.alternative_funding_confirmed.as_ref() {
5260+
if *conf_height == height {
5261+
self.alternative_funding_confirmed.take();
5262+
}
5263+
}
5264+
51815265
let bounded_fee_estimator = LowerBoundedFeeEstimator::new(fee_estimator);
51825266
let conf_target = self.closure_conf_target();
51835267
self.onchain_tx_handler.block_disconnected(
@@ -5217,6 +5301,13 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
52175301

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

5304+
// TODO: Replace with `take_if` once our MSRV is >= 1.80.
5305+
if let Some((alternative_funding_txid, _)) = self.alternative_funding_confirmed.as_ref() {
5306+
if alternative_funding_txid == txid {
5307+
self.alternative_funding_confirmed.take();
5308+
}
5309+
}
5310+
52205311
let conf_target = self.closure_conf_target();
52215312
self.onchain_tx_handler.transaction_unconfirmed(
52225313
txid, broadcaster, conf_target, &self.destination_script, fee_estimator, logger
@@ -5870,6 +5961,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
58705961
let mut first_negotiated_funding_txo = RequiredWrapper(None);
58715962
let mut channel_parameters = None;
58725963
let mut pending_funding = None;
5964+
let mut alternative_funding_confirmed = None;
58735965
read_tlv_fields!(reader, {
58745966
(1, funding_spend_confirmed, option),
58755967
(3, htlcs_resolved_on_chain, optional_vec),
@@ -5888,6 +5980,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
58885980
(29, initial_counterparty_commitment_tx, option),
58895981
(31, channel_parameters, (option: ReadableArgs, None)),
58905982
(32, pending_funding, optional_vec),
5983+
(34, alternative_funding_confirmed, option),
58915984
});
58925985
if let Some(payment_preimages_with_info) = payment_preimages_with_info {
58935986
if payment_preimages_with_info.len() != payment_preimages.len() {
@@ -6057,6 +6150,8 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
60576150

60586151
current_holder_htlc_data,
60596152
prev_holder_htlc_data,
6153+
6154+
alternative_funding_confirmed,
60606155
})))
60616156
}
60626157
}

0 commit comments

Comments
 (0)