Skip to content

Commit da958b0

Browse files
committed
Detect commitment transaction confirmation in ChannelMonitor instead
Previously, the `ChannelManager` would assume a `Channel` was closed the moment it saw a spend for its funding input. With splicing, this will no longer be the case. Since the `ChannelMonitor` is already responsible for reliably tracking each onchain transaction relevant to a channel, we now produce a `MonitorEvent::CommitmentTxConfirmed` event to inform the `ChannelManager` the channel can be considered closed and removed. Note that this now makes it possible for a `Channel` to have an update while the event has yet to be processed by the `ChannelManager`, but this is still safe as the monitor should reject it, since it's seen a spending transaction confirm, causing the channel to fail. As a result of this change, many tests failed now that we rely on handling the `MonitorEvent::CommitmentTxConfirmed` first before seeing the `ChannelMonitorUpdateStep::ChannelForceClosed` go out.
1 parent 6a4169d commit da958b0

11 files changed

+175
-172
lines changed

lightning/src/chain/chainmonitor.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1490,8 +1490,8 @@ mod tests {
14901490
assert_eq!(close_tx.len(), 1);
14911491

14921492
mine_transaction(&nodes[2], &close_tx[0]);
1493-
check_added_monitors(&nodes[2], 1);
14941493
check_closed_broadcast(&nodes[2], 1, true);
1494+
check_added_monitors(&nodes[2], 1);
14951495
let closure_reason = ClosureReason::CommitmentTxConfirmed;
14961496
check_closed_event!(&nodes[2], 1, closure_reason, false, [node_a_id], 1000000);
14971497

lightning/src/chain/channelmonitor.rs

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,10 @@ pub enum MonitorEvent {
205205
/// channel.
206206
HolderForceClosed(OutPoint),
207207

208+
/// Indicates that we've detected a commitment transaction (either holder's or counterparty's)
209+
/// be included in a block and should consider the channel closed.
210+
CommitmentTxConfirmed(()),
211+
208212
/// Indicates a [`ChannelMonitor`] update has completed. See
209213
/// [`ChannelMonitorUpdateStatus::InProgress`] for more information on how this is used.
210214
///
@@ -236,6 +240,7 @@ impl_writeable_tlv_based_enum_upgradable_legacy!(MonitorEvent,
236240
(4, channel_id, required),
237241
},
238242
;
243+
(1, CommitmentTxConfirmed),
239244
(2, HTLCEvent),
240245
(4, HolderForceClosed),
241246
// 6 was `UpdateFailed` until LDK 0.0.117
@@ -5088,6 +5093,9 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
50885093
);
50895094
log_info!(logger, "Channel {} closed by funding output spend in txid {txid}",
50905095
self.channel_id());
5096+
if !self.funding_spend_seen {
5097+
self.pending_monitor_events.push(MonitorEvent::CommitmentTxConfirmed(()));
5098+
}
50915099
self.funding_spend_seen = true;
50925100

50935101
let mut balance_spendable_csv = None;
@@ -6369,6 +6377,7 @@ mod tests {
63696377
use bitcoin::{Sequence, Witness};
63706378

63716379
use crate::chain::chaininterface::LowerBoundedFeeEstimator;
6380+
use crate::events::ClosureReason;
63726381

63736382
use super::ChannelMonitorUpdateStep;
63746383
use crate::chain::channelmonitor::{ChannelMonitor, WithChannelMonitor};
@@ -6466,22 +6475,26 @@ mod tests {
64666475
// Build a new ChannelMonitorUpdate which contains both the failing commitment tx update
64676476
// and provides the claim preimages for the two pending HTLCs. The first update generates
64686477
// an error, but the point of this test is to ensure the later updates are still applied.
6469-
let monitor_updates = nodes[1].chain_monitor.monitor_updates.lock().unwrap();
6470-
let mut replay_update = monitor_updates.get(&channel.2).unwrap().iter().next_back().unwrap().clone();
6471-
assert_eq!(replay_update.updates.len(), 1);
6472-
if let ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTXInfo { .. } = replay_update.updates[0] {
6473-
} else { panic!(); }
6474-
replay_update.updates.push(ChannelMonitorUpdateStep::PaymentPreimage {
6475-
payment_preimage: payment_preimage_1, payment_info: None,
6476-
});
6477-
replay_update.updates.push(ChannelMonitorUpdateStep::PaymentPreimage {
6478-
payment_preimage: payment_preimage_2, payment_info: None,
6479-
});
6478+
let replay_update = {
6479+
let monitor_updates = nodes[1].chain_monitor.monitor_updates.lock().unwrap();
6480+
let mut replay_update = monitor_updates.get(&channel.2).unwrap().iter().next_back().unwrap().clone();
6481+
assert_eq!(replay_update.updates.len(), 1);
6482+
if let ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTXInfo { .. } = replay_update.updates[0] {
6483+
} else { panic!(); }
6484+
replay_update.updates.push(ChannelMonitorUpdateStep::PaymentPreimage {
6485+
payment_preimage: payment_preimage_1, payment_info: None,
6486+
});
6487+
replay_update.updates.push(ChannelMonitorUpdateStep::PaymentPreimage {
6488+
payment_preimage: payment_preimage_2, payment_info: None,
6489+
});
6490+
replay_update
6491+
};
64806492

64816493
let broadcaster = TestBroadcaster::with_blocks(Arc::clone(&nodes[1].blocks));
64826494
assert!(
64836495
pre_update_monitor.update_monitor(&replay_update, &&broadcaster, &&chanmon_cfgs[1].fee_estimator, &nodes[1].logger)
64846496
.is_err());
6497+
64856498
// Even though we error'd on the first update, we should still have generated an HTLC claim
64866499
// transaction
64876500
let txn_broadcasted = broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
@@ -6493,7 +6506,12 @@ mod tests {
64936506
assert_eq!(htlc_txn.len(), 2);
64946507
check_spends!(htlc_txn[0], broadcast_tx);
64956508
check_spends!(htlc_txn[1], broadcast_tx);
6509+
6510+
check_closed_broadcast(&nodes[1], 1, true);
6511+
check_closed_event(&nodes[1], 1, ClosureReason::CommitmentTxConfirmed, false, &[nodes[0].node.get_our_node_id()], 100000);
6512+
check_added_monitors(&nodes[1], 1);
64966513
}
6514+
64976515
#[test]
64986516
fn test_funding_spend_refuses_updates() {
64996517
do_test_funding_spend_refuses_updates(true);

lightning/src/ln/async_signer_tests.rs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1086,16 +1086,14 @@ fn do_test_async_holder_signatures(anchors: bool, remote_commitment: bool) {
10861086
nodes[0].disable_channel_signer_op(&node_b_id, &chan_id, sign_htlc_op);
10871087
mine_transaction(&nodes[0], &commitment_tx);
10881088

1089-
check_added_monitors(&nodes[0], 1);
10901089
check_closed_broadcast(&nodes[0], 1, true);
1091-
check_closed_event(
1092-
&nodes[0],
1093-
1,
1094-
ClosureReason::CommitmentTxConfirmed,
1095-
false,
1096-
&[node_b_id],
1097-
100_000,
1098-
);
1090+
check_added_monitors(&nodes[0], 1);
1091+
let closure_reason = if remote_commitment {
1092+
ClosureReason::CommitmentTxConfirmed
1093+
} else {
1094+
ClosureReason::HTLCsTimedOut
1095+
};
1096+
check_closed_event(&nodes[0], 1, closure_reason, false, &[node_b_id], 100_000);
10991097

11001098
// If the counterparty broadcast its latest commitment, we need to mine enough blocks for the
11011099
// HTLC timeout.

lightning/src/ln/chanmon_update_fail_tests.rs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3895,6 +3895,11 @@ fn do_test_durable_preimages_on_closed_channel(
38953895
persister.set_update_ret(ChannelMonitorUpdateStatus::InProgress);
38963896
}
38973897
}
3898+
if !close_chans_before_reload {
3899+
check_closed_broadcast(&nodes[1], 1, true);
3900+
let reason = ClosureReason::CommitmentTxConfirmed;
3901+
check_closed_event(&nodes[1], 1, reason, false, &[node_a_id], 100000);
3902+
}
38983903
nodes[1].node.timer_tick_occurred();
38993904
check_added_monitors(&nodes[1], mons_added);
39003905

@@ -3907,12 +3912,6 @@ fn do_test_durable_preimages_on_closed_channel(
39073912
.unwrap();
39083913
check_spends!(bs_preimage_tx, as_closing_tx[0]);
39093914

3910-
if !close_chans_before_reload {
3911-
check_closed_broadcast(&nodes[1], 1, true);
3912-
let reason = ClosureReason::CommitmentTxConfirmed;
3913-
check_closed_event(&nodes[1], 1, reason, false, &[node_a_id], 100000);
3914-
}
3915-
39163915
mine_transactions(&nodes[0], &[&as_closing_tx[0], bs_preimage_tx]);
39173916
check_closed_broadcast(&nodes[0], 1, true);
39183917
expect_payment_sent(&nodes[0], payment_preimage, None, true, true);
@@ -4048,7 +4047,7 @@ fn do_test_reload_mon_update_completion_actions(close_during_reload: bool) {
40484047
let mut events = nodes[1].node.get_and_clear_pending_events();
40494048
assert_eq!(events.len(), if close_during_reload { 2 } else { 1 });
40504049
expect_payment_forwarded(
4051-
events.pop().unwrap(),
4050+
events.remove(0),
40524051
&nodes[1],
40534052
&nodes[0],
40544053
&nodes[2],
@@ -4491,9 +4490,9 @@ fn test_claim_to_closed_channel_blocks_forwarded_preimage_removal() {
44914490
assert_eq!(as_commit_tx.len(), 1);
44924491

44934492
mine_transaction(&nodes[1], &as_commit_tx[0]);
4493+
check_closed_broadcast!(nodes[1], true);
44944494
check_added_monitors!(nodes[1], 1);
44954495
check_closed_event!(nodes[1], 1, ClosureReason::CommitmentTxConfirmed, [node_a_id], 1000000);
4496-
check_closed_broadcast!(nodes[1], true);
44974496

44984497
// Now that B has a pending forwarded payment across it with the inbound edge on-chain, claim
44994498
// the payment on C and give B the preimage for it.
@@ -4567,9 +4566,9 @@ fn test_claim_to_closed_channel_blocks_claimed_event() {
45674566
assert_eq!(as_commit_tx.len(), 1);
45684567

45694568
mine_transaction(&nodes[1], &as_commit_tx[0]);
4569+
check_closed_broadcast!(nodes[1], true);
45704570
check_added_monitors!(nodes[1], 1);
45714571
check_closed_event!(nodes[1], 1, ClosureReason::CommitmentTxConfirmed, [node_a_id], 1000000);
4572-
check_closed_broadcast!(nodes[1], true);
45734572

45744573
// Now that B has a pending payment with the inbound HTLC on a closed channel, claim the
45754574
// payment on disk, but don't let the `ChannelMonitorUpdate` complete. This should prevent the

lightning/src/ln/channel.rs

Lines changed: 0 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -5775,35 +5775,6 @@ where
57755775
Ok(false)
57765776
}
57775777

5778-
#[rustfmt::skip]
5779-
fn check_for_funding_tx_spent<L: Deref>(
5780-
&mut self, funding: &FundingScope, tx: &Transaction, logger: &L,
5781-
) -> Result<(), ClosureReason>
5782-
where
5783-
L::Target: Logger,
5784-
{
5785-
let funding_txo = match funding.get_funding_txo() {
5786-
Some(funding_txo) => funding_txo,
5787-
None => {
5788-
debug_assert!(false);
5789-
return Ok(());
5790-
},
5791-
};
5792-
5793-
for input in tx.input.iter() {
5794-
if input.previous_output == funding_txo.into_bitcoin_outpoint() {
5795-
log_info!(
5796-
logger, "Detected channel-closing tx {} spending {}:{}, closing channel {}",
5797-
tx.compute_txid(), input.previous_output.txid, input.previous_output.vout,
5798-
&self.channel_id(),
5799-
);
5800-
return Err(ClosureReason::CommitmentTxConfirmed);
5801-
}
5802-
}
5803-
5804-
Ok(())
5805-
}
5806-
58075778
/// Returns SCIDs that have been associated with the channel's funding transactions.
58085779
pub fn historical_scids(&self) -> &[u64] {
58095780
&self.historical_scids[..]
@@ -10005,12 +9976,6 @@ where
100059976
}
100069977

100079978
if let Some(channel_ready) = self.check_get_channel_ready(height, logger) {
10008-
for &(idx, tx) in txdata.iter() {
10009-
if idx > index_in_block {
10010-
self.context.check_for_funding_tx_spent(&self.funding, tx, logger)?;
10011-
}
10012-
}
10013-
100149979
log_info!(logger, "Sending a channel_ready to our peer for channel {}", &self.context.channel_id);
100159980
let announcement_sigs = self.get_announcement_sigs(node_signer, chain_hash, user_config, height, logger);
100169981
return Ok((Some(FundingConfirmedMessage::Establishment(channel_ready)), announcement_sigs));
@@ -10051,12 +10016,6 @@ where
1005110016
let funding = self.pending_funding.get(confirmed_funding_index).unwrap();
1005210017

1005310018
if let Some(splice_locked) = pending_splice.check_get_splice_locked(&self.context, funding, height) {
10054-
for &(idx, tx) in txdata.iter() {
10055-
if idx > index_in_block {
10056-
self.context.check_for_funding_tx_spent(funding, tx, logger)?;
10057-
}
10058-
}
10059-
1006010019
log_info!(
1006110020
logger,
1006210021
"Sending splice_locked txid {} to our peer for channel {}",
@@ -10076,13 +10035,6 @@ where
1007610035
return Ok((Some(FundingConfirmedMessage::Splice(splice_locked, funding_txo, monitor_update)), announcement_sigs));
1007710036
}
1007810037
}
10079-
10080-
self.context.check_for_funding_tx_spent(&self.funding, tx, logger)?;
10081-
#[cfg(splicing)]
10082-
for funding in self.pending_funding.iter() {
10083-
self.context.check_for_funding_tx_spent(funding, tx, logger)?;
10084-
}
10085-
1008610038
}
1008710039

1008810040
Ok((None, None))

lightning/src/ln/channelmanager.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11329,6 +11329,22 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1132911329
}
1133011330
}
1133111331
},
11332+
MonitorEvent::CommitmentTxConfirmed(_) => {
11333+
let per_peer_state = self.per_peer_state.read().unwrap();
11334+
if let Some(peer_state_mutex) = per_peer_state.get(&counterparty_node_id) {
11335+
let mut peer_state_lock = peer_state_mutex.lock().unwrap();
11336+
let peer_state = &mut *peer_state_lock;
11337+
if let hash_map::Entry::Occupied(chan_entry) =
11338+
peer_state.channel_by_id.entry(channel_id)
11339+
{
11340+
let reason = ClosureReason::CommitmentTxConfirmed;
11341+
let err = ChannelError::Close((reason.to_string(), reason));
11342+
let mut chan = chan_entry.remove();
11343+
let (_, e) = convert_channel_err!(self, peer_state, err, &mut chan);
11344+
failed_channels.push((Err(e), counterparty_node_id));
11345+
}
11346+
}
11347+
},
1133211348
MonitorEvent::Completed { channel_id, monitor_update_id, .. } => {
1133311349
self.channel_monitor_updated(
1133411350
&channel_id,

0 commit comments

Comments
 (0)