Skip to content

Commit b38ae32

Browse files
committed
Allow sending closing tx signatures asynchronously
1 parent 7162ad1 commit b38ae32

File tree

2 files changed

+95
-34
lines changed

2 files changed

+95
-34
lines changed

lightning/src/ln/channel.rs

Lines changed: 55 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -909,6 +909,8 @@ pub(super) struct SignerResumeUpdates {
909909
pub commitment_update: Option<msgs::CommitmentUpdate>,
910910
pub funding_signed: Option<msgs::FundingSigned>,
911911
pub channel_ready: Option<msgs::ChannelReady>,
912+
pub closing_signed: Option<msgs::ClosingSigned>,
913+
pub signed_closing_tx: Option<Transaction>,
912914
}
913915

914916
/// The return value of `channel_reestablish`
@@ -1227,6 +1229,9 @@ pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
12271229
/// [`msgs::FundingCreated`] or [`msgs::FundingSigned`] depending on if this channel is
12281230
/// outbound or inbound.
12291231
signer_pending_funding: bool,
1232+
/// If we attempted to sign a cooperative close transaction but the signer wasn't ready, then this
1233+
/// will be set to `true`.
1234+
signer_pending_closing: bool,
12301235

12311236
// pending_update_fee is filled when sending and receiving update_fee.
12321237
//
@@ -1258,7 +1263,9 @@ pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
12581263
/// Max to_local and to_remote outputs in a remote-generated commitment transaction
12591264
counterparty_max_commitment_tx_output: Mutex<(u64, u64)>,
12601265

1261-
last_sent_closing_fee: Option<(u64, Signature)>, // (fee, holder_sig)
1266+
// (fee, skip_remote_output, fee_range, holder_sig)
1267+
last_sent_closing_fee: Option<(u64, bool, ClosingSignedFeeRange, Option<Signature>)>,
1268+
last_received_closing_sig: Option<Signature>,
12621269
target_closing_feerate_sats_per_kw: Option<u32>,
12631270

12641271
/// If our counterparty sent us a closing_signed while we were waiting for a `ChannelMonitor`
@@ -1686,6 +1693,7 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
16861693

16871694
signer_pending_commitment_update: false,
16881695
signer_pending_funding: false,
1696+
signer_pending_closing: false,
16891697

16901698

16911699
#[cfg(debug_assertions)]
@@ -1694,6 +1702,7 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
16941702
counterparty_max_commitment_tx_output: Mutex::new((value_to_self_msat, (channel_value_satoshis * 1000 - msg_push_msat).saturating_sub(value_to_self_msat))),
16951703

16961704
last_sent_closing_fee: None,
1705+
last_received_closing_sig: None,
16971706
pending_counterparty_closing_signed: None,
16981707
expecting_peer_commitment_signed: false,
16991708
closing_fee_limits: None,
@@ -1911,6 +1920,7 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
19111920

19121921
signer_pending_commitment_update: false,
19131922
signer_pending_funding: false,
1923+
signer_pending_closing: false,
19141924

19151925
// We'll add our counterparty's `funding_satoshis` to these max commitment output assertions
19161926
// when we receive `accept_channel2`.
@@ -1920,6 +1930,7 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
19201930
counterparty_max_commitment_tx_output: Mutex::new((channel_value_satoshis * 1000 - push_msat, push_msat)),
19211931

19221932
last_sent_closing_fee: None,
1933+
last_received_closing_sig: None,
19231934
pending_counterparty_closing_signed: None,
19241935
expecting_peer_commitment_signed: false,
19251936
closing_fee_limits: None,
@@ -5387,15 +5398,35 @@ impl<SP: Deref> Channel<SP> where
53875398
self.check_get_channel_ready(0, logger)
53885399
} else { None };
53895400

5390-
log_trace!(logger, "Signer unblocked with {} commitment_update, {} funding_signed and {} channel_ready",
5401+
let (closing_signed, signed_closing_tx) = if self.context.signer_pending_closing {
5402+
debug_assert!(self.context.last_sent_closing_fee.is_some());
5403+
if let Some((fee, skip_remote_output, fee_range, holder_sig)) = self.context.last_sent_closing_fee.clone() {
5404+
debug_assert!(holder_sig.is_none());
5405+
log_trace!(logger, "Attempting to generate pending closing_signed...");
5406+
let (closing_tx, fee) = self.build_closing_transaction(fee, skip_remote_output);
5407+
let closing_signed = self.get_closing_signed_msg(&closing_tx, skip_remote_output,
5408+
fee, fee_range.min_fee_satoshis, fee_range.max_fee_satoshis, logger);
5409+
let signed_tx = if let (Some(ClosingSigned { signature, .. }), Some(counterparty_sig)) =
5410+
(closing_signed.as_ref(), self.context.last_received_closing_sig) {
5411+
Some(self.build_signed_closing_transaction(&closing_tx, &counterparty_sig, signature))
5412+
} else { None };
5413+
(closing_signed, signed_tx)
5414+
} else { (None, None) }
5415+
} else { (None, None) };
5416+
5417+
log_trace!(logger, "Signer unblocked with {} commitment_update, {} funding_signed, {} channel_ready, {} closing_signed, and {} signed_closing_tx",
53915418
if commitment_update.is_some() { "a" } else { "no" },
53925419
if funding_signed.is_some() { "a" } else { "no" },
5393-
if channel_ready.is_some() { "a" } else { "no" });
5420+
if channel_ready.is_some() { "a" } else { "no" },
5421+
if closing_signed.is_some() { "a" } else { "no" },
5422+
if signed_closing_tx.is_some() { "a" } else { "no" });
53945423

53955424
SignerResumeUpdates {
53965425
commitment_update,
53975426
funding_signed,
53985427
channel_ready,
5428+
closing_signed,
5429+
signed_closing_tx,
53995430
}
54005431
}
54015432

@@ -5801,9 +5832,6 @@ impl<SP: Deref> Channel<SP> where
58015832
our_min_fee, our_max_fee, total_fee_satoshis);
58025833

58035834
let closing_signed = self.get_closing_signed_msg(&closing_tx, false, total_fee_satoshis, our_min_fee, our_max_fee, logger);
5804-
if closing_signed.is_none() {
5805-
return Err(ChannelError::close("Failed to get signature for closing transaction.".to_owned()));
5806-
}
58075835
Ok((closing_signed, None, None))
58085836
}
58095837

@@ -5957,26 +5985,26 @@ impl<SP: Deref> Channel<SP> where
59575985
) -> Option<msgs::ClosingSigned>
59585986
where L::Target: Logger
59595987
{
5960-
match &self.context.holder_signer {
5961-
ChannelSignerType::Ecdsa(ecdsa) => {
5962-
let fee_range = msgs::ClosingSignedFeeRange {
5963-
min_fee_satoshis,
5964-
max_fee_satoshis,
5965-
};
5966-
let sig = ecdsa.sign_closing_transaction(closing_tx, &self.context.secp_ctx).ok()?;
5967-
5968-
self.context.last_sent_closing_fee = Some((fee_satoshis, sig.clone()));
5969-
Some(msgs::ClosingSigned {
5970-
channel_id: self.context.channel_id,
5971-
fee_satoshis,
5972-
signature: sig,
5973-
fee_range: Some(fee_range),
5974-
})
5975-
},
5988+
let sig = match &self.context.holder_signer {
5989+
ChannelSignerType::Ecdsa(ecdsa) => ecdsa.sign_closing_transaction(closing_tx, &self.context.secp_ctx).ok(),
59765990
// TODO (taproot|arik)
59775991
#[cfg(taproot)]
59785992
_ => todo!()
5993+
};
5994+
if sig.is_none() {
5995+
log_trace!(logger, "Closing transaction signature unavailable, waiting on signer");
5996+
self.context.signer_pending_closing = true;
5997+
} else {
5998+
self.context.signer_pending_closing = false;
59795999
}
6000+
let fee_range = msgs::ClosingSignedFeeRange { min_fee_satoshis, max_fee_satoshis };
6001+
self.context.last_sent_closing_fee = Some((fee_satoshis, skip_remote_output, fee_range.clone(), sig.clone()));
6002+
sig.map(|signature| msgs::ClosingSigned {
6003+
channel_id: self.context.channel_id,
6004+
fee_satoshis,
6005+
signature,
6006+
fee_range: Some(fee_range),
6007+
})
59806008
}
59816009

59826010
pub fn closing_signed<F: Deref, L: Deref>(
@@ -6039,7 +6067,7 @@ impl<SP: Deref> Channel<SP> where
60396067
};
60406068

60416069
assert!(self.context.shutdown_scriptpubkey.is_some());
6042-
if let Some((last_fee, sig)) = self.context.last_sent_closing_fee {
6070+
if let Some((last_fee, _, _, Some(sig))) = self.context.last_sent_closing_fee {
60436071
if last_fee == msg.fee_satoshis {
60446072
let shutdown_result = ShutdownResult {
60456073
closure_reason,
@@ -6072,9 +6100,6 @@ impl<SP: Deref> Channel<SP> where
60726100
};
60736101

60746102
let closing_signed = self.get_closing_signed_msg(&closing_tx, skip_remote_output, used_fee, our_min_fee, our_max_fee, logger);
6075-
if closing_signed.is_none() {
6076-
return Err(ChannelError::close("Failed to get signature for closing transaction.".to_owned()));
6077-
}
60786103
let (signed_tx, shutdown_result) = if $new_fee == msg.fee_satoshis {
60796104
let shutdown_result = ShutdownResult {
60806105
closure_reason,
@@ -6090,6 +6115,7 @@ impl<SP: Deref> Channel<SP> where
60906115
};
60916116
self.context.channel_state = ChannelState::ShutdownComplete;
60926117
self.context.update_time_counter += 1;
6118+
self.context.last_received_closing_sig = Some(msg.signature.clone());
60936119
let tx = closing_signed.as_ref().map(|ClosingSigned { signature, .. }|
60946120
self.build_signed_closing_transaction(&closing_tx, &msg.signature, signature));
60956121
(tx, Some(shutdown_result))
@@ -6127,7 +6153,7 @@ impl<SP: Deref> Channel<SP> where
61276153
} else {
61286154
// Old fee style negotiation. We don't bother to enforce whether they are complying
61296155
// with the "making progress" requirements, we just comply and hope for the best.
6130-
if let Some((last_fee, _)) = self.context.last_sent_closing_fee {
6156+
if let Some((last_fee, _, _, _)) = self.context.last_sent_closing_fee {
61316157
if msg.fee_satoshis > last_fee {
61326158
if msg.fee_satoshis < our_max_fee {
61336159
propose_fee!(msg.fee_satoshis);
@@ -9282,6 +9308,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
92829308

92839309
signer_pending_commitment_update: false,
92849310
signer_pending_funding: false,
9311+
signer_pending_closing: false,
92859312

92869313
pending_update_fee,
92879314
holding_cell_update_fee,
@@ -9296,6 +9323,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
92969323
counterparty_max_commitment_tx_output: Mutex::new((0, 0)),
92979324

92989325
last_sent_closing_fee: None,
9326+
last_received_closing_sig: None,
92999327
pending_counterparty_closing_signed: None,
93009328
expecting_peer_commitment_signed: false,
93019329
closing_fee_limits: None,

lightning/src/ln/channelmanager.rs

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

8195-
let unblock_chan = |phase: &mut ChannelPhase<SP>, pending_msg_events: &mut Vec<MessageSendEvent>| {
8195+
// Returns whether we should remove this channel as it's just been closed.
8196+
let unblock_chan = |phase: &mut ChannelPhase<SP>, pending_msg_events: &mut Vec<MessageSendEvent>| -> bool {
81968197
let node_id = phase.context().get_counterparty_node_id();
81978198
match phase {
81988199
ChannelPhase::Funded(chan) => {
@@ -8212,6 +8213,29 @@ where
82128213
if let Some(msg) = msgs.channel_ready {
82138214
send_channel_ready!(self, pending_msg_events, chan, msg);
82148215
}
8216+
if let Some(msg) = msgs.closing_signed {
8217+
pending_msg_events.push(events::MessageSendEvent::SendClosingSigned {
8218+
node_id,
8219+
msg,
8220+
});
8221+
}
8222+
if let Some(broadcast_tx) = msgs.signed_closing_tx {
8223+
let channel_id = chan.context.channel_id();
8224+
let counterparty_node_id = chan.context.get_counterparty_node_id();
8225+
let logger = WithContext::from(&self.logger, Some(counterparty_node_id), Some(channel_id), None);
8226+
log_info!(logger, "Broadcasting closing tx {}", log_tx!(broadcast_tx));
8227+
self.tx_broadcaster.broadcast_transactions(&[&broadcast_tx]);
8228+
8229+
if let Ok(update) = self.get_channel_update_for_broadcast(&chan) {
8230+
pending_msg_events.push(events::MessageSendEvent::BroadcastChannelUpdate {
8231+
msg: update
8232+
});
8233+
}
8234+
8235+
// We should return true to remove the channel if we just
8236+
// broadcasted the closing transaction.
8237+
true
8238+
} else { false }
82158239
}
82168240
ChannelPhase::UnfundedOutboundV1(chan) => {
82178241
if let Some(msg) = chan.signer_maybe_unblocked(&self.logger) {
@@ -8220,8 +8244,9 @@ where
82208244
msg,
82218245
});
82228246
}
8247+
false
82238248
}
8224-
ChannelPhase::UnfundedInboundV1(_) => {},
8249+
ChannelPhase::UnfundedInboundV1(_) => false,
82258250
}
82268251
};
82278252

@@ -8230,17 +8255,25 @@ where
82308255
if let Some(peer_state_mutex) = per_peer_state.get(&counterparty_node_id) {
82318256
let mut peer_state_lock = peer_state_mutex.lock().unwrap();
82328257
let peer_state = &mut *peer_state_lock;
8233-
if let Some(chan) = peer_state.channel_by_id.get_mut(&channel_id) {
8234-
unblock_chan(chan, &mut peer_state.pending_msg_events);
8258+
let should_remove = if let Some(chan) = peer_state.channel_by_id.get_mut(&channel_id) {
8259+
unblock_chan(chan, &mut peer_state.pending_msg_events)
8260+
} else { false };
8261+
if should_remove {
8262+
log_trace!(self.logger, "Removing channel after unblocking signer");
8263+
peer_state.channel_by_id.remove(&channel_id);
82358264
}
82368265
}
82378266
} else {
82388267
for (_cp_id, peer_state_mutex) in per_peer_state.iter() {
82398268
let mut peer_state_lock = peer_state_mutex.lock().unwrap();
82408269
let peer_state = &mut *peer_state_lock;
8241-
for (_, chan) in peer_state.channel_by_id.iter_mut() {
8242-
unblock_chan(chan, &mut peer_state.pending_msg_events);
8243-
}
8270+
peer_state.channel_by_id.retain(|_, chan| {
8271+
let should_remove = unblock_chan(chan, &mut peer_state.pending_msg_events);
8272+
if should_remove {
8273+
log_trace!(self.logger, "Removing channel after unblocking signer");
8274+
}
8275+
!should_remove
8276+
});
82448277
}
82458278
}
82468279
}

0 commit comments

Comments
 (0)