Skip to content

Commit 9738739

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 e36abb7 commit 9738739

File tree

1 file changed

+93
-3
lines changed

1 file changed

+93
-3
lines changed

lightning/src/chain/channelmonitor.rs

Lines changed: 93 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,8 @@ 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+
alternative_funding_confirmed: Option<(Txid, u32)>,
12831292
}
12841293

12851294
// Macro helper to access holder commitment HTLC data (including both non-dust and dust) while
@@ -1555,6 +1564,7 @@ impl<Signer: EcdsaChannelSigner> Writeable for ChannelMonitorImpl<Signer> {
15551564
(29, self.initial_counterparty_commitment_tx, option),
15561565
(31, self.funding.channel_parameters, required),
15571566
(32, self.pending_funding, optional_vec),
1567+
(34, self.alternative_funding_confirmed, option),
15581568
});
15591569

15601570
Ok(())
@@ -1780,6 +1790,8 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
17801790
// There are never any HTLCs in the initial commitment transaction
17811791
current_holder_htlc_data: CommitmentHTLCData::new(),
17821792
prev_holder_htlc_data: None,
1793+
1794+
alternative_funding_confirmed: None,
17831795
})
17841796
}
17851797

@@ -4873,6 +4885,57 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
48734885
}
48744886
}
48754887

4888+
// A splice/dual-funded RBF transaction has confirmed. We can't promote the
4889+
// `FundingScope` scope until we see the
4890+
// [`ChannelMonitorUpdateStep::RenegotiatedFundingLocked`] for it, but we track the txid
4891+
// so we know which holder commitment transaction we may need to broadcast.
4892+
if let Some(alternative_funding) = self.pending_funding.iter()
4893+
.find(|funding| funding.funding_txid() == txid)
4894+
{
4895+
debug_assert!(self.funding_spend_confirmed.is_none());
4896+
debug_assert!(
4897+
!self.onchain_events_awaiting_threshold_conf.iter()
4898+
.any(|e| matches!(e.event, OnchainEvent::FundingSpendConfirmation { .. }))
4899+
);
4900+
4901+
let (desc, msg) = if alternative_funding.channel_parameters.splice_parent_funding_txid.is_some() {
4902+
debug_assert!(tx.input.iter().any(|input| {
4903+
let funding_outpoint = self.funding.funding_outpoint().into_bitcoin_outpoint();
4904+
input.previous_output == funding_outpoint
4905+
}));
4906+
("Splice", "splice_locked")
4907+
} else {
4908+
("RBF", "channel_ready")
4909+
};
4910+
let action = if self.no_further_updates_allowed() {
4911+
if self.holder_tx_signed {
4912+
", broadcasting post-splice holder commitment transaction".to_string()
4913+
} else {
4914+
"".to_string()
4915+
}
4916+
} else {
4917+
format!(", waiting for `{msg}` exchange")
4918+
};
4919+
log_info!(logger, "{desc} for channel {} confirmed with txid {txid}{action}", self.channel_id());
4920+
4921+
self.alternative_funding_confirmed = Some((txid, height));
4922+
4923+
if self.no_further_updates_allowed() {
4924+
// We can no longer rely on
4925+
// [`ChannelMonitorUpdateStep::RenegotiatedFundingLocked`] to promote the
4926+
// scope, do so when the funding is no longer under reorg risk.
4927+
self.onchain_events_awaiting_threshold_conf.push(OnchainEventEntry {
4928+
txid,
4929+
transaction: Some((*tx).clone()),
4930+
height,
4931+
block_hash: Some(block_hash),
4932+
event: OnchainEvent::AlternativeFundingConfirmation {},
4933+
});
4934+
}
4935+
4936+
continue 'tx_iter;
4937+
}
4938+
48764939
if tx.input.len() == 1 {
48774940
// Assuming our keys were not leaked (in which case we're screwed no matter what),
48784941
// commitment transactions and HTLC transactions will all only ever have one input
@@ -5004,7 +5067,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
50045067
let unmatured_htlcs: Vec<_> = self.onchain_events_awaiting_threshold_conf
50055068
.iter()
50065069
.filter_map(|entry| match &entry.event {
5007-
OnchainEvent::HTLCUpdate { source, .. } => Some(source),
5070+
OnchainEvent::HTLCUpdate { source, .. } => Some(source.clone()),
50085071
_ => None,
50095072
})
50105073
.collect();
@@ -5019,7 +5082,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
50195082
#[cfg(debug_assertions)]
50205083
{
50215084
debug_assert!(
5022-
!unmatured_htlcs.contains(&&source),
5085+
!unmatured_htlcs.contains(&source),
50235086
"An unmature HTLC transaction conflicts with a maturing one; failed to \
50245087
call either transaction_unconfirmed for the conflicting transaction \
50255088
or block_disconnected for a block containing it.");
@@ -5066,6 +5129,15 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
50665129
self.funding_spend_confirmed = Some(entry.txid);
50675130
self.confirmed_commitment_tx_counterparty_output = commitment_tx_to_counterparty_output;
50685131
},
5132+
OnchainEvent::AlternativeFundingConfirmation {} => {
5133+
// An alternative funding transaction has irrevocably confirmed and we're no
5134+
// longer allowing monitor updates, so promote the `FundingScope` now.
5135+
debug_assert!(self.no_further_updates_allowed());
5136+
debug_assert_ne!(self.funding.funding_txid(), entry.txid);
5137+
if let Err(_) = self.promote_funding(entry.txid) {
5138+
log_error!(logger, "Missing scope for alternative funding confirmation with txid {}", entry.txid);
5139+
}
5140+
},
50695141
}
50705142
}
50715143

@@ -5173,6 +5245,13 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
51735245
{
51745246
log_trace!(logger, "Block {} at height {} disconnected", header.block_hash(), height);
51755247

5248+
// TODO: Replace with `take_if` once our MSRV is >= 1.80.
5249+
if let Some((_, conf_height)) = self.alternative_funding_confirmed.as_ref() {
5250+
if *conf_height == height {
5251+
self.alternative_funding_confirmed.take();
5252+
}
5253+
}
5254+
51765255
//We may discard:
51775256
//- htlc update there as failure-trigger tx (revoked commitment tx, non-revoked commitment tx, HTLC-timeout tx) has been disconnected
51785257
//- maturing spendable output has transaction paying us has been disconnected
@@ -5199,6 +5278,13 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
51995278
F::Target: FeeEstimator,
52005279
L::Target: Logger,
52015280
{
5281+
// TODO: Replace with `take_if` once our MSRV is >= 1.80.
5282+
if let Some((funding_txid, _)) = self.alternative_funding_confirmed.as_ref() {
5283+
if funding_txid == txid {
5284+
self.alternative_funding_confirmed.take();
5285+
}
5286+
}
5287+
52025288
let mut removed_height = None;
52035289
for entry in self.onchain_events_awaiting_threshold_conf.iter() {
52045290
if entry.txid == *txid {
@@ -5870,6 +5956,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
58705956
let mut first_negotiated_funding_txo = RequiredWrapper(None);
58715957
let mut channel_parameters = None;
58725958
let mut pending_funding = None;
5959+
let mut alternative_funding_confirmed = None;
58735960
read_tlv_fields!(reader, {
58745961
(1, funding_spend_confirmed, option),
58755962
(3, htlcs_resolved_on_chain, optional_vec),
@@ -5888,6 +5975,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
58885975
(29, initial_counterparty_commitment_tx, option),
58895976
(31, channel_parameters, (option: ReadableArgs, None)),
58905977
(32, pending_funding, optional_vec),
5978+
(34, alternative_funding_confirmed, option),
58915979
});
58925980
if let Some(payment_preimages_with_info) = payment_preimages_with_info {
58935981
if payment_preimages_with_info.len() != payment_preimages.len() {
@@ -6057,6 +6145,8 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
60576145

60586146
current_holder_htlc_data,
60596147
prev_holder_htlc_data,
6148+
6149+
alternative_funding_confirmed,
60606150
})))
60616151
}
60626152
}

0 commit comments

Comments
 (0)