Skip to content

Commit 8d9d099

Browse files
authored
Merge pull request #2725 from shaavan/issue2096
Ensure successful message propagation in case of disconnection mid-handshake
2 parents f70c113 + a6a6b48 commit 8d9d099

File tree

2 files changed

+160
-35
lines changed

2 files changed

+160
-35
lines changed

lightning/src/ln/channelmanager.rs

+33-13
Original file line numberDiff line numberDiff line change
@@ -903,7 +903,9 @@ impl <SP: Deref> PeerState<SP> where SP::Target: SignerProvider {
903903
if require_disconnected && self.is_connected {
904904
return false
905905
}
906-
self.channel_by_id.iter().filter(|(_, phase)| matches!(phase, ChannelPhase::Funded(_))).count() == 0
906+
!self.channel_by_id.iter().any(|(_, phase)|
907+
matches!(phase, ChannelPhase::Funded(_) | ChannelPhase::UnfundedOutboundV1(_))
908+
)
907909
&& self.monitor_update_blocked_actions.is_empty()
908910
&& self.in_flight_monitor_updates.is_empty()
909911
}
@@ -8911,10 +8913,12 @@ where
89118913
}
89128914
&mut chan.context
89138915
},
8914-
// Unfunded channels will always be removed.
8915-
ChannelPhase::UnfundedOutboundV1(chan) => {
8916-
&mut chan.context
8916+
// We retain UnfundedOutboundV1 channel for some time in case
8917+
// peer unexpectedly disconnects, and intends to reconnect again.
8918+
ChannelPhase::UnfundedOutboundV1(_) => {
8919+
return true;
89178920
},
8921+
// Unfunded inbound channels will always be removed.
89188922
ChannelPhase::UnfundedInboundV1(chan) => {
89198923
&mut chan.context
89208924
},
@@ -9053,15 +9057,31 @@ where
90539057
let peer_state = &mut *peer_state_lock;
90549058
let pending_msg_events = &mut peer_state.pending_msg_events;
90559059

9056-
peer_state.channel_by_id.iter_mut().filter_map(|(_, phase)|
9057-
if let ChannelPhase::Funded(chan) = phase { Some(chan) } else { None }
9058-
).for_each(|chan| {
9059-
let logger = WithChannelContext::from(&self.logger, &chan.context);
9060-
pending_msg_events.push(events::MessageSendEvent::SendChannelReestablish {
9061-
node_id: chan.context.get_counterparty_node_id(),
9062-
msg: chan.get_channel_reestablish(&&logger),
9063-
});
9064-
});
9060+
for (_, phase) in peer_state.channel_by_id.iter_mut() {
9061+
match phase {
9062+
ChannelPhase::Funded(chan) => {
9063+
let logger = WithChannelContext::from(&self.logger, &chan.context);
9064+
pending_msg_events.push(events::MessageSendEvent::SendChannelReestablish {
9065+
node_id: chan.context.get_counterparty_node_id(),
9066+
msg: chan.get_channel_reestablish(&&logger),
9067+
});
9068+
}
9069+
9070+
ChannelPhase::UnfundedOutboundV1(chan) => {
9071+
pending_msg_events.push(events::MessageSendEvent::SendOpenChannel {
9072+
node_id: chan.context.get_counterparty_node_id(),
9073+
msg: chan.get_open_channel(self.chain_hash),
9074+
});
9075+
}
9076+
9077+
ChannelPhase::UnfundedInboundV1(_) => {
9078+
// Since unfunded inbound channel maps are cleared upon disconnecting a peer,
9079+
// they are not persisted and won't be recovered after a crash.
9080+
// Therefore, they shouldn't exist at this point.
9081+
debug_assert!(false);
9082+
}
9083+
}
9084+
}
90659085
}
90669086

90679087
return NotifyOption::SkipPersistHandleEvents;

lightning/src/ln/functional_tests.rs

+127-22
Original file line numberDiff line numberDiff line change
@@ -3699,7 +3699,7 @@ fn test_dup_events_on_peer_disconnect() {
36993699
#[test]
37003700
fn test_peer_disconnected_before_funding_broadcasted() {
37013701
// Test that channels are closed with `ClosureReason::DisconnectedPeer` if the peer disconnects
3702-
// before the funding transaction has been broadcasted.
3702+
// before the funding transaction has been broadcasted, and doesn't reconnect back within time.
37033703
let chanmon_cfgs = create_chanmon_cfgs(2);
37043704
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
37053705
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
@@ -3728,12 +3728,19 @@ fn test_peer_disconnected_before_funding_broadcasted() {
37283728
assert_eq!(nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().len(), 0);
37293729
}
37303730

3731-
// Ensure that the channel is closed with `ClosureReason::DisconnectedPeer` when the peers are
3732-
// disconnected before the funding transaction was broadcasted.
3731+
// The peers disconnect before the funding is broadcasted.
37333732
nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id());
37343733
nodes[1].node.peer_disconnected(&nodes[0].node.get_our_node_id());
37353734

3736-
check_closed_event!(&nodes[0], 2, ClosureReason::DisconnectedPeer, true
3735+
// The time for peers to reconnect expires.
3736+
for _ in 0..UNFUNDED_CHANNEL_AGE_LIMIT_TICKS {
3737+
nodes[0].node.timer_tick_occurred();
3738+
}
3739+
3740+
// Ensure that the channel is closed with `ClosureReason::HolderForceClosed`
3741+
// when the peers are disconnected and do not reconnect before the funding
3742+
// transaction is broadcasted.
3743+
check_closed_event!(&nodes[0], 2, ClosureReason::HolderForceClosed, true
37373744
, [nodes[1].node.get_our_node_id()], 1000000);
37383745
check_closed_event!(&nodes[1], 1, ClosureReason::DisconnectedPeer, false
37393746
, [nodes[0].node.get_our_node_id()], 1000000);
@@ -10512,6 +10519,90 @@ fn test_remove_expired_inbound_unfunded_channels() {
1051210519
check_closed_event(&nodes[1], 1, ClosureReason::HolderForceClosed, false, &[nodes[0].node.get_our_node_id()], 100000);
1051310520
}
1051410521

10522+
#[test]
10523+
fn test_channel_close_when_not_timely_accepted() {
10524+
// Create network of two nodes
10525+
let chanmon_cfgs = create_chanmon_cfgs(2);
10526+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
10527+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
10528+
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
10529+
10530+
// Simulate peer-disconnects mid-handshake
10531+
// The channel is initiated from the node 0 side,
10532+
// but the nodes disconnect before node 1 could send accept channel
10533+
let create_chan_id = nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), 100000, 10001, 42, None, None).unwrap();
10534+
let open_channel_msg = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel, nodes[1].node.get_our_node_id());
10535+
assert_eq!(open_channel_msg.temporary_channel_id, create_chan_id);
10536+
10537+
nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id());
10538+
nodes[1].node.peer_disconnected(&nodes[0].node.get_our_node_id());
10539+
10540+
// Make sure that we have not removed the OutboundV1Channel from node[0] immediately.
10541+
assert_eq!(nodes[0].node.list_channels().len(), 1);
10542+
10543+
// Since channel was inbound from node[1] perspective, it should have been dropped immediately.
10544+
assert_eq!(nodes[1].node.list_channels().len(), 0);
10545+
10546+
// In the meantime, some time passes.
10547+
for _ in 0..UNFUNDED_CHANNEL_AGE_LIMIT_TICKS {
10548+
nodes[0].node.timer_tick_occurred();
10549+
}
10550+
10551+
// Since we disconnected from peer and did not connect back within time,
10552+
// we should have forced-closed the channel by now.
10553+
check_closed_event!(nodes[0], 1, ClosureReason::HolderForceClosed, [nodes[1].node.get_our_node_id()], 100000);
10554+
assert_eq!(nodes[0].node.list_channels().len(), 0);
10555+
10556+
{
10557+
// Since accept channel message was never received
10558+
// The channel should be forced close by now from node 0 side
10559+
// and the peer removed from per_peer_state
10560+
let node_0_per_peer_state = nodes[0].node.per_peer_state.read().unwrap();
10561+
assert_eq!(node_0_per_peer_state.len(), 0);
10562+
}
10563+
}
10564+
10565+
#[test]
10566+
fn test_rebroadcast_open_channel_when_reconnect_mid_handshake() {
10567+
// Create network of two nodes
10568+
let chanmon_cfgs = create_chanmon_cfgs(2);
10569+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
10570+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
10571+
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
10572+
10573+
// Simulate peer-disconnects mid-handshake
10574+
// The channel is initiated from the node 0 side,
10575+
// but the nodes disconnect before node 1 could send accept channel
10576+
let create_chan_id = nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), 100000, 10001, 42, None, None).unwrap();
10577+
let open_channel_msg = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel, nodes[1].node.get_our_node_id());
10578+
assert_eq!(open_channel_msg.temporary_channel_id, create_chan_id);
10579+
10580+
nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id());
10581+
nodes[1].node.peer_disconnected(&nodes[0].node.get_our_node_id());
10582+
10583+
// Make sure that we have not removed the OutboundV1Channel from node[0] immediately.
10584+
assert_eq!(nodes[0].node.list_channels().len(), 1);
10585+
10586+
// Since channel was inbound from node[1] perspective, it should have been immediately dropped.
10587+
assert_eq!(nodes[1].node.list_channels().len(), 0);
10588+
10589+
// The peers now reconnect
10590+
nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init {
10591+
features: nodes[1].node.init_features(), networks: None, remote_network_address: None
10592+
}, true).unwrap();
10593+
nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init {
10594+
features: nodes[0].node.init_features(), networks: None, remote_network_address: None
10595+
}, false).unwrap();
10596+
10597+
// Make sure the SendOpenChannel message is added to node_0 pending message events
10598+
let msg_events = nodes[0].node.get_and_clear_pending_msg_events();
10599+
assert_eq!(msg_events.len(), 1);
10600+
match &msg_events[0] {
10601+
MessageSendEvent::SendOpenChannel { msg, .. } => assert_eq!(msg, &open_channel_msg),
10602+
_ => panic!("Unexpected message."),
10603+
}
10604+
}
10605+
1051510606
fn do_test_multi_post_event_actions(do_reload: bool) {
1051610607
// Tests handling multiple post-Event actions at once.
1051710608
// There is specific code in ChannelManager to handle channels where multiple post-Event
@@ -10668,7 +10759,9 @@ fn test_batch_channel_open() {
1066810759
}
1066910760

1067010761
#[test]
10671-
fn test_disconnect_in_funding_batch() {
10762+
fn test_close_in_funding_batch() {
10763+
// This test ensures that if one of the channels
10764+
// in the batch closes, the complete batch will close.
1067210765
let chanmon_cfgs = create_chanmon_cfgs(3);
1067310766
let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
1067410767
let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
@@ -10692,14 +10785,39 @@ fn test_disconnect_in_funding_batch() {
1069210785
// The transaction should not have been broadcast before all channels are ready.
1069310786
assert_eq!(nodes[0].tx_broadcaster.txn_broadcast().len(), 0);
1069410787

10695-
// The remaining peer in the batch disconnects.
10696-
nodes[0].node.peer_disconnected(&nodes[2].node.get_our_node_id());
10697-
10698-
// The channels in the batch will close immediately.
10788+
// Force-close the channel for which we've completed the initial monitor.
1069910789
let funding_txo_1 = OutPoint { txid: tx.txid(), index: 0 };
1070010790
let funding_txo_2 = OutPoint { txid: tx.txid(), index: 1 };
1070110791
let channel_id_1 = ChannelId::v1_from_funding_outpoint(funding_txo_1);
1070210792
let channel_id_2 = ChannelId::v1_from_funding_outpoint(funding_txo_2);
10793+
10794+
nodes[0].node.force_close_broadcasting_latest_txn(&channel_id_1, &nodes[1].node.get_our_node_id()).unwrap();
10795+
10796+
// The monitor should become closed.
10797+
check_added_monitors(&nodes[0], 1);
10798+
{
10799+
let mut monitor_updates = nodes[0].chain_monitor.monitor_updates.lock().unwrap();
10800+
let monitor_updates_1 = monitor_updates.get(&channel_id_1).unwrap();
10801+
assert_eq!(monitor_updates_1.len(), 1);
10802+
assert_eq!(monitor_updates_1[0].update_id, CLOSED_CHANNEL_UPDATE_ID);
10803+
}
10804+
10805+
let msg_events = nodes[0].node.get_and_clear_pending_msg_events();
10806+
match msg_events[0] {
10807+
MessageSendEvent::HandleError { .. } => (),
10808+
_ => panic!("Unexpected message."),
10809+
}
10810+
10811+
// We broadcast the commitment transaction as part of the force-close.
10812+
{
10813+
let broadcasted_txs = nodes[0].tx_broadcaster.txn_broadcast();
10814+
assert_eq!(broadcasted_txs.len(), 1);
10815+
assert!(broadcasted_txs[0].txid() != tx.txid());
10816+
assert_eq!(broadcasted_txs[0].input.len(), 1);
10817+
assert_eq!(broadcasted_txs[0].input[0].previous_output.txid, tx.txid());
10818+
}
10819+
10820+
// All channels in the batch should close immediately.
1070310821
check_closed_events(&nodes[0], &[
1070410822
ExpectedCloseEvent {
1070510823
channel_id: Some(channel_id_1),
@@ -10717,19 +10835,6 @@ fn test_disconnect_in_funding_batch() {
1071710835
},
1071810836
]);
1071910837

10720-
// The monitor should become closed.
10721-
check_added_monitors(&nodes[0], 1);
10722-
{
10723-
let mut monitor_updates = nodes[0].chain_monitor.monitor_updates.lock().unwrap();
10724-
let monitor_updates_1 = monitor_updates.get(&channel_id_1).unwrap();
10725-
assert_eq!(monitor_updates_1.len(), 1);
10726-
assert_eq!(monitor_updates_1[0].update_id, CLOSED_CHANNEL_UPDATE_ID);
10727-
}
10728-
10729-
// The funding transaction should not have been broadcast, and therefore, we don't need
10730-
// to broadcast a force-close transaction for the closed monitor.
10731-
assert_eq!(nodes[0].tx_broadcaster.txn_broadcast().len(), 0);
10732-
1073310838
// Ensure the channels don't exist anymore.
1073410839
assert!(nodes[0].node.list_channels().is_empty());
1073510840
}

0 commit comments

Comments
 (0)