Skip to content

Commit 257c6a0

Browse files
TheBlueMattwaterson
authored andcommitted
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 ebae20c commit 257c6a0

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
@@ -2109,6 +2109,36 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
21092109
next_local_nonce: None,
21102110
})
21112111
}
2112+
2113+
/// Only allowed after [`Self::channel_transaction_parameters`] is set.
2114+
fn get_funding_signed_msg<L: Deref>(&mut self, logger: &L) -> (CommitmentTransaction, Option<msgs::FundingSigned>) where L::Target: Logger {
2115+
let counterparty_keys = self.build_remote_transaction_keys();
2116+
let counterparty_initial_commitment_tx = self.build_commitment_transaction(self.cur_counterparty_commitment_transaction_number + 1, &counterparty_keys, false, false, logger).tx;
2117+
2118+
let counterparty_trusted_tx = counterparty_initial_commitment_tx.trust();
2119+
let counterparty_initial_bitcoin_tx = counterparty_trusted_tx.built_transaction();
2120+
log_trace!(logger, "Initial counterparty tx for channel {} is: txid {} tx {}",
2121+
&self.channel_id(), counterparty_initial_bitcoin_tx.txid, encode::serialize_hex(&counterparty_initial_bitcoin_tx.transaction));
2122+
2123+
match &self.holder_signer {
2124+
// TODO (arik): move match into calling method for Taproot
2125+
ChannelSignerType::Ecdsa(ecdsa) => {
2126+
let funding_signed = ecdsa.sign_counterparty_commitment(&counterparty_initial_commitment_tx, Vec::new(), &self.secp_ctx)
2127+
.map(|(signature, _)| msgs::FundingSigned {
2128+
channel_id: self.channel_id(),
2129+
signature,
2130+
#[cfg(taproot)]
2131+
partial_signature_with_nonce: None,
2132+
})
2133+
.ok();
2134+
self.signer_pending_funding = funding_signed.is_none();
2135+
2136+
// We sign "counterparty" commitment transaction, allowing them to broadcast the tx if they wish.
2137+
(counterparty_initial_commitment_tx, funding_signed)
2138+
}
2139+
}
2140+
}
2141+
21122142
}
21132143

21142144
// Internal utility functions for channels
@@ -3933,7 +3963,9 @@ impl<SP: Deref> Channel<SP> where
39333963
let commitment_update = if self.context.signer_pending_commitment_update {
39343964
None
39353965
} else { None };
3936-
let funding_signed = None;
3966+
let funding_signed = if self.context.signer_pending_funding && !self.context.is_outbound() {
3967+
self.context.get_funding_signed_msg(logger).1
3968+
} else { None };
39373969
let funding_created = if self.context.signer_pending_funding && self.context.is_outbound() {
39383970
self.context.get_funding_created_msg(logger)
39393971
} else { None };
@@ -6659,41 +6691,22 @@ impl<SP: Deref> InboundV1Channel<SP> where SP::Target: SignerProvider {
66596691
self.generate_accept_channel_message()
66606692
}
66616693

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

66656697
let keys = self.context.build_holder_transaction_keys(self.context.cur_holder_commitment_transaction_number);
66666698
let initial_commitment_tx = self.context.build_commitment_transaction(self.context.cur_holder_commitment_transaction_number, &keys, true, false, logger).tx;
6667-
{
6668-
let trusted_tx = initial_commitment_tx.trust();
6669-
let initial_commitment_bitcoin_tx = trusted_tx.built_transaction();
6670-
let sighash = initial_commitment_bitcoin_tx.get_sighash_all(&funding_script, self.context.channel_value_satoshis);
6671-
// They sign the holder commitment transaction...
6672-
log_trace!(logger, "Checking funding_created tx signature {} by key {} against tx {} (sighash {}) with redeemscript {} for channel {}.",
6673-
log_bytes!(sig.serialize_compact()[..]), log_bytes!(self.context.counterparty_funding_pubkey().serialize()),
6674-
encode::serialize_hex(&initial_commitment_bitcoin_tx.transaction), log_bytes!(sighash[..]),
6675-
encode::serialize_hex(&funding_script), &self.context.channel_id());
6676-
secp_check!(self.context.secp_ctx.verify_ecdsa(&sighash, &sig, self.context.counterparty_funding_pubkey()), "Invalid funding_created signature from peer".to_owned());
6677-
}
6678-
6679-
let counterparty_keys = self.context.build_remote_transaction_keys();
6680-
let counterparty_initial_commitment_tx = self.context.build_commitment_transaction(self.context.cur_counterparty_commitment_transaction_number, &counterparty_keys, false, false, logger).tx;
6681-
6682-
let counterparty_trusted_tx = counterparty_initial_commitment_tx.trust();
6683-
let counterparty_initial_bitcoin_tx = counterparty_trusted_tx.built_transaction();
6684-
log_trace!(logger, "Initial counterparty tx for channel {} is: txid {} tx {}",
6685-
&self.context.channel_id(), counterparty_initial_bitcoin_tx.txid, encode::serialize_hex(&counterparty_initial_bitcoin_tx.transaction));
6686-
6687-
match &self.context.holder_signer {
6688-
// TODO (arik): move match into calling method for Taproot
6689-
ChannelSignerType::Ecdsa(ecdsa) => {
6690-
let counterparty_signature = ecdsa.sign_counterparty_commitment(&counterparty_initial_commitment_tx, Vec::new(), &self.context.secp_ctx)
6691-
.map(|(sig, _)| sig).ok();
6699+
let trusted_tx = initial_commitment_tx.trust();
6700+
let initial_commitment_bitcoin_tx = trusted_tx.built_transaction();
6701+
let sighash = initial_commitment_bitcoin_tx.get_sighash_all(&funding_script, self.context.channel_value_satoshis);
6702+
// They sign the holder commitment transaction...
6703+
log_trace!(logger, "Checking funding_created tx signature {} by key {} against tx {} (sighash {}) with redeemscript {} for channel {}.",
6704+
log_bytes!(sig.serialize_compact()[..]), log_bytes!(self.context.counterparty_funding_pubkey().serialize()),
6705+
encode::serialize_hex(&initial_commitment_bitcoin_tx.transaction), log_bytes!(sighash[..]),
6706+
encode::serialize_hex(&funding_script), &self.context.channel_id());
6707+
secp_check!(self.context.secp_ctx.verify_ecdsa(&sighash, &sig, self.context.counterparty_funding_pubkey()), "Invalid funding_created signature from peer".to_owned());
66926708

6693-
// We sign "counterparty" commitment transaction, allowing them to broadcast the tx if they wish.
6694-
Ok((counterparty_initial_commitment_tx, initial_commitment_tx, counterparty_signature))
6695-
}
6696-
}
6709+
Ok(initial_commitment_tx)
66976710
}
66986711

66996712
pub fn funding_created<L: Deref>(
@@ -6720,10 +6733,10 @@ impl<SP: Deref> InboundV1Channel<SP> where SP::Target: SignerProvider {
67206733
let funding_txo = OutPoint { txid: msg.funding_txid, index: msg.funding_output_index };
67216734
self.context.channel_transaction_parameters.funding_outpoint = Some(funding_txo);
67226735
// This is an externally observable change before we finish all our checks. In particular
6723-
// funding_created_signature may fail.
6736+
// check_funding_created_signature may fail.
67246737
self.context.holder_signer.as_mut().provide_channel_parameters(&self.context.channel_transaction_parameters);
67256738

6726-
let (counterparty_initial_commitment_tx, initial_commitment_tx, sig_opt) = match self.funding_created_signature(&msg.signature, logger) {
6739+
let initial_commitment_tx = match self.check_funding_created_signature(&msg.signature, logger) {
67276740
Ok(res) => res,
67286741
Err(ChannelError::Close(e)) => {
67296742
self.context.channel_transaction_parameters.funding_outpoint = None;
@@ -6732,7 +6745,7 @@ impl<SP: Deref> InboundV1Channel<SP> where SP::Target: SignerProvider {
67326745
Err(e) => {
67336746
// The only error we know how to handle is ChannelError::Close, so we fall over here
67346747
// to make sure we don't continue with an inconsistent state.
6735-
panic!("unexpected error type from funding_created_signature {:?}", e);
6748+
panic!("unexpected error type from check_funding_created_signature {:?}", e);
67366749
}
67376750
};
67386751

@@ -6748,6 +6761,13 @@ impl<SP: Deref> InboundV1Channel<SP> where SP::Target: SignerProvider {
67486761
return Err((self, ChannelError::Close("Failed to validate our commitment".to_owned())));
67496762
}
67506763

6764+
self.context.channel_state = ChannelState::FundingSent as u32;
6765+
self.context.channel_id = funding_txo.to_channel_id();
6766+
self.context.cur_counterparty_commitment_transaction_number -= 1;
6767+
self.context.cur_holder_commitment_transaction_number -= 1;
6768+
6769+
let (counterparty_initial_commitment_tx, funding_signed) = self.context.get_funding_signed_msg(logger);
6770+
67516771
// Now that we're past error-generating stuff, update our local state:
67526772

67536773
let funding_redeemscript = self.context.get_funding_redeemscript();
@@ -6766,16 +6786,11 @@ impl<SP: Deref> InboundV1Channel<SP> where SP::Target: SignerProvider {
67666786

67676787
channel_monitor.provide_initial_counterparty_commitment_tx(
67686788
counterparty_initial_commitment_tx.trust().txid(), Vec::new(),
6769-
self.context.cur_counterparty_commitment_transaction_number,
6789+
self.context.cur_counterparty_commitment_transaction_number + 1,
67706790
self.context.counterparty_cur_commitment_point.unwrap(), self.context.feerate_per_kw,
67716791
counterparty_initial_commitment_tx.to_broadcaster_value_sat(),
67726792
counterparty_initial_commitment_tx.to_countersignatory_value_sat(), logger);
67736793

6774-
self.context.channel_state = ChannelState::FundingSent as u32;
6775-
self.context.channel_id = funding_txo.to_channel_id();
6776-
self.context.cur_counterparty_commitment_transaction_number -= 1;
6777-
self.context.cur_holder_commitment_transaction_number -= 1;
6778-
67796794
log_info!(logger, "Generated funding_signed for peer for channel {}", &self.context.channel_id());
67806795

67816796
// Promote the channel to a full-fledged one now that we have updated the state and have a
@@ -6787,18 +6802,6 @@ impl<SP: Deref> InboundV1Channel<SP> where SP::Target: SignerProvider {
67876802
let need_channel_ready = channel.check_get_channel_ready(0).is_some();
67886803
channel.monitor_updating_paused(false, false, need_channel_ready, Vec::new(), Vec::new(), Vec::new());
67896804

6790-
let funding_signed = if let Some(signature) = sig_opt {
6791-
Some(msgs::FundingSigned {
6792-
channel_id,
6793-
signature,
6794-
#[cfg(taproot)]
6795-
partial_signature_with_nonce: None,
6796-
})
6797-
} else {
6798-
channel.context.signer_pending_funding = true;
6799-
None
6800-
};
6801-
68026805
Ok((channel, funding_signed, channel_monitor))
68036806
}
68046807
}

0 commit comments

Comments
 (0)