Skip to content

Commit 1fa7bf9

Browse files
committed
Finish closing channel after async closing signed
In addressing a followup to test reconnection during closing negotation with async signing, we change things to only return a `ShutdownResult` when we actually finish shutting down the channel, i.e. we have the signature ready to send the final closing signed. This slightly simplifies the logic where we would shutdown our channel prematurely before we got the final signature. This also means that we don't push multiple `ChannelClosed` events if we receive closing signed, reconnect, and receive closing signed again.
1 parent d35239c commit 1fa7bf9

File tree

4 files changed

+102
-59
lines changed

4 files changed

+102
-59
lines changed

lightning/src/ln/async_signer_tests.rs

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -854,11 +854,13 @@ fn test_async_holder_signatures_remote_commitment_anchors() {
854854

855855
#[test]
856856
fn test_closing_signed() {
857-
do_test_closing_signed(false);
858-
do_test_closing_signed(true);
857+
do_test_closing_signed(false, false);
858+
do_test_closing_signed(true, false);
859+
do_test_closing_signed(false, true);
860+
do_test_closing_signed(true, true);
859861
}
860862

861-
fn do_test_closing_signed(extra_closing_signed: bool) {
863+
fn do_test_closing_signed(extra_closing_signed: bool, reconnect: bool) {
862864
// Based off of `expect_channel_shutdown_state`.
863865
// Test that we can asynchronously sign closing transactions.
864866
let chanmon_cfgs = create_chanmon_cfgs(2);
@@ -867,6 +869,9 @@ fn do_test_closing_signed(extra_closing_signed: bool) {
867869
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
868870
let chan_1 = create_announced_chan_between_nodes(&nodes, 0, 1);
869871

872+
// Avoid extra channel ready message upon reestablish later
873+
send_payment(&nodes[0], &vec![&nodes[1]][..], 8_000_000);
874+
870875
expect_channel_shutdown_state!(nodes[0], chan_1.2, ChannelShutdownState::NotShuttingDown);
871876

872877
nodes[0].node.close_channel(&chan_1.2, &nodes[1].node.get_our_node_id()).unwrap();
@@ -914,7 +919,7 @@ fn do_test_closing_signed(extra_closing_signed: bool) {
914919
let mut node_1_closing_signed_2 = node_1_closing_signed.clone();
915920
let holder_script = nodes[0].keys_manager.get_shutdown_scriptpubkey().unwrap();
916921
let counterparty_script = nodes[1].keys_manager.get_shutdown_scriptpubkey().unwrap();
917-
let funding_outpoint = bitcoin::OutPoint { txid: chan_1.3.txid(), vout: 0 };
922+
let funding_outpoint = bitcoin::OutPoint { txid: chan_1.3.compute_txid(), vout: 0 };
918923
let closing_tx_2 = ClosingTransaction::new(50000, 0, holder_script.into(),
919924
counterparty_script.into(), funding_outpoint);
920925

@@ -941,9 +946,45 @@ fn do_test_closing_signed(extra_closing_signed: bool) {
941946
};
942947
}
943948

949+
if reconnect {
950+
nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id());
951+
nodes[1].node.peer_disconnected(&nodes[0].node.get_our_node_id());
952+
953+
*nodes[0].fee_estimator.sat_per_kw.lock().unwrap() *= 8;
954+
*nodes[1].fee_estimator.sat_per_kw.lock().unwrap() *= 8;
955+
956+
connect_nodes(&nodes[0], &nodes[1]);
957+
let node_0_reestablish = get_chan_reestablish_msgs!(nodes[0], nodes[1]).pop().unwrap();
958+
let node_1_reestablish = get_chan_reestablish_msgs!(nodes[1], nodes[0]).pop().unwrap();
959+
nodes[1].node.handle_channel_reestablish(&nodes[0].node.get_our_node_id(), &node_0_reestablish);
960+
nodes[0].node.handle_channel_reestablish(&nodes[1].node.get_our_node_id(), &node_1_reestablish);
961+
962+
let node_0_msgs = nodes[0].node.get_and_clear_pending_msg_events();
963+
assert_eq!(node_0_msgs.len(), 2);
964+
let node_0_2nd_shutdown = match node_0_msgs[0] {
965+
MessageSendEvent::SendShutdown { ref msg, .. } => {
966+
msg.clone()
967+
},
968+
_ => panic!(),
969+
};
970+
let node_0_2nd_closing_signed = match node_0_msgs[1] {
971+
MessageSendEvent::SendClosingSigned { ref msg, .. } => {
972+
msg.clone()
973+
},
974+
_ => panic!(),
975+
};
976+
let node_1_2nd_shutdown = get_event_msg!(nodes[1], MessageSendEvent::SendShutdown, nodes[0].node.get_our_node_id());
977+
978+
nodes[1].node.handle_shutdown(&nodes[0].node.get_our_node_id(), &node_0_2nd_shutdown);
979+
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
980+
nodes[0].node.handle_shutdown(&nodes[1].node.get_our_node_id(), &node_1_2nd_shutdown);
981+
nodes[1].node.handle_closing_signed(&nodes[0].node.get_our_node_id(), &node_0_2nd_closing_signed);
982+
let node_1_closing_signed = get_event_msg!(nodes[1], MessageSendEvent::SendClosingSigned, nodes[0].node.get_our_node_id());
983+
nodes[0].node.handle_closing_signed(&nodes[1].node.get_our_node_id(), &node_1_closing_signed);
984+
}
985+
944986
nodes[0].node.signer_unblocked(None);
945987
let (_, node_0_2nd_closing_signed) = get_closing_signed_broadcast!(nodes[0].node, nodes[1].node.get_our_node_id());
946-
947988
nodes[1].node.handle_closing_signed(&nodes[0].node.get_our_node_id(), &node_0_2nd_closing_signed.unwrap());
948989
let (_, node_1_closing_signed) = get_closing_signed_broadcast!(nodes[1].node, nodes[0].node.get_our_node_id());
949990
assert!(node_1_closing_signed.is_none());
@@ -952,4 +993,5 @@ fn do_test_closing_signed(extra_closing_signed: bool) {
952993
assert!(nodes[1].node.list_channels().is_empty());
953994
check_closed_event!(nodes[0], 1, ClosureReason::LocallyInitiatedCooperativeClosure, [nodes[1].node.get_our_node_id()], 100000);
954995
check_closed_event!(nodes[1], 1, ClosureReason::CounterpartyInitiatedCooperativeClosure, [nodes[0].node.get_our_node_id()], 100000);
996+
955997
}

lightning/src/ln/channel.rs

Lines changed: 35 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -909,6 +909,7 @@ pub(super) struct SignerResumeUpdates {
909909
pub order: RAACommitmentOrder,
910910
pub closing_signed: Option<msgs::ClosingSigned>,
911911
pub signed_closing_tx: Option<Transaction>,
912+
pub shutdown_result: Option<ShutdownResult>,
912913
}
913914

914915
/// The return value of `channel_reestablish`
@@ -5508,7 +5509,7 @@ impl<SP: Deref> Channel<SP> where
55085509
commitment_update = None;
55095510
}
55105511

5511-
let (closing_signed, signed_closing_tx) = if self.context.signer_pending_closing {
5512+
let (closing_signed, signed_closing_tx, shutdown_result) = if self.context.signer_pending_closing {
55125513
debug_assert!(self.context.last_sent_closing_fee.is_some());
55135514
if let Some((fee, skip_remote_output, fee_range, holder_sig)) = self.context.last_sent_closing_fee.clone() {
55145515
debug_assert!(holder_sig.is_none());
@@ -5524,19 +5525,21 @@ impl<SP: Deref> Channel<SP> where
55245525
&self.context.get_counterparty_pubkeys().funding_pubkey).is_ok());
55255526
Some(self.build_signed_closing_transaction(&closing_tx, &counterparty_sig, signature))
55265527
} else { None };
5527-
(closing_signed, signed_tx)
5528-
} else { (None, None) }
5529-
} else { (None, None) };
5528+
let shutdown_result = signed_tx.as_ref().map(|_| self.shutdown_result_coop_close());
5529+
(closing_signed, signed_tx, shutdown_result)
5530+
} else { (None, None, None) }
5531+
} else { (None, None, None) };
55305532

55315533
log_trace!(logger, "Signer unblocked with {} commitment_update, {} revoke_and_ack, with resend order {:?}, {} funding_signed, {} channel_ready,
5532-
{} closing_signed, and {} signed_closing_tx",
5534+
{} closing_signed, {} signed_closing_tx, and {} shutdown result",
55335535
if commitment_update.is_some() { "a" } else { "no" },
55345536
if revoke_and_ack.is_some() { "a" } else { "no" },
55355537
self.context.resend_order,
55365538
if funding_signed.is_some() { "a" } else { "no" },
55375539
if channel_ready.is_some() { "a" } else { "no" },
55385540
if closing_signed.is_some() { "a" } else { "no" },
5539-
if signed_closing_tx.is_some() { "a" } else { "no" });
5541+
if signed_closing_tx.is_some() { "a" } else { "no" },
5542+
if shutdown_result.is_some() { "a" } else { "no" });
55405543

55415544
SignerResumeUpdates {
55425545
commitment_update,
@@ -5546,6 +5549,7 @@ impl<SP: Deref> Channel<SP> where
55465549
order: self.context.resend_order.clone(),
55475550
closing_signed,
55485551
signed_closing_tx,
5552+
shutdown_result,
55495553
}
55505554
}
55515555

@@ -6170,6 +6174,27 @@ impl<SP: Deref> Channel<SP> where
61706174
})
61716175
}
61726176

6177+
fn shutdown_result_coop_close(&self) -> ShutdownResult {
6178+
let closure_reason = if self.initiated_shutdown() {
6179+
ClosureReason::LocallyInitiatedCooperativeClosure
6180+
} else {
6181+
ClosureReason::CounterpartyInitiatedCooperativeClosure
6182+
};
6183+
ShutdownResult {
6184+
closure_reason,
6185+
monitor_update: None,
6186+
dropped_outbound_htlcs: Vec::new(),
6187+
unbroadcasted_batch_funding_txid: self.context.unbroadcasted_batch_funding_txid(),
6188+
channel_id: self.context.channel_id,
6189+
user_channel_id: self.context.user_id,
6190+
channel_capacity_satoshis: self.context.channel_value_satoshis,
6191+
counterparty_node_id: self.context.counterparty_node_id,
6192+
unbroadcasted_funding_tx: self.context.unbroadcasted_funding(),
6193+
is_manual_broadcast: self.context.is_manual_broadcast,
6194+
channel_funding_txo: self.context.get_funding_txo(),
6195+
}
6196+
}
6197+
61736198
pub fn closing_signed<F: Deref, L: Deref>(
61746199
&mut self, fee_estimator: &LowerBoundedFeeEstimator<F>, msg: &msgs::ClosingSigned, logger: &L)
61756200
-> Result<(Option<msgs::ClosingSigned>, Option<Transaction>, Option<ShutdownResult>), ChannelError>
@@ -6226,28 +6251,10 @@ impl<SP: Deref> Channel<SP> where
62266251
}
62276252
}
62286253

6229-
let closure_reason = if self.initiated_shutdown() {
6230-
ClosureReason::LocallyInitiatedCooperativeClosure
6231-
} else {
6232-
ClosureReason::CounterpartyInitiatedCooperativeClosure
6233-
};
6234-
62356254
assert!(self.context.shutdown_scriptpubkey.is_some());
62366255
if let Some((last_fee, _, _, Some(sig))) = self.context.last_sent_closing_fee {
62376256
if last_fee == msg.fee_satoshis {
6238-
let shutdown_result = ShutdownResult {
6239-
closure_reason,
6240-
monitor_update: None,
6241-
dropped_outbound_htlcs: Vec::new(),
6242-
unbroadcasted_batch_funding_txid: self.context.unbroadcasted_batch_funding_txid(),
6243-
channel_id: self.context.channel_id,
6244-
user_channel_id: self.context.user_id,
6245-
channel_capacity_satoshis: self.context.channel_value_satoshis,
6246-
counterparty_node_id: self.context.counterparty_node_id,
6247-
unbroadcasted_funding_tx: self.context.unbroadcasted_funding(),
6248-
is_manual_broadcast: self.context.is_manual_broadcast,
6249-
channel_funding_txo: self.context.get_funding_txo(),
6250-
};
6257+
let shutdown_result = self.shutdown_result_coop_close();
62516258
let tx = self.build_signed_closing_transaction(&mut closing_tx, &msg.signature, &sig);
62526259
self.context.channel_state = ChannelState::ShutdownComplete;
62536260
self.context.update_time_counter += 1;
@@ -6268,27 +6275,16 @@ impl<SP: Deref> Channel<SP> where
62686275

62696276
let closing_signed = self.get_closing_signed_msg(&closing_tx, skip_remote_output, used_fee, our_min_fee, our_max_fee, logger);
62706277
let (signed_tx, shutdown_result) = if $new_fee == msg.fee_satoshis {
6271-
let shutdown_result = ShutdownResult {
6272-
closure_reason,
6273-
monitor_update: None,
6274-
dropped_outbound_htlcs: Vec::new(),
6275-
unbroadcasted_batch_funding_txid: self.context.unbroadcasted_batch_funding_txid(),
6276-
channel_id: self.context.channel_id,
6277-
user_channel_id: self.context.user_id,
6278-
channel_capacity_satoshis: self.context.channel_value_satoshis,
6279-
counterparty_node_id: self.context.counterparty_node_id,
6280-
unbroadcasted_funding_tx: self.context.unbroadcasted_funding(),
6281-
is_manual_broadcast: self.context.is_manual_broadcast,
6282-
channel_funding_txo: self.context.get_funding_txo(),
6283-
};
6278+
let shutdown_result = closing_signed.as_ref()
6279+
.map(|_| self.shutdown_result_coop_close());
62846280
if closing_signed.is_some() {
62856281
self.context.channel_state = ChannelState::ShutdownComplete;
62866282
}
62876283
self.context.update_time_counter += 1;
62886284
self.context.last_received_closing_sig = Some(msg.signature.clone());
62896285
let tx = closing_signed.as_ref().map(|ClosingSigned { signature, .. }|
62906286
self.build_signed_closing_transaction(&closing_tx, &msg.signature, signature));
6291-
(tx, Some(shutdown_result))
6287+
(tx, shutdown_result)
62926288
} else {
62936289
(None, None)
62946290
};

lightning/src/ln/channelmanager.rs

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7827,7 +7827,7 @@ where
78277827
if let ChannelPhase::Funded(chan) = chan_phase_entry.get_mut() {
78287828
let logger = WithChannelContext::from(&self.logger, &chan.context, None);
78297829
let (closing_signed, tx, shutdown_result) = try_chan_phase_entry!(self, chan.closing_signed(&self.fee_estimator, &msg, &&logger), chan_phase_entry);
7830-
debug_assert_eq!(shutdown_result.is_some(), chan.is_shutdown() || chan.is_shutdown_pending_signature());
7830+
debug_assert_eq!(shutdown_result.is_some(), chan.is_shutdown());
78317831
if let Some(msg) = closing_signed {
78327832
peer_state.pending_msg_events.push(events::MessageSendEvent::SendClosingSigned {
78337833
node_id: counterparty_node_id.clone(),
@@ -8650,7 +8650,7 @@ where
86508650
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
86518651

86528652
// Returns whether we should remove this channel as it's just been closed.
8653-
let unblock_chan = |phase: &mut ChannelPhase<SP>, pending_msg_events: &mut Vec<MessageSendEvent>| -> bool {
8653+
let unblock_chan = |phase: &mut ChannelPhase<SP>, pending_msg_events: &mut Vec<MessageSendEvent>| -> Option<ShutdownResult> {
86548654
let node_id = phase.context().get_counterparty_node_id();
86558655
match phase {
86568656
ChannelPhase::Funded(chan) => {
@@ -8703,11 +8703,8 @@ where
87038703
msg: update
87048704
});
87058705
}
8706-
8707-
// We should return true to remove the channel if we just
8708-
// broadcasted the closing transaction.
8709-
true
8710-
} else { false }
8706+
}
8707+
msgs.shutdown_result
87118708
}
87128709
ChannelPhase::UnfundedOutboundV1(chan) => {
87138710
if let Some(msg) = chan.signer_maybe_unblocked(&self.logger) {
@@ -8716,12 +8713,13 @@ where
87168713
msg,
87178714
});
87188715
}
8719-
false
8716+
None
87208717
}
8721-
ChannelPhase::UnfundedInboundV1(_) => false,
8718+
ChannelPhase::UnfundedInboundV1(_) => None,
87228719
}
87238720
};
87248721

8722+
let mut shutdown_results = Vec::new();
87258723
let per_peer_state = self.per_peer_state.read().unwrap();
87268724
let per_peer_state_iter = per_peer_state.iter().filter(|(cp_id, _)| {
87278725
if let Some((counterparty_node_id, _)) = channel_opt {
@@ -8732,16 +8730,23 @@ where
87328730
let mut peer_state_lock = peer_state_mutex.lock().unwrap();
87338731
let peer_state = &mut *peer_state_lock;
87348732
peer_state.channel_by_id.retain(|_, chan| {
8735-
let should_remove = match channel_opt {
8736-
Some((_, channel_id)) if chan.context().channel_id() != channel_id => false,
8733+
let shutdown_result = match channel_opt {
8734+
Some((_, channel_id)) if chan.context().channel_id() != channel_id => None,
87378735
_ => unblock_chan(chan, &mut peer_state.pending_msg_events),
87388736
};
8739-
if should_remove {
8737+
if let Some(shutdown_result) = shutdown_result {
87408738
log_trace!(self.logger, "Removing channel after unblocking signer");
8739+
shutdown_results.push(shutdown_result);
8740+
false
8741+
} else {
8742+
true
87418743
}
8742-
!should_remove
87438744
});
87448745
}
8746+
drop(per_peer_state);
8747+
for shutdown_result in shutdown_results.drain(..) {
8748+
self.finish_close_channel(shutdown_result);
8749+
}
87458750
}
87468751

87478752
/// Check whether any channels have finished removing all pending updates after a shutdown
@@ -8770,8 +8775,8 @@ where
87708775
node_id: chan.context.get_counterparty_node_id(), msg,
87718776
});
87728777
}
8778+
debug_assert_eq!(shutdown_result_opt.is_some(), chan.is_shutdown());
87738779
if let Some(shutdown_result) = shutdown_result_opt {
8774-
debug_assert!(chan.is_shutdown() || chan.is_shutdown_pending_signature());
87758780
shutdown_results.push(shutdown_result);
87768781
}
87778782
if let Some(tx) = tx_opt {

lightning/src/ln/functional_test_utils.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3295,7 +3295,7 @@ pub fn create_network<'a, 'b: 'a, 'c: 'b>(node_count: usize, cfgs: &'b Vec<NodeC
32953295
nodes
32963296
}
32973297

3298-
fn connect_nodes<'a, 'b: 'a, 'c: 'b>(node_a: &Node<'a, 'b, 'c>, node_b: &Node<'a, 'b, 'c>) {
3298+
pub fn connect_nodes<'a, 'b: 'a, 'c: 'b>(node_a: &Node<'a, 'b, 'c>, node_b: &Node<'a, 'b, 'c>) {
32993299
let node_id_a = node_a.node.get_our_node_id();
33003300
let node_id_b = node_b.node.get_our_node_id();
33013301

0 commit comments

Comments
 (0)