Skip to content

Commit ddf75af

Browse files
committed
Do not remove Outbound Channel immediately when peer disconnects
- Do not remove channel immediately when peer_disconnect, instead removed it after some time if peer doesn't reconnect soon (handled in previous commit). - Do not mark per ok_to_remove if we have some OutboundV1Channels too. - Rebroadcast SendOpenChannel for outboundV1Channel when peer reconnects. - Update the relevant tests to account for the behavior change. - Repurpose the test_disconnect_in_funding_batch to test that all channels in the batch close when one them closes.
1 parent 5bf58f0 commit ddf75af

File tree

2 files changed

+76
-35
lines changed

2 files changed

+76
-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
}
@@ -8905,10 +8907,12 @@ where
89058907
}
89068908
&mut chan.context
89078909
},
8908-
// Unfunded channels will always be removed.
8909-
ChannelPhase::UnfundedOutboundV1(chan) => {
8910-
&mut chan.context
8910+
// We retain UnfundedOutboundV1 channel for some time in case
8911+
// peer unexpectedly disconnects, and intends to reconnect again.
8912+
ChannelPhase::UnfundedOutboundV1(_) => {
8913+
return true;
89118914
},
8915+
// Unfunded inbound channels will always be removed.
89128916
ChannelPhase::UnfundedInboundV1(chan) => {
89138917
&mut chan.context
89148918
},
@@ -9047,15 +9051,31 @@ where
90479051
let peer_state = &mut *peer_state_lock;
90489052
let pending_msg_events = &mut peer_state.pending_msg_events;
90499053

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

90619081
return NotifyOption::SkipPersistHandleEvents;

lightning/src/ln/functional_tests.rs

+43-22
Original file line numberDiff line numberDiff line change
@@ -3695,7 +3695,7 @@ fn test_dup_events_on_peer_disconnect() {
36953695
#[test]
36963696
fn test_peer_disconnected_before_funding_broadcasted() {
36973697
// Test that channels are closed with `ClosureReason::DisconnectedPeer` if the peer disconnects
3698-
// before the funding transaction has been broadcasted.
3698+
// before the funding transaction has been broadcasted, and doesn't reconnect back within time.
36993699
let chanmon_cfgs = create_chanmon_cfgs(2);
37003700
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
37013701
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
@@ -3724,12 +3724,19 @@ fn test_peer_disconnected_before_funding_broadcasted() {
37243724
assert_eq!(nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().len(), 0);
37253725
}
37263726

3727-
// Ensure that the channel is closed with `ClosureReason::DisconnectedPeer` when the peers are
3728-
// disconnected before the funding transaction was broadcasted.
3727+
// The peers disconnect before the funding is broadcasted.
37293728
nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id());
37303729
nodes[1].node.peer_disconnected(&nodes[0].node.get_our_node_id());
37313730

3732-
check_closed_event!(&nodes[0], 2, ClosureReason::DisconnectedPeer, true
3731+
// The time for peers to reconnect expires.
3732+
for _ in 0..UNFUNDED_CHANNEL_AGE_LIMIT_TICKS {
3733+
nodes[0].node.timer_tick_occurred();
3734+
}
3735+
3736+
// Ensure that the channel is closed with `ClosureReason::HolderForceClosed`
3737+
// when the peers are disconnected and do not reconnect before the funding
3738+
// transaction is broadcasted.
3739+
check_closed_event!(&nodes[0], 2, ClosureReason::HolderForceClosed, true
37333740
, [nodes[1].node.get_our_node_id()], 1000000);
37343741
check_closed_event!(&nodes[1], 1, ClosureReason::DisconnectedPeer, false
37353742
, [nodes[0].node.get_our_node_id()], 1000000);
@@ -10662,7 +10669,9 @@ fn test_batch_channel_open() {
1066210669
}
1066310670

1066410671
#[test]
10665-
fn test_disconnect_in_funding_batch() {
10672+
fn test_close_in_funding_batch() {
10673+
// This test ensures that if one of the channels
10674+
// in the batch closes, the complete batch will close.
1066610675
let chanmon_cfgs = create_chanmon_cfgs(3);
1066710676
let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
1066810677
let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
@@ -10686,14 +10695,39 @@ fn test_disconnect_in_funding_batch() {
1068610695
// The transaction should not have been broadcast before all channels are ready.
1068710696
assert_eq!(nodes[0].tx_broadcaster.txn_broadcast().len(), 0);
1068810697

10689-
// The remaining peer in the batch disconnects.
10690-
nodes[0].node.peer_disconnected(&nodes[2].node.get_our_node_id());
10691-
10692-
// The channels in the batch will close immediately.
10698+
// Force-close the channel for which we've completed the initial monitor.
1069310699
let funding_txo_1 = OutPoint { txid: tx.txid(), index: 0 };
1069410700
let funding_txo_2 = OutPoint { txid: tx.txid(), index: 1 };
1069510701
let channel_id_1 = ChannelId::v1_from_funding_outpoint(funding_txo_1);
1069610702
let channel_id_2 = ChannelId::v1_from_funding_outpoint(funding_txo_2);
10703+
10704+
nodes[0].node.force_close_broadcasting_latest_txn(&channel_id_1, &nodes[1].node.get_our_node_id()).unwrap();
10705+
10706+
// The monitor should become closed.
10707+
check_added_monitors(&nodes[0], 1);
10708+
{
10709+
let mut monitor_updates = nodes[0].chain_monitor.monitor_updates.lock().unwrap();
10710+
let monitor_updates_1 = monitor_updates.get(&channel_id_1).unwrap();
10711+
assert_eq!(monitor_updates_1.len(), 1);
10712+
assert_eq!(monitor_updates_1[0].update_id, CLOSED_CHANNEL_UPDATE_ID);
10713+
}
10714+
10715+
let msg_events = nodes[0].node.get_and_clear_pending_msg_events();
10716+
match msg_events[0] {
10717+
MessageSendEvent::HandleError { .. } => (),
10718+
_ => panic!("Unexpected message."),
10719+
}
10720+
10721+
// We broadcast the commitment transaction as part of the force-close.
10722+
{
10723+
let broadcasted_txs = nodes[0].tx_broadcaster.txn_broadcast();
10724+
assert_eq!(broadcasted_txs.len(), 1);
10725+
assert!(broadcasted_txs[0].txid() != tx.txid());
10726+
assert_eq!(broadcasted_txs[0].input.len(), 1);
10727+
assert_eq!(broadcasted_txs[0].input[0].previous_output.txid, tx.txid());
10728+
}
10729+
10730+
// All channels in the batch should close immediately.
1069710731
check_closed_events(&nodes[0], &[
1069810732
ExpectedCloseEvent {
1069910733
channel_id: Some(channel_id_1),
@@ -10711,19 +10745,6 @@ fn test_disconnect_in_funding_batch() {
1071110745
},
1071210746
]);
1071310747

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

0 commit comments

Comments
 (0)