Skip to content

Commit 1ae9733

Browse files
committed
Add force flag to allow channel force-closure
.. which we somehow so far ommitted exposing in the API. We now introduce a `force` flag to `close_channel` and broadcast if the counterparty is not trusted.
1 parent 45c0669 commit 1ae9733

File tree

6 files changed

+57
-18
lines changed

6 files changed

+57
-18
lines changed

bindings/kotlin/ldk-node-jvm/lib/src/test/kotlin/org/lightningdevkit/ldknode/LibraryTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ class LibraryTest {
239239
assert(node1.listPayments().size == 1)
240240
assert(node2.listPayments().size == 1)
241241

242-
node2.closeChannel(userChannelId, nodeId1)
242+
node2.closeChannel(userChannelId, nodeId1, false)
243243

244244
val channelClosedEvent1 = node1.waitNextEvent()
245245
println("Got event: $channelClosedEvent1")

bindings/ldk_node.udl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ interface Node {
6969
[Throws=NodeError]
7070
UserChannelId connect_open_channel(PublicKey node_id, SocketAddress address, u64 channel_amount_sats, u64? push_to_counterparty_msat, ChannelConfig? channel_config, boolean announce_channel);
7171
[Throws=NodeError]
72-
void close_channel([ByRef]UserChannelId user_channel_id, PublicKey counterparty_node_id);
72+
void close_channel([ByRef]UserChannelId user_channel_id, PublicKey counterparty_node_id, boolean force);
7373
[Throws=NodeError]
7474
void update_channel_config([ByRef]UserChannelId user_channel_id, PublicKey counterparty_node_id, ChannelConfig channel_config);
7575
[Throws=NodeError]

bindings/python/src/ldk_node/test_ldk_node.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ def test_channel_full_cycle(self):
198198
print("EVENT:", payment_received_event_2)
199199
node_2.event_handled()
200200

201-
node_2.close_channel(channel_ready_event_2.user_channel_id, node_id_1)
201+
node_2.close_channel(channel_ready_event_2.user_channel_id, node_id_1, False)
202202

203203
channel_closed_event_1 = node_1.wait_next_event()
204204
assert isinstance(channel_closed_event_1, Event.CHANNEL_CLOSED)

src/lib.rs

Lines changed: 52 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1146,27 +1146,66 @@ impl Node {
11461146
}
11471147

11481148
/// Close a previously opened channel.
1149+
///
1150+
/// If `force` is set to `true`, we will force-close the channel, potentially broadcasting our
1151+
/// latest state. Note that in contrast to cooperative closure, force-closing will have the
1152+
/// channel funds time-locked, i.e., they will only be available after the counterparty had
1153+
/// time to contest our claim. Force-closing channels also more costly in terms of on-chain
1154+
/// fees. So cooperative closure should always be preferred (and tried first).
1155+
///
1156+
/// Broadcasting the closing transactions will be omitted for Anchor channels if we trust the
1157+
/// counterparty to broadcast for us (see [`AnchorChannelsConfig::trusted_peers_no_reserve`]
1158+
/// for more information).
11491159
pub fn close_channel(
1150-
&self, user_channel_id: &UserChannelId, counterparty_node_id: PublicKey,
1160+
&self, user_channel_id: &UserChannelId, counterparty_node_id: PublicKey, force: bool,
11511161
) -> Result<(), Error> {
11521162
let open_channels =
11531163
self.channel_manager.list_channels_with_counterparty(&counterparty_node_id);
11541164
if let Some(channel_details) =
11551165
open_channels.iter().find(|c| c.user_channel_id == user_channel_id.0)
11561166
{
1157-
match self
1158-
.channel_manager
1159-
.close_channel(&channel_details.channel_id, &counterparty_node_id)
1160-
{
1161-
Ok(_) => {
1162-
// Check if this was the last open channel, if so, forget the peer.
1163-
if open_channels.len() == 1 {
1164-
self.peer_store.remove_peer(&counterparty_node_id)?;
1165-
}
1166-
Ok(())
1167-
},
1168-
Err(_) => Err(Error::ChannelClosingFailed),
1167+
if force {
1168+
if self.config.anchor_channels_config.as_ref().map_or(false, |acc| {
1169+
acc.trusted_peers_no_reserve.contains(&counterparty_node_id)
1170+
}) {
1171+
self.channel_manager
1172+
.force_close_without_broadcasting_txn(
1173+
&channel_details.channel_id,
1174+
&counterparty_node_id,
1175+
)
1176+
.map_err(|e| {
1177+
log_error!(
1178+
self.logger,
1179+
"Failed to force-close channel to trusted peer: {:?}",
1180+
e
1181+
);
1182+
Error::ChannelClosingFailed
1183+
})?;
1184+
} else {
1185+
self.channel_manager
1186+
.force_close_broadcasting_latest_txn(
1187+
&channel_details.channel_id,
1188+
&counterparty_node_id,
1189+
)
1190+
.map_err(|e| {
1191+
log_error!(self.logger, "Failed to force-close channel: {:?}", e);
1192+
Error::ChannelClosingFailed
1193+
})?;
1194+
}
1195+
} else {
1196+
self.channel_manager
1197+
.close_channel(&channel_details.channel_id, &counterparty_node_id)
1198+
.map_err(|e| {
1199+
log_error!(self.logger, "Failed to close channel: {:?}", e);
1200+
Error::ChannelClosingFailed
1201+
})?;
11691202
}
1203+
1204+
// Check if this was the last open channel, if so, forget the peer.
1205+
if open_channels.len() == 1 {
1206+
self.peer_store.remove_peer(&counterparty_node_id)?;
1207+
}
1208+
Ok(())
11701209
} else {
11711210
Ok(())
11721211
}

tests/common/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -565,7 +565,7 @@ pub(crate) fn do_channel_full_cycle<E: ElectrumApi>(
565565
assert_eq!(node_b.payment(&keysend_payment_id).unwrap().amount_msat, Some(keysend_amount_msat));
566566

567567
println!("\nB close_channel");
568-
node_b.close_channel(&user_channel_id, node_a.node_id()).unwrap();
568+
node_b.close_channel(&user_channel_id, node_a.node_id(), false).unwrap();
569569
expect_event!(node_a, ChannelClosed);
570570
expect_event!(node_b, ChannelClosed);
571571

tests/integration_tests_cln.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ fn test_cln() {
111111
cln_client.pay(&ldk_invoice.to_string(), Default::default()).unwrap();
112112
common::expect_event!(node, PaymentReceived);
113113

114-
node.close_channel(&user_channel_id, cln_node_id).unwrap();
114+
node.close_channel(&user_channel_id, cln_node_id, false).unwrap();
115115
common::expect_event!(node, ChannelClosed);
116116
node.stop().unwrap();
117117
}

0 commit comments

Comments
 (0)