Skip to content

Commit 9051703

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

File tree

2 files changed

+148
-54
lines changed

2 files changed

+148
-54
lines changed

lightning/src/chain/channelmonitor.rs

Lines changed: 133 additions & 50 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();
@@ -4858,6 +4893,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
48584893

48594894
let mut watch_outputs = Vec::new();
48604895
let mut claimable_outpoints = Vec::new();
4896+
let mut should_broadcast_commitment = false;
48614897
'tx_iter: for tx in &txn_matched {
48624898
let txid = tx.compute_txid();
48634899
log_trace!(logger, "Transaction {} confirmed in block {}", txid , block_hash);
@@ -4938,6 +4974,26 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
49384974
});
49394975
}
49404976

4977+
if self.holder_tx_signed {
4978+
// Cancel any previous claims that are no longer valid as they stemmed from a
4979+
// different funding transaction.
4980+
let alternative_holder_commitment_txid =
4981+
alternative_funding.current_holder_commitment_tx.trust().txid();
4982+
self.cancel_prev_commitment_claims(&logger, &alternative_holder_commitment_txid);
4983+
4984+
// Queue claims for the alternative holder commitment since it is the only one
4985+
// that can currently confirm so far (until we see a reorg of its funding
4986+
// transaction).
4987+
//
4988+
// It's possible we process a holder/counterparty commitment within this same
4989+
// block that would invalidate the one we're intending to broadcast. If we were
4990+
// to broadcast our holder commitment now, we wouldn't be able to cancel it via
4991+
// our usual `cancel_prev_commitment_claims` path once we see a confirmed
4992+
// commitment since the claim would still be pending in `claimable_outpoints`
4993+
// (i.e., it wouldn't have been registered with the `OnchainTxHandler` yet).
4994+
should_broadcast_commitment = true;
4995+
}
4996+
49414997
continue 'tx_iter;
49424998
}
49434999

@@ -4975,6 +5031,10 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
49755031

49765032
claimable_outpoints.append(&mut new_outpoints);
49775033
}
5034+
5035+
// We've just seen a commitment confirm, which conflicts with the holder
5036+
// commitment we intend to broadcast
5037+
should_broadcast_commitment = false;
49785038
}
49795039
self.onchain_events_awaiting_threshold_conf.push(OnchainEventEntry {
49805040
txid,
@@ -5023,6 +5083,13 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
50235083
self.best_block = BestBlock::new(block_hash, height);
50245084
}
50255085

5086+
if should_broadcast_commitment {
5087+
let (mut claimables, mut outputs) =
5088+
self.generate_claimable_outpoints_and_watch_outputs(None);
5089+
claimable_outpoints.append(&mut claimables);
5090+
watch_outputs.append(&mut outputs);
5091+
}
5092+
50265093
self.block_confirmed(height, block_hash, txn_matched, watch_outputs, claimable_outpoints, &broadcaster, &fee_estimator, logger)
50275094
}
50285095

@@ -5056,7 +5123,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
50565123

50575124
let should_broadcast = self.should_broadcast_holder_commitment_txn(logger);
50585125
if should_broadcast {
5059-
let (mut new_outpoints, mut new_outputs) = self.generate_claimable_outpoints_and_watch_outputs(ClosureReason::HTLCsTimedOut);
5126+
let (mut new_outpoints, mut new_outputs) = self.generate_claimable_outpoints_and_watch_outputs(Some(ClosureReason::HTLCsTimedOut));
50605127
claimable_outpoints.append(&mut new_outpoints);
50615128
watch_outputs.append(&mut new_outputs);
50625129
}
@@ -5259,6 +5326,14 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
52595326
if let Some((_, conf_height)) = self.alternative_funding_confirmed.as_ref() {
52605327
if *conf_height == height {
52615328
self.alternative_funding_confirmed.take();
5329+
if self.holder_tx_signed {
5330+
// Cancel any previous claims that are no longer valid as they stemmed from a
5331+
// different funding transaction. We'll wait until we see a funding transaction
5332+
// confirm again before attempting to broadcast the new valid holder commitment.
5333+
let new_holder_commitment_txid =
5334+
self.funding.current_holder_commitment_tx.trust().txid();
5335+
self.cancel_prev_commitment_claims(&logger, &new_holder_commitment_txid);
5336+
}
52625337
}
52635338
}
52645339

@@ -5305,6 +5380,14 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
53055380
if let Some((alternative_funding_txid, _)) = self.alternative_funding_confirmed.as_ref() {
53065381
if alternative_funding_txid == txid {
53075382
self.alternative_funding_confirmed.take();
5383+
if self.holder_tx_signed {
5384+
// Cancel any previous claims that are no longer valid as they stemmed from a
5385+
// different funding transaction. We'll wait until we see a funding transaction
5386+
// confirm again before attempting to broadcast the new valid holder commitment.
5387+
let new_holder_commitment_txid =
5388+
self.funding.current_holder_commitment_tx.trust().txid();
5389+
self.cancel_prev_commitment_claims(&logger, &new_holder_commitment_txid);
5390+
}
53085391
}
53095392
}
53105393

lightning/src/chain/onchaintx.rs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -723,7 +723,8 @@ impl<ChannelSigner: EcdsaChannelSigner> OnchainTxHandler<ChannelSigner> {
723723
}
724724

725725
#[rustfmt::skip]
726-
pub fn abandon_claim(&mut self, outpoint: &BitcoinOutPoint) {
726+
pub fn abandon_claim(&mut self, outpoint: &BitcoinOutPoint) -> bool {
727+
let mut found_claim = false;
727728
let claim_id = self.claimable_outpoints.get(outpoint).map(|(claim_id, _)| *claim_id)
728729
.or_else(|| {
729730
self.pending_claim_requests.iter()
@@ -733,13 +734,23 @@ impl<ChannelSigner: EcdsaChannelSigner> OnchainTxHandler<ChannelSigner> {
733734
if let Some(claim_id) = claim_id {
734735
if let Some(claim) = self.pending_claim_requests.remove(&claim_id) {
735736
for outpoint in claim.outpoints() {
736-
self.claimable_outpoints.remove(outpoint);
737+
if self.claimable_outpoints.remove(outpoint).is_some() {
738+
found_claim = true;
739+
}
737740
}
738741
}
739742
} else {
740-
self.locktimed_packages.values_mut().for_each(|claims|
741-
claims.retain(|claim| !claim.outpoints().contains(&outpoint)));
743+
self.locktimed_packages.values_mut().for_each(|claims| {
744+
claims.retain(|claim| {
745+
let includes_outpoint = claim.outpoints().contains(&outpoint);
746+
if includes_outpoint {
747+
found_claim = true;
748+
}
749+
!includes_outpoint
750+
})
751+
});
742752
}
753+
found_claim
743754
}
744755

745756
/// Upon channelmonitor.block_connected(..) or upon provision of a preimage on the forward link

0 commit comments

Comments
 (0)