Skip to content

Commit f4a381a

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 8af6c28 commit f4a381a

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
@@ -70,7 +70,7 @@ interface Node {
7070
[Throws=NodeError]
7171
UserChannelId connect_open_channel(PublicKey node_id, SocketAddress address, u64 channel_amount_sats, u64? push_to_counterparty_msat, ChannelConfig? channel_config, boolean announce_channel);
7272
[Throws=NodeError]
73-
void close_channel([ByRef]UserChannelId user_channel_id, PublicKey counterparty_node_id);
73+
void close_channel([ByRef]UserChannelId user_channel_id, PublicKey counterparty_node_id, boolean force);
7474
[Throws=NodeError]
7575
void update_channel_config([ByRef]UserChannelId user_channel_id, PublicKey counterparty_node_id, ChannelConfig channel_config);
7676
[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
@@ -1176,27 +1176,66 @@ impl Node {
11761176
}
11771177

11781178
/// Close a previously opened channel.
1179+
///
1180+
/// If `force` is set to `true`, we will force-close the channel, potentially broadcasting our
1181+
/// latest state. Note that in contrast to cooperative closure, force-closing will have the
1182+
/// channel funds time-locked, i.e., they will only be available after the counterparty had
1183+
/// time to contest our claim. Force-closing channels also more costly in terms of on-chain
1184+
/// fees. So cooperative closure should always be preferred (and tried first).
1185+
///
1186+
/// Broadcasting the closing transactions will be omitted for Anchor channels if we trust the
1187+
/// counterparty to broadcast for us (see [`AnchorChannelsConfig::trusted_peers_no_reserve`]
1188+
/// for more information).
11791189
pub fn close_channel(
1180-
&self, user_channel_id: &UserChannelId, counterparty_node_id: PublicKey,
1190+
&self, user_channel_id: &UserChannelId, counterparty_node_id: PublicKey, force: bool,
11811191
) -> Result<(), Error> {
11821192
let open_channels =
11831193
self.channel_manager.list_channels_with_counterparty(&counterparty_node_id);
11841194
if let Some(channel_details) =
11851195
open_channels.iter().find(|c| c.user_channel_id == user_channel_id.0)
11861196
{
1187-
match self
1188-
.channel_manager
1189-
.close_channel(&channel_details.channel_id, &counterparty_node_id)
1190-
{
1191-
Ok(_) => {
1192-
// Check if this was the last open channel, if so, forget the peer.
1193-
if open_channels.len() == 1 {
1194-
self.peer_store.remove_peer(&counterparty_node_id)?;
1195-
}
1196-
Ok(())
1197-
},
1198-
Err(_) => Err(Error::ChannelClosingFailed),
1197+
if force {
1198+
if self.config.anchor_channels_config.as_ref().map_or(false, |acc| {
1199+
acc.trusted_peers_no_reserve.contains(&counterparty_node_id)
1200+
}) {
1201+
self.channel_manager
1202+
.force_close_without_broadcasting_txn(
1203+
&channel_details.channel_id,
1204+
&counterparty_node_id,
1205+
)
1206+
.map_err(|e| {
1207+
log_error!(
1208+
self.logger,
1209+
"Failed to force-close channel to trusted peer: {:?}",
1210+
e
1211+
);
1212+
Error::ChannelClosingFailed
1213+
})?;
1214+
} else {
1215+
self.channel_manager
1216+
.force_close_broadcasting_latest_txn(
1217+
&channel_details.channel_id,
1218+
&counterparty_node_id,
1219+
)
1220+
.map_err(|e| {
1221+
log_error!(self.logger, "Failed to force-close channel: {:?}", e);
1222+
Error::ChannelClosingFailed
1223+
})?;
1224+
}
1225+
} else {
1226+
self.channel_manager
1227+
.close_channel(&channel_details.channel_id, &counterparty_node_id)
1228+
.map_err(|e| {
1229+
log_error!(self.logger, "Failed to close channel: {:?}", e);
1230+
Error::ChannelClosingFailed
1231+
})?;
11991232
}
1233+
1234+
// Check if this was the last open channel, if so, forget the peer.
1235+
if open_channels.len() == 1 {
1236+
self.peer_store.remove_peer(&counterparty_node_id)?;
1237+
}
1238+
Ok(())
12001239
} else {
12011240
Ok(())
12021241
}

tests/common/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -583,7 +583,7 @@ pub(crate) fn do_channel_full_cycle<E: ElectrumApi>(
583583
assert_eq!(node_b.list_payments().len(), 5);
584584

585585
println!("\nB close_channel");
586-
node_b.close_channel(&user_channel_id, node_a.node_id()).unwrap();
586+
node_b.close_channel(&user_channel_id, node_a.node_id(), false).unwrap();
587587
expect_event!(node_a, ChannelClosed);
588588
expect_event!(node_b, ChannelClosed);
589589

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)