Skip to content

Commit 48aafb5

Browse files
committed
Handle retrying sign_counterparty_commitment inb 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 inbound funding_created signing failures, regenerating the `FundingSigned` message, attempting to re-sign, and sending it to our peers if we succeed.
1 parent 400ad00 commit 48aafb5

File tree

1 file changed

+55
-52
lines changed

1 file changed

+55
-52
lines changed

lightning/src/ln/channel.rs

Lines changed: 55 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -2021,6 +2021,36 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
20212021
next_local_nonce: None,
20222022
})
20232023
}
2024+
2025+
/// Only allowed after [`Self::channel_transaction_parameters`] is set.
2026+
fn get_funding_signed_msg<L: Deref>(&mut self, logger: &L) -> (CommitmentTransaction, Option<msgs::FundingSigned>) where L::Target: Logger {
2027+
let counterparty_keys = self.build_remote_transaction_keys();
2028+
let counterparty_initial_commitment_tx = self.build_commitment_transaction(self.cur_counterparty_commitment_transaction_number + 1, &counterparty_keys, false, false, logger).tx;
2029+
2030+
let counterparty_trusted_tx = counterparty_initial_commitment_tx.trust();
2031+
let counterparty_initial_bitcoin_tx = counterparty_trusted_tx.built_transaction();
2032+
log_trace!(logger, "Initial counterparty tx for channel {} is: txid {} tx {}",
2033+
&self.channel_id(), counterparty_initial_bitcoin_tx.txid, encode::serialize_hex(&counterparty_initial_bitcoin_tx.transaction));
2034+
2035+
match &self.holder_signer {
2036+
// TODO (arik): move match into calling method for Taproot
2037+
ChannelSignerType::Ecdsa(ecdsa) => {
2038+
let funding_signed = ecdsa.sign_counterparty_commitment(&counterparty_initial_commitment_tx, Vec::new(), &self.secp_ctx)
2039+
.map(|(signature, _)| msgs::FundingSigned {
2040+
channel_id: self.channel_id(),
2041+
signature,
2042+
#[cfg(taproot)]
2043+
partial_signature_with_nonce: None,
2044+
})
2045+
.ok();
2046+
self.signer_pending_funding = funding_signed.is_none();
2047+
2048+
// We sign "counterparty" commitment transaction, allowing them to broadcast the tx if they wish.
2049+
(counterparty_initial_commitment_tx, funding_signed)
2050+
}
2051+
}
2052+
}
2053+
20242054
}
20252055

20262056
// Internal utility functions for channels
@@ -3830,7 +3860,9 @@ impl<SP: Deref> Channel<SP> where
38303860
let commitment_update = if self.context.signer_pending_commitment_update {
38313861
None
38323862
} else { None };
3833-
let funding_signed = None;
3863+
let funding_signed = if self.context.signer_pending_funding && !self.context.is_outbound() {
3864+
self.context.get_funding_signed_msg(logger).1
3865+
} else { None };
38343866
let funding_created = if self.context.signer_pending_funding && self.context.is_outbound() {
38353867
self.context.get_funding_created_msg(logger)
38363868
} else { None };
@@ -6547,41 +6579,22 @@ impl<SP: Deref> InboundV1Channel<SP> where SP::Target: SignerProvider {
65476579
self.generate_accept_channel_message()
65486580
}
65496581

6550-
fn funding_created_signature<L: Deref>(&mut self, sig: &Signature, logger: &L) -> Result<(CommitmentTransaction, CommitmentTransaction, Option<Signature>), ChannelError> where L::Target: Logger {
6582+
fn check_funding_created_signature<L: Deref>(&mut self, sig: &Signature, logger: &L) -> Result<CommitmentTransaction, ChannelError> where L::Target: Logger {
65516583
let funding_script = self.context.get_funding_redeemscript();
65526584

65536585
let keys = self.context.build_holder_transaction_keys(self.context.cur_holder_commitment_transaction_number);
65546586
let initial_commitment_tx = self.context.build_commitment_transaction(self.context.cur_holder_commitment_transaction_number, &keys, true, false, logger).tx;
6555-
{
6556-
let trusted_tx = initial_commitment_tx.trust();
6557-
let initial_commitment_bitcoin_tx = trusted_tx.built_transaction();
6558-
let sighash = initial_commitment_bitcoin_tx.get_sighash_all(&funding_script, self.context.channel_value_satoshis);
6559-
// They sign the holder commitment transaction...
6560-
log_trace!(logger, "Checking funding_created tx signature {} by key {} against tx {} (sighash {}) with redeemscript {} for channel {}.",
6561-
log_bytes!(sig.serialize_compact()[..]), log_bytes!(self.context.counterparty_funding_pubkey().serialize()),
6562-
encode::serialize_hex(&initial_commitment_bitcoin_tx.transaction), log_bytes!(sighash[..]),
6563-
encode::serialize_hex(&funding_script), &self.context.channel_id());
6564-
secp_check!(self.context.secp_ctx.verify_ecdsa(&sighash, &sig, self.context.counterparty_funding_pubkey()), "Invalid funding_created signature from peer".to_owned());
6565-
}
6566-
6567-
let counterparty_keys = self.context.build_remote_transaction_keys();
6568-
let counterparty_initial_commitment_tx = self.context.build_commitment_transaction(self.context.cur_counterparty_commitment_transaction_number, &counterparty_keys, false, false, logger).tx;
6569-
6570-
let counterparty_trusted_tx = counterparty_initial_commitment_tx.trust();
6571-
let counterparty_initial_bitcoin_tx = counterparty_trusted_tx.built_transaction();
6572-
log_trace!(logger, "Initial counterparty tx for channel {} is: txid {} tx {}",
6573-
&self.context.channel_id(), counterparty_initial_bitcoin_tx.txid, encode::serialize_hex(&counterparty_initial_bitcoin_tx.transaction));
6574-
6575-
match &self.context.holder_signer {
6576-
// TODO (arik): move match into calling method for Taproot
6577-
ChannelSignerType::Ecdsa(ecdsa) => {
6578-
let counterparty_signature = ecdsa.sign_counterparty_commitment(&counterparty_initial_commitment_tx, Vec::new(), &self.context.secp_ctx)
6579-
.map(|(sig, _)| sig).ok();
6587+
let trusted_tx = initial_commitment_tx.trust();
6588+
let initial_commitment_bitcoin_tx = trusted_tx.built_transaction();
6589+
let sighash = initial_commitment_bitcoin_tx.get_sighash_all(&funding_script, self.context.channel_value_satoshis);
6590+
// They sign the holder commitment transaction...
6591+
log_trace!(logger, "Checking funding_created tx signature {} by key {} against tx {} (sighash {}) with redeemscript {} for channel {}.",
6592+
log_bytes!(sig.serialize_compact()[..]), log_bytes!(self.context.counterparty_funding_pubkey().serialize()),
6593+
encode::serialize_hex(&initial_commitment_bitcoin_tx.transaction), log_bytes!(sighash[..]),
6594+
encode::serialize_hex(&funding_script), &self.context.channel_id());
6595+
secp_check!(self.context.secp_ctx.verify_ecdsa(&sighash, &sig, self.context.counterparty_funding_pubkey()), "Invalid funding_created signature from peer".to_owned());
65806596

6581-
// We sign "counterparty" commitment transaction, allowing them to broadcast the tx if they wish.
6582-
Ok((counterparty_initial_commitment_tx, initial_commitment_tx, counterparty_signature))
6583-
}
6584-
}
6597+
Ok(initial_commitment_tx)
65856598
}
65866599

65876600
pub fn funding_created<L: Deref>(
@@ -6608,10 +6621,10 @@ impl<SP: Deref> InboundV1Channel<SP> where SP::Target: SignerProvider {
66086621
let funding_txo = OutPoint { txid: msg.funding_txid, index: msg.funding_output_index };
66096622
self.context.channel_transaction_parameters.funding_outpoint = Some(funding_txo);
66106623
// This is an externally observable change before we finish all our checks. In particular
6611-
// funding_created_signature may fail.
6624+
// check_funding_created_signature may fail.
66126625
self.context.holder_signer.as_mut().provide_channel_parameters(&self.context.channel_transaction_parameters);
66136626

6614-
let (counterparty_initial_commitment_tx, initial_commitment_tx, sig_opt) = match self.funding_created_signature(&msg.signature, logger) {
6627+
let initial_commitment_tx = match self.check_funding_created_signature(&msg.signature, logger) {
66156628
Ok(res) => res,
66166629
Err(ChannelError::Close(e)) => {
66176630
self.context.channel_transaction_parameters.funding_outpoint = None;
@@ -6620,7 +6633,7 @@ impl<SP: Deref> InboundV1Channel<SP> where SP::Target: SignerProvider {
66206633
Err(e) => {
66216634
// The only error we know how to handle is ChannelError::Close, so we fall over here
66226635
// to make sure we don't continue with an inconsistent state.
6623-
panic!("unexpected error type from funding_created_signature {:?}", e);
6636+
panic!("unexpected error type from check_funding_created_signature {:?}", e);
66246637
}
66256638
};
66266639

@@ -6636,6 +6649,13 @@ impl<SP: Deref> InboundV1Channel<SP> where SP::Target: SignerProvider {
66366649
return Err((self, ChannelError::Close("Failed to validate our commitment".to_owned())));
66376650
}
66386651

6652+
self.context.channel_state = ChannelState::FundingSent as u32;
6653+
self.context.channel_id = funding_txo.to_channel_id();
6654+
self.context.cur_counterparty_commitment_transaction_number -= 1;
6655+
self.context.cur_holder_commitment_transaction_number -= 1;
6656+
6657+
let (counterparty_initial_commitment_tx, funding_signed) = self.context.get_funding_signed_msg(logger);
6658+
66396659
// Now that we're past error-generating stuff, update our local state:
66406660

66416661
let funding_redeemscript = self.context.get_funding_redeemscript();
@@ -6654,16 +6674,11 @@ impl<SP: Deref> InboundV1Channel<SP> where SP::Target: SignerProvider {
66546674

66556675
channel_monitor.provide_initial_counterparty_commitment_tx(
66566676
counterparty_initial_commitment_tx.trust().txid(), Vec::new(),
6657-
self.context.cur_counterparty_commitment_transaction_number,
6677+
self.context.cur_counterparty_commitment_transaction_number + 1,
66586678
self.context.counterparty_cur_commitment_point.unwrap(), self.context.feerate_per_kw,
66596679
counterparty_initial_commitment_tx.to_broadcaster_value_sat(),
66606680
counterparty_initial_commitment_tx.to_countersignatory_value_sat(), logger);
66616681

6662-
self.context.channel_state = ChannelState::FundingSent as u32;
6663-
self.context.channel_id = funding_txo.to_channel_id();
6664-
self.context.cur_counterparty_commitment_transaction_number -= 1;
6665-
self.context.cur_holder_commitment_transaction_number -= 1;
6666-
66676682
log_info!(logger, "Generated funding_signed for peer for channel {}", &self.context.channel_id());
66686683

66696684
// Promote the channel to a full-fledged one now that we have updated the state and have a
@@ -6675,18 +6690,6 @@ impl<SP: Deref> InboundV1Channel<SP> where SP::Target: SignerProvider {
66756690
let need_channel_ready = channel.check_get_channel_ready(0).is_some();
66766691
channel.monitor_updating_paused(false, false, need_channel_ready, Vec::new(), Vec::new(), Vec::new());
66776692

6678-
let funding_signed = if let Some(signature) = sig_opt {
6679-
Some(msgs::FundingSigned {
6680-
channel_id,
6681-
signature,
6682-
#[cfg(taproot)]
6683-
partial_signature_with_nonce: None,
6684-
})
6685-
} else {
6686-
channel.context.signer_pending_funding = true;
6687-
None
6688-
};
6689-
66906693
Ok((channel, funding_signed, channel_monitor))
66916694
}
66926695
}

0 commit comments

Comments
 (0)