Skip to content

Commit 88b2c22

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 ebfe6af commit 88b2c22

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, {
@@ -3610,7 +3614,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
36103614
// Assume that the broadcasted commitment transaction confirmed in the current best
36113615
// block. Even if not, its a reasonable metric for the bump criteria on the HTLC
36123616
// transactions.
3613-
let (claim_reqs, _) = self.get_broadcasted_holder_claims(holder_commitment_tx, self.best_block.height);
3617+
let (claim_reqs, _) = self.get_broadcasted_holder_claims(&self.funding, holder_commitment_tx, self.best_block.height);
36143618
let conf_target = self.closure_conf_target();
36153619
self.onchain_tx_handler.update_claims_view_from_requests(
36163620
claim_reqs, self.best_block.height, self.best_block.height, broadcaster,
@@ -3621,25 +3625,37 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
36213625
}
36223626

36233627
#[rustfmt::skip]
3624-
fn generate_claimable_outpoints_and_watch_outputs(&mut self, reason: ClosureReason) -> (Vec<PackageTemplate>, Vec<TransactionOutputs>) {
3625-
let holder_commitment_tx = &self.funding.current_holder_commitment_tx;
3628+
fn generate_claimable_outpoints_and_watch_outputs(
3629+
&mut self, generate_monitor_event_with_reason: Option<ClosureReason>,
3630+
) -> (Vec<PackageTemplate>, Vec<TransactionOutputs>) {
3631+
let funding = self.alternative_funding_confirmed
3632+
.map(|(alternative_funding_txid, _)| {
3633+
self.pending_funding
3634+
.iter()
3635+
.find(|funding| funding.funding_txid() == alternative_funding_txid)
3636+
.expect("FundingScope for confirmed alternative funding must exist")
3637+
})
3638+
.unwrap_or(&self.funding);
3639+
let holder_commitment_tx = &funding.current_holder_commitment_tx;
36263640
let funding_outp = HolderFundingOutput::build(
36273641
holder_commitment_tx.clone(),
3628-
self.funding.channel_parameters.clone(),
3642+
funding.channel_parameters.clone(),
36293643
);
3630-
let funding_outpoint = self.get_funding_txo();
3644+
let funding_outpoint = funding.funding_outpoint();
36313645
let commitment_package = PackageTemplate::build_package(
36323646
funding_outpoint.txid.clone(), funding_outpoint.index as u32,
36333647
PackageSolvingData::HolderFundingOutput(funding_outp),
36343648
self.best_block.height,
36353649
);
36363650
let mut claimable_outpoints = vec![commitment_package];
3637-
let event = MonitorEvent::HolderForceClosedWithInfo {
3638-
reason,
3639-
outpoint: funding_outpoint,
3640-
channel_id: self.channel_id,
3641-
};
3642-
self.pending_monitor_events.push(event);
3651+
if let Some(reason) = generate_monitor_event_with_reason {
3652+
let event = MonitorEvent::HolderForceClosedWithInfo {
3653+
reason,
3654+
outpoint: funding_outpoint,
3655+
channel_id: self.channel_id,
3656+
};
3657+
self.pending_monitor_events.push(event);
3658+
}
36433659

36443660
// Although we aren't signing the transaction directly here, the transaction will be signed
36453661
// in the claim that is queued to OnchainTxHandler. We set holder_tx_signed here to reject
@@ -3649,12 +3665,12 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
36493665
// We can't broadcast our HTLC transactions while the commitment transaction is
36503666
// unconfirmed. We'll delay doing so until we detect the confirmed commitment in
36513667
// `transactions_confirmed`.
3652-
if !self.channel_type_features().supports_anchors_zero_fee_htlc_tx() {
3668+
if !funding.channel_type_features().supports_anchors_zero_fee_htlc_tx() {
36533669
// Because we're broadcasting a commitment transaction, we should construct the package
36543670
// assuming it gets confirmed in the next block. Sadly, we have code which considers
36553671
// "not yet confirmed" things as discardable, so we cannot do that here.
36563672
let (mut new_outpoints, _) = self.get_broadcasted_holder_claims(
3657-
holder_commitment_tx, self.best_block.height,
3673+
&funding, holder_commitment_tx, self.best_block.height,
36583674
);
36593675
let new_outputs = self.get_broadcasted_holder_watch_outputs(holder_commitment_tx);
36603676
if !new_outputs.is_empty() {
@@ -3678,7 +3694,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
36783694
broadcasted_latest_txn: Some(true),
36793695
message: "ChannelMonitor-initiated commitment transaction broadcast".to_owned(),
36803696
};
3681-
let (claimable_outpoints, _) = self.generate_claimable_outpoints_and_watch_outputs(reason);
3697+
let (claimable_outpoints, _) = self.generate_claimable_outpoints_and_watch_outputs(Some(reason));
36823698
let conf_target = self.closure_conf_target();
36833699
self.onchain_tx_handler.update_claims_view_from_requests(
36843700
claimable_outpoints, self.best_block.height, self.best_block.height, broadcaster,
@@ -4524,7 +4540,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
45244540

45254541
#[rustfmt::skip]
45264542
fn get_broadcasted_holder_htlc_descriptors(
4527-
&self, holder_tx: &HolderCommitmentTransaction,
4543+
&self, funding: &FundingScope, holder_tx: &HolderCommitmentTransaction,
45284544
) -> Vec<HTLCDescriptor> {
45294545
let tx = holder_tx.trust();
45304546
let mut htlcs = Vec::with_capacity(holder_tx.nondust_htlcs().len());
@@ -4542,11 +4558,10 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
45424558
};
45434559

45444560
htlcs.push(HTLCDescriptor {
4545-
// TODO(splicing): Consider alternative funding scopes.
45464561
channel_derivation_parameters: ChannelDerivationParameters {
4547-
value_satoshis: self.funding.channel_parameters.channel_value_satoshis,
4562+
value_satoshis: funding.channel_parameters.channel_value_satoshis,
45484563
keys_id: self.channel_keys_id,
4549-
transaction_parameters: self.funding.channel_parameters.clone(),
4564+
transaction_parameters: funding.channel_parameters.clone(),
45504565
},
45514566
commitment_txid: tx.txid(),
45524567
per_commitment_number: tx.commitment_number(),
@@ -4566,7 +4581,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
45664581
// script so we can detect whether a holder transaction has been seen on-chain.
45674582
#[rustfmt::skip]
45684583
fn get_broadcasted_holder_claims(
4569-
&self, holder_tx: &HolderCommitmentTransaction, conf_height: u32,
4584+
&self, funding: &FundingScope, holder_tx: &HolderCommitmentTransaction, conf_height: u32,
45704585
) -> (Vec<PackageTemplate>, Option<(ScriptBuf, PublicKey, RevocationKey)>) {
45714586
let tx = holder_tx.trust();
45724587
let keys = tx.keys();
@@ -4577,7 +4592,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
45774592
redeem_script.to_p2wsh(), holder_tx.per_commitment_point(), keys.revocation_key.clone(),
45784593
));
45794594

4580-
let claim_requests = self.get_broadcasted_holder_htlc_descriptors(holder_tx).into_iter()
4595+
let claim_requests = self.get_broadcasted_holder_htlc_descriptors(funding, holder_tx).into_iter()
45814596
.map(|htlc_descriptor| {
45824597
let counterparty_spendable_height = if htlc_descriptor.htlc.offered {
45834598
conf_height
@@ -4644,7 +4659,8 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
46444659
is_holder_tx = true;
46454660
log_info!(logger, "Got broadcast of latest holder commitment tx {}, searching for available HTLCs to claim", commitment_txid);
46464661
let holder_commitment_tx = &self.funding.current_holder_commitment_tx;
4647-
let res = self.get_broadcasted_holder_claims(holder_commitment_tx, height);
4662+
let res =
4663+
self.get_broadcasted_holder_claims(&self.funding, holder_commitment_tx, height);
46484664
let mut to_watch = self.get_broadcasted_holder_watch_outputs(holder_commitment_tx);
46494665
append_onchain_update!(res, to_watch);
46504666
fail_unbroadcast_htlcs!(
@@ -4661,7 +4677,8 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
46614677
if holder_commitment_tx.trust().txid() == commitment_txid {
46624678
is_holder_tx = true;
46634679
log_info!(logger, "Got broadcast of previous holder commitment tx {}, searching for available HTLCs to claim", commitment_txid);
4664-
let res = self.get_broadcasted_holder_claims(holder_commitment_tx, height);
4680+
let res =
4681+
self.get_broadcasted_holder_claims(&self.funding, holder_commitment_tx, height);
46654682
let mut to_watch = self.get_broadcasted_holder_watch_outputs(holder_commitment_tx);
46664683
append_onchain_update!(res, to_watch);
46674684
fail_unbroadcast_htlcs!(
@@ -4697,45 +4714,63 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
46974714
}
46984715
// If we have generated claims for counterparty_commitment_txid earlier, we can rely on always
46994716
// having claim related htlcs for counterparty_commitment_txid in counterparty_claimable_outpoints.
4700-
for (htlc, _) in self.funding.counterparty_claimable_outpoints.get(counterparty_commitment_txid).unwrap_or(&vec![]) {
4701-
log_trace!(logger, "Canceling claims for previously confirmed counterparty commitment {}",
4702-
counterparty_commitment_txid);
4703-
let mut outpoint = BitcoinOutPoint { txid: *counterparty_commitment_txid, vout: 0 };
4704-
if let Some(vout) = htlc.transaction_output_index {
4705-
outpoint.vout = vout;
4706-
self.onchain_tx_handler.abandon_claim(&outpoint);
4717+
for funding in core::iter::once(&self.funding).chain(self.pending_funding.iter()) {
4718+
let mut found_claim = false;
4719+
for (htlc, _) in funding.counterparty_claimable_outpoints.get(counterparty_commitment_txid).unwrap_or(&vec![]) {
4720+
let mut outpoint = BitcoinOutPoint { txid: *counterparty_commitment_txid, vout: 0 };
4721+
if let Some(vout) = htlc.transaction_output_index {
4722+
outpoint.vout = vout;
4723+
if self.onchain_tx_handler.abandon_claim(&outpoint) {
4724+
found_claim = true;
4725+
}
4726+
}
4727+
}
4728+
if found_claim {
4729+
log_trace!(logger, "Canceled claims for previously confirmed counterparty commitment with txid {counterparty_commitment_txid}");
47074730
}
47084731
}
47094732
}
47104733
// Cancel any pending claims for any holder commitments in case they had previously
47114734
// confirmed or been signed (in which case we will start attempting to claim without
47124735
// waiting for confirmation).
4713-
if self.funding.current_holder_commitment_tx.trust().txid() != *confirmed_commitment_txid {
4714-
let txid = self.funding.current_holder_commitment_tx.trust().txid();
4715-
log_trace!(logger, "Canceling claims for previously broadcast holder commitment {}", txid);
4716-
let mut outpoint = BitcoinOutPoint { txid, vout: 0 };
4717-
for htlc in self.funding.current_holder_commitment_tx.nondust_htlcs() {
4718-
if let Some(vout) = htlc.transaction_output_index {
4719-
outpoint.vout = vout;
4720-
self.onchain_tx_handler.abandon_claim(&outpoint);
4721-
} else {
4722-
debug_assert!(false, "Expected transaction output index for non-dust HTLC");
4723-
}
4724-
}
4725-
}
4726-
if let Some(prev_holder_commitment_tx) = &self.funding.prev_holder_commitment_tx {
4727-
let txid = prev_holder_commitment_tx.trust().txid();
4728-
if txid != *confirmed_commitment_txid {
4729-
log_trace!(logger, "Canceling claims for previously broadcast holder commitment {}", txid);
4736+
for funding in core::iter::once(&self.funding).chain(self.pending_funding.iter()) {
4737+
if funding.current_holder_commitment_tx.trust().txid() != *confirmed_commitment_txid {
4738+
let mut found_claim = false;
4739+
let txid = funding.current_holder_commitment_tx.trust().txid();
47304740
let mut outpoint = BitcoinOutPoint { txid, vout: 0 };
4731-
for htlc in prev_holder_commitment_tx.nondust_htlcs() {
4741+
for htlc in funding.current_holder_commitment_tx.nondust_htlcs() {
47324742
if let Some(vout) = htlc.transaction_output_index {
47334743
outpoint.vout = vout;
4734-
self.onchain_tx_handler.abandon_claim(&outpoint);
4744+
if self.onchain_tx_handler.abandon_claim(&outpoint) {
4745+
found_claim = true;
4746+
}
47354747
} else {
47364748
debug_assert!(false, "Expected transaction output index for non-dust HTLC");
47374749
}
47384750
}
4751+
if found_claim {
4752+
log_trace!(logger, "Canceled claims for previously broadcast holder commitment with txid {txid}");
4753+
}
4754+
}
4755+
if let Some(prev_holder_commitment_tx) = &funding.prev_holder_commitment_tx {
4756+
let txid = prev_holder_commitment_tx.trust().txid();
4757+
if txid != *confirmed_commitment_txid {
4758+
let mut found_claim = false;
4759+
let mut outpoint = BitcoinOutPoint { txid, vout: 0 };
4760+
for htlc in prev_holder_commitment_tx.nondust_htlcs() {
4761+
if let Some(vout) = htlc.transaction_output_index {
4762+
outpoint.vout = vout;
4763+
if self.onchain_tx_handler.abandon_claim(&outpoint) {
4764+
found_claim = true;
4765+
}
4766+
} else {
4767+
debug_assert!(false, "Expected transaction output index for non-dust HTLC");
4768+
}
4769+
}
4770+
if found_claim {
4771+
log_trace!(logger, "Canceled claims for previously broadcast holder commitment with txid {txid}");
4772+
}
4773+
}
47394774
}
47404775
}
47414776
}
@@ -4762,7 +4797,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
47624797
return holder_transactions;
47634798
}
47644799

4765-
self.get_broadcasted_holder_htlc_descriptors(&self.funding.current_holder_commitment_tx)
4800+
self.get_broadcasted_holder_htlc_descriptors(&self.funding, &self.funding.current_holder_commitment_tx)
47664801
.into_iter()
47674802
.for_each(|htlc_descriptor| {
47684803
let txid = self.funding.current_holder_commitment_tx.trust().txid();
@@ -4856,6 +4891,13 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
48564891

48574892
let block_hash = header.block_hash();
48584893

4894+
// We may need to broadcast our holder commitment if we see a funding transaction reorg,
4895+
// with a different funding transaction confirming. It's possible we process a
4896+
// holder/counterparty commitment within this same block that would invalidate the one we're
4897+
// intending to broadcast, so we track whether we should broadcast and wait until all
4898+
// transactions in the block have been processed.
4899+
let mut should_broadcast_commitment = false;
4900+
48594901
let mut watch_outputs = Vec::new();
48604902
let mut claimable_outpoints = Vec::new();
48614903
'tx_iter: for tx in &txn_matched {
@@ -4894,7 +4936,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
48944936
// `FundingScope` scope until we see the
48954937
// [`ChannelMonitorUpdateStep::RenegotiatedFundingLocked`] for it, but we track the txid
48964938
// so we know which holder commitment transaction we may need to broadcast.
4897-
if let Some(_alternative_funding) = self
4939+
if let Some(alternative_funding) = self
48984940
.pending_funding
48994941
.iter()
49004942
.find(|funding| funding.funding_txid() == txid)
@@ -4910,6 +4952,24 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
49104952
.any(|e| matches!(e.event, OnchainEvent::FundingSpendConfirmation { .. }))
49114953
);
49124954

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

49154975
if self.no_further_updates_allowed() {
@@ -4925,6 +4985,19 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
49254985
});
49264986
}
49274987

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

@@ -4933,6 +5006,8 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
49335006
// commitment transactions and HTLC transactions will all only ever have one input
49345007
// (except for HTLC transactions for channels with anchor outputs), which is an easy
49355008
// way to filter out any potential non-matching txn for lazy filters.
5009+
//
5010+
// TODO(splicing): Produce commitment claims for currently confirmed funding.
49365011
let prevout = &tx.input[0].previous_output;
49375012
let funding_outpoint = self.get_funding_txo();
49385013
if prevout.txid == funding_outpoint.txid && prevout.vout == funding_outpoint.index as u32 {
@@ -4962,6 +5037,13 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
49625037

49635038
claimable_outpoints.append(&mut new_outpoints);
49645039
}
5040+
5041+
// We've just seen a commitment confirm, which conflicts with the holder
5042+
// commitment we intend to broadcast
5043+
if should_broadcast_commitment {
5044+
log_info!(logger, "Canceling our queued holder commitment broadcast as we've found a conflict confirm instead");
5045+
should_broadcast_commitment = false;
5046+
}
49655047
}
49665048
self.onchain_events_awaiting_threshold_conf.push(OnchainEventEntry {
49675049
txid,
@@ -5010,6 +5092,13 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
50105092
self.best_block = BestBlock::new(block_hash, height);
50115093
}
50125094

5095+
if should_broadcast_commitment {
5096+
let (mut claimables, mut outputs) =
5097+
self.generate_claimable_outpoints_and_watch_outputs(None);
5098+
claimable_outpoints.append(&mut claimables);
5099+
watch_outputs.append(&mut outputs);
5100+
}
5101+
50135102
self.block_confirmed(height, block_hash, txn_matched, watch_outputs, claimable_outpoints, &broadcaster, &fee_estimator, logger)
50145103
}
50155104

@@ -5043,7 +5132,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
50435132

50445133
let should_broadcast = self.should_broadcast_holder_commitment_txn(logger);
50455134
if should_broadcast {
5046-
let (mut new_outpoints, mut new_outputs) = self.generate_claimable_outpoints_and_watch_outputs(ClosureReason::HTLCsTimedOut);
5135+
let (mut new_outpoints, mut new_outputs) = self.generate_claimable_outpoints_and_watch_outputs(Some(ClosureReason::HTLCsTimedOut));
50475136
claimable_outpoints.append(&mut new_outpoints);
50485137
watch_outputs.append(&mut new_outputs);
50495138
}
@@ -5246,6 +5335,14 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
52465335
if let Some((_, conf_height)) = self.alternative_funding_confirmed.as_ref() {
52475336
if *conf_height == height {
52485337
self.alternative_funding_confirmed.take();
5338+
if self.holder_tx_signed {
5339+
// Cancel any previous claims that are no longer valid as they stemmed from a
5340+
// different funding transaction. We'll wait until we see a funding transaction
5341+
// confirm again before attempting to broadcast the new valid holder commitment.
5342+
let new_holder_commitment_txid =
5343+
self.funding.current_holder_commitment_tx.trust().txid();
5344+
self.cancel_prev_commitment_claims(&logger, &new_holder_commitment_txid);
5345+
}
52495346
}
52505347
}
52515348

@@ -5292,6 +5389,14 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
52925389
if let Some((alternative_funding_txid, _)) = self.alternative_funding_confirmed.as_ref() {
52935390
if alternative_funding_txid == txid {
52945391
self.alternative_funding_confirmed.take();
5392+
if self.holder_tx_signed {
5393+
// Cancel any previous claims that are no longer valid as they stemmed from a
5394+
// different funding transaction. We'll wait until we see a funding transaction
5395+
// confirm again before attempting to broadcast the new valid holder commitment.
5396+
let new_holder_commitment_txid =
5397+
self.funding.current_holder_commitment_tx.trust().txid();
5398+
self.cancel_prev_commitment_claims(&logger, &new_holder_commitment_txid);
5399+
}
52955400
}
52965401
}
52975402

0 commit comments

Comments
 (0)