Skip to content

Commit 92a865d

Browse files
committed
Allow sending closing tx signatures asynchronously
1 parent 27276f8 commit 92a865d

File tree

2 files changed

+96
-34
lines changed

2 files changed

+96
-34
lines changed

lightning/src/ln/channel.rs

Lines changed: 56 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -911,6 +911,8 @@ pub(super) struct SignerResumeUpdates {
911911
pub funding_signed: Option<msgs::FundingSigned>,
912912
pub channel_ready: Option<msgs::ChannelReady>,
913913
pub order: RAACommitmentOrder,
914+
pub closing_signed: Option<msgs::ClosingSigned>,
915+
pub signed_closing_tx: Option<Transaction>,
914916
}
915917

916918
/// The return value of `channel_reestablish`
@@ -1237,6 +1239,9 @@ pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
12371239
/// [`msgs::FundingCreated`] or [`msgs::FundingSigned`] depending on if this channel is
12381240
/// outbound or inbound.
12391241
signer_pending_funding: bool,
1242+
/// If we attempted to sign a cooperative close transaction but the signer wasn't ready, then this
1243+
/// will be set to `true`.
1244+
signer_pending_closing: bool,
12401245

12411246
// pending_update_fee is filled when sending and receiving update_fee.
12421247
//
@@ -1268,7 +1273,9 @@ pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
12681273
/// Max to_local and to_remote outputs in a remote-generated commitment transaction
12691274
counterparty_max_commitment_tx_output: Mutex<(u64, u64)>,
12701275

1271-
last_sent_closing_fee: Option<(u64, Signature)>, // (fee, holder_sig)
1276+
// (fee, skip_remote_output, fee_range, holder_sig)
1277+
last_sent_closing_fee: Option<(u64, bool, ClosingSignedFeeRange, Option<Signature>)>,
1278+
last_received_closing_sig: Option<Signature>,
12721279
target_closing_feerate_sats_per_kw: Option<u32>,
12731280

12741281
/// If our counterparty sent us a closing_signed while we were waiting for a `ChannelMonitor`
@@ -1697,6 +1704,7 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
16971704
signer_pending_revoke_and_ack: false,
16981705
signer_pending_commitment_update: false,
16991706
signer_pending_funding: false,
1707+
signer_pending_closing: false,
17001708

17011709

17021710
#[cfg(debug_assertions)]
@@ -1705,6 +1713,7 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
17051713
counterparty_max_commitment_tx_output: Mutex::new((value_to_self_msat, (channel_value_satoshis * 1000 - msg_push_msat).saturating_sub(value_to_self_msat))),
17061714

17071715
last_sent_closing_fee: None,
1716+
last_received_closing_sig: None,
17081717
pending_counterparty_closing_signed: None,
17091718
expecting_peer_commitment_signed: false,
17101719
closing_fee_limits: None,
@@ -1923,6 +1932,7 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
19231932
signer_pending_revoke_and_ack: false,
19241933
signer_pending_commitment_update: false,
19251934
signer_pending_funding: false,
1935+
signer_pending_closing: false,
19261936

19271937
// We'll add our counterparty's `funding_satoshis` to these max commitment output assertions
19281938
// when we receive `accept_channel2`.
@@ -1932,6 +1942,7 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
19321942
counterparty_max_commitment_tx_output: Mutex::new((channel_value_satoshis * 1000 - push_msat, push_msat)),
19331943

19341944
last_sent_closing_fee: None,
1945+
last_received_closing_sig: None,
19351946
pending_counterparty_closing_signed: None,
19361947
expecting_peer_commitment_signed: false,
19371948
closing_fee_limits: None,
@@ -5424,19 +5435,40 @@ impl<SP: Deref> Channel<SP> where
54245435
commitment_update = None;
54255436
}
54265437

5427-
log_trace!(logger, "Signer unblocked with {} commitment_update, {} revoke_and_ack, {} funding_signed and {} channel_ready, with resend order {:?}",
5438+
let (closing_signed, signed_closing_tx) = if self.context.signer_pending_closing {
5439+
debug_assert!(self.context.last_sent_closing_fee.is_some());
5440+
if let Some((fee, skip_remote_output, fee_range, holder_sig)) = self.context.last_sent_closing_fee.clone() {
5441+
debug_assert!(holder_sig.is_none());
5442+
log_trace!(logger, "Attempting to generate pending closing_signed...");
5443+
let (closing_tx, fee) = self.build_closing_transaction(fee, skip_remote_output);
5444+
let closing_signed = self.get_closing_signed_msg(&closing_tx, skip_remote_output,
5445+
fee, fee_range.min_fee_satoshis, fee_range.max_fee_satoshis, logger);
5446+
let signed_tx = if let (Some(ClosingSigned { signature, .. }), Some(counterparty_sig)) =
5447+
(closing_signed.as_ref(), self.context.last_received_closing_sig) {
5448+
Some(self.build_signed_closing_transaction(&closing_tx, &counterparty_sig, signature))
5449+
} else { None };
5450+
(closing_signed, signed_tx)
5451+
} else { (None, None) }
5452+
} else { (None, None) };
5453+
5454+
log_trace!(logger, "Signer unblocked with {} commitment_update, {} revoke_and_ack, with resend order {:?}, {} funding_signed, {} channel_ready,
5455+
{} closing_signed, and {} signed_closing_tx",
54285456
if commitment_update.is_some() { "a" } else { "no" },
54295457
if revoke_and_ack.is_some() { "a" } else { "no" },
5458+
self.context.resend_order,
54305459
if funding_signed.is_some() { "a" } else { "no" },
54315460
if channel_ready.is_some() { "a" } else { "no" },
5432-
self.context.resend_order);
5461+
if closing_signed.is_some() { "a" } else { "no" },
5462+
if signed_closing_tx.is_some() { "a" } else { "no" });
54335463

54345464
SignerResumeUpdates {
54355465
commitment_update,
54365466
revoke_and_ack,
54375467
funding_signed,
54385468
channel_ready,
54395469
order: self.context.resend_order.clone(),
5470+
closing_signed,
5471+
signed_closing_tx,
54405472
}
54415473
}
54425474

@@ -5851,9 +5883,6 @@ impl<SP: Deref> Channel<SP> where
58515883
our_min_fee, our_max_fee, total_fee_satoshis);
58525884

58535885
let closing_signed = self.get_closing_signed_msg(&closing_tx, false, total_fee_satoshis, our_min_fee, our_max_fee, logger);
5854-
if closing_signed.is_none() {
5855-
return Err(ChannelError::close("Failed to get signature for closing transaction.".to_owned()));
5856-
}
58575886
Ok((closing_signed, None, None))
58585887
}
58595888

@@ -6007,26 +6036,26 @@ impl<SP: Deref> Channel<SP> where
60076036
) -> Option<msgs::ClosingSigned>
60086037
where L::Target: Logger
60096038
{
6010-
match &self.context.holder_signer {
6011-
ChannelSignerType::Ecdsa(ecdsa) => {
6012-
let fee_range = msgs::ClosingSignedFeeRange {
6013-
min_fee_satoshis,
6014-
max_fee_satoshis,
6015-
};
6016-
let sig = ecdsa.sign_closing_transaction(closing_tx, &self.context.secp_ctx).ok()?;
6017-
6018-
self.context.last_sent_closing_fee = Some((fee_satoshis, sig.clone()));
6019-
Some(msgs::ClosingSigned {
6020-
channel_id: self.context.channel_id,
6021-
fee_satoshis,
6022-
signature: sig,
6023-
fee_range: Some(fee_range),
6024-
})
6025-
},
6039+
let sig = match &self.context.holder_signer {
6040+
ChannelSignerType::Ecdsa(ecdsa) => ecdsa.sign_closing_transaction(closing_tx, &self.context.secp_ctx).ok(),
60266041
// TODO (taproot|arik)
60276042
#[cfg(taproot)]
60286043
_ => todo!()
6044+
};
6045+
if sig.is_none() {
6046+
log_trace!(logger, "Closing transaction signature unavailable, waiting on signer");
6047+
self.context.signer_pending_closing = true;
6048+
} else {
6049+
self.context.signer_pending_closing = false;
60296050
}
6051+
let fee_range = msgs::ClosingSignedFeeRange { min_fee_satoshis, max_fee_satoshis };
6052+
self.context.last_sent_closing_fee = Some((fee_satoshis, skip_remote_output, fee_range.clone(), sig.clone()));
6053+
sig.map(|signature| msgs::ClosingSigned {
6054+
channel_id: self.context.channel_id,
6055+
fee_satoshis,
6056+
signature,
6057+
fee_range: Some(fee_range),
6058+
})
60306059
}
60316060

60326061
pub fn closing_signed<F: Deref, L: Deref>(
@@ -6089,7 +6118,7 @@ impl<SP: Deref> Channel<SP> where
60896118
};
60906119

60916120
assert!(self.context.shutdown_scriptpubkey.is_some());
6092-
if let Some((last_fee, sig)) = self.context.last_sent_closing_fee {
6121+
if let Some((last_fee, _, _, Some(sig))) = self.context.last_sent_closing_fee {
60936122
if last_fee == msg.fee_satoshis {
60946123
let shutdown_result = ShutdownResult {
60956124
closure_reason,
@@ -6122,9 +6151,6 @@ impl<SP: Deref> Channel<SP> where
61226151
};
61236152

61246153
let closing_signed = self.get_closing_signed_msg(&closing_tx, skip_remote_output, used_fee, our_min_fee, our_max_fee, logger);
6125-
if closing_signed.is_none() {
6126-
return Err(ChannelError::close("Failed to get signature for closing transaction.".to_owned()));
6127-
}
61286154
let (signed_tx, shutdown_result) = if $new_fee == msg.fee_satoshis {
61296155
let shutdown_result = ShutdownResult {
61306156
closure_reason,
@@ -6140,6 +6166,7 @@ impl<SP: Deref> Channel<SP> where
61406166
};
61416167
self.context.channel_state = ChannelState::ShutdownComplete;
61426168
self.context.update_time_counter += 1;
6169+
self.context.last_received_closing_sig = Some(msg.signature.clone());
61436170
let tx = closing_signed.as_ref().map(|ClosingSigned { signature, .. }|
61446171
self.build_signed_closing_transaction(&closing_tx, &msg.signature, signature));
61456172
(tx, Some(shutdown_result))
@@ -6177,7 +6204,7 @@ impl<SP: Deref> Channel<SP> where
61776204
} else {
61786205
// Old fee style negotiation. We don't bother to enforce whether they are complying
61796206
// with the "making progress" requirements, we just comply and hope for the best.
6180-
if let Some((last_fee, _)) = self.context.last_sent_closing_fee {
6207+
if let Some((last_fee, _, _, _)) = self.context.last_sent_closing_fee {
61816208
if msg.fee_satoshis > last_fee {
61826209
if msg.fee_satoshis < our_max_fee {
61836210
propose_fee!(msg.fee_satoshis);
@@ -9333,6 +9360,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
93339360
signer_pending_revoke_and_ack: false,
93349361
signer_pending_commitment_update: false,
93359362
signer_pending_funding: false,
9363+
signer_pending_closing: false,
93369364

93379365
pending_update_fee,
93389366
holding_cell_update_fee,
@@ -9347,6 +9375,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
93479375
counterparty_max_commitment_tx_output: Mutex::new((0, 0)),
93489376

93499377
last_sent_closing_fee: None,
9378+
last_received_closing_sig: None,
93509379
pending_counterparty_closing_signed: None,
93519380
expecting_peer_commitment_signed: false,
93529381
closing_fee_limits: None,

lightning/src/ln/channelmanager.rs

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8423,7 +8423,8 @@ where
84238423
pub fn signer_unblocked(&self, channel_opt: Option<(PublicKey, ChannelId)>) {
84248424
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
84258425

8426-
let unblock_chan = |phase: &mut ChannelPhase<SP>, pending_msg_events: &mut Vec<MessageSendEvent>| {
8426+
// Returns whether we should remove this channel as it's just been closed.
8427+
let unblock_chan = |phase: &mut ChannelPhase<SP>, pending_msg_events: &mut Vec<MessageSendEvent>| -> bool {
84278428
let node_id = phase.context().get_counterparty_node_id();
84288429
match phase {
84298430
ChannelPhase::Funded(chan) => {
@@ -8458,6 +8459,29 @@ where
84588459
if let Some(msg) = msgs.channel_ready {
84598460
send_channel_ready!(self, pending_msg_events, chan, msg);
84608461
}
8462+
if let Some(msg) = msgs.closing_signed {
8463+
pending_msg_events.push(events::MessageSendEvent::SendClosingSigned {
8464+
node_id,
8465+
msg,
8466+
});
8467+
}
8468+
if let Some(broadcast_tx) = msgs.signed_closing_tx {
8469+
let channel_id = chan.context.channel_id();
8470+
let counterparty_node_id = chan.context.get_counterparty_node_id();
8471+
let logger = WithContext::from(&self.logger, Some(counterparty_node_id), Some(channel_id), None);
8472+
log_info!(logger, "Broadcasting closing tx {}", log_tx!(broadcast_tx));
8473+
self.tx_broadcaster.broadcast_transactions(&[&broadcast_tx]);
8474+
8475+
if let Ok(update) = self.get_channel_update_for_broadcast(&chan) {
8476+
pending_msg_events.push(events::MessageSendEvent::BroadcastChannelUpdate {
8477+
msg: update
8478+
});
8479+
}
8480+
8481+
// We should return true to remove the channel if we just
8482+
// broadcasted the closing transaction.
8483+
true
8484+
} else { false }
84618485
}
84628486
ChannelPhase::UnfundedOutboundV1(chan) => {
84638487
if let Some(msg) = chan.signer_maybe_unblocked(&self.logger) {
@@ -8466,8 +8490,9 @@ where
84668490
msg,
84678491
});
84688492
}
8493+
false
84698494
}
8470-
ChannelPhase::UnfundedInboundV1(_) => {},
8495+
ChannelPhase::UnfundedInboundV1(_) => false,
84718496
}
84728497
};
84738498

@@ -8476,17 +8501,25 @@ where
84768501
if let Some(peer_state_mutex) = per_peer_state.get(&counterparty_node_id) {
84778502
let mut peer_state_lock = peer_state_mutex.lock().unwrap();
84788503
let peer_state = &mut *peer_state_lock;
8479-
if let Some(chan) = peer_state.channel_by_id.get_mut(&channel_id) {
8480-
unblock_chan(chan, &mut peer_state.pending_msg_events);
8504+
let should_remove = if let Some(chan) = peer_state.channel_by_id.get_mut(&channel_id) {
8505+
unblock_chan(chan, &mut peer_state.pending_msg_events)
8506+
} else { false };
8507+
if should_remove {
8508+
log_trace!(self.logger, "Removing channel after unblocking signer");
8509+
peer_state.channel_by_id.remove(&channel_id);
84818510
}
84828511
}
84838512
} else {
84848513
for (_cp_id, peer_state_mutex) in per_peer_state.iter() {
84858514
let mut peer_state_lock = peer_state_mutex.lock().unwrap();
84868515
let peer_state = &mut *peer_state_lock;
8487-
for (_, chan) in peer_state.channel_by_id.iter_mut() {
8488-
unblock_chan(chan, &mut peer_state.pending_msg_events);
8489-
}
8516+
peer_state.channel_by_id.retain(|_, chan| {
8517+
let should_remove = unblock_chan(chan, &mut peer_state.pending_msg_events);
8518+
if should_remove {
8519+
log_trace!(self.logger, "Removing channel after unblocking signer");
8520+
}
8521+
!should_remove
8522+
});
84908523
}
84918524
}
84928525
}

0 commit comments

Comments
 (0)