Skip to content

Commit 89ce01d

Browse files
committed
Broadcast holder commitment for currently confirmed funding
A `FundingScope` can only be promoted once a `ChannelMonitorUpdateStep::RenegotiatedFundingLocked` is applied, or if the monitor is no longer accepting updates, once the renegotiated funding transaction is no longer under reorg risk. Because of this, our current `FundingScope` may not reflect the latest confirmed state in the chain. Before making a holder commitment broadcast, we must check which `FundingScope` is currently confirmed to ensure that it can propogate throughout the network.
1 parent ad99d9a commit 89ce01d

File tree

2 files changed

+171
-55
lines changed

2 files changed

+171
-55
lines changed

lightning/src/chain/channelmonitor.rs

Lines changed: 156 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1106,6 +1106,10 @@ impl FundingScope {
11061106
fn is_splice(&self) -> bool {
11071107
self.channel_parameters.splice_parent_funding_txid.is_some()
11081108
}
1109+
1110+
fn channel_type_features(&self) -> &ChannelTypeFeatures {
1111+
&self.channel_parameters.channel_type_features
1112+
}
11091113
}
11101114

11111115
impl_writeable_tlv_based!(FundingScope, {
@@ -3615,7 +3619,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
36153619
// Assume that the broadcasted commitment transaction confirmed in the current best
36163620
// block. Even if not, its a reasonable metric for the bump criteria on the HTLC
36173621
// transactions.
3618-
let (claim_reqs, _) = self.get_broadcasted_holder_claims(holder_commitment_tx, self.best_block.height);
3622+
let (claim_reqs, _) = self.get_broadcasted_holder_claims(&self.funding, holder_commitment_tx, self.best_block.height);
36193623
let conf_target = self.closure_conf_target();
36203624
self.onchain_tx_handler.update_claims_view_from_requests(
36213625
claim_reqs, self.best_block.height, self.best_block.height, broadcaster,
@@ -3626,25 +3630,37 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
36263630
}
36273631

36283632
#[rustfmt::skip]
3629-
fn generate_claimable_outpoints_and_watch_outputs(&mut self, reason: ClosureReason) -> (Vec<PackageTemplate>, Vec<TransactionOutputs>) {
3630-
let holder_commitment_tx = &self.funding.current_holder_commitment_tx;
3633+
fn generate_claimable_outpoints_and_watch_outputs(
3634+
&mut self, generate_monitor_event_with_reason: Option<ClosureReason>,
3635+
) -> (Vec<PackageTemplate>, Vec<TransactionOutputs>) {
3636+
let funding = self.alternative_funding_confirmed
3637+
.map(|(alternative_funding_txid, _)| {
3638+
self.pending_funding
3639+
.iter()
3640+
.find(|funding| funding.funding_txid() == alternative_funding_txid)
3641+
.expect("FundingScope for confirmed alternative funding must exist")
3642+
})
3643+
.unwrap_or(&self.funding);
3644+
let holder_commitment_tx = &funding.current_holder_commitment_tx;
36313645
let funding_outp = HolderFundingOutput::build(
36323646
holder_commitment_tx.clone(),
3633-
self.funding.channel_parameters.clone(),
3647+
funding.channel_parameters.clone(),
36343648
);
3635-
let funding_outpoint = self.get_funding_txo();
3649+
let funding_outpoint = funding.funding_outpoint();
36363650
let commitment_package = PackageTemplate::build_package(
36373651
funding_outpoint.txid.clone(), funding_outpoint.index as u32,
36383652
PackageSolvingData::HolderFundingOutput(funding_outp),
36393653
self.best_block.height,
36403654
);
36413655
let mut claimable_outpoints = vec![commitment_package];
3642-
let event = MonitorEvent::HolderForceClosedWithInfo {
3643-
reason,
3644-
outpoint: funding_outpoint,
3645-
channel_id: self.channel_id,
3646-
};
3647-
self.pending_monitor_events.push(event);
3656+
if let Some(reason) = generate_monitor_event_with_reason {
3657+
let event = MonitorEvent::HolderForceClosedWithInfo {
3658+
reason,
3659+
outpoint: funding_outpoint,
3660+
channel_id: self.channel_id,
3661+
};
3662+
self.pending_monitor_events.push(event);
3663+
}
36483664

36493665
// Although we aren't signing the transaction directly here, the transaction will be signed
36503666
// in the claim that is queued to OnchainTxHandler. We set holder_tx_signed here to reject
@@ -3654,12 +3670,12 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
36543670
// We can't broadcast our HTLC transactions while the commitment transaction is
36553671
// unconfirmed. We'll delay doing so until we detect the confirmed commitment in
36563672
// `transactions_confirmed`.
3657-
if !self.channel_type_features().supports_anchors_zero_fee_htlc_tx() {
3673+
if !funding.channel_type_features().supports_anchors_zero_fee_htlc_tx() {
36583674
// Because we're broadcasting a commitment transaction, we should construct the package
36593675
// assuming it gets confirmed in the next block. Sadly, we have code which considers
36603676
// "not yet confirmed" things as discardable, so we cannot do that here.
36613677
let (mut new_outpoints, _) = self.get_broadcasted_holder_claims(
3662-
holder_commitment_tx, self.best_block.height,
3678+
funding, holder_commitment_tx, self.best_block.height,
36633679
);
36643680
let new_outputs = self.get_broadcasted_holder_watch_outputs(holder_commitment_tx);
36653681
if !new_outputs.is_empty() {
@@ -3683,7 +3699,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
36833699
broadcasted_latest_txn: Some(true),
36843700
message: "ChannelMonitor-initiated commitment transaction broadcast".to_owned(),
36853701
};
3686-
let (claimable_outpoints, _) = self.generate_claimable_outpoints_and_watch_outputs(reason);
3702+
let (claimable_outpoints, _) = self.generate_claimable_outpoints_and_watch_outputs(Some(reason));
36873703
let conf_target = self.closure_conf_target();
36883704
self.onchain_tx_handler.update_claims_view_from_requests(
36893705
claimable_outpoints, self.best_block.height, self.best_block.height, broadcaster,
@@ -4529,7 +4545,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
45294545

45304546
#[rustfmt::skip]
45314547
fn get_broadcasted_holder_htlc_descriptors(
4532-
&self, holder_tx: &HolderCommitmentTransaction,
4548+
&self, funding: &FundingScope, holder_tx: &HolderCommitmentTransaction,
45334549
) -> Vec<HTLCDescriptor> {
45344550
let tx = holder_tx.trust();
45354551
let mut htlcs = Vec::with_capacity(holder_tx.nondust_htlcs().len());
@@ -4547,11 +4563,10 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
45474563
};
45484564

45494565
htlcs.push(HTLCDescriptor {
4550-
// TODO(splicing): Consider alternative funding scopes.
45514566
channel_derivation_parameters: ChannelDerivationParameters {
4552-
value_satoshis: self.funding.channel_parameters.channel_value_satoshis,
4567+
value_satoshis: funding.channel_parameters.channel_value_satoshis,
45534568
keys_id: self.channel_keys_id,
4554-
transaction_parameters: self.funding.channel_parameters.clone(),
4569+
transaction_parameters: funding.channel_parameters.clone(),
45554570
},
45564571
commitment_txid: tx.txid(),
45574572
per_commitment_number: tx.commitment_number(),
@@ -4571,7 +4586,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
45714586
// script so we can detect whether a holder transaction has been seen on-chain.
45724587
#[rustfmt::skip]
45734588
fn get_broadcasted_holder_claims(
4574-
&self, holder_tx: &HolderCommitmentTransaction, conf_height: u32,
4589+
&self, funding: &FundingScope, holder_tx: &HolderCommitmentTransaction, conf_height: u32,
45754590
) -> (Vec<PackageTemplate>, Option<(ScriptBuf, PublicKey, RevocationKey)>) {
45764591
let tx = holder_tx.trust();
45774592
let keys = tx.keys();
@@ -4582,7 +4597,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
45824597
redeem_script.to_p2wsh(), holder_tx.per_commitment_point(), keys.revocation_key.clone(),
45834598
));
45844599

4585-
let claim_requests = self.get_broadcasted_holder_htlc_descriptors(holder_tx).into_iter()
4600+
let claim_requests = self.get_broadcasted_holder_htlc_descriptors(funding, holder_tx).into_iter()
45864601
.map(|htlc_descriptor| {
45874602
let counterparty_spendable_height = if htlc_descriptor.htlc.offered {
45884603
conf_height
@@ -4649,7 +4664,8 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
46494664
is_holder_tx = true;
46504665
log_info!(logger, "Got broadcast of latest holder commitment tx {}, searching for available HTLCs to claim", commitment_txid);
46514666
let holder_commitment_tx = &self.funding.current_holder_commitment_tx;
4652-
let res = self.get_broadcasted_holder_claims(holder_commitment_tx, height);
4667+
let res =
4668+
self.get_broadcasted_holder_claims(&self.funding, holder_commitment_tx, height);
46534669
let mut to_watch = self.get_broadcasted_holder_watch_outputs(holder_commitment_tx);
46544670
append_onchain_update!(res, to_watch);
46554671
fail_unbroadcast_htlcs!(
@@ -4666,7 +4682,8 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
46664682
if holder_commitment_tx.trust().txid() == commitment_txid {
46674683
is_holder_tx = true;
46684684
log_info!(logger, "Got broadcast of previous holder commitment tx {}, searching for available HTLCs to claim", commitment_txid);
4669-
let res = self.get_broadcasted_holder_claims(holder_commitment_tx, height);
4685+
let res =
4686+
self.get_broadcasted_holder_claims(&self.funding, holder_commitment_tx, height);
46704687
let mut to_watch = self.get_broadcasted_holder_watch_outputs(holder_commitment_tx);
46714688
append_onchain_update!(res, to_watch);
46724689
fail_unbroadcast_htlcs!(
@@ -4702,45 +4719,63 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
47024719
}
47034720
// If we have generated claims for counterparty_commitment_txid earlier, we can rely on always
47044721
// having claim related htlcs for counterparty_commitment_txid in counterparty_claimable_outpoints.
4705-
for (htlc, _) in self.funding.counterparty_claimable_outpoints.get(counterparty_commitment_txid).unwrap_or(&vec![]) {
4706-
log_trace!(logger, "Canceling claims for previously confirmed counterparty commitment {}",
4707-
counterparty_commitment_txid);
4708-
let mut outpoint = BitcoinOutPoint { txid: *counterparty_commitment_txid, vout: 0 };
4709-
if let Some(vout) = htlc.transaction_output_index {
4710-
outpoint.vout = vout;
4711-
self.onchain_tx_handler.abandon_claim(&outpoint);
4722+
for funding in core::iter::once(&self.funding).chain(self.pending_funding.iter()) {
4723+
let mut found_claim = false;
4724+
for (htlc, _) in funding.counterparty_claimable_outpoints.get(counterparty_commitment_txid).unwrap_or(&vec![]) {
4725+
let mut outpoint = BitcoinOutPoint { txid: *counterparty_commitment_txid, vout: 0 };
4726+
if let Some(vout) = htlc.transaction_output_index {
4727+
outpoint.vout = vout;
4728+
if self.onchain_tx_handler.abandon_claim(&outpoint) {
4729+
found_claim = true;
4730+
}
4731+
}
4732+
}
4733+
if found_claim {
4734+
log_trace!(logger, "Canceled claims for previously confirmed counterparty commitment with txid {counterparty_commitment_txid}");
47124735
}
47134736
}
47144737
}
47154738
// Cancel any pending claims for any holder commitments in case they had previously
47164739
// confirmed or been signed (in which case we will start attempting to claim without
47174740
// waiting for confirmation).
4718-
if self.funding.current_holder_commitment_tx.trust().txid() != *confirmed_commitment_txid {
4719-
let txid = self.funding.current_holder_commitment_tx.trust().txid();
4720-
log_trace!(logger, "Canceling claims for previously broadcast holder commitment {}", txid);
4721-
let mut outpoint = BitcoinOutPoint { txid, vout: 0 };
4722-
for htlc in self.funding.current_holder_commitment_tx.nondust_htlcs() {
4723-
if let Some(vout) = htlc.transaction_output_index {
4724-
outpoint.vout = vout;
4725-
self.onchain_tx_handler.abandon_claim(&outpoint);
4726-
} else {
4727-
debug_assert!(false, "Expected transaction output index for non-dust HTLC");
4728-
}
4729-
}
4730-
}
4731-
if let Some(prev_holder_commitment_tx) = &self.funding.prev_holder_commitment_tx {
4732-
let txid = prev_holder_commitment_tx.trust().txid();
4733-
if txid != *confirmed_commitment_txid {
4734-
log_trace!(logger, "Canceling claims for previously broadcast holder commitment {}", txid);
4741+
for funding in core::iter::once(&self.funding).chain(self.pending_funding.iter()) {
4742+
if funding.current_holder_commitment_tx.trust().txid() != *confirmed_commitment_txid {
4743+
let mut found_claim = false;
4744+
let txid = funding.current_holder_commitment_tx.trust().txid();
47354745
let mut outpoint = BitcoinOutPoint { txid, vout: 0 };
4736-
for htlc in prev_holder_commitment_tx.nondust_htlcs() {
4746+
for htlc in funding.current_holder_commitment_tx.nondust_htlcs() {
47374747
if let Some(vout) = htlc.transaction_output_index {
47384748
outpoint.vout = vout;
4739-
self.onchain_tx_handler.abandon_claim(&outpoint);
4749+
if self.onchain_tx_handler.abandon_claim(&outpoint) {
4750+
found_claim = true;
4751+
}
47404752
} else {
47414753
debug_assert!(false, "Expected transaction output index for non-dust HTLC");
47424754
}
47434755
}
4756+
if found_claim {
4757+
log_trace!(logger, "Canceled claims for previously broadcast holder commitment with txid {txid}");
4758+
}
4759+
}
4760+
if let Some(prev_holder_commitment_tx) = &funding.prev_holder_commitment_tx {
4761+
let txid = prev_holder_commitment_tx.trust().txid();
4762+
if txid != *confirmed_commitment_txid {
4763+
let mut found_claim = false;
4764+
let mut outpoint = BitcoinOutPoint { txid, vout: 0 };
4765+
for htlc in prev_holder_commitment_tx.nondust_htlcs() {
4766+
if let Some(vout) = htlc.transaction_output_index {
4767+
outpoint.vout = vout;
4768+
if self.onchain_tx_handler.abandon_claim(&outpoint) {
4769+
found_claim = true;
4770+
}
4771+
} else {
4772+
debug_assert!(false, "Expected transaction output index for non-dust HTLC");
4773+
}
4774+
}
4775+
if found_claim {
4776+
log_trace!(logger, "Canceled claims for previously broadcast holder commitment with txid {txid}");
4777+
}
4778+
}
47444779
}
47454780
}
47464781
}
@@ -4767,7 +4802,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
47674802
return holder_transactions;
47684803
}
47694804

4770-
self.get_broadcasted_holder_htlc_descriptors(&self.funding.current_holder_commitment_tx)
4805+
self.get_broadcasted_holder_htlc_descriptors(&self.funding, &self.funding.current_holder_commitment_tx)
47714806
.into_iter()
47724807
.for_each(|htlc_descriptor| {
47734808
let txid = self.funding.current_holder_commitment_tx.trust().txid();
@@ -4861,6 +4896,13 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
48614896

48624897
let block_hash = header.block_hash();
48634898

4899+
// We may need to broadcast our holder commitment if we see a funding transaction reorg,
4900+
// with a different funding transaction confirming. It's possible we process a
4901+
// holder/counterparty commitment within this same block that would invalidate the one we're
4902+
// intending to broadcast, so we track whether we should broadcast and wait until all
4903+
// transactions in the block have been processed.
4904+
let mut should_broadcast_commitment = false;
4905+
48644906
let mut watch_outputs = Vec::new();
48654907
let mut claimable_outpoints = Vec::new();
48664908
'tx_iter: for tx in &txn_matched {
@@ -4899,7 +4941,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
48994941
// `FundingScope` scope until we see the
49004942
// [`ChannelMonitorUpdateStep::RenegotiatedFundingLocked`] for it, but we track the txid
49014943
// so we know which holder commitment transaction we may need to broadcast.
4902-
if let Some(_alternative_funding) = self
4944+
if let Some(alternative_funding) = self
49034945
.pending_funding
49044946
.iter()
49054947
.find(|funding| funding.funding_txid() == txid)
@@ -4915,6 +4957,24 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
49154957
.any(|e| matches!(e.event, OnchainEvent::FundingSpendConfirmation { .. }))
49164958
);
49174959

4960+
let (desc, msg) = if alternative_funding.is_splice() {
4961+
debug_assert!(tx.input.iter().any(|input| {
4962+
let funding_outpoint = self.funding.funding_outpoint().into_bitcoin_outpoint();
4963+
input.previous_output == funding_outpoint
4964+
}));
4965+
("Splice", "splice_locked")
4966+
} else {
4967+
("Dual-funded RBF", "channel_ready")
4968+
};
4969+
let action = if self.holder_tx_signed || self.funding_spend_seen {
4970+
", broadcasting holder commitment transaction".to_string()
4971+
} else if !self.no_further_updates_allowed() {
4972+
format!(", waiting for `{msg}` exchange")
4973+
} else {
4974+
"".to_string()
4975+
};
4976+
log_info!(logger, "{desc} for channel {} confirmed with txid {txid}{action}", self.channel_id());
4977+
49184978
self.alternative_funding_confirmed = Some((txid, height));
49194979

49204980
if self.no_further_updates_allowed() {
@@ -4930,6 +4990,19 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
49304990
});
49314991
}
49324992

4993+
if self.holder_tx_signed || self.funding_spend_seen {
4994+
// Cancel any previous claims that are no longer valid as they stemmed from a
4995+
// different funding transaction.
4996+
let new_holder_commitment_txid =
4997+
alternative_funding.current_holder_commitment_tx.trust().txid();
4998+
self.cancel_prev_commitment_claims(&logger, &new_holder_commitment_txid);
4999+
5000+
// We either attempted to broadcast a holder commitment, or saw one confirm
5001+
// onchain, so broadcast the new holder commitment for the confirmed funding to
5002+
// claim our funds as the channel is no longer operational.
5003+
should_broadcast_commitment = true;
5004+
}
5005+
49335006
continue 'tx_iter;
49345007
}
49355008

@@ -4938,6 +5011,8 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
49385011
// commitment transactions and HTLC transactions will all only ever have one input
49395012
// (except for HTLC transactions for channels with anchor outputs), which is an easy
49405013
// way to filter out any potential non-matching txn for lazy filters.
5014+
//
5015+
// TODO(splicing): Produce commitment claims for currently confirmed funding.
49415016
let prevout = &tx.input[0].previous_output;
49425017
let funding_outpoint = self.get_funding_txo();
49435018
if prevout.txid == funding_outpoint.txid && prevout.vout == funding_outpoint.index as u32 {
@@ -4967,6 +5042,13 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
49675042

49685043
claimable_outpoints.append(&mut new_outpoints);
49695044
}
5045+
5046+
// We've just seen a commitment confirm, which conflicts with the holder
5047+
// commitment we intend to broadcast
5048+
if should_broadcast_commitment {
5049+
log_info!(logger, "Canceling our queued holder commitment broadcast as we've found a conflict confirm instead");
5050+
should_broadcast_commitment = false;
5051+
}
49705052
}
49715053
self.onchain_events_awaiting_threshold_conf.push(OnchainEventEntry {
49725054
txid,
@@ -5015,6 +5097,13 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
50155097
self.best_block = BestBlock::new(block_hash, height);
50165098
}
50175099

5100+
if should_broadcast_commitment {
5101+
let (mut claimables, mut outputs) =
5102+
self.generate_claimable_outpoints_and_watch_outputs(None);
5103+
claimable_outpoints.append(&mut claimables);
5104+
watch_outputs.append(&mut outputs);
5105+
}
5106+
50185107
self.block_confirmed(height, block_hash, txn_matched, watch_outputs, claimable_outpoints, &broadcaster, &fee_estimator, logger)
50195108
}
50205109

@@ -5048,7 +5137,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
50485137

50495138
let should_broadcast = self.should_broadcast_holder_commitment_txn(logger);
50505139
if should_broadcast {
5051-
let (mut new_outpoints, mut new_outputs) = self.generate_claimable_outpoints_and_watch_outputs(ClosureReason::HTLCsTimedOut);
5140+
let (mut new_outpoints, mut new_outputs) = self.generate_claimable_outpoints_and_watch_outputs(Some(ClosureReason::HTLCsTimedOut));
50525141
claimable_outpoints.append(&mut new_outpoints);
50535142
watch_outputs.append(&mut new_outputs);
50545143
}
@@ -5252,6 +5341,14 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
52525341
if let Some((_, conf_height)) = self.alternative_funding_confirmed.as_ref() {
52535342
if *conf_height == height {
52545343
self.alternative_funding_confirmed.take();
5344+
if self.holder_tx_signed {
5345+
// Cancel any previous claims that are no longer valid as they stemmed from a
5346+
// different funding transaction. We'll wait until we see a funding transaction
5347+
// confirm again before attempting to broadcast the new valid holder commitment.
5348+
let new_holder_commitment_txid =
5349+
self.funding.current_holder_commitment_tx.trust().txid();
5350+
self.cancel_prev_commitment_claims(&logger, &new_holder_commitment_txid);
5351+
}
52555352
}
52565353
}
52575354

@@ -5298,6 +5395,14 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
52985395
if let Some((alternative_funding_txid, _)) = self.alternative_funding_confirmed.as_ref() {
52995396
if alternative_funding_txid == txid {
53005397
self.alternative_funding_confirmed.take();
5398+
if self.holder_tx_signed {
5399+
// Cancel any previous claims that are no longer valid as they stemmed from a
5400+
// different funding transaction. We'll wait until we see a funding transaction
5401+
// confirm again before attempting to broadcast the new valid holder commitment.
5402+
let new_holder_commitment_txid =
5403+
self.funding.current_holder_commitment_tx.trust().txid();
5404+
self.cancel_prev_commitment_claims(&logger, &new_holder_commitment_txid);
5405+
}
53015406
}
53025407
}
53035408

0 commit comments

Comments
 (0)