Handle retrying sign_counterparty_commitment failures
authorMatt Corallo <git@bluematt.me>
Tue, 5 Sep 2023 22:06:53 +0000 (22:06 +0000)
committerMatt Corallo <git@bluematt.me>
Tue, 5 Sep 2023 22:09:49 +0000 (22:09 +0000)
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 bbaa751f4a7a2f5780dd7cc022d3e42f05d65310..b483a8564e086165cce0eca2d131e99b34228949 100644 (file)
@@ -515,6 +515,13 @@ pub(super) struct MonitorRestoreUpdates {
        pub announcement_sigs: Option<msgs::AnnouncementSignatures>,
 }
 
+/// The return value of `signer_maybe_unblocked`
+pub(super) struct SignerResumeUpdates {
+       pub commitment_update: Option<msgs::CommitmentUpdate>,
+       pub funding_signed: Option<msgs::FundingSigned>,
+       pub funding_created: Option<msgs::FundingCreated>,
+}
+
 /// The return value of `channel_reestablish`
 pub(super) struct ReestablishResponses {
        pub channel_ready: Option<msgs::ChannelReady>,
@@ -3792,6 +3799,21 @@ 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.
+       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 {
+                       None
+               } 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);
index bcfca4cc246625f6b57f455e22cfce69d31cf1de..e087f8856a843415f712286a397dd0f81e297acd 100644 (file)
@@ -6479,6 +6479,58 @@ 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.
+       #[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 = |chan: &mut Channel<SP>, pending_msg_events: &mut Vec<MessageSendEvent>| {
+                       let msgs = chan.signer_maybe_unblocked(&self.logger);
+                       if let Some(updates) = msgs.commitment_update {
+                               pending_msg_events.push(events::MessageSendEvent::UpdateHTLCs {
+                                       node_id: chan.context.get_counterparty_node_id(),
+                                       updates,
+                               });
+                       }
+                       if let Some(msg) = msgs.funding_signed {
+                               pending_msg_events.push(events::MessageSendEvent::SendFundingSigned {
+                                       node_id: chan.context.get_counterparty_node_id(),
+                                       msg,
+                               });
+                       }
+                       if let Some(msg) = msgs.funding_created {
+                               pending_msg_events.push(events::MessageSendEvent::SendFundingCreated {
+                                       node_id: chan.context.get_counterparty_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.