From: Matt Corallo Date: Tue, 5 Sep 2023 22:06:53 +0000 (+0000) Subject: Handle retrying sign_counterparty_commitment failures X-Git-Url: http://git.bitcoin.ninja/?a=commitdiff_plain;h=4e7c168948e76d37f8a5a74a70975d7dd7fa29ed;p=rust-lightning Handle retrying sign_counterparty_commitment failures If sign_counterparty_commitment fails (i.e. because the signer is temporarily disconnected), this really indicates that we should retry the message sending which required the signature later, rather than force-closing the channel (which probably won't even work if the signer is missing). This commit adds initial retrying of failures, specifically regenerating commitment updates, attempting to re-sign the `CommitmentSigned` message, and sending it to our peers if we succed. --- diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index 81f453243..afe78a6bc 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -533,6 +533,14 @@ pub(super) struct MonitorRestoreUpdates { pub announcement_sigs: Option, } +/// The return value of `signer_maybe_unblocked` +#[allow(unused)] +pub(super) struct SignerResumeUpdates { + pub commitment_update: Option, + pub funding_signed: Option, + pub funding_created: Option, +} + /// The return value of `channel_reestablish` pub(super) struct ReestablishResponses { pub channel_ready: Option, @@ -3909,6 +3917,22 @@ impl Channel where Ok(()) } + /// Indicates that the signer may have some signatures for us, so we should retry if we're + /// blocked. + #[allow(unused)] + pub fn signer_maybe_unblocked(&mut self, logger: &L) -> SignerResumeUpdates where L::Target: Logger { + let commitment_update = if self.context.signer_pending_commitment_update { + self.get_last_commitment_update_for_send(logger).ok() + } else { None }; + let funding_signed = None; + let funding_created = None; + SignerResumeUpdates { + commitment_update, + funding_signed, + funding_created, + } + } + fn get_last_revoke_and_ack(&self) -> msgs::RevokeAndACK { let next_per_commitment_point = self.context.holder_signer.as_ref().get_per_commitment_point(self.context.cur_holder_commitment_transaction_number, &self.context.secp_ctx); let per_commitment_secret = self.context.holder_signer.as_ref().release_commitment_secret(self.context.cur_holder_commitment_transaction_number + 2); diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 56820a6c4..5e1604b3f 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -7231,6 +7231,63 @@ where has_update } + /// When a call to a [`ChannelSigner`] method returns an error, this indicates that the signer + /// is (temporarily) unavailable, and the operation should be retried later. + /// + /// This method allows for that retry - either checking for any signer-pending messages to be + /// attempted in every channel, or in the specifically provided channel. + /// + /// [`ChannelSigner`]: crate::sign::ChannelSigner + #[cfg(test)] // This is only implemented for one signer method, and should be private until we + // actually finish implementing it fully. + pub fn signer_unblocked(&self, channel_opt: Option<(PublicKey, ChannelId)>) { + let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self); + + let unblock_chan = |phase: &mut ChannelPhase, pending_msg_events: &mut Vec| { + if let ChannelPhase::Funded(chan) = phase { + let msgs = chan.signer_maybe_unblocked(&self.logger); + let node_id = phase.context().get_counterparty_node_id(); + if let Some(updates) = msgs.commitment_update { + pending_msg_events.push(events::MessageSendEvent::UpdateHTLCs { + node_id, + updates, + }); + } + if let Some(msg) = msgs.funding_signed { + pending_msg_events.push(events::MessageSendEvent::SendFundingSigned { + node_id, + msg, + }); + } + if let Some(msg) = msgs.funding_created { + pending_msg_events.push(events::MessageSendEvent::SendFundingCreated { + node_id, + msg, + }); + } + } + }; + + let per_peer_state = self.per_peer_state.read().unwrap(); + if let Some((counterparty_node_id, channel_id)) = channel_opt { + if let Some(peer_state_mutex) = per_peer_state.get(&counterparty_node_id) { + let mut peer_state_lock = peer_state_mutex.lock().unwrap(); + let peer_state = &mut *peer_state_lock; + if let Some(chan) = peer_state.channel_by_id.get_mut(&channel_id) { + unblock_chan(chan, &mut peer_state.pending_msg_events); + } + } + } else { + for (_cp_id, peer_state_mutex) in per_peer_state.iter() { + let mut peer_state_lock = peer_state_mutex.lock().unwrap(); + let peer_state = &mut *peer_state_lock; + for (_, chan) in peer_state.channel_by_id.iter_mut() { + unblock_chan(chan, &mut peer_state.pending_msg_events); + } + } + } + } + /// Check whether any channels have finished removing all pending updates after a shutdown /// exchange and can now send a closing_signed. /// Returns whether any closing_signed messages were generated.