]> git.bitcoin.ninja Git - rust-lightning/commitdiff
Handle retrying sign_counterparty_commitment failures
authorMatt Corallo <git@bluematt.me>
Tue, 5 Sep 2023 22:06:53 +0000 (22:06 +0000)
committerChris Waterson <waterson@gmail.com>
Wed, 1 Nov 2023 22:24:14 +0000 (15:24 -0700)
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.

lightning/src/ln/channel.rs
lightning/src/ln/channelmanager.rs

index 332cd2a24b3b6ca25c8b2a1ca9e5485037169013..62a601f9347875781a84e09f6ae8eabcd389ea86 100644 (file)
@@ -533,6 +533,15 @@ pub(super) struct MonitorRestoreUpdates {
        pub announcement_sigs: Option<msgs::AnnouncementSignatures>,
 }
 
+/// The return value of `signer_maybe_unblocked`
+#[allow(unused)]
+pub(super) struct SignerResumeUpdates {
+       pub commitment_update: Option<msgs::CommitmentUpdate>,
+       pub funding_signed: Option<msgs::FundingSigned>,
+       pub funding_created: Option<msgs::FundingCreated>,
+       pub channel_ready: Option<msgs::ChannelReady>,
+}
+
 /// The return value of `channel_reestablish`
 pub(super) struct ReestablishResponses {
        pub channel_ready: Option<msgs::ChannelReady>,
@@ -3912,6 +3921,31 @@ impl<SP: Deref> Channel<SP> 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<L: Deref>(&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 channel_ready = None;
+               let funding_created = None;
+
+               log_trace!(logger, "Signer unblocked with {} commitment_update, {} funding_signed, {} funding_created, and {} channel_ready",
+                       if commitment_update.is_some() { "a" } else { "no" },
+                       if funding_signed.is_some() { "a" } else { "no" },
+                       if funding_created.is_some() { "a" } else { "no" },
+                       if channel_ready.is_some() { "a" } else { "no" });
+
+               SignerResumeUpdates {
+                       commitment_update,
+                       funding_signed,
+                       funding_created,
+                       channel_ready,
+               }
+       }
+
        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);
index e712cb5055fefb5ff9933433a5c07424b22afaf2..37a8feb446d6865bcdbe7849e9c1a854d318b4d9 100644 (file)
@@ -7227,6 +7227,66 @@ 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<SP>, pending_msg_events: &mut Vec<MessageSendEvent>| {
+                       let node_id = phase.context().get_counterparty_node_id();
+                       if let ChannelPhase::Funded(chan) = phase {
+                               let msgs = chan.signer_maybe_unblocked(&self.logger);
+                               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,
+                                       });
+                               }
+                               if let Some(msg) = msgs.channel_ready {
+                                       send_channel_ready!(self, pending_msg_events, chan, 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.