Skip to content

Commit 0ce1c5a

Browse files
authored
Merge pull request #2634 from TheBlueMatt/2023-09-claimable-unwrap
Avoid unwrap'ing `channel_parameters` in to_counterparty signing
2 parents 04841ac + 60567da commit 0ce1c5a

File tree

2 files changed

+94
-44
lines changed

2 files changed

+94
-44
lines changed

lightning/src/sign/mod.rs

Lines changed: 83 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -896,42 +896,68 @@ impl InMemorySigner {
896896

897897
/// Returns the counterparty's pubkeys.
898898
///
899-
/// Will panic if [`ChannelSigner::provide_channel_parameters`] has not been called before.
900-
pub fn counterparty_pubkeys(&self) -> &ChannelPublicKeys { &self.get_channel_parameters().counterparty_parameters.as_ref().unwrap().pubkeys }
899+
/// Will return `None` if [`ChannelSigner::provide_channel_parameters`] has not been called.
900+
/// In general, this is safe to `unwrap` only in [`ChannelSigner`] implementation.
901+
pub fn counterparty_pubkeys(&self) -> Option<&ChannelPublicKeys> {
902+
self.get_channel_parameters()
903+
.and_then(|params| params.counterparty_parameters.as_ref().map(|params| &params.pubkeys))
904+
}
905+
901906
/// Returns the `contest_delay` value specified by our counterparty and applied on holder-broadcastable
902907
/// transactions, i.e., the amount of time that we have to wait to recover our funds if we
903908
/// broadcast a transaction.
904909
///
905-
/// Will panic if [`ChannelSigner::provide_channel_parameters`] has not been called before.
906-
pub fn counterparty_selected_contest_delay(&self) -> u16 { self.get_channel_parameters().counterparty_parameters.as_ref().unwrap().selected_contest_delay }
910+
/// Will return `None` if [`ChannelSigner::provide_channel_parameters`] has not been called.
911+
/// In general, this is safe to `unwrap` only in [`ChannelSigner`] implementation.
912+
pub fn counterparty_selected_contest_delay(&self) -> Option<u16> {
913+
self.get_channel_parameters()
914+
.and_then(|params| params.counterparty_parameters.as_ref().map(|params| params.selected_contest_delay))
915+
}
916+
907917
/// Returns the `contest_delay` value specified by us and applied on transactions broadcastable
908918
/// by our counterparty, i.e., the amount of time that they have to wait to recover their funds
909919
/// if they broadcast a transaction.
910920
///
911-
/// Will panic if [`ChannelSigner::provide_channel_parameters`] has not been called before.
912-
pub fn holder_selected_contest_delay(&self) -> u16 { self.get_channel_parameters().holder_selected_contest_delay }
921+
/// Will return `None` if [`ChannelSigner::provide_channel_parameters`] has not been called.
922+
/// In general, this is safe to `unwrap` only in [`ChannelSigner`] implementation.
923+
pub fn holder_selected_contest_delay(&self) -> Option<u16> {
924+
self.get_channel_parameters().map(|params| params.holder_selected_contest_delay)
925+
}
926+
913927
/// Returns whether the holder is the initiator.
914928
///
915-
/// Will panic if [`ChannelSigner::provide_channel_parameters`] has not been called before.
916-
pub fn is_outbound(&self) -> bool { self.get_channel_parameters().is_outbound_from_holder }
929+
/// Will return `None` if [`ChannelSigner::provide_channel_parameters`] has not been called.
930+
/// In general, this is safe to `unwrap` only in [`ChannelSigner`] implementation.
931+
pub fn is_outbound(&self) -> Option<bool> {
932+
self.get_channel_parameters().map(|params| params.is_outbound_from_holder)
933+
}
934+
917935
/// Funding outpoint
918936
///
919-
/// Will panic if [`ChannelSigner::provide_channel_parameters`] has not been called before.
920-
pub fn funding_outpoint(&self) -> &OutPoint { self.get_channel_parameters().funding_outpoint.as_ref().unwrap() }
937+
/// Will return `None` if [`ChannelSigner::provide_channel_parameters`] has not been called.
938+
/// In general, this is safe to `unwrap` only in [`ChannelSigner`] implementation.
939+
pub fn funding_outpoint(&self) -> Option<&OutPoint> {
940+
self.get_channel_parameters().map(|params| params.funding_outpoint.as_ref()).flatten()
941+
}
942+
921943
/// Returns a [`ChannelTransactionParameters`] for this channel, to be used when verifying or
922944
/// building transactions.
923945
///
924-
/// Will panic if [`ChannelSigner::provide_channel_parameters`] has not been called before.
925-
pub fn get_channel_parameters(&self) -> &ChannelTransactionParameters {
926-
self.channel_parameters.as_ref().unwrap()
946+
/// Will return `None` if [`ChannelSigner::provide_channel_parameters`] has not been called.
947+
/// In general, this is safe to `unwrap` only in [`ChannelSigner`] implementation.
948+
pub fn get_channel_parameters(&self) -> Option<&ChannelTransactionParameters> {
949+
self.channel_parameters.as_ref()
927950
}
951+
928952
/// Returns the channel type features of the channel parameters. Should be helpful for
929953
/// determining a channel's category, i. e. legacy/anchors/taproot/etc.
930954
///
931-
/// Will panic if [`ChannelSigner::provide_channel_parameters`] has not been called before.
932-
pub fn channel_type_features(&self) -> &ChannelTypeFeatures {
933-
&self.get_channel_parameters().channel_type_features
955+
/// Will return `None` if [`ChannelSigner::provide_channel_parameters`] has not been called.
956+
/// In general, this is safe to `unwrap` only in [`ChannelSigner`] implementation.
957+
pub fn channel_type_features(&self) -> Option<&ChannelTypeFeatures> {
958+
self.get_channel_parameters().map(|params| &params.channel_type_features)
934959
}
960+
935961
/// Sign the single input of `spend_tx` at index `input_idx`, which spends the output described
936962
/// by `descriptor`, returning the witness stack for the input.
937963
///
@@ -950,14 +976,20 @@ impl InMemorySigner {
950976
if spend_tx.input[input_idx].previous_output != descriptor.outpoint.into_bitcoin_outpoint() { return Err(()); }
951977

952978
let remotepubkey = bitcoin::PublicKey::new(self.pubkeys().payment_point);
953-
let witness_script = if self.channel_type_features().supports_anchors_zero_fee_htlc_tx() {
979+
// We cannot always assume that `channel_parameters` is set, so can't just call
980+
// `self.channel_parameters()` or anything that relies on it
981+
let supports_anchors_zero_fee_htlc_tx = self.channel_type_features()
982+
.map(|features| features.supports_anchors_zero_fee_htlc_tx())
983+
.unwrap_or(false);
984+
985+
let witness_script = if supports_anchors_zero_fee_htlc_tx {
954986
chan_utils::get_to_countersignatory_with_anchors_redeemscript(&remotepubkey.inner)
955987
} else {
956988
Script::new_p2pkh(&remotepubkey.pubkey_hash())
957989
};
958990
let sighash = hash_to_message!(&sighash::SighashCache::new(spend_tx).segwit_signature_hash(input_idx, &witness_script, descriptor.output.value, EcdsaSighashType::All).unwrap()[..]);
959991
let remotesig = sign_with_aux_rand(secp_ctx, &sighash, &self.payment_key, &self);
960-
let payment_script = if self.channel_type_features().supports_anchors_zero_fee_htlc_tx() {
992+
let payment_script = if supports_anchors_zero_fee_htlc_tx {
961993
witness_script.to_v0_p2wsh()
962994
} else {
963995
Script::new_v0_p2wpkh(&remotepubkey.wpubkey_hash().unwrap())
@@ -968,7 +1000,7 @@ impl InMemorySigner {
9681000
let mut witness = Vec::with_capacity(2);
9691001
witness.push(remotesig.serialize_der().to_vec());
9701002
witness[0].push(EcdsaSighashType::All as u8);
971-
if self.channel_type_features().supports_anchors_zero_fee_htlc_tx() {
1003+
if supports_anchors_zero_fee_htlc_tx {
9721004
witness.push(witness_script.to_bytes());
9731005
} else {
9741006
witness.push(remotepubkey.to_bytes());
@@ -1052,24 +1084,30 @@ impl ChannelSigner for InMemorySigner {
10521084
}
10531085
}
10541086

1087+
const MISSING_PARAMS_ERR: &'static str = "ChannelSigner::provide_channel_parameters must be called before signing operations";
1088+
10551089
impl EcdsaChannelSigner for InMemorySigner {
10561090
fn sign_counterparty_commitment(&self, commitment_tx: &CommitmentTransaction, _preimages: Vec<PaymentPreimage>, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<(Signature, Vec<Signature>), ()> {
10571091
let trusted_tx = commitment_tx.trust();
10581092
let keys = trusted_tx.keys();
10591093

10601094
let funding_pubkey = PublicKey::from_secret_key(secp_ctx, &self.funding_key);
1061-
let channel_funding_redeemscript = make_funding_redeemscript(&funding_pubkey, &self.counterparty_pubkeys().funding_pubkey);
1095+
let counterparty_keys = self.counterparty_pubkeys().expect(MISSING_PARAMS_ERR);
1096+
let channel_funding_redeemscript = make_funding_redeemscript(&funding_pubkey, &counterparty_keys.funding_pubkey);
10621097

10631098
let built_tx = trusted_tx.built_transaction();
10641099
let commitment_sig = built_tx.sign_counterparty_commitment(&self.funding_key, &channel_funding_redeemscript, self.channel_value_satoshis, secp_ctx);
10651100
let commitment_txid = built_tx.txid;
10661101

10671102
let mut htlc_sigs = Vec::with_capacity(commitment_tx.htlcs().len());
10681103
for htlc in commitment_tx.htlcs() {
1069-
let channel_parameters = self.get_channel_parameters();
1070-
let htlc_tx = chan_utils::build_htlc_transaction(&commitment_txid, commitment_tx.feerate_per_kw(), self.holder_selected_contest_delay(), htlc, &channel_parameters.channel_type_features, &keys.broadcaster_delayed_payment_key, &keys.revocation_key);
1071-
let htlc_redeemscript = chan_utils::get_htlc_redeemscript(&htlc, self.channel_type_features(), &keys);
1072-
let htlc_sighashtype = if self.channel_type_features().supports_anchors_zero_fee_htlc_tx() { EcdsaSighashType::SinglePlusAnyoneCanPay } else { EcdsaSighashType::All };
1104+
let channel_parameters = self.get_channel_parameters().expect(MISSING_PARAMS_ERR);
1105+
let holder_selected_contest_delay =
1106+
self.holder_selected_contest_delay().expect(MISSING_PARAMS_ERR);
1107+
let chan_type = &channel_parameters.channel_type_features;
1108+
let htlc_tx = chan_utils::build_htlc_transaction(&commitment_txid, commitment_tx.feerate_per_kw(), holder_selected_contest_delay, htlc, chan_type, &keys.broadcaster_delayed_payment_key, &keys.revocation_key);
1109+
let htlc_redeemscript = chan_utils::get_htlc_redeemscript(&htlc, chan_type, &keys);
1110+
let htlc_sighashtype = if chan_type.supports_anchors_zero_fee_htlc_tx() { EcdsaSighashType::SinglePlusAnyoneCanPay } else { EcdsaSighashType::All };
10731111
let htlc_sighash = hash_to_message!(&sighash::SighashCache::new(&htlc_tx).segwit_signature_hash(0, &htlc_redeemscript, htlc.amount_msat / 1000, htlc_sighashtype).unwrap()[..]);
10741112
let holder_htlc_key = chan_utils::derive_private_key(&secp_ctx, &keys.per_commitment_point, &self.htlc_base_key);
10751113
htlc_sigs.push(sign(secp_ctx, &htlc_sighash, &holder_htlc_key));
@@ -1084,21 +1122,23 @@ impl EcdsaChannelSigner for InMemorySigner {
10841122

10851123
fn sign_holder_commitment_and_htlcs(&self, commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<(Signature, Vec<Signature>), ()> {
10861124
let funding_pubkey = PublicKey::from_secret_key(secp_ctx, &self.funding_key);
1087-
let funding_redeemscript = make_funding_redeemscript(&funding_pubkey, &self.counterparty_pubkeys().funding_pubkey);
1125+
let counterparty_keys = self.counterparty_pubkeys().expect(MISSING_PARAMS_ERR);
1126+
let funding_redeemscript = make_funding_redeemscript(&funding_pubkey, &counterparty_keys.funding_pubkey);
10881127
let trusted_tx = commitment_tx.trust();
10891128
let sig = trusted_tx.built_transaction().sign_holder_commitment(&self.funding_key, &funding_redeemscript, self.channel_value_satoshis, &self, secp_ctx);
1090-
let channel_parameters = self.get_channel_parameters();
1129+
let channel_parameters = self.get_channel_parameters().expect(MISSING_PARAMS_ERR);
10911130
let htlc_sigs = trusted_tx.get_htlc_sigs(&self.htlc_base_key, &channel_parameters.as_holder_broadcastable(), &self, secp_ctx)?;
10921131
Ok((sig, htlc_sigs))
10931132
}
10941133

10951134
#[cfg(any(test,feature = "unsafe_revoked_tx_signing"))]
10961135
fn unsafe_sign_holder_commitment_and_htlcs(&self, commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<(Signature, Vec<Signature>), ()> {
10971136
let funding_pubkey = PublicKey::from_secret_key(secp_ctx, &self.funding_key);
1098-
let funding_redeemscript = make_funding_redeemscript(&funding_pubkey, &self.counterparty_pubkeys().funding_pubkey);
1137+
let counterparty_keys = self.counterparty_pubkeys().expect(MISSING_PARAMS_ERR);
1138+
let funding_redeemscript = make_funding_redeemscript(&funding_pubkey, &counterparty_keys.funding_pubkey);
10991139
let trusted_tx = commitment_tx.trust();
11001140
let sig = trusted_tx.built_transaction().sign_holder_commitment(&self.funding_key, &funding_redeemscript, self.channel_value_satoshis, &self, secp_ctx);
1101-
let channel_parameters = self.get_channel_parameters();
1141+
let channel_parameters = self.get_channel_parameters().expect(MISSING_PARAMS_ERR);
11021142
let htlc_sigs = trusted_tx.get_htlc_sigs(&self.htlc_base_key, &channel_parameters.as_holder_broadcastable(), &self, secp_ctx)?;
11031143
Ok((sig, htlc_sigs))
11041144
}
@@ -1108,8 +1148,11 @@ impl EcdsaChannelSigner for InMemorySigner {
11081148
let per_commitment_point = PublicKey::from_secret_key(secp_ctx, &per_commitment_key);
11091149
let revocation_pubkey = chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &self.pubkeys().revocation_basepoint);
11101150
let witness_script = {
1111-
let counterparty_delayedpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.counterparty_pubkeys().delayed_payment_basepoint);
1112-
chan_utils::get_revokeable_redeemscript(&revocation_pubkey, self.holder_selected_contest_delay(), &counterparty_delayedpubkey)
1151+
let counterparty_keys = self.counterparty_pubkeys().expect(MISSING_PARAMS_ERR);
1152+
let holder_selected_contest_delay =
1153+
self.holder_selected_contest_delay().expect(MISSING_PARAMS_ERR);
1154+
let counterparty_delayedpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &counterparty_keys.delayed_payment_basepoint);
1155+
chan_utils::get_revokeable_redeemscript(&revocation_pubkey, holder_selected_contest_delay, &counterparty_delayedpubkey)
11131156
};
11141157
let mut sighash_parts = sighash::SighashCache::new(justice_tx);
11151158
let sighash = hash_to_message!(&sighash_parts.segwit_signature_hash(input, &witness_script, amount, EcdsaSighashType::All).unwrap()[..]);
@@ -1121,9 +1164,11 @@ impl EcdsaChannelSigner for InMemorySigner {
11211164
let per_commitment_point = PublicKey::from_secret_key(secp_ctx, &per_commitment_key);
11221165
let revocation_pubkey = chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &self.pubkeys().revocation_basepoint);
11231166
let witness_script = {
1124-
let counterparty_htlcpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.counterparty_pubkeys().htlc_basepoint);
1167+
let counterparty_keys = self.counterparty_pubkeys().expect(MISSING_PARAMS_ERR);
1168+
let counterparty_htlcpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &counterparty_keys.htlc_basepoint);
11251169
let holder_htlcpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.pubkeys().htlc_basepoint);
1126-
chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, self.channel_type_features(), &counterparty_htlcpubkey, &holder_htlcpubkey, &revocation_pubkey)
1170+
let chan_type = self.channel_type_features().expect(MISSING_PARAMS_ERR);
1171+
chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, chan_type, &counterparty_htlcpubkey, &holder_htlcpubkey, &revocation_pubkey)
11271172
};
11281173
let mut sighash_parts = sighash::SighashCache::new(justice_tx);
11291174
let sighash = hash_to_message!(&sighash_parts.segwit_signature_hash(input, &witness_script, amount, EcdsaSighashType::All).unwrap()[..]);
@@ -1147,17 +1192,20 @@ impl EcdsaChannelSigner for InMemorySigner {
11471192
fn sign_counterparty_htlc_transaction(&self, htlc_tx: &Transaction, input: usize, amount: u64, per_commitment_point: &PublicKey, htlc: &HTLCOutputInCommitment, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()> {
11481193
let htlc_key = chan_utils::derive_private_key(&secp_ctx, &per_commitment_point, &self.htlc_base_key);
11491194
let revocation_pubkey = chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &self.pubkeys().revocation_basepoint);
1150-
let counterparty_htlcpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.counterparty_pubkeys().htlc_basepoint);
1195+
let counterparty_keys = self.counterparty_pubkeys().expect(MISSING_PARAMS_ERR);
1196+
let counterparty_htlcpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &counterparty_keys.htlc_basepoint);
11511197
let htlcpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.pubkeys().htlc_basepoint);
1152-
let witness_script = chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, self.channel_type_features(), &counterparty_htlcpubkey, &htlcpubkey, &revocation_pubkey);
1198+
let chan_type = self.channel_type_features().expect(MISSING_PARAMS_ERR);
1199+
let witness_script = chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, chan_type, &counterparty_htlcpubkey, &htlcpubkey, &revocation_pubkey);
11531200
let mut sighash_parts = sighash::SighashCache::new(htlc_tx);
11541201
let sighash = hash_to_message!(&sighash_parts.segwit_signature_hash(input, &witness_script, amount, EcdsaSighashType::All).unwrap()[..]);
11551202
Ok(sign_with_aux_rand(secp_ctx, &sighash, &htlc_key, &self))
11561203
}
11571204

11581205
fn sign_closing_transaction(&self, closing_tx: &ClosingTransaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()> {
11591206
let funding_pubkey = PublicKey::from_secret_key(secp_ctx, &self.funding_key);
1160-
let channel_funding_redeemscript = make_funding_redeemscript(&funding_pubkey, &self.counterparty_pubkeys().funding_pubkey);
1207+
let counterparty_funding_key = &self.counterparty_pubkeys().expect(MISSING_PARAMS_ERR).funding_pubkey;
1208+
let channel_funding_redeemscript = make_funding_redeemscript(&funding_pubkey, counterparty_funding_key);
11611209
Ok(closing_tx.trust().sign(&self.funding_key, &channel_funding_redeemscript, self.channel_value_satoshis, secp_ctx))
11621210
}
11631211

lightning/src/util/test_channel_signer.rs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ impl TestChannelSigner {
8888
}
8989
}
9090

91-
pub fn channel_type_features(&self) -> &ChannelTypeFeatures { self.inner.channel_type_features() }
91+
pub fn channel_type_features(&self) -> &ChannelTypeFeatures { self.inner.channel_type_features().unwrap() }
9292

9393
#[cfg(test)]
9494
pub fn get_enforcement_state(&self) -> MutexGuard<EnforcementState> {
@@ -158,7 +158,7 @@ impl EcdsaChannelSigner for TestChannelSigner {
158158
fn sign_holder_commitment_and_htlcs(&self, commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<(Signature, Vec<Signature>), ()> {
159159
let trusted_tx = self.verify_holder_commitment_tx(commitment_tx, secp_ctx);
160160
let commitment_txid = trusted_tx.txid();
161-
let holder_csv = self.inner.counterparty_selected_contest_delay();
161+
let holder_csv = self.inner.counterparty_selected_contest_delay().unwrap();
162162

163163
let state = self.state.lock().unwrap();
164164
let commitment_number = trusted_tx.commitment_number();
@@ -219,7 +219,7 @@ impl EcdsaChannelSigner for TestChannelSigner {
219219
}
220220

221221
fn sign_closing_transaction(&self, closing_tx: &ClosingTransaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()> {
222-
closing_tx.verify(self.inner.funding_outpoint().into_bitcoin_outpoint())
222+
closing_tx.verify(self.inner.funding_outpoint().unwrap().into_bitcoin_outpoint())
223223
.expect("derived different closing transaction");
224224
Ok(self.inner.sign_closing_transaction(closing_tx, secp_ctx).unwrap())
225225
}
@@ -256,15 +256,17 @@ impl Writeable for TestChannelSigner {
256256

257257
impl TestChannelSigner {
258258
fn verify_counterparty_commitment_tx<'a, T: secp256k1::Signing + secp256k1::Verification>(&self, commitment_tx: &'a CommitmentTransaction, secp_ctx: &Secp256k1<T>) -> TrustedCommitmentTransaction<'a> {
259-
commitment_tx.verify(&self.inner.get_channel_parameters().as_counterparty_broadcastable(),
260-
self.inner.counterparty_pubkeys(), self.inner.pubkeys(), secp_ctx)
261-
.expect("derived different per-tx keys or built transaction")
259+
commitment_tx.verify(
260+
&self.inner.get_channel_parameters().unwrap().as_counterparty_broadcastable(),
261+
self.inner.counterparty_pubkeys().unwrap(), self.inner.pubkeys(), secp_ctx
262+
).expect("derived different per-tx keys or built transaction")
262263
}
263264

264265
fn verify_holder_commitment_tx<'a, T: secp256k1::Signing + secp256k1::Verification>(&self, commitment_tx: &'a CommitmentTransaction, secp_ctx: &Secp256k1<T>) -> TrustedCommitmentTransaction<'a> {
265-
commitment_tx.verify(&self.inner.get_channel_parameters().as_holder_broadcastable(),
266-
self.inner.pubkeys(), self.inner.counterparty_pubkeys(), secp_ctx)
267-
.expect("derived different per-tx keys or built transaction")
266+
commitment_tx.verify(
267+
&self.inner.get_channel_parameters().unwrap().as_holder_broadcastable(),
268+
self.inner.pubkeys(), self.inner.counterparty_pubkeys().unwrap(), secp_ctx
269+
).expect("derived different per-tx keys or built transaction")
268270
}
269271
}
270272

0 commit comments

Comments
 (0)