From 8d01309555097d361bc0f9b03e99e4f9b7a73d90 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Tue, 5 Sep 2023 22:10:34 +0000 Subject: [PATCH] Handle retrying sign_counterparty_commitment outb funding 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 retrying of outbound funding_created signing failures, regenerating the `FundingCreated` message, attempting to re-sign, and sending it to our peers if we succeed. --- lightning/src/ln/channel.rs | 69 +++++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 30 deletions(-) diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index 62a601f93..134be90ea 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -2100,6 +2100,35 @@ impl ChannelContext where SP::Target: SignerProvider { unbroadcasted_batch_funding_txid, } } + + /// Only allowed after [`Self::channel_transaction_parameters`] is set. + fn get_funding_created_msg(&mut self, logger: &L) -> Option where L::Target: Logger { + let counterparty_keys = self.build_remote_transaction_keys(); + let counterparty_initial_commitment_tx = self.build_commitment_transaction(self.cur_counterparty_commitment_transaction_number, &counterparty_keys, false, false, logger).tx; + let signature = match &self.holder_signer { + // TODO (taproot|arik): move match into calling method for Taproot + ChannelSignerType::Ecdsa(ecdsa) => { + ecdsa.sign_counterparty_commitment(&counterparty_initial_commitment_tx, Vec::new(), &self.secp_ctx) + .map(|(sig, _)| sig).ok()? + } + }; + + if self.signer_pending_funding { + log_trace!(logger, "Counterparty commitment signature ready for funding_created message: clearing signer_pending_funding"); + self.signer_pending_funding = false; + } + + Some(msgs::FundingCreated { + temporary_channel_id: self.temporary_channel_id.unwrap(), + funding_txid: self.channel_transaction_parameters.funding_outpoint.as_ref().unwrap().txid, + funding_output_index: self.channel_transaction_parameters.funding_outpoint.as_ref().unwrap().index, + signature, + #[cfg(taproot)] + partial_signature_with_nonce: None, + #[cfg(taproot)] + next_local_nonce: None, + }) + } } // Internal utility functions for channels @@ -3930,7 +3959,9 @@ impl Channel where } else { None }; let funding_signed = None; let channel_ready = None; - let funding_created = None; + let funding_created = if self.context.signer_pending_funding && self.context.is_outbound() { + self.context.get_funding_created_msg(logger) + } else { None }; log_trace!(logger, "Signer unblocked with {} commitment_update, {} funding_signed, {} funding_created, and {} channel_ready", if commitment_update.is_some() { "a" } else { "no" }, @@ -6000,18 +6031,6 @@ impl OutboundV1Channel where SP::Target: SignerProvider { }) } - fn get_funding_created_signature(&mut self, logger: &L) -> Result where L::Target: Logger { - let counterparty_keys = self.context.build_remote_transaction_keys(); - let counterparty_initial_commitment_tx = self.context.build_commitment_transaction(self.context.cur_counterparty_commitment_transaction_number, &counterparty_keys, false, false, logger).tx; - match &self.context.holder_signer { - // TODO (taproot|arik): move match into calling method for Taproot - ChannelSignerType::Ecdsa(ecdsa) => { - ecdsa.sign_counterparty_commitment(&counterparty_initial_commitment_tx, Vec::new(), &self.context.secp_ctx) - .map(|(sig, _)| sig) - } - } - } - /// Updates channel state with knowledge of the funding transaction's txid/index, and generates /// a funding_created message for the remote peer. /// Panics if called at some time other than immediately after initial handshake, if called twice, @@ -6036,8 +6055,6 @@ impl OutboundV1Channel where SP::Target: SignerProvider { self.context.channel_transaction_parameters.funding_outpoint = Some(funding_txo); self.context.holder_signer.as_mut().provide_channel_parameters(&self.context.channel_transaction_parameters); - let temporary_channel_id = self.context.channel_id; - // Now that we're past error-generating stuff, update our local state: self.context.channel_state = ChannelState::FundingCreated as u32; @@ -6054,21 +6071,13 @@ impl OutboundV1Channel where SP::Target: SignerProvider { self.context.funding_transaction = Some(funding_transaction); self.context.is_batch_funding = Some(()).filter(|_| is_batch_funding); - let funding_created = if let Ok(signature) = self.get_funding_created_signature(logger) { - Some(msgs::FundingCreated { - temporary_channel_id, - funding_txid: funding_txo.txid, - funding_output_index: funding_txo.index, - signature, - #[cfg(taproot)] - partial_signature_with_nonce: None, - #[cfg(taproot)] - next_local_nonce: None, - }) - } else { - self.context.signer_pending_funding = true; - None - }; + let funding_created = self.context.get_funding_created_msg(logger); + if funding_created.is_none() { + if !self.context.signer_pending_funding { + log_trace!(logger, "funding_created awaiting signer; setting signer_pending_funding"); + self.context.signer_pending_funding = true; + } + } let channel = Channel { context: self.context, -- 2.39.5