Skip to content

Commit 30954c0

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 30954c0

File tree

2 files changed

+91
-8
lines changed

2 files changed

+91
-8
lines changed

lightning/src/chain/channelmonitor.rs

Lines changed: 85 additions & 8 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)]
@@ -677,6 +684,7 @@ pub(crate) enum ChannelMonitorUpdateStep {
677684
channel_parameters: ChannelTransactionParameters,
678685
holder_commitment_tx: HolderCommitmentTransaction,
679686
counterparty_commitment_tx: CommitmentTransaction,
687+
confirmation_depth: u32,
680688
},
681689
RenegotiatedFundingLocked {
682690
funding_txid: Txid,
@@ -744,6 +752,7 @@ impl_writeable_tlv_based_enum_upgradable!(ChannelMonitorUpdateStep,
744752
(1, channel_parameters, (required: ReadableArgs, None)),
745753
(3, holder_commitment_tx, required),
746754
(5, counterparty_commitment_tx, required),
755+
(7, confirmation_depth, required),
747756
},
748757
(12, RenegotiatedFundingLocked) => {
749758
(1, funding_txid, required),
@@ -1280,6 +1289,8 @@ pub(crate) struct ChannelMonitorImpl<Signer: EcdsaChannelSigner> {
12801289
// commitment transactions, their ordering with respect to each other must remain the same.
12811290
current_holder_htlc_data: CommitmentHTLCData,
12821291
prev_holder_htlc_data: Option<CommitmentHTLCData>,
1292+
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

@@ -3413,7 +3427,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
34133427

34143428
let mut other_commitment_tx = None::<&CommitmentTransaction>;
34153429
for (funding, commitment_tx) in
3416-
core::iter::once(&self.funding).chain(self.pending_funding.iter()).zip(commitment_txs)
3430+
core::iter::once(&self.funding).chain(&self.pending_funding).zip(commitment_txs)
34173431
{
34183432
let trusted_tx = &commitment_tx.trust().built_transaction().transaction;
34193433
if trusted_tx.input.len() != 1 {
@@ -3468,7 +3482,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
34683482
self.current_holder_commitment_number = current_funding_commitment_tx.commitment_number();
34693483
self.onchain_tx_handler.provide_latest_holder_tx(current_funding_commitment_tx.clone());
34703484
for (funding, mut commitment_tx) in core::iter::once(&mut self.funding)
3471-
.chain(self.pending_funding.iter_mut())
3485+
.chain(&mut self.pending_funding)
34723486
.zip(commitment_txs.into_iter())
34733487
{
34743488
mem::swap(&mut commitment_tx, &mut funding.current_holder_commitment_tx);
@@ -3676,7 +3690,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
36763690
&mut self, logger: &WithChannelMonitor<L>,
36773691
channel_parameters: &ChannelTransactionParameters,
36783692
alternative_holder_commitment_tx: &HolderCommitmentTransaction,
3679-
alternative_counterparty_commitment_tx: &CommitmentTransaction,
3693+
alternative_counterparty_commitment_tx: &CommitmentTransaction, confirmation_depth: u32,
36803694
) -> Result<(), ()>
36813695
where
36823696
L::Target: Logger,
@@ -3803,9 +3817,9 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
38033817
if new_funding.is_none() {
38043818
return Err(());
38053819
}
3806-
let mut new_funding = new_funding.unwrap();
3820+
let new_funding = new_funding.unwrap();
38073821

3808-
mem::swap(&mut self.funding, &mut new_funding);
3822+
mem::swap(&mut self.funding, new_funding);
38093823
self.onchain_tx_handler.update_after_renegotiated_funding_locked(
38103824
self.funding.current_holder_commitment_tx.clone(),
38113825
self.funding.prev_holder_commitment_tx.clone(),
@@ -3928,11 +3942,13 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
39283942
},
39293943
ChannelMonitorUpdateStep::RenegotiatedFunding {
39303944
channel_parameters, holder_commitment_tx, counterparty_commitment_tx,
3945+
confirmation_depth,
39313946
} => {
39323947
log_trace!(logger, "Updating ChannelMonitor with alternative holder and counterparty commitment transactions for funding txid {}",
39333948
channel_parameters.funding_outpoint.unwrap().txid);
39343949
if let Err(_) = self.renegotiated_funding(
39353950
logger, channel_parameters, holder_commitment_tx, counterparty_commitment_tx,
3951+
*confirmation_depth,
39363952
) {
39373953
ret = Err(());
39383954
}
@@ -4873,6 +4889,50 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
48734889
}
48744890
}
48754891

4892+
// A splice/dual-funded RBF transaction has confirmed. We can't promote the
4893+
// `FundingScope` scope until we see the
4894+
// [`ChannelMonitorUpdateStep::RenegotiatedFundingLocked`] for it, but we track the txid
4895+
// so we know which holder commitment transaction we may need to broadcast.
4896+
if let Some(alternative_funding) = self.pending_funding.iter()
4897+
.find(|funding| funding.funding_txid() == txid)
4898+
{
4899+
debug_assert!(self.funding_spend_confirmed.is_none());
4900+
debug_assert!(
4901+
!self.onchain_events_awaiting_threshold_conf.iter()
4902+
.any(|e| matches!(e.event, OnchainEvent::FundingSpendConfirmation { .. }))
4903+
);
4904+
4905+
let (desc, msg) = if alternative_funding.channel_parameters.splice_parent_funding_txid.is_some() {
4906+
debug_assert!(tx.input.iter().any(|input| {
4907+
let funding_outpoint = self.funding.funding_outpoint().into_bitcoin_outpoint();
4908+
input.previous_output == funding_outpoint
4909+
}));
4910+
("Splice", "splice_locked")
4911+
} else {
4912+
("RBF", "channel_ready")
4913+
};
4914+
let action = if self.no_further_updates_allowed() {
4915+
if self.holder_tx_signed {
4916+
", broadcasting post-splice holder commitment transaction".to_string()
4917+
} else {
4918+
"".to_string()
4919+
}
4920+
} else {
4921+
format!(", waiting for `{msg}` exchange")
4922+
};
4923+
log_info!(logger, "{desc} for channel {} confirmed with txid {txid}{action}", self.channel_id());
4924+
4925+
self.onchain_events_awaiting_threshold_conf.push(OnchainEventEntry {
4926+
txid,
4927+
transaction: Some((*tx).clone()),
4928+
height,
4929+
block_hash: Some(block_hash),
4930+
event: OnchainEvent::AlternativeFundingConfirmation {},
4931+
});
4932+
4933+
continue 'tx_iter;
4934+
}
4935+
48764936
if tx.input.len() == 1 {
48774937
// Assuming our keys were not leaked (in which case we're screwed no matter what),
48784938
// commitment transactions and HTLC transactions will all only ever have one input
@@ -5004,7 +5064,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
50045064
let unmatured_htlcs: Vec<_> = self.onchain_events_awaiting_threshold_conf
50055065
.iter()
50065066
.filter_map(|entry| match &entry.event {
5007-
OnchainEvent::HTLCUpdate { source, .. } => Some(source),
5067+
OnchainEvent::HTLCUpdate { source, .. } => Some(source.clone()),
50085068
_ => None,
50095069
})
50105070
.collect();
@@ -5019,7 +5079,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
50195079
#[cfg(debug_assertions)]
50205080
{
50215081
debug_assert!(
5022-
!unmatured_htlcs.contains(&&source),
5082+
!unmatured_htlcs.contains(&source),
50235083
"An unmature HTLC transaction conflicts with a maturing one; failed to \
50245084
call either transaction_unconfirmed for the conflicting transaction \
50255085
or block_disconnected for a block containing it.");
@@ -5066,6 +5126,15 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
50665126
self.funding_spend_confirmed = Some(entry.txid);
50675127
self.confirmed_commitment_tx_counterparty_output = commitment_tx_to_counterparty_output;
50685128
},
5129+
OnchainEvent::AlternativeFundingConfirmation {} => {
5130+
// An alternative funding transaction has irrevocably confirmed and we're no
5131+
// longer allowing monitor updates, so promote the `FundingScope` now.
5132+
debug_assert!(self.no_further_updates_allowed());
5133+
debug_assert_ne!(self.funding.funding_txid(), entry.txid);
5134+
if let Err(_) = self.promote_funding(entry.txid) {
5135+
log_error!(logger, "Missing scope for alternative funding confirmation with txid {}", entry.txid);
5136+
}
5137+
},
50695138
}
50705139
}
50715140

@@ -5173,6 +5242,8 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
51735242
{
51745243
log_trace!(logger, "Block {} at height {} disconnected", header.block_hash(), height);
51755244

5245+
self.alternative_funding_confirmed.take_if(|(_, conf_height)| *conf_height == height);
5246+
51765247
//We may discard:
51775248
//- htlc update there as failure-trigger tx (revoked commitment tx, non-revoked commitment tx, HTLC-timeout tx) has been disconnected
51785249
//- maturing spendable output has transaction paying us has been disconnected
@@ -5199,6 +5270,8 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
51995270
F::Target: FeeEstimator,
52005271
L::Target: Logger,
52015272
{
5273+
self.alternative_funding_confirmed.take_if(|(funding_txid, _)| funding_txid == txid);
5274+
52025275
let mut removed_height = None;
52035276
for entry in self.onchain_events_awaiting_threshold_conf.iter() {
52045277
if entry.txid == *txid {
@@ -5870,6 +5943,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
58705943
let mut first_negotiated_funding_txo = RequiredWrapper(None);
58715944
let mut channel_parameters = None;
58725945
let mut pending_funding = None;
5946+
let mut alternative_funding_confirmed = None;
58735947
read_tlv_fields!(reader, {
58745948
(1, funding_spend_confirmed, option),
58755949
(3, htlcs_resolved_on_chain, optional_vec),
@@ -5888,6 +5962,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
58885962
(29, initial_counterparty_commitment_tx, option),
58895963
(31, channel_parameters, (option: ReadableArgs, None)),
58905964
(32, pending_funding, optional_vec),
5965+
(34, alternative_funding_confirmed, option),
58915966
});
58925967
if let Some(payment_preimages_with_info) = payment_preimages_with_info {
58935968
if payment_preimages_with_info.len() != payment_preimages.len() {
@@ -6057,6 +6132,8 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
60576132

60586133
current_holder_htlc_data,
60596134
prev_holder_htlc_data,
6135+
6136+
alternative_funding_confirmed,
60606137
})))
60616138
}
60626139
}

lightning/src/ln/channel.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6842,6 +6842,11 @@ where
68426842
);
68436843
}
68446844

6845+
let confirmation_depth = self
6846+
.context
6847+
.minimum_depth(pending_splice_funding)
6848+
.expect("ChannelContext::minimum_depth should be set for FundedChannel");
6849+
68456850
log_info!(logger, "Received splice initial commitment_signed from peer for channel {} with funding txid {}",
68466851
&self.context.channel_id(), pending_splice_funding.get_funding_txo().unwrap().txid);
68476852

@@ -6852,6 +6857,7 @@ where
68526857
channel_parameters: pending_splice_funding.channel_transaction_parameters.clone(),
68536858
holder_commitment_tx,
68546859
counterparty_commitment_tx,
6860+
confirmation_depth,
68556861
}],
68566862
channel_id: Some(self.context.channel_id()),
68576863
};

0 commit comments

Comments
 (0)