From 935a716cc6c4fada075e2b740a70bb1b7b349d49 Mon Sep 17 00:00:00 2001 From: olegkubrakov Date: Tue, 14 Nov 2023 10:08:25 -0800 Subject: [PATCH] Implement struct wrappers for channel key types to avoid confusion. Currently all channel keys and their basepoints exist uniformly as `PublicKey` type, which not only makes in harder for a developer to distinguish those entities, but also does not engage the language type system to check if the correct key is being used in any particular function. Having struct wrappers around keys also enables more nuanced semantics allowing to express Lightning Protocol rules in language. For example, the code allows to derive `HtlcKey` from `HtlcBasepoint` and not from `PaymentBasepoint`. This change is transparent for channel monitors that will use the internal public key of a wrapper. Payment, DelayedPayment, HTLC and Revocation basepoints and their derived keys are now wrapped into a specific struct that make it distinguishable for the Rust type system. Functions that require a specific key or basepoint should not use generic Public Key, but require a specific key wrapper struct to engage Rust type verification system and make it more clear for developers which key is used. --- lightning/src/chain/channelmonitor.rs | 61 +++--- lightning/src/chain/package.rs | 37 ++-- lightning/src/ln/chan_utils.rs | 117 +++------- lightning/src/ln/channel.rs | 56 ++--- lightning/src/ln/channel_keys.rs | 253 ++++++++++++++++++++++ lightning/src/ln/mod.rs | 1 + lightning/src/sign/mod.rs | 65 +++--- lightning/src/util/test_channel_signer.rs | 10 +- 8 files changed, 407 insertions(+), 193 deletions(-) create mode 100644 lightning/src/ln/channel_keys.rs diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs index d78f40f3f..718b8bf51 100644 --- a/lightning/src/chain/channelmonitor.rs +++ b/lightning/src/chain/channelmonitor.rs @@ -36,8 +36,8 @@ use bitcoin::sighash::EcdsaSighashType; use crate::ln::channel::INITIAL_COMMITMENT_NUMBER; use crate::ln::{PaymentHash, PaymentPreimage}; use crate::ln::msgs::DecodeError; -use crate::ln::chan_utils; -use crate::ln::chan_utils::{CommitmentTransaction, CounterpartyCommitmentSecrets, HTLCOutputInCommitment, HTLCClaim, ChannelTransactionParameters, HolderCommitmentTransaction, TxCreationKeys}; +use crate::ln::channel_keys::{DelayedPaymentKey, DelayedPaymentBasepoint, HtlcBasepoint, HtlcKey, RevocationKey, RevocationBasepoint}; +use crate::ln::chan_utils::{self,CommitmentTransaction, CounterpartyCommitmentSecrets, HTLCOutputInCommitment, HTLCClaim, ChannelTransactionParameters, HolderCommitmentTransaction, TxCreationKeys}; use crate::ln::channelmanager::{HTLCSource, SentHTLCId}; use crate::chain; use crate::chain::{BestBlock, WatchedOutput}; @@ -238,10 +238,10 @@ pub(crate) const HTLC_FAIL_BACK_BUFFER: u32 = CLTV_CLAIM_BUFFER + LATENCY_GRACE_ struct HolderSignedTx { /// txid of the transaction in tx, just used to make comparison faster txid: Txid, - revocation_key: PublicKey, - a_htlc_key: PublicKey, - b_htlc_key: PublicKey, - delayed_payment_key: PublicKey, + revocation_key: RevocationKey, + a_htlc_key: HtlcKey, + b_htlc_key: HtlcKey, + delayed_payment_key: DelayedPaymentKey, per_commitment_point: PublicKey, htlc_outputs: Vec<(HTLCOutputInCommitment, Option, Option)>, to_self_value_sat: u64, @@ -278,8 +278,8 @@ impl HolderSignedTx { /// justice or 2nd-stage preimage/timeout transactions. #[derive(Clone, PartialEq, Eq)] struct CounterpartyCommitmentParameters { - counterparty_delayed_payment_base_key: PublicKey, - counterparty_htlc_base_key: PublicKey, + counterparty_delayed_payment_base_key: DelayedPaymentBasepoint, + counterparty_htlc_base_key: HtlcBasepoint, on_counterparty_tx_csv: u16, } @@ -752,12 +752,12 @@ pub(crate) struct ChannelMonitorImpl { commitment_transaction_number_obscure_factor: u64, destination_script: ScriptBuf, - broadcasted_holder_revokable_script: Option<(ScriptBuf, PublicKey, PublicKey)>, + broadcasted_holder_revokable_script: Option<(ScriptBuf, PublicKey, RevocationKey)>, counterparty_payment_script: ScriptBuf, shutdown_script: Option, channel_keys_id: [u8; 32], - holder_revocation_basepoint: PublicKey, + holder_revocation_basepoint: RevocationBasepoint, funding_info: (OutPoint, ScriptBuf), current_counterparty_commitment_txid: Option, prev_counterparty_commitment_txid: Option, @@ -2924,12 +2924,10 @@ impl ChannelMonitorImpl { let their_per_commitment_point = PublicKey::from_secret_key( &self.onchain_tx_handler.secp_ctx, &per_commitment_key); - let revocation_pubkey = chan_utils::derive_public_revocation_key( - &self.onchain_tx_handler.secp_ctx, &their_per_commitment_point, - &self.holder_revocation_basepoint); - let delayed_key = chan_utils::derive_public_key(&self.onchain_tx_handler.secp_ctx, - &their_per_commitment_point, - &self.counterparty_commitment_params.counterparty_delayed_payment_base_key); + let revocation_pubkey = RevocationKey::from_basepoint(&self.onchain_tx_handler.secp_ctx, + &self.holder_revocation_basepoint, &their_per_commitment_point); + let delayed_key = DelayedPaymentKey::from_basepoint(&self.onchain_tx_handler.secp_ctx, + &self.counterparty_commitment_params.counterparty_delayed_payment_base_key, &their_per_commitment_point); let revokeable_redeemscript = chan_utils::get_revokeable_redeemscript(&revocation_pubkey, self.counterparty_commitment_params.on_counterparty_tx_csv, &delayed_key); @@ -2992,8 +2990,8 @@ impl ChannelMonitorImpl { let secret = self.get_secret(commitment_number).unwrap(); let per_commitment_key = ignore_error!(SecretKey::from_slice(&secret)); let per_commitment_point = PublicKey::from_secret_key(&self.onchain_tx_handler.secp_ctx, &per_commitment_key); - let revocation_pubkey = chan_utils::derive_public_revocation_key(&self.onchain_tx_handler.secp_ctx, &per_commitment_point, &self.holder_revocation_basepoint); - let delayed_key = chan_utils::derive_public_key(&self.onchain_tx_handler.secp_ctx, &PublicKey::from_secret_key(&self.onchain_tx_handler.secp_ctx, &per_commitment_key), &self.counterparty_commitment_params.counterparty_delayed_payment_base_key); + let revocation_pubkey = RevocationKey::from_basepoint(&self.onchain_tx_handler.secp_ctx, &self.holder_revocation_basepoint, &per_commitment_point,); + let delayed_key = DelayedPaymentKey::from_basepoint(&self.onchain_tx_handler.secp_ctx, &self.counterparty_commitment_params.counterparty_delayed_payment_base_key, &PublicKey::from_secret_key(&self.onchain_tx_handler.secp_ctx, &per_commitment_key)); let revokeable_redeemscript = chan_utils::get_revokeable_redeemscript(&revocation_pubkey, self.counterparty_commitment_params.on_counterparty_tx_csv, &delayed_key); let revokeable_p2wsh = revokeable_redeemscript.to_v0_p2wsh(); @@ -3105,11 +3103,11 @@ impl ChannelMonitorImpl { } else { return (claimable_outpoints, to_counterparty_output_info); }; if let Some(transaction) = tx { - let revocation_pubkey = chan_utils::derive_public_revocation_key( - &self.onchain_tx_handler.secp_ctx, &per_commitment_point, &self.holder_revocation_basepoint); - let delayed_key = chan_utils::derive_public_key(&self.onchain_tx_handler.secp_ctx, - &per_commitment_point, - &self.counterparty_commitment_params.counterparty_delayed_payment_base_key); + let revocation_pubkey = RevocationKey::from_basepoint( + &self.onchain_tx_handler.secp_ctx, &self.holder_revocation_basepoint, &per_commitment_point); + + let delayed_key = DelayedPaymentKey::from_basepoint(&self.onchain_tx_handler.secp_ctx, &self.counterparty_commitment_params.counterparty_delayed_payment_base_key, &per_commitment_point); + let revokeable_p2wsh = chan_utils::get_revokeable_redeemscript(&revocation_pubkey, self.counterparty_commitment_params.on_counterparty_tx_csv, &delayed_key).to_v0_p2wsh(); @@ -3204,7 +3202,7 @@ impl ChannelMonitorImpl { // Returns (1) `PackageTemplate`s that can be given to the OnchainTxHandler, so that the handler can // broadcast transactions claiming holder HTLC commitment outputs and (2) a holder revokable // script so we can detect whether a holder transaction has been seen on-chain. - fn get_broadcasted_holder_claims(&self, holder_tx: &HolderSignedTx, conf_height: u32) -> (Vec, Option<(ScriptBuf, PublicKey, PublicKey)>) { + fn get_broadcasted_holder_claims(&self, holder_tx: &HolderSignedTx, conf_height: u32) -> (Vec, Option<(ScriptBuf, PublicKey, RevocationKey)>) { let mut claim_requests = Vec::with_capacity(holder_tx.htlc_outputs.len()); let redeemscript = chan_utils::get_revokeable_redeemscript(&holder_tx.revocation_key, self.on_holder_tx_csv, &holder_tx.delayed_payment_key); @@ -4093,7 +4091,7 @@ impl ChannelMonitorImpl { per_commitment_point: broadcasted_holder_revokable_script.1, to_self_delay: self.on_holder_tx_csv, output: outp.clone(), - revocation_pubkey: broadcasted_holder_revokable_script.2.clone(), + revocation_pubkey: broadcasted_holder_revokable_script.2, channel_keys_id: self.channel_keys_id, channel_value_satoshis: self.channel_value_satoshis, })); @@ -4506,8 +4504,8 @@ mod tests { use crate::chain::transaction::OutPoint; use crate::sign::InMemorySigner; use crate::ln::{PaymentPreimage, PaymentHash}; - use crate::ln::chan_utils; - use crate::ln::chan_utils::{HTLCOutputInCommitment, ChannelPublicKeys, ChannelTransactionParameters, HolderCommitmentTransaction, CounterpartyChannelTransactionParameters}; + use crate::ln::channel_keys::{DelayedPaymentBasepoint, DelayedPaymentKey, HtlcBasepoint, RevocationBasepoint, RevocationKey}; + use crate::ln::chan_utils::{self,HTLCOutputInCommitment, ChannelPublicKeys, ChannelTransactionParameters, HolderCommitmentTransaction, CounterpartyChannelTransactionParameters}; use crate::ln::channelmanager::{PaymentSendFailure, PaymentId, RecipientOnionFields}; use crate::ln::functional_test_utils::*; use crate::ln::script::ShutdownScript; @@ -4674,10 +4672,10 @@ mod tests { let counterparty_pubkeys = ChannelPublicKeys { funding_pubkey: PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[44; 32]).unwrap()), - revocation_basepoint: PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[45; 32]).unwrap()), + revocation_basepoint: RevocationBasepoint::from(PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[45; 32]).unwrap())), payment_point: PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[46; 32]).unwrap()), - delayed_payment_basepoint: PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[47; 32]).unwrap()), - htlc_basepoint: PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[48; 32]).unwrap()) + delayed_payment_basepoint: DelayedPaymentBasepoint::from(PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[47; 32]).unwrap())), + htlc_basepoint: HtlcBasepoint::from(PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[48; 32]).unwrap())) }; let funding_outpoint = OutPoint { txid: Txid::all_zeros(), index: u16::max_value() }; let channel_parameters = ChannelTransactionParameters { @@ -4767,6 +4765,7 @@ mod tests { let privkey = SecretKey::from_slice(&>::from_hex("0101010101010101010101010101010101010101010101010101010101010101").unwrap()[..]).unwrap(); let pubkey = PublicKey::from_secret_key(&secp_ctx, &privkey); + use crate::ln::channel_keys::{HtlcKey, HtlcBasepoint}; macro_rules! sign_input { ($sighash_parts: expr, $idx: expr, $amount: expr, $weight: expr, $sum_actual_sigs: expr, $opt_anchors: expr) => { let htlc = HTLCOutputInCommitment { @@ -4776,7 +4775,7 @@ mod tests { payment_hash: PaymentHash([1; 32]), transaction_output_index: Some($idx as u32), }; - let redeem_script = if *$weight == WEIGHT_REVOKED_OUTPUT { chan_utils::get_revokeable_redeemscript(&pubkey, 256, &pubkey) } else { chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, $opt_anchors, &pubkey, &pubkey, &pubkey) }; + let redeem_script = if *$weight == WEIGHT_REVOKED_OUTPUT { chan_utils::get_revokeable_redeemscript(&RevocationKey::from_basepoint(&secp_ctx, &RevocationBasepoint::from(pubkey), &pubkey), 256, &DelayedPaymentKey::from_basepoint(&secp_ctx, &DelayedPaymentBasepoint::from(pubkey), &pubkey)) } else { chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, $opt_anchors, &HtlcKey::from_basepoint(&secp_ctx, &HtlcBasepoint::from(pubkey), &pubkey), &HtlcKey::from_basepoint(&secp_ctx, &HtlcBasepoint::from(pubkey), &pubkey), &RevocationKey::from_basepoint(&secp_ctx, &RevocationBasepoint::from(pubkey), &pubkey)) }; let sighash = hash_to_message!(&$sighash_parts.segwit_signature_hash($idx, &redeem_script, $amount, EcdsaSighashType::All).unwrap()[..]); let sig = secp_ctx.sign_ecdsa(&sighash, &privkey); let mut ser_sig = sig.serialize_der().to_vec(); diff --git a/lightning/src/chain/package.rs b/lightning/src/chain/package.rs index 5fb893d8a..0759e80eb 100644 --- a/lightning/src/chain/package.rs +++ b/lightning/src/chain/package.rs @@ -23,9 +23,9 @@ use bitcoin::secp256k1::{SecretKey,PublicKey}; use bitcoin::sighash::EcdsaSighashType; use crate::ln::PaymentPreimage; -use crate::ln::chan_utils::{TxCreationKeys, HTLCOutputInCommitment}; -use crate::ln::chan_utils; +use crate::ln::chan_utils::{self, TxCreationKeys, HTLCOutputInCommitment}; use crate::ln::features::ChannelTypeFeatures; +use crate::ln::channel_keys::{DelayedPaymentBasepoint, HtlcBasepoint}; use crate::ln::msgs::DecodeError; use crate::chain::chaininterface::{FeeEstimator, ConfirmationTarget, MIN_RELAY_FEE_SAT_PER_1000_WEIGHT, compute_feerate_sat_per_1000_weight, FEERATE_FLOOR_SATS_PER_KW}; use crate::sign::WriteableEcdsaChannelSigner; @@ -115,8 +115,8 @@ const HIGH_FREQUENCY_BUMP_INTERVAL: u32 = 1; #[derive(Clone, PartialEq, Eq)] pub(crate) struct RevokedOutput { per_commitment_point: PublicKey, - counterparty_delayed_payment_base_key: PublicKey, - counterparty_htlc_base_key: PublicKey, + counterparty_delayed_payment_base_key: DelayedPaymentBasepoint, + counterparty_htlc_base_key: HtlcBasepoint, per_commitment_key: SecretKey, weight: u64, amount: u64, @@ -125,7 +125,7 @@ pub(crate) struct RevokedOutput { } impl RevokedOutput { - pub(crate) fn build(per_commitment_point: PublicKey, counterparty_delayed_payment_base_key: PublicKey, counterparty_htlc_base_key: PublicKey, per_commitment_key: SecretKey, amount: u64, on_counterparty_tx_csv: u16, is_counterparty_balance_on_anchors: bool) -> Self { + pub(crate) fn build(per_commitment_point: PublicKey, counterparty_delayed_payment_base_key: DelayedPaymentBasepoint, counterparty_htlc_base_key: HtlcBasepoint, per_commitment_key: SecretKey, amount: u64, on_counterparty_tx_csv: u16, is_counterparty_balance_on_anchors: bool) -> Self { RevokedOutput { per_commitment_point, counterparty_delayed_payment_base_key, @@ -161,8 +161,8 @@ impl_writeable_tlv_based!(RevokedOutput, { #[derive(Clone, PartialEq, Eq)] pub(crate) struct RevokedHTLCOutput { per_commitment_point: PublicKey, - counterparty_delayed_payment_base_key: PublicKey, - counterparty_htlc_base_key: PublicKey, + counterparty_delayed_payment_base_key: DelayedPaymentBasepoint, + counterparty_htlc_base_key: HtlcBasepoint, per_commitment_key: SecretKey, weight: u64, amount: u64, @@ -170,7 +170,7 @@ pub(crate) struct RevokedHTLCOutput { } impl RevokedHTLCOutput { - pub(crate) fn build(per_commitment_point: PublicKey, counterparty_delayed_payment_base_key: PublicKey, counterparty_htlc_base_key: PublicKey, per_commitment_key: SecretKey, amount: u64, htlc: HTLCOutputInCommitment, channel_type_features: &ChannelTypeFeatures) -> Self { + pub(crate) fn build(per_commitment_point: PublicKey, counterparty_delayed_payment_base_key: DelayedPaymentBasepoint, counterparty_htlc_base_key: HtlcBasepoint, per_commitment_key: SecretKey, amount: u64, htlc: HTLCOutputInCommitment, channel_type_features: &ChannelTypeFeatures) -> Self { let weight = if htlc.offered { weight_revoked_offered_htlc(channel_type_features) } else { weight_revoked_received_htlc(channel_type_features) }; RevokedHTLCOutput { per_commitment_point, @@ -205,15 +205,15 @@ impl_writeable_tlv_based!(RevokedHTLCOutput, { #[derive(Clone, PartialEq, Eq)] pub(crate) struct CounterpartyOfferedHTLCOutput { per_commitment_point: PublicKey, - counterparty_delayed_payment_base_key: PublicKey, - counterparty_htlc_base_key: PublicKey, + counterparty_delayed_payment_base_key: DelayedPaymentBasepoint, + counterparty_htlc_base_key: HtlcBasepoint, preimage: PaymentPreimage, htlc: HTLCOutputInCommitment, channel_type_features: ChannelTypeFeatures, } impl CounterpartyOfferedHTLCOutput { - pub(crate) fn build(per_commitment_point: PublicKey, counterparty_delayed_payment_base_key: PublicKey, counterparty_htlc_base_key: PublicKey, preimage: PaymentPreimage, htlc: HTLCOutputInCommitment, channel_type_features: ChannelTypeFeatures) -> Self { + pub(crate) fn build(per_commitment_point: PublicKey, counterparty_delayed_payment_base_key: DelayedPaymentBasepoint, counterparty_htlc_base_key: HtlcBasepoint, preimage: PaymentPreimage, htlc: HTLCOutputInCommitment, channel_type_features: ChannelTypeFeatures) -> Self { CounterpartyOfferedHTLCOutput { per_commitment_point, counterparty_delayed_payment_base_key, @@ -283,14 +283,14 @@ impl Readable for CounterpartyOfferedHTLCOutput { #[derive(Clone, PartialEq, Eq)] pub(crate) struct CounterpartyReceivedHTLCOutput { per_commitment_point: PublicKey, - counterparty_delayed_payment_base_key: PublicKey, - counterparty_htlc_base_key: PublicKey, + counterparty_delayed_payment_base_key: DelayedPaymentBasepoint, + counterparty_htlc_base_key: HtlcBasepoint, htlc: HTLCOutputInCommitment, channel_type_features: ChannelTypeFeatures, } impl CounterpartyReceivedHTLCOutput { - pub(crate) fn build(per_commitment_point: PublicKey, counterparty_delayed_payment_base_key: PublicKey, counterparty_htlc_base_key: PublicKey, htlc: HTLCOutputInCommitment, channel_type_features: ChannelTypeFeatures) -> Self { + pub(crate) fn build(per_commitment_point: PublicKey, counterparty_delayed_payment_base_key: DelayedPaymentBasepoint, counterparty_htlc_base_key: HtlcBasepoint, htlc: HTLCOutputInCommitment, channel_type_features: ChannelTypeFeatures) -> Self { CounterpartyReceivedHTLCOutput { per_commitment_point, counterparty_delayed_payment_base_key, @@ -600,7 +600,7 @@ impl PackageSolvingData { let mut ser_sig = sig.serialize_der().to_vec(); ser_sig.push(EcdsaSighashType::All as u8); bumped_tx.input[i].witness.push(ser_sig); - bumped_tx.input[i].witness.push(chan_keys.revocation_key.clone().serialize().to_vec()); + bumped_tx.input[i].witness.push(chan_keys.revocation_key.to_public_key().serialize().to_vec()); bumped_tx.input[i].witness.push(witness_script.clone().into_bytes()); } else { return false; } }, @@ -1191,6 +1191,7 @@ mod tests { use crate::chain::Txid; use crate::ln::chan_utils::HTLCOutputInCommitment; use crate::ln::{PaymentPreimage, PaymentHash}; + use crate::ln::channel_keys::{DelayedPaymentBasepoint, HtlcBasepoint}; use bitcoin::blockdata::constants::WITNESS_SCALE_FACTOR; use bitcoin::blockdata::script::ScriptBuf; @@ -1209,7 +1210,7 @@ mod tests { { let dumb_scalar = SecretKey::from_slice(&>::from_hex("0101010101010101010101010101010101010101010101010101010101010101").unwrap()[..]).unwrap(); let dumb_point = PublicKey::from_secret_key(&$secp_ctx, &dumb_scalar); - PackageSolvingData::RevokedOutput(RevokedOutput::build(dumb_point, dumb_point, dumb_point, dumb_scalar, 0, 0, $is_counterparty_balance_on_anchors)) + PackageSolvingData::RevokedOutput(RevokedOutput::build(dumb_point, DelayedPaymentBasepoint::from(dumb_point), HtlcBasepoint::from(dumb_point), dumb_scalar, 0, 0, $is_counterparty_balance_on_anchors)) } } } @@ -1221,7 +1222,7 @@ mod tests { let dumb_point = PublicKey::from_secret_key(&$secp_ctx, &dumb_scalar); let hash = PaymentHash([1; 32]); let htlc = HTLCOutputInCommitment { offered: true, amount_msat: $amt, cltv_expiry: 0, payment_hash: hash, transaction_output_index: None }; - PackageSolvingData::CounterpartyReceivedHTLCOutput(CounterpartyReceivedHTLCOutput::build(dumb_point, dumb_point, dumb_point, htlc, $opt_anchors)) + PackageSolvingData::CounterpartyReceivedHTLCOutput(CounterpartyReceivedHTLCOutput::build(dumb_point, DelayedPaymentBasepoint::from(dumb_point), HtlcBasepoint::from(dumb_point), htlc, $opt_anchors)) } } } @@ -1234,7 +1235,7 @@ mod tests { let hash = PaymentHash([1; 32]); let preimage = PaymentPreimage([2;32]); let htlc = HTLCOutputInCommitment { offered: false, amount_msat: $amt, cltv_expiry: 1000, payment_hash: hash, transaction_output_index: None }; - PackageSolvingData::CounterpartyOfferedHTLCOutput(CounterpartyOfferedHTLCOutput::build(dumb_point, dumb_point, dumb_point, preimage, htlc, $opt_anchors)) + PackageSolvingData::CounterpartyOfferedHTLCOutput(CounterpartyOfferedHTLCOutput::build(dumb_point, DelayedPaymentBasepoint::from(dumb_point), HtlcBasepoint::from(dumb_point), preimage, htlc, $opt_anchors)) } } } diff --git a/lightning/src/ln/chan_utils.rs b/lightning/src/ln/chan_utils.rs index 8dc31115f..3552748b3 100644 --- a/lightning/src/ln/chan_utils.rs +++ b/lightning/src/ln/chan_utils.rs @@ -7,7 +7,7 @@ // You may not use this file except in accordance with one or both of these // licenses. -//! Various utilities for building scripts and deriving keys related to channels. These are +//! Various utilities for building scripts related to channels. These are //! largely of interest for those implementing the traits on [`crate::sign`] by hand. use bitcoin::blockdata::script::{Script, ScriptBuf, Builder}; @@ -46,6 +46,7 @@ use core::ops::Deref; use crate::chain; use crate::ln::features::ChannelTypeFeatures; use crate::util::crypto::{sign, sign_with_aux_rand}; +use super::channel_keys::{DelayedPaymentBasepoint, DelayedPaymentKey, HtlcKey, HtlcBasepoint, RevocationKey, RevocationBasepoint}; /// Maximum number of one-way in-flight HTLC (protocol-level value). pub const MAX_HTLCS: u16 = 483; @@ -354,21 +355,6 @@ pub fn derive_private_key(secp_ctx: &Secp256k1, per_co .expect("Addition only fails if the tweak is the inverse of the key. This is not possible when the tweak contains the hash of the key.") } -/// Derives a per-commitment-transaction public key (eg an htlc key or a delayed_payment key) -/// from the base point and the per_commitment_key. This is the public equivalent of -/// derive_private_key - using only public keys to derive a public key instead of private keys. -pub fn derive_public_key(secp_ctx: &Secp256k1, per_commitment_point: &PublicKey, base_point: &PublicKey) -> PublicKey { - let mut sha = Sha256::engine(); - sha.input(&per_commitment_point.serialize()); - sha.input(&base_point.serialize()); - let res = Sha256::from_engine(sha).to_byte_array(); - - let hashkey = PublicKey::from_secret_key(&secp_ctx, - &SecretKey::from_slice(&res).expect("Hashes should always be valid keys unless SHA-256 is broken")); - base_point.combine(&hashkey) - .expect("Addition only fails if the tweak is the inverse of the key. This is not possible when the tweak contains the hash of the key.") -} - /// Derives a per-commitment-transaction revocation key from its constituent parts. /// /// Only the cheating participant owns a valid witness to propagate a revoked @@ -404,43 +390,6 @@ pub fn derive_private_revocation_key(secp_ctx: &Secp256k1 .expect("Addition only fails if the tweak is the inverse of the key. This is not possible when the tweak commits to the key.") } -/// Derives a per-commitment-transaction revocation public key from its constituent parts. This is -/// the public equivalend of derive_private_revocation_key - using only public keys to derive a -/// public key instead of private keys. -/// -/// Only the cheating participant owns a valid witness to propagate a revoked -/// commitment transaction, thus per_commitment_point always come from cheater -/// and revocation_base_point always come from punisher, which is the broadcaster -/// of the transaction spending with this key knowledge. -/// -/// Note that this is infallible iff we trust that at least one of the two input keys are randomly -/// generated (ie our own). -pub fn derive_public_revocation_key(secp_ctx: &Secp256k1, - per_commitment_point: &PublicKey, countersignatory_revocation_base_point: &PublicKey) --> PublicKey { - let rev_append_commit_hash_key = { - let mut sha = Sha256::engine(); - sha.input(&countersignatory_revocation_base_point.serialize()); - sha.input(&per_commitment_point.serialize()); - - Sha256::from_engine(sha).to_byte_array() - }; - let commit_append_rev_hash_key = { - let mut sha = Sha256::engine(); - sha.input(&per_commitment_point.serialize()); - sha.input(&countersignatory_revocation_base_point.serialize()); - - Sha256::from_engine(sha).to_byte_array() - }; - - let countersignatory_contrib = countersignatory_revocation_base_point.clone().mul_tweak(&secp_ctx, &Scalar::from_be_bytes(rev_append_commit_hash_key).unwrap()) - .expect("Multiplying a valid public key by a hash is expected to never fail per secp256k1 docs"); - let broadcaster_contrib = per_commitment_point.clone().mul_tweak(&secp_ctx, &Scalar::from_be_bytes(commit_append_rev_hash_key).unwrap()) - .expect("Multiplying a valid public key by a hash is expected to never fail per secp256k1 docs"); - countersignatory_contrib.combine(&broadcaster_contrib) - .expect("Addition only fails if the tweak is the inverse of the key. This is not possible when the tweak commits to the key.") -} - /// The set of public keys which are used in the creation of one commitment transaction. /// These are derived from the channel base keys and per-commitment data. /// @@ -459,13 +408,13 @@ pub struct TxCreationKeys { /// The revocation key which is used to allow the broadcaster of the commitment /// transaction to provide their counterparty the ability to punish them if they broadcast /// an old state. - pub revocation_key: PublicKey, + pub revocation_key: RevocationKey, /// Broadcaster's HTLC Key - pub broadcaster_htlc_key: PublicKey, + pub broadcaster_htlc_key: HtlcKey, /// Countersignatory's HTLC Key - pub countersignatory_htlc_key: PublicKey, + pub countersignatory_htlc_key: HtlcKey, /// Broadcaster's Payment Key (which isn't allowed to be spent from for some delay) - pub broadcaster_delayed_payment_key: PublicKey, + pub broadcaster_delayed_payment_key: DelayedPaymentKey, } impl_writeable_tlv_based!(TxCreationKeys, { @@ -486,7 +435,7 @@ pub struct ChannelPublicKeys { /// revocation keys. This is combined with the per-commitment-secret generated by the /// counterparty to create a secret which the counterparty can reveal to revoke previous /// states. - pub revocation_basepoint: PublicKey, + pub revocation_basepoint: RevocationBasepoint, /// The public key on which the non-broadcaster (ie the countersignatory) receives an immediately /// spendable primary channel balance on the broadcaster's commitment transaction. This key is /// static across every commitment transaction. @@ -494,10 +443,10 @@ pub struct ChannelPublicKeys { /// The base point which is used (with derive_public_key) to derive a per-commitment payment /// public key which receives non-HTLC-encumbered funds which are only available for spending /// after some delay (or can be claimed via the revocation path). - pub delayed_payment_basepoint: PublicKey, + pub delayed_payment_basepoint: DelayedPaymentBasepoint, /// The base point which is used (with derive_public_key) to derive a per-commitment public key /// which is used to encumber HTLC-in-flight outputs. - pub htlc_basepoint: PublicKey, + pub htlc_basepoint: HtlcBasepoint, } impl_writeable_tlv_based!(ChannelPublicKeys, { @@ -511,13 +460,13 @@ impl_writeable_tlv_based!(ChannelPublicKeys, { impl TxCreationKeys { /// Create per-state keys from channel base points and the per-commitment point. /// Key set is asymmetric and can't be used as part of counter-signatory set of transactions. - pub fn derive_new(secp_ctx: &Secp256k1, per_commitment_point: &PublicKey, broadcaster_delayed_payment_base: &PublicKey, broadcaster_htlc_base: &PublicKey, countersignatory_revocation_base: &PublicKey, countersignatory_htlc_base: &PublicKey) -> TxCreationKeys { + pub fn derive_new(secp_ctx: &Secp256k1, per_commitment_point: &PublicKey, broadcaster_delayed_payment_base: &DelayedPaymentBasepoint, broadcaster_htlc_base: &HtlcBasepoint, countersignatory_revocation_base: &RevocationBasepoint, countersignatory_htlc_base: &HtlcBasepoint) -> TxCreationKeys { TxCreationKeys { per_commitment_point: per_commitment_point.clone(), - revocation_key: derive_public_revocation_key(&secp_ctx, &per_commitment_point, &countersignatory_revocation_base), - broadcaster_htlc_key: derive_public_key(&secp_ctx, &per_commitment_point, &broadcaster_htlc_base), - countersignatory_htlc_key: derive_public_key(&secp_ctx, &per_commitment_point, &countersignatory_htlc_base), - broadcaster_delayed_payment_key: derive_public_key(&secp_ctx, &per_commitment_point, &broadcaster_delayed_payment_base), + revocation_key: RevocationKey::from_basepoint(&secp_ctx, &countersignatory_revocation_base, &per_commitment_point), + broadcaster_htlc_key: HtlcKey::from_basepoint(&secp_ctx, &broadcaster_htlc_base, &per_commitment_point), + countersignatory_htlc_key: HtlcKey::from_basepoint(&secp_ctx, &countersignatory_htlc_base, &per_commitment_point), + broadcaster_delayed_payment_key: DelayedPaymentKey::from_basepoint(&secp_ctx, &broadcaster_delayed_payment_base, &per_commitment_point), } } @@ -543,14 +492,14 @@ pub const REVOKEABLE_REDEEMSCRIPT_MAX_LENGTH: usize = 6 + 3 + 34*2; /// A script either spendable by the revocation /// key or the broadcaster_delayed_payment_key and satisfying the relative-locktime OP_CSV constrain. /// Encumbering a `to_holder` output on a commitment transaction or 2nd-stage HTLC transactions. -pub fn get_revokeable_redeemscript(revocation_key: &PublicKey, contest_delay: u16, broadcaster_delayed_payment_key: &PublicKey) -> ScriptBuf { +pub fn get_revokeable_redeemscript(revocation_key: &RevocationKey, contest_delay: u16, broadcaster_delayed_payment_key: &DelayedPaymentKey) -> ScriptBuf { let res = Builder::new().push_opcode(opcodes::all::OP_IF) - .push_slice(&revocation_key.serialize()) + .push_slice(&revocation_key.to_public_key().serialize()) .push_opcode(opcodes::all::OP_ELSE) .push_int(contest_delay as i64) .push_opcode(opcodes::all::OP_CSV) .push_opcode(opcodes::all::OP_DROP) - .push_slice(&broadcaster_delayed_payment_key.serialize()) + .push_slice(&broadcaster_delayed_payment_key.to_public_key().serialize()) .push_opcode(opcodes::all::OP_ENDIF) .push_opcode(opcodes::all::OP_CHECKSIG) .into_script(); @@ -598,17 +547,17 @@ impl_writeable_tlv_based!(HTLCOutputInCommitment, { }); #[inline] -pub(crate) fn get_htlc_redeemscript_with_explicit_keys(htlc: &HTLCOutputInCommitment, channel_type_features: &ChannelTypeFeatures, broadcaster_htlc_key: &PublicKey, countersignatory_htlc_key: &PublicKey, revocation_key: &PublicKey) -> ScriptBuf { +pub(crate) fn get_htlc_redeemscript_with_explicit_keys(htlc: &HTLCOutputInCommitment, channel_type_features: &ChannelTypeFeatures, broadcaster_htlc_key: &HtlcKey, countersignatory_htlc_key: &HtlcKey, revocation_key: &RevocationKey) -> ScriptBuf { let payment_hash160 = Ripemd160::hash(&htlc.payment_hash.0[..]).to_byte_array(); if htlc.offered { let mut bldr = Builder::new().push_opcode(opcodes::all::OP_DUP) .push_opcode(opcodes::all::OP_HASH160) - .push_slice(PubkeyHash::hash(&revocation_key.serialize())) + .push_slice(PubkeyHash::hash(&revocation_key.to_public_key().serialize())) .push_opcode(opcodes::all::OP_EQUAL) .push_opcode(opcodes::all::OP_IF) .push_opcode(opcodes::all::OP_CHECKSIG) .push_opcode(opcodes::all::OP_ELSE) - .push_slice(countersignatory_htlc_key.serialize()) + .push_slice(&countersignatory_htlc_key.to_public_key().serialize()) .push_opcode(opcodes::all::OP_SWAP) .push_opcode(opcodes::all::OP_SIZE) .push_int(32) @@ -617,7 +566,7 @@ pub(crate) fn get_htlc_redeemscript_with_explicit_keys(htlc: &HTLCOutputInCommit .push_opcode(opcodes::all::OP_DROP) .push_int(2) .push_opcode(opcodes::all::OP_SWAP) - .push_slice(broadcaster_htlc_key.serialize()) + .push_slice(&broadcaster_htlc_key.to_public_key().serialize()) .push_int(2) .push_opcode(opcodes::all::OP_CHECKMULTISIG) .push_opcode(opcodes::all::OP_ELSE) @@ -636,12 +585,12 @@ pub(crate) fn get_htlc_redeemscript_with_explicit_keys(htlc: &HTLCOutputInCommit } else { let mut bldr = Builder::new().push_opcode(opcodes::all::OP_DUP) .push_opcode(opcodes::all::OP_HASH160) - .push_slice(PubkeyHash::hash(&revocation_key.serialize())) + .push_slice(&PubkeyHash::hash(&revocation_key.to_public_key().serialize())) .push_opcode(opcodes::all::OP_EQUAL) .push_opcode(opcodes::all::OP_IF) .push_opcode(opcodes::all::OP_CHECKSIG) .push_opcode(opcodes::all::OP_ELSE) - .push_slice(countersignatory_htlc_key.serialize()) + .push_slice(&countersignatory_htlc_key.to_public_key().serialize()) .push_opcode(opcodes::all::OP_SWAP) .push_opcode(opcodes::all::OP_SIZE) .push_int(32) @@ -652,7 +601,7 @@ pub(crate) fn get_htlc_redeemscript_with_explicit_keys(htlc: &HTLCOutputInCommit .push_opcode(opcodes::all::OP_EQUALVERIFY) .push_int(2) .push_opcode(opcodes::all::OP_SWAP) - .push_slice(broadcaster_htlc_key.serialize()) + .push_slice(&broadcaster_htlc_key.to_public_key().serialize()) .push_int(2) .push_opcode(opcodes::all::OP_CHECKMULTISIG) .push_opcode(opcodes::all::OP_ELSE) @@ -706,7 +655,7 @@ pub(crate) fn make_funding_redeemscript_from_slices(broadcaster_funding_key: &[u /// /// Panics if htlc.transaction_output_index.is_none() (as such HTLCs do not appear in the /// commitment transaction). -pub fn build_htlc_transaction(commitment_txid: &Txid, feerate_per_kw: u32, contest_delay: u16, htlc: &HTLCOutputInCommitment, channel_type_features: &ChannelTypeFeatures, broadcaster_delayed_payment_key: &PublicKey, revocation_key: &PublicKey) -> Transaction { +pub fn build_htlc_transaction(commitment_txid: &Txid, feerate_per_kw: u32, contest_delay: u16, htlc: &HTLCOutputInCommitment, channel_type_features: &ChannelTypeFeatures, broadcaster_delayed_payment_key: &DelayedPaymentKey, revocation_key: &RevocationKey) -> Transaction { let mut txins: Vec = Vec::new(); txins.push(build_htlc_input(commitment_txid, htlc, channel_type_features)); @@ -737,7 +686,7 @@ pub(crate) fn build_htlc_input(commitment_txid: &Txid, htlc: &HTLCOutputInCommit } pub(crate) fn build_htlc_output( - feerate_per_kw: u32, contest_delay: u16, htlc: &HTLCOutputInCommitment, channel_type_features: &ChannelTypeFeatures, broadcaster_delayed_payment_key: &PublicKey, revocation_key: &PublicKey + feerate_per_kw: u32, contest_delay: u16, htlc: &HTLCOutputInCommitment, channel_type_features: &ChannelTypeFeatures, broadcaster_delayed_payment_key: &DelayedPaymentKey, revocation_key: &RevocationKey ) -> TxOut { let weight = if htlc.offered { htlc_timeout_tx_weight(channel_type_features) @@ -1082,17 +1031,17 @@ impl HolderCommitmentTransaction { let keys = TxCreationKeys { per_commitment_point: dummy_key.clone(), - revocation_key: dummy_key.clone(), - broadcaster_htlc_key: dummy_key.clone(), - countersignatory_htlc_key: dummy_key.clone(), - broadcaster_delayed_payment_key: dummy_key.clone(), + revocation_key: RevocationKey::from_basepoint(&secp_ctx, &RevocationBasepoint::from(dummy_key), &dummy_key), + broadcaster_htlc_key: HtlcKey::from_basepoint(&secp_ctx, &HtlcBasepoint::from(dummy_key), &dummy_key), + countersignatory_htlc_key: HtlcKey::from_basepoint(&secp_ctx, &HtlcBasepoint::from(dummy_key), &dummy_key), + broadcaster_delayed_payment_key: DelayedPaymentKey::from_basepoint(&secp_ctx, &DelayedPaymentBasepoint::from(dummy_key), &dummy_key), }; let channel_pubkeys = ChannelPublicKeys { funding_pubkey: dummy_key.clone(), - revocation_basepoint: dummy_key.clone(), + revocation_basepoint: RevocationBasepoint::from(dummy_key), payment_point: dummy_key.clone(), - delayed_payment_basepoint: dummy_key.clone(), - htlc_basepoint: dummy_key.clone() + delayed_payment_basepoint: DelayedPaymentBasepoint::from(dummy_key.clone()), + htlc_basepoint: HtlcBasepoint::from(dummy_key.clone()) }; let channel_parameters = ChannelTransactionParameters { holder_pubkeys: channel_pubkeys.clone(), diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index 6803b1128..4773f4242 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -55,6 +55,8 @@ use core::ops::Deref; use crate::sync::Mutex; use crate::sign::type_resolver::ChannelSignerType; +use super::channel_keys::{DelayedPaymentBasepoint, HtlcBasepoint, RevocationBasepoint}; + #[cfg(test)] pub struct ChannelValueStat { pub value_to_self_msat: u64, @@ -3164,9 +3166,9 @@ impl Channel where let htlc_sighashtype = if self.context.channel_type.supports_anchors_zero_fee_htlc_tx() { EcdsaSighashType::SinglePlusAnyoneCanPay } else { EcdsaSighashType::All }; let htlc_sighash = hash_to_message!(&sighash::SighashCache::new(&htlc_tx).segwit_signature_hash(0, &htlc_redeemscript, htlc.amount_msat / 1000, htlc_sighashtype).unwrap()[..]); log_trace!(logger, "Checking HTLC tx signature {} by key {} against tx {} (sighash {}) with redeemscript {} in channel {}.", - log_bytes!(msg.htlc_signatures[idx].serialize_compact()[..]), log_bytes!(keys.countersignatory_htlc_key.serialize()), + log_bytes!(msg.htlc_signatures[idx].serialize_compact()[..]), log_bytes!(keys.countersignatory_htlc_key.to_public_key().serialize()), encode::serialize_hex(&htlc_tx), log_bytes!(htlc_sighash[..]), encode::serialize_hex(&htlc_redeemscript), &self.context.channel_id()); - if let Err(_) = self.context.secp_ctx.verify_ecdsa(&htlc_sighash, &msg.htlc_signatures[idx], &keys.countersignatory_htlc_key) { + if let Err(_) = self.context.secp_ctx.verify_ecdsa(&htlc_sighash, &msg.htlc_signatures[idx], &keys.countersignatory_htlc_key.to_public_key()) { return Err(ChannelError::Close("Invalid HTLC tx signature from peer".to_owned())); } if !separate_nondust_htlc_sources { @@ -5696,7 +5698,7 @@ impl Channel where log_trace!(logger, "Signed remote HTLC tx {} with redeemscript {} with pubkey {} -> {} in channel {}", encode::serialize_hex(&chan_utils::build_htlc_transaction(&counterparty_commitment_txid, commitment_stats.feerate_per_kw, self.context.get_holder_selected_contest_delay(), htlc, &self.context.channel_type, &counterparty_keys.broadcaster_delayed_payment_key, &counterparty_keys.revocation_key)), encode::serialize_hex(&chan_utils::get_htlc_redeemscript(&htlc, &self.context.channel_type, &counterparty_keys)), - log_bytes!(counterparty_keys.broadcaster_htlc_key.serialize()), + log_bytes!(counterparty_keys.broadcaster_htlc_key.to_public_key().serialize()), log_bytes!(htlc_sig.serialize_compact()[..]), &self.context.channel_id()); } } @@ -6232,10 +6234,10 @@ impl OutboundV1Channel where SP::Target: SignerProvider { to_self_delay: self.context.get_holder_selected_contest_delay(), max_accepted_htlcs: self.context.holder_max_accepted_htlcs, funding_pubkey: keys.funding_pubkey, - revocation_basepoint: keys.revocation_basepoint, + revocation_basepoint: keys.revocation_basepoint.to_public_key(), payment_point: keys.payment_point, - delayed_payment_basepoint: keys.delayed_payment_basepoint, - htlc_basepoint: keys.htlc_basepoint, + delayed_payment_basepoint: keys.delayed_payment_basepoint.to_public_key(), + htlc_basepoint: keys.htlc_basepoint.to_public_key(), first_per_commitment_point, channel_flags: if self.context.config.announced_channel {1} else {0}, shutdown_scriptpubkey: Some(match &self.context.shutdown_scriptpubkey { @@ -6357,10 +6359,10 @@ impl OutboundV1Channel where SP::Target: SignerProvider { let counterparty_pubkeys = ChannelPublicKeys { funding_pubkey: msg.funding_pubkey, - revocation_basepoint: msg.revocation_basepoint, + revocation_basepoint: RevocationBasepoint::from(msg.revocation_basepoint), payment_point: msg.payment_point, - delayed_payment_basepoint: msg.delayed_payment_basepoint, - htlc_basepoint: msg.htlc_basepoint + delayed_payment_basepoint: DelayedPaymentBasepoint::from(msg.delayed_payment_basepoint), + htlc_basepoint: HtlcBasepoint::from(msg.htlc_basepoint) }; self.context.channel_transaction_parameters.counterparty_parameters = Some(CounterpartyChannelTransactionParameters { @@ -6433,10 +6435,10 @@ impl InboundV1Channel where SP::Target: SignerProvider { let pubkeys = holder_signer.pubkeys().clone(); let counterparty_pubkeys = ChannelPublicKeys { funding_pubkey: msg.funding_pubkey, - revocation_basepoint: msg.revocation_basepoint, + revocation_basepoint: RevocationBasepoint::from(msg.revocation_basepoint), payment_point: msg.payment_point, - delayed_payment_basepoint: msg.delayed_payment_basepoint, - htlc_basepoint: msg.htlc_basepoint + delayed_payment_basepoint: DelayedPaymentBasepoint::from(msg.delayed_payment_basepoint), + htlc_basepoint: HtlcBasepoint::from(msg.htlc_basepoint) }; if config.channel_handshake_config.our_to_self_delay < BREAKDOWN_TIMEOUT { @@ -6766,10 +6768,10 @@ impl InboundV1Channel where SP::Target: SignerProvider { to_self_delay: self.context.get_holder_selected_contest_delay(), max_accepted_htlcs: self.context.holder_max_accepted_htlcs, funding_pubkey: keys.funding_pubkey, - revocation_basepoint: keys.revocation_basepoint, + revocation_basepoint: keys.revocation_basepoint.to_public_key(), payment_point: keys.payment_point, - delayed_payment_basepoint: keys.delayed_payment_basepoint, - htlc_basepoint: keys.htlc_basepoint, + delayed_payment_basepoint: keys.delayed_payment_basepoint.to_public_key(), + htlc_basepoint: keys.htlc_basepoint.to_public_key(), first_per_commitment_point, shutdown_scriptpubkey: Some(match &self.context.shutdown_scriptpubkey { Some(script) => script.clone().into_inner(), @@ -7796,15 +7798,15 @@ mod tests { use bitcoin::blockdata::opcodes; use bitcoin::network::constants::Network; use crate::ln::PaymentHash; - use crate::ln::channelmanager::{self, HTLCSource, PaymentId}; + use crate::ln::channel_keys::{RevocationKey, RevocationBasepoint}; +use crate::ln::channelmanager::{self, HTLCSource, PaymentId}; use crate::ln::channel::InitFeatures; use crate::ln::channel::{ChannelState, InboundHTLCOutput, OutboundV1Channel, InboundV1Channel, OutboundHTLCOutput, InboundHTLCState, OutboundHTLCState, HTLCCandidate, HTLCInitiator, commit_tx_fee_msat}; use crate::ln::channel::{MAX_FUNDING_SATOSHIS_NO_WUMBO, TOTAL_BITCOIN_SUPPLY_SATOSHIS, MIN_THEIR_CHAN_RESERVE_SATOSHIS}; use crate::ln::features::ChannelTypeFeatures; use crate::ln::msgs::{ChannelUpdate, DecodeError, UnsignedChannelUpdate, MAX_VALUE_MSAT}; use crate::ln::script::ShutdownScript; - use crate::ln::chan_utils; - use crate::ln::chan_utils::{htlc_success_tx_weight, htlc_timeout_tx_weight}; + use crate::ln::chan_utils::{self, htlc_success_tx_weight, htlc_timeout_tx_weight}; use crate::chain::BestBlock; use crate::chain::chaininterface::{FeeEstimator, LowerBoundedFeeEstimator, ConfirmationTarget}; use crate::sign::{ChannelSigner, InMemorySigner, EntropySource, SignerProvider}; @@ -8334,6 +8336,7 @@ mod tests { use crate::sign::{ChannelDerivationParameters, HTLCDescriptor, EcdsaChannelSigner}; use crate::ln::PaymentPreimage; use crate::ln::channel::{HTLCOutputInCommitment ,TxCreationKeys}; + use crate::ln::channel_keys::{DelayedPaymentBasepoint, HtlcBasepoint}; use crate::ln::chan_utils::{ChannelPublicKeys, HolderCommitmentTransaction, CounterpartyChannelTransactionParameters}; use crate::util::logger::Logger; use crate::sync::Arc; @@ -8375,10 +8378,10 @@ mod tests { let counterparty_pubkeys = ChannelPublicKeys { funding_pubkey: public_from_secret_hex(&secp_ctx, "1552dfba4f6cf29a62a0af13c8d6981d36d0ef8d61ba10fb0fe90da7634d7e13"), - revocation_basepoint: PublicKey::from_slice(&>::from_hex("02466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f27").unwrap()[..]).unwrap(), + revocation_basepoint: RevocationBasepoint::from(PublicKey::from_slice(&>::from_hex("02466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f27").unwrap()[..]).unwrap()), payment_point: public_from_secret_hex(&secp_ctx, "4444444444444444444444444444444444444444444444444444444444444444"), - delayed_payment_basepoint: public_from_secret_hex(&secp_ctx, "1552dfba4f6cf29a62a0af13c8d6981d36d0ef8d61ba10fb0fe90da7634d7e13"), - htlc_basepoint: public_from_secret_hex(&secp_ctx, "4444444444444444444444444444444444444444444444444444444444444444") + delayed_payment_basepoint: DelayedPaymentBasepoint::from(public_from_secret_hex(&secp_ctx, "1552dfba4f6cf29a62a0af13c8d6981d36d0ef8d61ba10fb0fe90da7634d7e13")), + htlc_basepoint: HtlcBasepoint::from(public_from_secret_hex(&secp_ctx, "4444444444444444444444444444444444444444444444444444444444444444")) }; chan.context.channel_transaction_parameters.counterparty_parameters = Some( CounterpartyChannelTransactionParameters { @@ -8394,7 +8397,7 @@ mod tests { assert_eq!(counterparty_pubkeys.funding_pubkey.serialize()[..], >::from_hex("030e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6d3c39fa711c1").unwrap()[..]); - assert_eq!(counterparty_pubkeys.htlc_basepoint.serialize()[..], + assert_eq!(counterparty_pubkeys.htlc_basepoint.to_public_key().serialize()[..], >::from_hex("032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991").unwrap()[..]); // We can't just use build_holder_transaction_keys here as the per_commitment_secret is not @@ -8479,7 +8482,7 @@ mod tests { let htlc_redeemscript = chan_utils::get_htlc_redeemscript(&htlc, $opt_anchors, &keys); let htlc_sighashtype = if $opt_anchors.supports_anchors_zero_fee_htlc_tx() { EcdsaSighashType::SinglePlusAnyoneCanPay } else { EcdsaSighashType::All }; let htlc_sighash = Message::from_slice(&sighash::SighashCache::new(&htlc_tx).segwit_signature_hash(0, &htlc_redeemscript, htlc.amount_msat / 1000, htlc_sighashtype).unwrap()[..]).unwrap(); - assert!(secp_ctx.verify_ecdsa(&htlc_sighash, &remote_signature, &keys.countersignatory_htlc_key).is_ok(), "verify counterparty htlc sig"); + assert!(secp_ctx.verify_ecdsa(&htlc_sighash, &remote_signature, &keys.countersignatory_htlc_key.to_public_key()).is_ok(), "verify counterparty htlc sig"); let mut preimage: Option = None; if !htlc.offered { @@ -9073,7 +9076,7 @@ mod tests { assert_eq!(chan_utils::build_commitment_secret(&seed, 1), >::from_hex("915c75942a26bb3a433a8ce2cb0427c29ec6c1775cfc78328b57f6ba7bfeaa9c").unwrap()[..]); } - + #[test] fn test_key_derivation() { // Test vectors from BOLT 3 Appendix E: @@ -9088,13 +9091,10 @@ mod tests { let per_commitment_point = PublicKey::from_secret_key(&secp_ctx, &per_commitment_secret); assert_eq!(per_commitment_point.serialize()[..], >::from_hex("025f7117a78150fe2ef97db7cfc83bd57b2e2c0d0dd25eaf467a4a1c2a45ce1486").unwrap()[..]); - assert_eq!(chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &base_point).serialize()[..], - >::from_hex("0235f2dbfaa89b57ec7b055afe29849ef7ddfeb1cefdb9ebdc43f5494984db29e5").unwrap()[..]); - assert_eq!(chan_utils::derive_private_key(&secp_ctx, &per_commitment_point, &base_secret), SecretKey::from_slice(&>::from_hex("cbced912d3b21bf196a766651e436aff192362621ce317704ea2f75d87e7be0f").unwrap()[..]).unwrap()); - assert_eq!(chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &base_point).serialize()[..], + assert_eq!(RevocationKey::from_basepoint(&secp_ctx, &RevocationBasepoint::from(base_point), &per_commitment_point).to_public_key().serialize()[..], >::from_hex("02916e326636d19c33f13e8c0c3a03dd157f332f3e99c317c141dd865eb01f8ff0").unwrap()[..]); assert_eq!(chan_utils::derive_private_revocation_key(&secp_ctx, &per_commitment_secret, &base_secret), diff --git a/lightning/src/ln/channel_keys.rs b/lightning/src/ln/channel_keys.rs new file mode 100644 index 000000000..f737dd234 --- /dev/null +++ b/lightning/src/ln/channel_keys.rs @@ -0,0 +1,253 @@ +// This file is Copyright its original authors, visible in version control +// history. +// +// This file is licensed under the Apache License, Version 2.0 or the MIT license +// , at your option. +// You may not use this file except in accordance with one or both of these +// licenses. + +//! Keys used to generate commitment transactions. +//! See: + +use bitcoin::hashes::Hash; +use bitcoin::hashes::HashEngine; +use bitcoin::secp256k1::Scalar; +use bitcoin::secp256k1::SecretKey; +use bitcoin::secp256k1::Secp256k1; +use bitcoin::secp256k1; +use crate::ln::msgs::DecodeError; +use crate::util::ser::Readable; +use crate::io; +use crate::util::ser::Writer; +use crate::util::ser::Writeable; +use bitcoin::secp256k1::PublicKey; +use bitcoin::hashes::sha256::Hash as Sha256; + +macro_rules! doc_comment { + ($x:expr, $($tt:tt)*) => { + #[doc = $x] + $($tt)* + }; +} +macro_rules! basepoint_impl { + ($BasepointT:ty) => { + impl $BasepointT { + /// Get inner Public Key + pub fn to_public_key(&self) -> PublicKey { + self.0 + } + } + + impl From for $BasepointT { + fn from(value: PublicKey) -> Self { + Self(value) + } + } + + } +} +macro_rules! key_impl { + ($BasepointT:ty, $KeyName:expr) => { + doc_comment! { + concat!("Generate ", $KeyName, " using per_commitment_point"), + pub fn from_basepoint( + secp_ctx: &Secp256k1, + basepoint: &$BasepointT, + per_commitment_point: &PublicKey, + ) -> Self { + Self(derive_public_key(secp_ctx, per_commitment_point, &basepoint.0)) + } + } + + doc_comment! { + concat!("Generate ", $KeyName, " from privkey"), + pub fn from_secret_key(secp_ctx: &Secp256k1, sk: &SecretKey) -> Self { + Self(PublicKey::from_secret_key(&secp_ctx, &sk)) + } + } + + /// Get inner Public Key + pub fn to_public_key(&self) -> PublicKey { + self.0 + } + } +} +macro_rules! key_read_write { + ($SelfT:ty) => { + impl Writeable for $SelfT { + fn write(&self, w: &mut W) -> Result<(), io::Error> { + self.0.serialize().write(w) + } + } + + impl Readable for $SelfT { + fn read(r: &mut R) -> Result { + let key: PublicKey = Readable::read(r)?; + Ok(Self(key)) + } + } + } +} + + + +/// Master key used in conjunction with per_commitment_point to generate [`local_delayedpubkey`](https://github.com/lightning/bolts/blob/master/03-transactions.md#key-derivation) for the latest state of a channel. +/// A watcher can be given a [DelayedPaymentBasepoint] to generate per commitment [DelayedPaymentKey] to create justice transactions. +#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)] +pub struct DelayedPaymentBasepoint(pub PublicKey); +basepoint_impl!(DelayedPaymentBasepoint); +key_read_write!(DelayedPaymentBasepoint); + +/// [delayedpubkey](https://github.com/lightning/bolts/blob/master/03-transactions.md#localpubkey-local_htlcpubkey-remote_htlcpubkey-local_delayedpubkey-and-remote_delayedpubkey-derivation) +/// To allow a counterparty to contest a channel state published by a node, Lightning protocol sets delays for some of the outputs, before can be spend. +/// For example a commitment transaction has to_local output encumbered by a delay, negotiated at the channel establishment flow. +/// To spend from such output a node has to generate a script using, among others, a local delayed payment key. +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct DelayedPaymentKey(pub PublicKey); + +impl DelayedPaymentKey { + key_impl!(DelayedPaymentBasepoint, "delayedpubkey"); +} +key_read_write!(DelayedPaymentKey); + +/// Master key used in conjunction with per_commitment_point to generate a [localpubkey](https://github.com/lightning/bolts/blob/master/03-transactions.md#key-derivation) for the latest state of a channel. +/// Also used to generate a commitment number in a commitment transaction or as a Payment Key for a remote node (not us) in an anchor output if `option_static_remotekey` is enabled. +/// Shared by both nodes in a channel establishment message flow. +#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)] +pub struct PaymentBasepoint(pub PublicKey); +basepoint_impl!(PaymentBasepoint); +key_read_write!(PaymentBasepoint); + + +/// [localpubkey](https://github.com/lightning/bolts/blob/master/03-transactions.md#localpubkey-local_htlcpubkey-remote_htlcpubkey-local_delayedpubkey-and-remote_delayedpubkey-derivation) is a child key of a payment basepoint, +/// that enables a secure hash-lock for off-chain payments without risk of funds getting stuck or stolen. A payment key is normally shared with a counterparty so that it can generate +/// a commitment transaction's to_remote ouput, which our node can claim in case the counterparty force closes the channel. +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct PaymentKey(pub PublicKey); + +impl PaymentKey { + key_impl!(PaymentBasepoint, "localpubkey"); +} +key_read_write!(PaymentKey); + +/// Master key used in conjunction with per_commitment_point to generate [htlcpubkey](https://github.com/lightning/bolts/blob/master/03-transactions.md#key-derivation) for the latest state of a channel. +#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)] +pub struct HtlcBasepoint(pub PublicKey); +basepoint_impl!(HtlcBasepoint); +key_read_write!(HtlcBasepoint); + + +/// [htlcpubkey](https://github.com/lightning/bolts/blob/master/03-transactions.md#localpubkey-local_htlcpubkey-remote_htlcpubkey-local_delayedpubkey-and-remote_delayedpubkey-derivation) is a child key of an htlc basepoint, +/// that enables secure routing of payments in onion scheme without a risk of them getting stuck or diverted. It is used to claim the funds in successful or timed out htlc outputs. +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct HtlcKey(pub PublicKey); + +impl HtlcKey { + key_impl!(HtlcBasepoint, "htlcpubkey"); +} +key_read_write!(HtlcKey); + +/// Derives a per-commitment-transaction public key (eg an htlc key or a delayed_payment key) +/// from the base point and the per_commitment_key. This is the public equivalent of +/// derive_private_key - using only public keys to derive a public key instead of private keys. +fn derive_public_key(secp_ctx: &Secp256k1, per_commitment_point: &PublicKey, base_point: &PublicKey) -> PublicKey { + let mut sha = Sha256::engine(); + sha.input(&per_commitment_point.serialize()); + sha.input(&base_point.serialize()); + let res = Sha256::from_engine(sha).to_byte_array(); + + + let hashkey = PublicKey::from_secret_key(&secp_ctx, + &SecretKey::from_slice(&res).expect("Hashes should always be valid keys unless SHA-256 is broken")); + base_point.combine(&hashkey) + .expect("Addition only fails if the tweak is the inverse of the key. This is not possible when the tweak contains the hash of the key.") +} + +/// Master key used in conjunction with per_commitment_point to generate [htlcpubkey](https://github.com/lightning/bolts/blob/master/03-transactions.md#key-derivation) for the latest state of a channel. +/// A watcher can be given a [RevocationBasepoint] to generate per commitment [RevocationKey] to create justice transactions. +#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)] +pub struct RevocationBasepoint(pub PublicKey); +basepoint_impl!(RevocationBasepoint); +key_read_write!(RevocationBasepoint); + + +/// [htlcpubkey](https://github.com/lightning/bolts/blob/master/03-transactions.md#localpubkey-local_htlcpubkey-remote_htlcpubkey-local_delayedpubkey-and-remote_delayedpubkey-derivation) is a child key of a revocation basepoint, +/// that enables a node to create a justice transaction punishing a counterparty for an attempt to steal funds. Used to in generation of commitment and htlc outputs. +#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)] +pub struct RevocationKey(pub PublicKey); + +impl RevocationKey { + /// Derives a per-commitment-transaction revocation public key from its constituent parts. This is + /// the public equivalend of derive_private_revocation_key - using only public keys to derive a + /// public key instead of private keys. + /// + /// Only the cheating participant owns a valid witness to propagate a revoked + /// commitment transaction, thus per_commitment_point always come from cheater + /// and revocation_base_point always come from punisher, which is the broadcaster + /// of the transaction spending with this key knowledge. + /// + /// Note that this is infallible iff we trust that at least one of the two input keys are randomly + /// generated (ie our own). + pub fn from_basepoint( + secp_ctx: &Secp256k1, + basepoint: &RevocationBasepoint, + per_commitment_point: &PublicKey, + ) -> Self { + let rev_append_commit_hash_key = { + let mut sha = Sha256::engine(); + sha.input(&basepoint.to_public_key().serialize()); + sha.input(&per_commitment_point.serialize()); + + Sha256::from_engine(sha).to_byte_array() + }; + let commit_append_rev_hash_key = { + let mut sha = Sha256::engine(); + sha.input(&per_commitment_point.serialize()); + sha.input(&basepoint.to_public_key().serialize()); + + Sha256::from_engine(sha).to_byte_array() + }; + + let countersignatory_contrib = basepoint.to_public_key().mul_tweak(&secp_ctx, &Scalar::from_be_bytes(rev_append_commit_hash_key).unwrap()) + .expect("Multiplying a valid public key by a hash is expected to never fail per secp256k1 docs"); + let broadcaster_contrib = (&per_commitment_point).mul_tweak(&secp_ctx, &Scalar::from_be_bytes(commit_append_rev_hash_key).unwrap()) + .expect("Multiplying a valid public key by a hash is expected to never fail per secp256k1 docs"); + let pk = countersignatory_contrib.combine(&broadcaster_contrib) + .expect("Addition only fails if the tweak is the inverse of the key. This is not possible when the tweak commits to the key."); + Self(pk) + } + + /// Get inner Public Key + pub fn to_public_key(&self) -> PublicKey { + self.0 + } +} +key_read_write!(RevocationKey); + + + +#[cfg(test)] +mod test { + use bitcoin::secp256k1::{Secp256k1, SecretKey, PublicKey}; + use bitcoin::hashes::hex::FromHex; + use super::derive_public_key; + + #[test] + fn test_key_derivation() { + // Test vectors from BOLT 3 Appendix E: + let secp_ctx = Secp256k1::new(); + + let base_secret = SecretKey::from_slice(&>::from_hex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap()[..]).unwrap(); + let per_commitment_secret = SecretKey::from_slice(&>::from_hex("1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100").unwrap()[..]).unwrap(); + + let base_point = PublicKey::from_secret_key(&secp_ctx, &base_secret); + assert_eq!(base_point.serialize()[..], >::from_hex("036d6caac248af96f6afa7f904f550253a0f3ef3f5aa2fe6838a95b216691468e2").unwrap()[..]); + + let per_commitment_point = PublicKey::from_secret_key(&secp_ctx, &per_commitment_secret); + assert_eq!(per_commitment_point.serialize()[..], >::from_hex("025f7117a78150fe2ef97db7cfc83bd57b2e2c0d0dd25eaf467a4a1c2a45ce1486").unwrap()[..]); + + assert_eq!(derive_public_key(&secp_ctx, &per_commitment_point, &base_point).serialize()[..], + >::from_hex("0235f2dbfaa89b57ec7b055afe29849ef7ddfeb1cefdb9ebdc43f5494984db29e5").unwrap()[..]); + } +} diff --git a/lightning/src/ln/mod.rs b/lightning/src/ln/mod.rs index f9d364ed0..fb809041a 100644 --- a/lightning/src/ln/mod.rs +++ b/lightning/src/ln/mod.rs @@ -14,6 +14,7 @@ pub mod functional_test_utils; pub mod channelmanager; +pub mod channel_keys; pub mod inbound_payment; pub mod msgs; pub mod peer_handler; diff --git a/lightning/src/sign/mod.rs b/lightning/src/sign/mod.rs index bc15a3a76..c4d4bc002 100644 --- a/lightning/src/sign/mod.rs +++ b/lightning/src/sign/mod.rs @@ -42,6 +42,7 @@ use crate::chain::transaction::OutPoint; use crate::ln::channel::ANCHOR_OUTPUT_VALUE_SATOSHI; use crate::ln::{chan_utils, PaymentPreimage}; use crate::ln::chan_utils::{HTLCOutputInCommitment, make_funding_redeemscript, ChannelPublicKeys, HolderCommitmentTransaction, ChannelTransactionParameters, CommitmentTransaction, ClosingTransaction}; +use crate::ln::channel_keys::{DelayedPaymentBasepoint, DelayedPaymentKey, HtlcKey, HtlcBasepoint, RevocationKey, RevocationBasepoint}; use crate::ln::msgs::{UnsignedChannelAnnouncement, UnsignedGossipMessage}; use crate::ln::script::ShutdownScript; use crate::offers::invoice::UnsignedBolt12Invoice; @@ -83,7 +84,7 @@ pub struct DelayedPaymentOutputDescriptor { pub output: TxOut, /// The revocation point specific to the commitment transaction which was broadcast. Used to /// derive the witnessScript for this output. - pub revocation_pubkey: PublicKey, + pub revocation_pubkey: RevocationKey, /// Arbitrary identification information returned by a call to [`ChannelSigner::channel_keys_id`]. /// This may be useful in re-deriving keys used in the channel to spend the output. pub channel_keys_id: [u8; 32], @@ -224,8 +225,8 @@ pub enum SpendableOutputDescriptor { /// To derive the delayed payment key which is used to sign this input, you must pass the /// holder [`InMemorySigner::delayed_payment_base_key`] (i.e., the private key which corresponds to the /// [`ChannelPublicKeys::delayed_payment_basepoint`] in [`ChannelSigner::pubkeys`]) and the provided - /// [`DelayedPaymentOutputDescriptor::per_commitment_point`] to [`chan_utils::derive_private_key`]. The public key can be - /// generated without the secret key using [`chan_utils::derive_public_key`] and only the + /// [`DelayedPaymentOutputDescriptor::per_commitment_point`] to [`chan_utils::derive_private_key`]. The DelayedPaymentKey can be + /// generated without the secret key using [`DelayedPaymentKey::from_basepoint`] and only the /// [`ChannelPublicKeys::delayed_payment_basepoint`] which appears in [`ChannelSigner::pubkeys`]. /// /// To derive the [`DelayedPaymentOutputDescriptor::revocation_pubkey`] provided here (which is @@ -233,7 +234,7 @@ pub enum SpendableOutputDescriptor { /// [`ChannelPublicKeys::revocation_basepoint`] (which appears in the call to /// [`ChannelSigner::provide_channel_parameters`]) and the provided /// [`DelayedPaymentOutputDescriptor::per_commitment_point`] to - /// [`chan_utils::derive_public_revocation_key`]. + /// [`RevocationKey`]. /// /// The witness script which is hashed and included in the output `script_pubkey` may be /// regenerated by passing the [`DelayedPaymentOutputDescriptor::revocation_pubkey`] (derived @@ -493,12 +494,10 @@ impl HTLCDescriptor { let channel_params = self.channel_derivation_parameters.transaction_parameters.as_holder_broadcastable(); let broadcaster_keys = channel_params.broadcaster_pubkeys(); let counterparty_keys = channel_params.countersignatory_pubkeys(); - let broadcaster_delayed_key = chan_utils::derive_public_key( - secp, &self.per_commitment_point, &broadcaster_keys.delayed_payment_basepoint - ); - let counterparty_revocation_key = chan_utils::derive_public_revocation_key( - secp, &self.per_commitment_point, &counterparty_keys.revocation_basepoint + let broadcaster_delayed_key = DelayedPaymentKey::from_basepoint( + secp, &broadcaster_keys.delayed_payment_basepoint, &self.per_commitment_point ); + let counterparty_revocation_key = &RevocationKey::from_basepoint(&secp, &counterparty_keys.revocation_basepoint, &self.per_commitment_point); chan_utils::build_htlc_output( self.feerate_per_kw, channel_params.contest_delay(), &self.htlc, channel_params.channel_type_features(), &broadcaster_delayed_key, &counterparty_revocation_key @@ -510,15 +509,13 @@ impl HTLCDescriptor { let channel_params = self.channel_derivation_parameters.transaction_parameters.as_holder_broadcastable(); let broadcaster_keys = channel_params.broadcaster_pubkeys(); let counterparty_keys = channel_params.countersignatory_pubkeys(); - let broadcaster_htlc_key = chan_utils::derive_public_key( - secp, &self.per_commitment_point, &broadcaster_keys.htlc_basepoint - ); - let counterparty_htlc_key = chan_utils::derive_public_key( - secp, &self.per_commitment_point, &counterparty_keys.htlc_basepoint + let broadcaster_htlc_key = HtlcKey::from_basepoint( + secp, &broadcaster_keys.htlc_basepoint, &self.per_commitment_point ); - let counterparty_revocation_key = chan_utils::derive_public_revocation_key( - secp, &self.per_commitment_point, &counterparty_keys.revocation_basepoint + let counterparty_htlc_key = HtlcKey::from_basepoint( + secp, &counterparty_keys.htlc_basepoint, &self.per_commitment_point, ); + let counterparty_revocation_key = &RevocationKey::from_basepoint(&secp, &counterparty_keys.revocation_basepoint, &self.per_commitment_point); chan_utils::get_htlc_redeemscript_with_explicit_keys( &self.htlc, channel_params.channel_type_features(), &broadcaster_htlc_key, &counterparty_htlc_key, &counterparty_revocation_key, @@ -1031,10 +1028,10 @@ impl InMemorySigner { let from_secret = |s: &SecretKey| PublicKey::from_secret_key(secp_ctx, s); ChannelPublicKeys { funding_pubkey: from_secret(&funding_key), - revocation_basepoint: from_secret(&revocation_base_key), + revocation_basepoint: RevocationBasepoint::from(from_secret(&revocation_base_key)), payment_point: from_secret(&payment_key), - delayed_payment_basepoint: from_secret(&delayed_payment_base_key), - htlc_basepoint: from_secret(&htlc_base_key), + delayed_payment_basepoint: DelayedPaymentBasepoint::from(from_secret(&delayed_payment_base_key)), + htlc_basepoint: HtlcBasepoint::from(from_secret(&htlc_base_key)), } } @@ -1173,7 +1170,7 @@ impl InMemorySigner { if spend_tx.input[input_idx].sequence.0 != descriptor.to_self_delay as u32 { return Err(()); } let delayed_payment_key = chan_utils::derive_private_key(&secp_ctx, &descriptor.per_commitment_point, &self.delayed_payment_base_key); - let delayed_payment_pubkey = PublicKey::from_secret_key(&secp_ctx, &delayed_payment_key); + let delayed_payment_pubkey = DelayedPaymentKey::from_secret_key(&secp_ctx, &delayed_payment_key); let witness_script = chan_utils::get_revokeable_redeemscript(&descriptor.revocation_pubkey, descriptor.to_self_delay, &delayed_payment_pubkey); let sighash = hash_to_message!(&sighash::SighashCache::new(spend_tx).segwit_signature_hash(input_idx, &witness_script, descriptor.output.value, EcdsaSighashType::All).unwrap()[..]); let local_delayedsig = EcdsaSignature { @@ -1286,12 +1283,14 @@ impl EcdsaChannelSigner for InMemorySigner { fn sign_justice_revoked_output(&self, justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey, secp_ctx: &Secp256k1) -> Result { let revocation_key = chan_utils::derive_private_revocation_key(&secp_ctx, &per_commitment_key, &self.revocation_base_key); let per_commitment_point = PublicKey::from_secret_key(secp_ctx, &per_commitment_key); - let revocation_pubkey = chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &self.pubkeys().revocation_basepoint); + let revocation_pubkey = RevocationKey::from_basepoint( + &secp_ctx, &self.pubkeys().revocation_basepoint, &per_commitment_point, + ); let witness_script = { let counterparty_keys = self.counterparty_pubkeys().expect(MISSING_PARAMS_ERR); let holder_selected_contest_delay = self.holder_selected_contest_delay().expect(MISSING_PARAMS_ERR); - let counterparty_delayedpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &counterparty_keys.delayed_payment_basepoint); + let counterparty_delayedpubkey = DelayedPaymentKey::from_basepoint(&secp_ctx, &counterparty_keys.delayed_payment_basepoint, &per_commitment_point); chan_utils::get_revokeable_redeemscript(&revocation_pubkey, holder_selected_contest_delay, &counterparty_delayedpubkey) }; let mut sighash_parts = sighash::SighashCache::new(justice_tx); @@ -1302,11 +1301,17 @@ impl EcdsaChannelSigner for InMemorySigner { fn sign_justice_revoked_htlc(&self, justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey, htlc: &HTLCOutputInCommitment, secp_ctx: &Secp256k1) -> Result { let revocation_key = chan_utils::derive_private_revocation_key(&secp_ctx, &per_commitment_key, &self.revocation_base_key); let per_commitment_point = PublicKey::from_secret_key(secp_ctx, &per_commitment_key); - let revocation_pubkey = chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &self.pubkeys().revocation_basepoint); + let revocation_pubkey = RevocationKey::from_basepoint( + &secp_ctx, &self.pubkeys().revocation_basepoint, &per_commitment_point, + ); let witness_script = { let counterparty_keys = self.counterparty_pubkeys().expect(MISSING_PARAMS_ERR); - let counterparty_htlcpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &counterparty_keys.htlc_basepoint); - let holder_htlcpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.pubkeys().htlc_basepoint); + let counterparty_htlcpubkey = HtlcKey::from_basepoint( + &secp_ctx, &counterparty_keys.htlc_basepoint, &per_commitment_point, + ); + let holder_htlcpubkey = HtlcKey::from_basepoint( + &secp_ctx, &self.pubkeys().htlc_basepoint, &per_commitment_point, + ); let chan_type = self.channel_type_features().expect(MISSING_PARAMS_ERR); chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, chan_type, &counterparty_htlcpubkey, &holder_htlcpubkey, &revocation_pubkey) }; @@ -1331,10 +1336,14 @@ impl EcdsaChannelSigner for InMemorySigner { fn sign_counterparty_htlc_transaction(&self, htlc_tx: &Transaction, input: usize, amount: u64, per_commitment_point: &PublicKey, htlc: &HTLCOutputInCommitment, secp_ctx: &Secp256k1) -> Result { let htlc_key = chan_utils::derive_private_key(&secp_ctx, &per_commitment_point, &self.htlc_base_key); - let revocation_pubkey = chan_utils::derive_public_revocation_key(&secp_ctx, &per_commitment_point, &self.pubkeys().revocation_basepoint); + let revocation_pubkey = RevocationKey::from_basepoint( + &secp_ctx, &self.pubkeys().revocation_basepoint, &per_commitment_point, + ); let counterparty_keys = self.counterparty_pubkeys().expect(MISSING_PARAMS_ERR); - let counterparty_htlcpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &counterparty_keys.htlc_basepoint); - let htlcpubkey = chan_utils::derive_public_key(&secp_ctx, &per_commitment_point, &self.pubkeys().htlc_basepoint); + let counterparty_htlcpubkey = HtlcKey::from_basepoint( + &secp_ctx, &counterparty_keys.htlc_basepoint, &per_commitment_point, + ); + let htlcpubkey = HtlcKey::from_basepoint(&secp_ctx, &self.pubkeys().htlc_basepoint, &per_commitment_point); let chan_type = self.channel_type_features().expect(MISSING_PARAMS_ERR); let witness_script = chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, chan_type, &counterparty_htlcpubkey, &htlcpubkey, &revocation_pubkey); let mut sighash_parts = sighash::SighashCache::new(htlc_tx); diff --git a/lightning/src/util/test_channel_signer.rs b/lightning/src/util/test_channel_signer.rs index fe1c8cfe0..9c6a1e302 100644 --- a/lightning/src/util/test_channel_signer.rs +++ b/lightning/src/util/test_channel_signer.rs @@ -9,7 +9,8 @@ use crate::ln::channel::{ANCHOR_OUTPUT_VALUE_SATOSHI, MIN_CHAN_DUST_LIMIT_SATOSHIS}; use crate::ln::chan_utils::{HTLCOutputInCommitment, ChannelPublicKeys, HolderCommitmentTransaction, CommitmentTransaction, ChannelTransactionParameters, TrustedCommitmentTransaction, ClosingTransaction}; -use crate::ln::{chan_utils, msgs, PaymentPreimage}; +use crate::ln::channel_keys::{HtlcKey}; +use crate::ln::{msgs, PaymentPreimage}; use crate::sign::{WriteableEcdsaChannelSigner, InMemorySigner, ChannelSigner, EcdsaChannelSigner}; use crate::prelude::*; @@ -232,11 +233,12 @@ impl EcdsaChannelSigner for TestChannelSigner { let sighash = &sighash::SighashCache::new(&*htlc_tx).segwit_signature_hash( input, &witness_script, htlc_descriptor.htlc.amount_msat / 1000, sighash_type ).unwrap(); - let countersignatory_htlc_key = chan_utils::derive_public_key( - &secp_ctx, &htlc_descriptor.per_commitment_point, &self.inner.counterparty_pubkeys().unwrap().htlc_basepoint + let countersignatory_htlc_key = HtlcKey::from_basepoint( + &secp_ctx, &self.inner.counterparty_pubkeys().unwrap().htlc_basepoint, &htlc_descriptor.per_commitment_point, ); + secp_ctx.verify_ecdsa( - &hash_to_message!(sighash.as_byte_array()), &htlc_descriptor.counterparty_sig, &countersignatory_htlc_key + &hash_to_message!(sighash.as_byte_array()), &htlc_descriptor.counterparty_sig, &countersignatory_htlc_key.to_public_key() ).unwrap(); } Ok(self.inner.sign_holder_htlc_transaction(htlc_tx, input, htlc_descriptor, secp_ctx).unwrap()) -- 2.39.5