Skip to content

Commit 436941c

Browse files
committed
More regularly send an Error message when we force-close a channel
When we force-close a channel, for whatever reason, it is nice to send an error message to our peer. This allows them to closes the channel on their end instead of trying to send through it and failing. Further, it may induce them to broadcast their commitment transaction, possibly getting that confirmed and saving us on fees. This commit adds a few more cases where we should have been sending error messages but weren't. It also includes an almost-global replace in tests of the second argument in `check_closed_broadcast!()` from false to true (indicating an error message is expected). There are only a few exceptions, notably those where the closure is the result of our counterparty having sent *us* an error message.
1 parent d9d0b50 commit 436941c

File tree

6 files changed

+139
-66
lines changed

6 files changed

+139
-66
lines changed

lightning-persister/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -241,15 +241,15 @@ mod tests {
241241
// Force close because cooperative close doesn't result in any persisted
242242
// updates.
243243
nodes[0].node.force_close_channel(&nodes[0].node.list_channels()[0].channel_id).unwrap();
244-
check_closed_broadcast!(nodes[0], false);
244+
check_closed_broadcast!(nodes[0], true);
245245
check_added_monitors!(nodes[0], 1);
246246

247247
let node_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap();
248248
assert_eq!(node_txn.len(), 1);
249249

250250
let header = BlockHeader { version: 0x20000000, prev_blockhash: nodes[0].best_block_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
251251
connect_block(&nodes[1], &Block { header, txdata: vec![node_txn[0].clone(), node_txn[0].clone()]});
252-
check_closed_broadcast!(nodes[1], false);
252+
check_closed_broadcast!(nodes[1], true);
253253
check_added_monitors!(nodes[1], 1);
254254

255255
// Make sure everything is persisted as expected after close.

lightning/src/ln/chanmon_update_fail_tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ fn do_test_simple_monitor_temporary_update_fail(disconnect: bool, persister_fail
241241
// ...and make sure we can force-close a frozen channel
242242
nodes[0].node.force_close_channel(&channel_id).unwrap();
243243
check_added_monitors!(nodes[0], 1);
244-
check_closed_broadcast!(nodes[0], false);
244+
check_closed_broadcast!(nodes[0], true);
245245

246246
// TODO: Once we hit the chain with the failure transaction we should check that we get a
247247
// PaymentFailed event

lightning/src/ln/channelmanager.rs

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,16 +1002,14 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
10021002
}
10031003
}
10041004

1005-
fn force_close_channel_with_peer(&self, channel_id: &[u8; 32], peer_node_id: Option<&PublicKey>) -> Result<(), APIError> {
1005+
fn force_close_channel_with_peer(&self, channel_id: &[u8; 32], peer_node_id: Option<&PublicKey>) -> Result<PublicKey, APIError> {
10061006
let mut chan = {
10071007
let mut channel_state_lock = self.channel_state.lock().unwrap();
10081008
let channel_state = &mut *channel_state_lock;
10091009
if let hash_map::Entry::Occupied(chan) = channel_state.by_id.entry(channel_id.clone()) {
10101010
if let Some(node_id) = peer_node_id {
10111011
if chan.get().get_counterparty_node_id() != *node_id {
1012-
// Error or Ok here doesn't matter - the result is only exposed publicly
1013-
// when peer_node_id is None anyway.
1014-
return Ok(());
1012+
return Err(APIError::ChannelUnavailable{err: "No such channel".to_owned()});
10151013
}
10161014
}
10171015
if let Some(short_id) = chan.get().get_short_channel_id() {
@@ -1031,14 +1029,27 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
10311029
});
10321030
}
10331031

1034-
Ok(())
1032+
Ok(chan.get_counterparty_node_id())
10351033
}
10361034

10371035
/// Force closes a channel, immediately broadcasting the latest local commitment transaction to
10381036
/// the chain and rejecting new HTLCs on the given channel. Fails if channel_id is unknown to the manager.
10391037
pub fn force_close_channel(&self, channel_id: &[u8; 32]) -> Result<(), APIError> {
10401038
let _persistence_guard = PersistenceNotifierGuard::new(&self.total_consistency_lock, &self.persistence_notifier);
1041-
self.force_close_channel_with_peer(channel_id, None)
1039+
match self.force_close_channel_with_peer(channel_id, None) {
1040+
Ok(pk) => {
1041+
self.channel_state.lock().unwrap().pending_msg_events.push(
1042+
events::MessageSendEvent::HandleError {
1043+
node_id: pk,
1044+
action: msgs::ErrorAction::SendErrorMessage {
1045+
msg: msgs::ErrorMessage { channel_id: *channel_id, data: "Channel force-closed".to_owned() }
1046+
},
1047+
}
1048+
);
1049+
Ok(())
1050+
},
1051+
Err(e) => Err(e)
1052+
}
10421053
}
10431054

10441055
/// Force close all channels, immediately broadcasting the latest local commitment transaction
@@ -3194,6 +3205,12 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
31943205
msg: update
31953206
});
31963207
}
3208+
pending_msg_events.push(events::MessageSendEvent::HandleError {
3209+
node_id: chan.get_counterparty_node_id(),
3210+
action: msgs::ErrorAction::SendErrorMessage {
3211+
msg: msgs::ErrorMessage { channel_id: chan.channel_id(), data: "Channel force-closed".to_owned() }
3212+
},
3213+
});
31973214
}
31983215
},
31993216
}
@@ -3364,6 +3381,12 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
33643381
msg: update
33653382
});
33663383
}
3384+
pending_msg_events.push(events::MessageSendEvent::HandleError {
3385+
node_id: channel.get_counterparty_node_id(),
3386+
action: msgs::ErrorAction::SendErrorMessage {
3387+
msg: msgs::ErrorMessage { channel_id: channel.channel_id(), data: "Commitment transaction was confirmed on chain.".to_owned() }
3388+
},
3389+
});
33673390
return false;
33683391
}
33693392
}

lightning/src/ln/functional_test_utils.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1342,22 +1342,36 @@ pub fn check_preimage_claim<'a, 'b, 'c>(node: &Node<'a, 'b, 'c>, prev_txn: &Vec<
13421342

13431343
pub fn get_announce_close_broadcast_events<'a, 'b, 'c>(nodes: &Vec<Node<'a, 'b, 'c>>, a: usize, b: usize) {
13441344
let events_1 = nodes[a].node.get_and_clear_pending_msg_events();
1345-
assert_eq!(events_1.len(), 1);
1345+
assert_eq!(events_1.len(), 2);
13461346
let as_update = match events_1[0] {
13471347
MessageSendEvent::BroadcastChannelUpdate { ref msg } => {
13481348
msg.clone()
13491349
},
13501350
_ => panic!("Unexpected event"),
13511351
};
1352+
match events_1[1] {
1353+
MessageSendEvent::HandleError { node_id, action: msgs::ErrorAction::SendErrorMessage { ref msg } } => {
1354+
assert_eq!(node_id, nodes[b].node.get_our_node_id());
1355+
assert_eq!(msg.data, "Commitment transaction was confirmed on chain.");
1356+
},
1357+
_ => panic!("Unexpected event"),
1358+
}
13521359

13531360
let events_2 = nodes[b].node.get_and_clear_pending_msg_events();
1354-
assert_eq!(events_2.len(), 1);
1361+
assert_eq!(events_2.len(), 2);
13551362
let bs_update = match events_2[0] {
13561363
MessageSendEvent::BroadcastChannelUpdate { ref msg } => {
13571364
msg.clone()
13581365
},
13591366
_ => panic!("Unexpected event"),
13601367
};
1368+
match events_2[1] {
1369+
MessageSendEvent::HandleError { node_id, action: msgs::ErrorAction::SendErrorMessage { ref msg } } => {
1370+
assert_eq!(node_id, nodes[a].node.get_our_node_id());
1371+
assert_eq!(msg.data, "Commitment transaction was confirmed on chain.");
1372+
},
1373+
_ => panic!("Unexpected event"),
1374+
}
13611375

13621376
for node in nodes {
13631377
node.net_graph_msg_handler.handle_channel_update(&as_update).unwrap();

0 commit comments

Comments
 (0)