Skip to content

Commit d65dc9c

Browse files
authored
Merge pull request #3296 from alecchendev/2024-09-async-close-sign-followup
Finish closing channel after async closing signed
2 parents eba329c + 1fa7bf9 commit d65dc9c

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)