diff --git a/lightning/src/chain/chainmonitor.rs b/lightning/src/chain/chainmonitor.rs index f1b9f729cb3..f5ede9a6441 100644 --- a/lightning/src/chain/chainmonitor.rs +++ b/lightning/src/chain/chainmonitor.rs @@ -32,7 +32,7 @@ use crate::chain::chaininterface::{BroadcasterInterface, FeeEstimator}; use crate::chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, Balance, MonitorEvent, TransactionOutputs, WithChannelMonitor}; use crate::chain::transaction::{OutPoint, TransactionData}; use crate::ln::types::ChannelId; -use crate::sign::ecdsa::EcdsaChannelSigner; +use crate::sign::ChannelSigner; use crate::events::{self, Event, EventHandler, ReplayEvent}; use crate::util::logger::{Logger, WithContext}; use crate::util::errors::APIError; @@ -96,12 +96,12 @@ use bitcoin::secp256k1::PublicKey; /// provided for bulding transactions for a watchtower: /// [`ChannelMonitor::initial_counterparty_commitment_tx`], /// [`ChannelMonitor::counterparty_commitment_txs_from_update`], -/// [`ChannelMonitor::sign_to_local_justice_tx`], [`TrustedCommitmentTransaction::revokeable_output_index`], +/// [`ChannelMonitor::punish_revokeable_output`], [`TrustedCommitmentTransaction::revokeable_output_index`], /// [`TrustedCommitmentTransaction::build_to_local_justice_tx`]. /// /// [`TrustedCommitmentTransaction::revokeable_output_index`]: crate::ln::chan_utils::TrustedCommitmentTransaction::revokeable_output_index /// [`TrustedCommitmentTransaction::build_to_local_justice_tx`]: crate::ln::chan_utils::TrustedCommitmentTransaction::build_to_local_justice_tx -pub trait Persist { +pub trait Persist { /// Persist a new channel's data in response to a [`chain::Watch::watch_channel`] call. This is /// called by [`ChannelManager`] for new channels, or may be called directly, e.g. on startup. /// @@ -118,7 +118,7 @@ pub trait Persist { /// /// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager /// [`Writeable::write`]: crate::util::ser::Writeable::write - fn persist_new_channel(&self, channel_funding_outpoint: OutPoint, monitor: &ChannelMonitor) -> ChannelMonitorUpdateStatus; + fn persist_new_channel(&self, channel_funding_outpoint: OutPoint, monitor: &ChannelMonitor) -> ChannelMonitorUpdateStatus; /// Update one channel's data. The provided [`ChannelMonitor`] has already applied the given /// update. @@ -157,7 +157,7 @@ pub trait Persist { /// [`ChannelMonitorUpdateStatus`] for requirements when returning errors. /// /// [`Writeable::write`]: crate::util::ser::Writeable::write - fn update_persisted_channel(&self, channel_funding_outpoint: OutPoint, monitor_update: Option<&ChannelMonitorUpdate>, monitor: &ChannelMonitor) -> ChannelMonitorUpdateStatus; + fn update_persisted_channel(&self, channel_funding_outpoint: OutPoint, monitor_update: Option<&ChannelMonitorUpdate>, monitor: &ChannelMonitor) -> ChannelMonitorUpdateStatus; /// Prevents the channel monitor from being loaded on startup. /// /// Archiving the data in a backup location (rather than deleting it fully) is useful for @@ -172,8 +172,8 @@ pub trait Persist { fn archive_persisted_channel(&self, channel_funding_outpoint: OutPoint); } -struct MonitorHolder { - monitor: ChannelMonitor, +struct MonitorHolder { + monitor: ChannelMonitor, /// The full set of pending monitor updates for this Channel. /// /// Note that this lock must be held from [`ChannelMonitor::update_monitor`] through to @@ -191,7 +191,7 @@ struct MonitorHolder { pending_monitor_updates: Mutex>, } -impl MonitorHolder { +impl MonitorHolder { fn has_pending_updates(&self, pending_monitor_updates_lock: &MutexGuard>) -> bool { !pending_monitor_updates_lock.is_empty() } @@ -201,14 +201,14 @@ impl MonitorHolder { /// /// Note that this holds a mutex in [`ChainMonitor`] and may block other events until it is /// released. -pub struct LockedChannelMonitor<'a, ChannelSigner: EcdsaChannelSigner> { - lock: RwLockReadGuard<'a, HashMap>>, +pub struct LockedChannelMonitor<'a, Signer: ChannelSigner> { + lock: RwLockReadGuard<'a, HashMap>>, funding_txo: OutPoint, } -impl Deref for LockedChannelMonitor<'_, ChannelSigner> { - type Target = ChannelMonitor; - fn deref(&self) -> &ChannelMonitor { +impl Deref for LockedChannelMonitor<'_, Signer> { + type Target = ChannelMonitor; + fn deref(&self) -> &ChannelMonitor { &self.lock.get(&self.funding_txo).expect("Checked at construction").monitor } } @@ -229,14 +229,14 @@ impl Deref for LockedChannelMonitor<'_, Chann /// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager /// [module-level documentation]: crate::chain::chainmonitor /// [`rebroadcast_pending_claims`]: Self::rebroadcast_pending_claims -pub struct ChainMonitor +pub struct ChainMonitor where C::Target: chain::Filter, T::Target: BroadcasterInterface, F::Target: FeeEstimator, L::Target: Logger, - P::Target: Persist, + P::Target: Persist, { - monitors: RwLock>>, + monitors: RwLock>>, chain_source: Option, broadcaster: T, logger: L, @@ -253,12 +253,12 @@ pub struct ChainMonitor ChainMonitor +impl ChainMonitor where C::Target: chain::Filter, T::Target: BroadcasterInterface, F::Target: FeeEstimator, L::Target: Logger, - P::Target: Persist, + P::Target: Persist, { /// Dispatches to per-channel monitors, which are responsible for updating their on-chain view /// of a channel and reacting accordingly based on transactions in the given chain data. See @@ -273,7 +273,7 @@ where C::Target: chain::Filter, /// Calls which represent a new blockchain tip height should set `best_height`. fn process_chain_data(&self, header: &Header, best_height: Option, txdata: &TransactionData, process: FN) where - FN: Fn(&ChannelMonitor, &TransactionData) -> Vec + FN: Fn(&ChannelMonitor, &TransactionData) -> Vec { let err_str = "ChannelMonitor[Update] persistence failed unrecoverably. This indicates we cannot continue normal operation and must shut down."; let funding_outpoints = hash_set_from_iter(self.monitors.read().unwrap().keys().cloned()); @@ -316,8 +316,8 @@ where C::Target: chain::Filter, fn update_monitor_with_chain_data( &self, header: &Header, best_height: Option, txdata: &TransactionData, process: FN, funding_outpoint: &OutPoint, - monitor_state: &MonitorHolder, channel_count: usize, - ) -> Result<(), ()> where FN: Fn(&ChannelMonitor, &TransactionData) -> Vec { + monitor_state: &MonitorHolder, channel_count: usize, + ) -> Result<(), ()> where FN: Fn(&ChannelMonitor, &TransactionData) -> Vec { let monitor = &monitor_state.monitor; let logger = WithChannelMonitor::from(&self.logger, &monitor, None); @@ -428,7 +428,7 @@ where C::Target: chain::Filter, /// /// Note that the result holds a mutex over our monitor set, and should not be held /// indefinitely. - pub fn get_monitor(&self, funding_txo: OutPoint) -> Result, ()> { + pub fn get_monitor(&self, funding_txo: OutPoint) -> Result, ()> { let lock = self.monitors.read().unwrap(); if lock.get(&funding_txo).is_some() { Ok(LockedChannelMonitor { lock, funding_txo }) @@ -472,7 +472,7 @@ where C::Target: chain::Filter, #[cfg(test)] - pub fn remove_monitor(&self, funding_txo: &OutPoint) -> ChannelMonitor { + pub fn remove_monitor(&self, funding_txo: &OutPoint) -> ChannelMonitor { self.monitors.write().unwrap().remove(funding_txo).unwrap().monitor } @@ -670,14 +670,14 @@ where C::Target: chain::Filter, } } -impl -chain::Listen for ChainMonitor +impl +chain::Listen for ChainMonitor where C::Target: chain::Filter, T::Target: BroadcasterInterface, F::Target: FeeEstimator, L::Target: Logger, - P::Target: Persist, + P::Target: Persist, { fn filtered_block_connected(&self, header: &Header, txdata: &TransactionData, height: u32) { log_debug!(self.logger, "New best block {} at height {} provided via block_connected", header.block_hash(), height); @@ -699,14 +699,14 @@ where } } -impl -chain::Confirm for ChainMonitor +impl +chain::Confirm for ChainMonitor where C::Target: chain::Filter, T::Target: BroadcasterInterface, F::Target: FeeEstimator, L::Target: Logger, - P::Target: Persist, + P::Target: Persist, { fn transactions_confirmed(&self, header: &Header, txdata: &TransactionData, height: u32) { log_debug!(self.logger, "{} provided transactions confirmed at height {} in block {}", txdata.len(), height, header.block_hash()); @@ -753,15 +753,15 @@ where } } -impl -chain::Watch for ChainMonitor +impl +chain::Watch for ChainMonitor where C::Target: chain::Filter, T::Target: BroadcasterInterface, F::Target: FeeEstimator, L::Target: Logger, - P::Target: Persist, + P::Target: Persist, { - fn watch_channel(&self, funding_outpoint: OutPoint, monitor: ChannelMonitor) -> Result { + fn watch_channel(&self, funding_outpoint: OutPoint, monitor: ChannelMonitor) -> Result { let logger = WithChannelMonitor::from(&self.logger, &monitor, None); let mut monitors = self.monitors.write().unwrap(); let entry = match monitors.entry(funding_outpoint) { @@ -892,12 +892,12 @@ where C::Target: chain::Filter, } } -impl events::EventsProvider for ChainMonitor +impl events::EventsProvider for ChainMonitor where C::Target: chain::Filter, T::Target: BroadcasterInterface, F::Target: FeeEstimator, L::Target: Logger, - P::Target: Persist, + P::Target: Persist, { /// Processes [`SpendableOutputs`] events produced from each [`ChannelMonitor`] upon maturity. /// diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs index 3f6bdc3f256..7ea4208e36f 100644 --- a/lightning/src/chain/channelmonitor.rs +++ b/lightning/src/chain/channelmonitor.rs @@ -29,7 +29,6 @@ use bitcoin::hashes::Hash; use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::hash_types::{Txid, BlockHash}; -use bitcoin::ecdsa::Signature as BitcoinSignature; use bitcoin::secp256k1::{self, SecretKey, PublicKey, Secp256k1, ecdsa::Signature}; use crate::ln::channel::INITIAL_COMMITMENT_NUMBER; @@ -43,7 +42,7 @@ use crate::chain; use crate::chain::{BestBlock, WatchedOutput}; use crate::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator, LowerBoundedFeeEstimator}; use crate::chain::transaction::{OutPoint, TransactionData}; -use crate::sign::{ChannelDerivationParameters, HTLCDescriptor, SpendableOutputDescriptor, StaticPaymentOutputDescriptor, DelayedPaymentOutputDescriptor, ecdsa::EcdsaChannelSigner, SignerProvider, EntropySource}; +use crate::sign::{ChannelDerivationParameters, HTLCDescriptor, SpendableOutputDescriptor, StaticPaymentOutputDescriptor, DelayedPaymentOutputDescriptor, ChannelSigner, SignerProvider, EntropySource}; use crate::chain::onchaintx::{ClaimEvent, FeerateStrategy, OnchainTxHandler}; use crate::chain::package::{CounterpartyOfferedHTLCOutput, CounterpartyReceivedHTLCOutput, HolderFundingOutput, HolderHTLCOutput, PackageSolvingData, PackageTemplate, RevokedOutput, RevokedHTLCOutput}; use crate::chain::Filter; @@ -847,14 +846,14 @@ impl Readable for IrrevocablyResolvedHTLC { /// the "reorg path" (ie disconnecting blocks until you find a common ancestor from both the /// returned block hash and the the current chain and then reconnecting blocks to get to the /// best chain) upon deserializing the object! -pub struct ChannelMonitor { +pub struct ChannelMonitor { #[cfg(test)] pub(crate) inner: Mutex>, #[cfg(not(test))] pub(super) inner: Mutex>, } -impl Clone for ChannelMonitor where Signer: Clone { +impl Clone for ChannelMonitor where Signer: Clone { fn clone(&self) -> Self { let inner = self.inner.lock().unwrap().clone(); ChannelMonitor::from_impl(inner) @@ -862,7 +861,7 @@ impl Clone for ChannelMonitor where Signer: } #[derive(Clone, PartialEq)] -pub(crate) struct ChannelMonitorImpl { +pub(crate) struct ChannelMonitorImpl { latest_update_id: u64, commitment_transaction_number_obscure_factor: u64, @@ -879,6 +878,7 @@ pub(crate) struct ChannelMonitorImpl { prev_counterparty_commitment_txid: Option, counterparty_commitment_params: CounterpartyCommitmentParameters, + // this field is not used, but kept for backwards compatibility funding_redeemscript: ScriptBuf, channel_value_satoshis: u64, // first is the idx of the first of the two per-commitment points @@ -1028,7 +1028,7 @@ pub(crate) struct ChannelMonitorImpl { /// Transaction outputs to watch for on-chain spends. pub type TransactionOutputs = (Txid, Vec<(u32, TxOut)>); -impl PartialEq for ChannelMonitor where Signer: PartialEq { +impl PartialEq for ChannelMonitor where Signer: PartialEq { fn eq(&self, other: &Self) -> bool { // We need some kind of total lockorder. Absent a better idea, we sort by position in // memory and take locks in that order (assuming that we can't move within memory while a @@ -1040,7 +1040,7 @@ impl PartialEq for ChannelMonitor where Sign } } -impl Writeable for ChannelMonitor { +impl Writeable for ChannelMonitor { fn write(&self, writer: &mut W) -> Result<(), Error> { self.inner.lock().unwrap().write(writer) } @@ -1050,7 +1050,7 @@ impl Writeable for ChannelMonitor { const SERIALIZATION_VERSION: u8 = 1; const MIN_SERIALIZATION_VERSION: u8 = 1; -impl Writeable for ChannelMonitorImpl { +impl Writeable for ChannelMonitorImpl { fn write(&self, writer: &mut W) -> Result<(), Error> { write_ver_prefix!(writer, SERIALIZATION_VERSION, MIN_SERIALIZATION_VERSION); @@ -1317,11 +1317,11 @@ impl<'a, L: Deref> Logger for WithChannelMonitor<'a, L> where L::Target: Logger } impl<'a, L: Deref> WithChannelMonitor<'a, L> where L::Target: Logger { - pub(crate) fn from(logger: &'a L, monitor: &ChannelMonitor, payment_hash: Option) -> Self { + pub(crate) fn from(logger: &'a L, monitor: &ChannelMonitor, payment_hash: Option) -> Self { Self::from_impl(logger, &*monitor.inner.lock().unwrap(), payment_hash) } - pub(crate) fn from_impl(logger: &'a L, monitor_impl: &ChannelMonitorImpl, payment_hash: Option) -> Self { + pub(crate) fn from_impl(logger: &'a L, monitor_impl: &ChannelMonitorImpl, payment_hash: Option) -> Self { let peer_id = monitor_impl.counterparty_node_id; let channel_id = Some(monitor_impl.channel_id()); WithChannelMonitor { @@ -1330,7 +1330,7 @@ impl<'a, L: Deref> WithChannelMonitor<'a, L> where L::Target: Logger { } } -impl ChannelMonitor { +impl ChannelMonitor { /// For lockorder enforcement purposes, we need to have a single site which constructs the /// `inner` mutex, otherwise cases where we lock two monitors at the same time (eg in our /// PartialEq implementation) we may decide a lockorder violation has occurred. @@ -1338,19 +1338,19 @@ impl ChannelMonitor { ChannelMonitor { inner: Mutex::new(imp) } } - pub(crate) fn new(secp_ctx: Secp256k1, keys: Signer, shutdown_script: Option, + pub(crate) fn new(secp_ctx: Secp256k1, mut keys: Signer, shutdown_script: Option, on_counterparty_tx_csv: u16, destination_script: &Script, funding_info: (OutPoint, ScriptBuf), channel_parameters: &ChannelTransactionParameters, holder_pays_commitment_tx_fee: bool, - funding_redeemscript: ScriptBuf, channel_value_satoshis: u64, + channel_value_satoshis: u64, commitment_transaction_number_obscure_factor: u64, initial_holder_commitment_tx: HolderCommitmentTransaction, best_block: BestBlock, counterparty_node_id: PublicKey, channel_id: ChannelId, ) -> ChannelMonitor { + keys.provide_channel_parameters(channel_parameters); + assert!(commitment_transaction_number_obscure_factor <= (1 << 48)); - let counterparty_payment_script = chan_utils::get_counterparty_payment_script( - &channel_parameters.channel_type_features, &keys.pubkeys().payment_point - ); + let counterparty_payment_script = keys.get_counterparty_payment_script(false); let counterparty_channel_parameters = channel_parameters.counterparty_parameters.as_ref().unwrap(); let counterparty_delayed_payment_base_key = counterparty_channel_parameters.pubkeys.delayed_payment_basepoint; @@ -1405,7 +1405,7 @@ impl ChannelMonitor { prev_counterparty_commitment_txid: None, counterparty_commitment_params, - funding_redeemscript, + funding_redeemscript: ScriptBuf::new(), channel_value_satoshis, their_cur_per_commitment_points: None, @@ -1675,8 +1675,8 @@ impl ChannelMonitor { /// This is provided so that watchtower clients in the persistence pipeline are able to build /// justice transactions for each counterparty commitment upon each update. It's intended to be /// used within an implementation of [`Persist::update_persisted_channel`], which is provided - /// with a monitor and an update. Once revoked, signing a justice transaction can be done using - /// [`Self::sign_to_local_justice_tx`]. + /// with a monitor and an update. Once revoked, punishing a revokeable output can be done using + /// [`Self::punish_revokeable_output`]. /// /// It is expected that a watchtower client may use this method to retrieve the latest counterparty /// commitment transaction(s), and then hold the necessary data until a later update in which @@ -1692,12 +1692,12 @@ impl ChannelMonitor { self.inner.lock().unwrap().counterparty_commitment_txs_from_update(update) } - /// Wrapper around [`EcdsaChannelSigner::sign_justice_revoked_output`] to make - /// signing the justice transaction easier for implementors of + /// Wrapper around [`ChannelSigner::punish_revokeable_output`] to make + /// punishing a revokeable output easier for implementors of /// [`chain::chainmonitor::Persist`]. On success this method returns the provided transaction - /// signing the input at `input_idx`. This method will only produce a valid signature for + /// finalizing the input at `input_idx`. This method will only produce a valid transaction for /// a transaction spending the `to_local` output of a commitment transaction, i.e. this cannot - /// be used for revoked HTLC outputs. + /// be used for revoked HTLC outputs of a commitment transaction. /// /// `Value` is the value of the output being spent by the input at `input_idx`, committed /// in the BIP 143 signature. @@ -1707,10 +1707,10 @@ impl ChannelMonitor { /// to the commitment transaction being revoked, this will return a signed transaction, but /// the signature will not be valid. /// - /// [`EcdsaChannelSigner::sign_justice_revoked_output`]: crate::sign::ecdsa::EcdsaChannelSigner::sign_justice_revoked_output + /// [`ChannelSigner::punish_revokeable_output`]: crate::sign::ChannelSigner::punish_revokeable_output /// [`Persist`]: crate::chain::chainmonitor::Persist - pub fn sign_to_local_justice_tx(&self, justice_tx: Transaction, input_idx: usize, value: u64, commitment_number: u64) -> Result { - self.inner.lock().unwrap().sign_to_local_justice_tx(justice_tx, input_idx, value, commitment_number) + pub fn punish_revokeable_output(&self, justice_tx: Transaction, input_idx: usize, value: u64, commitment_number: u64) -> Result { + self.inner.lock().unwrap().punish_revokeable_output(justice_tx, input_idx, value, commitment_number) } pub(crate) fn get_min_seen_secret(&self) -> u64 { @@ -2086,7 +2086,7 @@ impl ChannelMonitor { } } -impl ChannelMonitorImpl { +impl ChannelMonitorImpl { /// Helper for get_claimable_balances which does the work for an individual HTLC, generating up /// to one `Balance` for the HTLC. fn get_htlc_balance(&self, htlc: &HTLCOutputInCommitment, source: Option<&HTLCSource>, @@ -2277,7 +2277,7 @@ impl ChannelMonitorImpl { } } -impl ChannelMonitor { +impl ChannelMonitor { /// Gets the balances in this channel which are either claimable by us if we were to /// force-close the channel now or which are claimable on-chain (possibly awaiting /// confirmation). @@ -2737,7 +2737,7 @@ pub fn deliberately_bogus_accepted_htlc_witness() -> Vec> { vec![Vec::new(), Vec::new(), Vec::new(), Vec::new(), deliberately_bogus_accepted_htlc_witness_program().into()].into() } -impl ChannelMonitorImpl { +impl ChannelMonitorImpl { /// Gets the [`ConfirmationTarget`] we should use when selecting feerates for channel closure /// transactions for this channel right now. fn closure_conf_target(&self) -> ConfirmationTarget { @@ -3047,21 +3047,21 @@ impl ChannelMonitorImpl { // holder commitment transactions. if self.broadcasted_holder_revokable_script.is_some() { let holder_commitment_tx = if self.current_holder_commitment_tx.txid == confirmed_spend_txid { - Some(&self.current_holder_commitment_tx) + Some((&self.current_holder_commitment_tx, false)) } else if let Some(prev_holder_commitment_tx) = &self.prev_holder_signed_commitment_tx { if prev_holder_commitment_tx.txid == confirmed_spend_txid { - Some(prev_holder_commitment_tx) + Some((prev_holder_commitment_tx, true)) } else { None } } else { None }; - if let Some(holder_commitment_tx) = holder_commitment_tx { + if let Some((holder_commitment_tx, is_previous_tx)) = holder_commitment_tx { // Assume that the broadcasted commitment transaction confirmed in the current best // block. Even if not, its a reasonable metric for the bump criteria on the HTLC // transactions. - let (claim_reqs, _) = self.get_broadcasted_holder_claims(&holder_commitment_tx, self.best_block.height); + let (claim_reqs, _) = self.get_broadcasted_holder_claims(&holder_commitment_tx, self.best_block.height, is_previous_tx); let conf_target = self.closure_conf_target(); self.onchain_tx_handler.update_claims_view_from_requests(claim_reqs, self.best_block.height, self.best_block.height, broadcaster, conf_target, fee_estimator, logger); } @@ -3070,7 +3070,6 @@ impl ChannelMonitorImpl { fn generate_claimable_outpoints_and_watch_outputs(&mut self, reason: ClosureReason) -> (Vec, Vec) { let funding_outp = HolderFundingOutput::build( - self.funding_redeemscript.clone(), self.channel_value_satoshis, self.onchain_tx_handler.channel_type_features().clone() ); @@ -3100,7 +3099,7 @@ impl ChannelMonitorImpl { // assuming it gets confirmed in the next block. Sadly, we have code which considers // "not yet confirmed" things as discardable, so we cannot do that here. let (mut new_outpoints, _) = self.get_broadcasted_holder_claims( - &self.current_holder_commitment_tx, self.best_block.height, + &self.current_holder_commitment_tx, self.best_block.height, false, ); let unsigned_commitment_tx = self.onchain_tx_handler.get_unsigned_holder_commitment_tx(); let new_outputs = self.get_broadcasted_holder_watch_outputs( @@ -3410,17 +3409,15 @@ impl ChannelMonitorImpl { let countersignatory_keys = &self.onchain_tx_handler.channel_transaction_parameters.holder_pubkeys; - let broadcaster_funding_key = broadcaster_keys.funding_pubkey; - let countersignatory_funding_key = countersignatory_keys.funding_pubkey; let keys = TxCreationKeys::from_channel_static_keys(&their_per_commitment_point, &broadcaster_keys, &countersignatory_keys, &self.onchain_tx_handler.secp_ctx); let channel_parameters = &self.onchain_tx_handler.channel_transaction_parameters.as_counterparty_broadcastable(); CommitmentTransaction::new_with_auxiliary_htlc_data(commitment_number, - to_broadcaster_value, to_countersignatory_value, broadcaster_funding_key, - countersignatory_funding_key, keys, feerate_per_kw, &mut nondust_htlcs, - channel_parameters) + to_broadcaster_value, to_countersignatory_value, + keys, feerate_per_kw, &mut nondust_htlcs, + channel_parameters, &self.onchain_tx_handler.signer, &self.onchain_tx_handler.secp_ctx, false) } fn counterparty_commitment_txs_from_update(&self, update: &ChannelMonitorUpdate) -> Vec { @@ -3449,26 +3446,14 @@ impl ChannelMonitorImpl { }).collect() } - fn sign_to_local_justice_tx( + fn punish_revokeable_output( &self, mut justice_tx: Transaction, input_idx: usize, value: u64, commitment_number: u64 ) -> Result { let secret = self.get_secret(commitment_number).ok_or(())?; let per_commitment_key = SecretKey::from_slice(&secret).map_err(|_| ())?; let their_per_commitment_point = PublicKey::from_secret_key( &self.onchain_tx_handler.secp_ctx, &per_commitment_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); - - let sig = self.onchain_tx_handler.signer.sign_justice_revoked_output( - &justice_tx, input_idx, value, &per_commitment_key, &self.onchain_tx_handler.secp_ctx)?; - justice_tx.input[input_idx].witness.push_ecdsa_signature(&BitcoinSignature::sighash_all(sig)); - justice_tx.input[input_idx].witness.push(&[1u8]); - justice_tx.input[input_idx].witness.push(revokeable_redeemscript.as_bytes()); + justice_tx.input[input_idx].witness = self.onchain_tx_handler.signer.punish_revokeable_output(&justice_tx, input_idx, value, &per_commitment_key, &self.onchain_tx_handler.secp_ctx, &their_per_commitment_point)?; Ok(justice_tx) } @@ -3521,16 +3506,13 @@ 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 = 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_p2wsh(); + let revokeable_spk = self.onchain_tx_handler.signer.get_revokeable_spk(false, commitment_number, &per_commitment_point, &self.onchain_tx_handler.secp_ctx); + let punishment_witness_weight = self.onchain_tx_handler.signer.get_punishment_witness_weight(); // First, process non-htlc outputs (to_holder & to_counterparty) for (idx, outp) in tx.output.iter().enumerate() { - if outp.script_pubkey == revokeable_p2wsh { - let revk_outp = RevokedOutput::build(per_commitment_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key, self.counterparty_commitment_params.counterparty_htlc_base_key, per_commitment_key, outp.value, self.counterparty_commitment_params.on_counterparty_tx_csv, self.onchain_tx_handler.channel_type_features().supports_anchors_zero_fee_htlc_tx()); + if outp.script_pubkey == revokeable_spk { + let revk_outp = RevokedOutput::build(per_commitment_point, per_commitment_key, outp.value, self.onchain_tx_handler.channel_type_features().supports_anchors_zero_fee_htlc_tx(), punishment_witness_weight); let justice_package = PackageTemplate::build_package( commitment_txid, idx as u32, PackageSolvingData::RevokedOutput(revk_outp), @@ -3551,7 +3533,8 @@ impl ChannelMonitorImpl { // per_commitment_data is corrupt or our commitment signing key leaked! return (claimable_outpoints, to_counterparty_output_info); } - let revk_htlc_outp = RevokedHTLCOutput::build(per_commitment_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key, self.counterparty_commitment_params.counterparty_htlc_base_key, per_commitment_key, htlc.amount_msat / 1000, htlc.clone(), &self.onchain_tx_handler.channel_transaction_parameters.channel_type_features); + let htlc_punishment_witness_weight = self.onchain_tx_handler.signer.get_htlc_punishment_witness_weight(htlc.offered); + let revk_htlc_outp = RevokedHTLCOutput::build(per_commitment_point, per_commitment_key, htlc.amount_msat / 1000, htlc.clone(), htlc_punishment_witness_weight); let justice_package = PackageTemplate::build_package( commitment_txid, transaction_output_index, @@ -3639,16 +3622,9 @@ impl ChannelMonitorImpl { } else { return (claimable_outpoints, to_counterparty_output_info); }; if let Some(transaction) = tx { - 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_p2wsh(); + let revokeable_spk = self.onchain_tx_handler.signer.get_revokeable_spk(false, commitment_number, &per_commitment_point, &self.onchain_tx_handler.secp_ctx); for (idx, outp) in transaction.output.iter().enumerate() { - if outp.script_pubkey == revokeable_p2wsh { + if outp.script_pubkey == revokeable_spk { to_counterparty_output_info = Some((idx.try_into().expect("Can't have > 2^32 outputs"), outp.value)); } @@ -3666,18 +3642,15 @@ impl ChannelMonitorImpl { } let preimage = if htlc.offered { if let Some((p, _)) = self.payment_preimages.get(&htlc.payment_hash) { Some(*p) } else { None } } else { None }; if preimage.is_some() || !htlc.offered { + let weight = self.onchain_tx_handler.signer.counterparty_htlc_output_witness_weight(htlc.offered); let counterparty_htlc_outp = if htlc.offered { PackageSolvingData::CounterpartyOfferedHTLCOutput( CounterpartyOfferedHTLCOutput::build(*per_commitment_point, - self.counterparty_commitment_params.counterparty_delayed_payment_base_key, - self.counterparty_commitment_params.counterparty_htlc_base_key, - preimage.unwrap(), htlc.clone(), self.onchain_tx_handler.channel_type_features().clone())) + preimage.unwrap(), htlc.clone(), self.onchain_tx_handler.channel_type_features().clone(), weight)) } else { PackageSolvingData::CounterpartyReceivedHTLCOutput( CounterpartyReceivedHTLCOutput::build(*per_commitment_point, - self.counterparty_commitment_params.counterparty_delayed_payment_base_key, - self.counterparty_commitment_params.counterparty_htlc_base_key, - htlc.clone(), self.onchain_tx_handler.channel_type_features().clone())) + htlc.clone(), self.onchain_tx_handler.channel_type_features().clone(), weight)) }; let counterparty_package = PackageTemplate::build_package(commitment_txid, transaction_output_index, counterparty_htlc_outp, htlc.cltv_expiry); claimable_outpoints.push(counterparty_package); @@ -3690,7 +3663,7 @@ impl ChannelMonitorImpl { /// Attempts to claim a counterparty HTLC-Success/HTLC-Timeout's outputs using the revocation key fn check_spend_counterparty_htlc( - &mut self, tx: &Transaction, commitment_number: u64, commitment_txid: &Txid, height: u32, logger: &L + &mut self, tx: &Transaction, commitment_number: u64, height: u32, logger: &L ) -> (Vec, Option) where L::Target: Logger { let secret = if let Some(secret) = self.get_secret(commitment_number) { secret } else { return (Vec::new(), None); }; let per_commitment_key = match SecretKey::from_slice(&secret) { @@ -3698,6 +3671,8 @@ impl ChannelMonitorImpl { Err(_) => return (Vec::new(), None) }; let per_commitment_point = PublicKey::from_secret_key(&self.onchain_tx_handler.secp_ctx, &per_commitment_key); + let revokeable_spk = self.onchain_tx_handler.signer.get_revokeable_spk(false, commitment_number, &per_commitment_point, &self.onchain_tx_handler.secp_ctx); + let punishment_witness_weight = self.onchain_tx_handler.signer.get_punishment_witness_weight(); let htlc_txid = tx.compute_txid(); let mut claimable_outpoints = vec![]; @@ -3709,17 +3684,16 @@ impl ChannelMonitorImpl { // confirmed revoked HTLC transaction (for more details, see // https://lists.linuxfoundation.org/pipermail/lightning-dev/2022-April/003561.html). // - // We make sure we're not vulnerable to this case by checking all inputs of the transaction, - // and claim those which spend the commitment transaction, have a witness of 5 elements, and - // have a corresponding output at the same index within the transaction. - for (idx, input) in tx.input.iter().enumerate() { - if input.previous_output.txid == *commitment_txid && input.witness.len() == 5 && tx.output.get(idx).is_some() { + // We make sure we're not vulnerable to this case by checking all outputs of the transaction, + // and claim those which have the expected revokeable script pubkey for the given commitment number. + for (idx, outp) in tx.output.iter().enumerate() { + if revokeable_spk == outp.script_pubkey { log_error!(logger, "Got broadcast of revoked counterparty HTLC transaction, spending {}:{}", htlc_txid, idx); let revk_outp = RevokedOutput::build( - per_commitment_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key, - self.counterparty_commitment_params.counterparty_htlc_base_key, per_commitment_key, - tx.output[idx].value, self.counterparty_commitment_params.on_counterparty_tx_csv, - false + per_commitment_point, + per_commitment_key, + tx.output[idx].value, + false, punishment_witness_weight, ); let justice_package = PackageTemplate::build_package( htlc_txid, idx as u32, PackageSolvingData::RevokedOutput(revk_outp), @@ -3738,11 +3712,15 @@ 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, RevocationKey)>) { + fn get_broadcasted_holder_claims(&self, holder_tx: &HolderSignedTx, conf_height: u32, is_previous_tx: bool) -> (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); - let broadcasted_holder_revokable_script = Some((redeemscript.to_p2wsh(), holder_tx.per_commitment_point.clone(), holder_tx.revocation_key.clone())); + let commitment_number = if is_previous_tx { + self.current_holder_commitment_number + 1 + } else { + self.current_holder_commitment_number + }; + let revokeable_spk = self.onchain_tx_handler.signer.get_revokeable_spk(true, commitment_number, &holder_tx.per_commitment_point, &self.onchain_tx_handler.secp_ctx); + let broadcasted_holder_revokable_script = Some((revokeable_spk, holder_tx.per_commitment_point.clone(), holder_tx.revocation_key.clone())); for &(ref htlc, _, _) in holder_tx.htlc_outputs.iter() { if let Some(transaction_output_index) = htlc.transaction_output_index { @@ -3809,7 +3787,7 @@ impl ChannelMonitorImpl { if self.current_holder_commitment_tx.txid == commitment_txid { is_holder_tx = true; log_info!(logger, "Got broadcast of latest holder commitment tx {}, searching for available HTLCs to claim", commitment_txid); - let res = self.get_broadcasted_holder_claims(&self.current_holder_commitment_tx, height); + let res = self.get_broadcasted_holder_claims(&self.current_holder_commitment_tx, height, false); let mut to_watch = self.get_broadcasted_holder_watch_outputs(&self.current_holder_commitment_tx, tx); append_onchain_update!(res, to_watch); fail_unbroadcast_htlcs!(self, "latest holder", commitment_txid, tx, height, @@ -3819,7 +3797,7 @@ impl ChannelMonitorImpl { if holder_tx.txid == commitment_txid { is_holder_tx = true; log_info!(logger, "Got broadcast of previous holder commitment tx {}, searching for available HTLCs to claim", commitment_txid); - let res = self.get_broadcasted_holder_claims(holder_tx, height); + let res = self.get_broadcasted_holder_claims(holder_tx, height, true); let mut to_watch = self.get_broadcasted_holder_watch_outputs(holder_tx, tx); append_onchain_update!(res, to_watch); fail_unbroadcast_htlcs!(self, "previous holder", commitment_txid, tx, height, block_hash, @@ -3895,7 +3873,7 @@ impl ChannelMonitorImpl { &mut self, logger: &WithChannelMonitor ) -> Vec where L::Target: Logger { log_debug!(logger, "Getting signed copy of latest holder commitment transaction!"); - let commitment_tx = self.onchain_tx_handler.get_fully_signed_copy_holder_tx(&self.funding_redeemscript); + let commitment_tx = self.onchain_tx_handler.get_fully_signed_copy_holder_tx(); let txid = commitment_tx.compute_txid(); let mut holder_transactions = vec![commitment_tx]; // When anchor outputs are present, the HTLC transactions are only final once the commitment @@ -4085,7 +4063,7 @@ impl ChannelMonitorImpl { let commitment_txid = tx_input.previous_output.txid; if let Some(&commitment_number) = self.counterparty_commitment_txn_on_chain.get(&commitment_txid) { let (mut new_outpoints, new_outputs_option) = self.check_spend_counterparty_htlc( - &tx, commitment_number, &commitment_txid, height, &logger + &tx, commitment_number, height, &logger ); claimable_outpoints.append(&mut new_outpoints); if let Some(new_outputs) = new_outputs_option { @@ -4633,6 +4611,7 @@ impl ChannelMonitorImpl { fn get_spendable_outputs(&self, tx: &Transaction) -> Vec { let mut spendable_outputs = Vec::new(); + let signer = &self.onchain_tx_handler.signer; for (i, outp) in tx.output.iter().enumerate() { if outp.script_pubkey == self.destination_script { spendable_outputs.push(SpendableOutputDescriptor::StaticOutput { @@ -4652,6 +4631,7 @@ impl ChannelMonitorImpl { channel_keys_id: self.channel_keys_id, channel_value_satoshis: self.channel_value_satoshis, channel_transaction_parameters: Some(self.onchain_tx_handler.channel_transaction_parameters.clone()), + witness_weight: signer.get_to_local_witness_weight(), })); } } @@ -4662,6 +4642,7 @@ impl ChannelMonitorImpl { channel_keys_id: self.channel_keys_id, channel_value_satoshis: self.channel_value_satoshis, channel_transaction_parameters: Some(self.onchain_tx_handler.channel_transaction_parameters.clone()), + witness_weight: signer.get_to_remote_witness_weight(), })); } if self.shutdown_script.as_ref() == Some(&outp.script_pubkey) { @@ -4694,7 +4675,7 @@ impl ChannelMonitorImpl { } } -impl chain::Listen for (ChannelMonitor, T, F, L) +impl chain::Listen for (ChannelMonitor, T, F, L) where T::Target: BroadcasterInterface, F::Target: FeeEstimator, @@ -4709,7 +4690,7 @@ where } } -impl chain::Confirm for (M, T, F, L) +impl chain::Confirm for (M, T, F, L) where M: Deref>, T::Target: BroadcasterInterface, @@ -5300,7 +5281,7 @@ mod tests { let monitor = ChannelMonitor::new(Secp256k1::new(), keys, Some(ShutdownScript::new_p2wpkh_from_pubkey(shutdown_pubkey).into_inner()), 0, &ScriptBuf::new(), (OutPoint { txid: Txid::from_slice(&[43; 32]).unwrap(), index: 0 }, ScriptBuf::new()), - &channel_parameters, true, ScriptBuf::new(), 46, 0, HolderCommitmentTransaction::dummy(&mut Vec::new()), + &channel_parameters, true, 46, 0, HolderCommitmentTransaction::dummy(&mut Vec::new()), best_block, dummy_key, channel_id); let mut htlcs = preimages_slice_to_htlcs!(preimages[0..10]); @@ -5550,7 +5531,7 @@ mod tests { let monitor = ChannelMonitor::new(Secp256k1::new(), keys, Some(ShutdownScript::new_p2wpkh_from_pubkey(shutdown_pubkey).into_inner()), 0, &ScriptBuf::new(), (OutPoint { txid: Txid::from_slice(&[43; 32]).unwrap(), index: 0 }, ScriptBuf::new()), - &channel_parameters, true, ScriptBuf::new(), 46, 0, HolderCommitmentTransaction::dummy(&mut Vec::new()), + &channel_parameters, true, 46, 0, HolderCommitmentTransaction::dummy(&mut Vec::new()), best_block, dummy_key, channel_id); let chan_id = monitor.inner.lock().unwrap().channel_id(); diff --git a/lightning/src/chain/mod.rs b/lightning/src/chain/mod.rs index 36b1ce57309..a94be9b8d94 100644 --- a/lightning/src/chain/mod.rs +++ b/lightning/src/chain/mod.rs @@ -18,7 +18,7 @@ use bitcoin::secp256k1::PublicKey; use crate::chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, MonitorEvent}; use crate::ln::types::ChannelId; -use crate::sign::ecdsa::EcdsaChannelSigner; +use crate::sign::ChannelSigner; use crate::chain::transaction::{OutPoint, TransactionData}; use crate::impl_writeable_tlv_based; @@ -260,7 +260,7 @@ pub enum ChannelMonitorUpdateStatus { /// application crashes. /// /// See method documentation and [`ChannelMonitorUpdateStatus`] for specific requirements. -pub trait Watch { +pub trait Watch { /// Watches a channel identified by `funding_txo` using `monitor`. /// /// Implementations are responsible for watching the chain for the funding transaction along @@ -276,7 +276,7 @@ pub trait Watch { /// [`get_outputs_to_watch`]: channelmonitor::ChannelMonitor::get_outputs_to_watch /// [`block_connected`]: channelmonitor::ChannelMonitor::block_connected /// [`block_disconnected`]: channelmonitor::ChannelMonitor::block_disconnected - fn watch_channel(&self, funding_txo: OutPoint, monitor: ChannelMonitor) -> Result; + fn watch_channel(&self, funding_txo: OutPoint, monitor: ChannelMonitor) -> Result; /// Updates a channel identified by `funding_txo` by applying `update` to its monitor. /// diff --git a/lightning/src/chain/onchaintx.rs b/lightning/src/chain/onchaintx.rs index cf7f8d7bfe0..df48e9da22c 100644 --- a/lightning/src/chain/onchaintx.rs +++ b/lightning/src/chain/onchaintx.rs @@ -16,7 +16,7 @@ use bitcoin::amount::Amount; use bitcoin::locktime::absolute::LockTime; use bitcoin::transaction::Transaction; use bitcoin::transaction::OutPoint as BitcoinOutPoint; -use bitcoin::script::{Script, ScriptBuf}; +use bitcoin::script::ScriptBuf; use bitcoin::hashes::{Hash, HashEngine}; use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::hash_types::{Txid, BlockHash}; @@ -25,10 +25,10 @@ use bitcoin::secp256k1::{Secp256k1, ecdsa::Signature}; use bitcoin::secp256k1; use crate::chain::chaininterface::{ConfirmationTarget, compute_feerate_sat_per_1000_weight}; -use crate::sign::{ChannelDerivationParameters, HTLCDescriptor, ChannelSigner, EntropySource, SignerProvider, ecdsa::EcdsaChannelSigner}; +use crate::sign::{ChannelDerivationParameters, HTLCDescriptor, ChannelSigner, EntropySource, SignerProvider}; use crate::ln::msgs::DecodeError; use crate::types::payment::PaymentPreimage; -use crate::ln::chan_utils::{self, ChannelTransactionParameters, HTLCOutputInCommitment, HolderCommitmentTransaction}; +use crate::ln::chan_utils::{ChannelTransactionParameters, HTLCOutputInCommitment, HolderCommitmentTransaction}; use crate::chain::ClaimId; use crate::chain::chaininterface::{FeeEstimator, BroadcasterInterface, LowerBoundedFeeEstimator}; use crate::chain::channelmonitor::ANTI_REORG_DELAY; @@ -228,14 +228,14 @@ pub(crate) enum FeerateStrategy { /// OnchainTxHandler receives claiming requests, aggregates them if it's sound, broadcast and /// do RBF bumping if possible. #[derive(Clone)] -pub struct OnchainTxHandler { +pub struct OnchainTxHandler { channel_value_satoshis: u64, channel_keys_id: [u8; 32], destination_script: ScriptBuf, holder_commitment: HolderCommitmentTransaction, prev_holder_commitment: Option, - pub(super) signer: ChannelSigner, + pub(super) signer: Signer, pub(crate) channel_transaction_parameters: ChannelTransactionParameters, // Used to track claiming requests. If claim tx doesn't confirm before height timer expiration we need to bump @@ -284,7 +284,7 @@ pub struct OnchainTxHandler { pub(super) secp_ctx: Secp256k1, } -impl PartialEq for OnchainTxHandler { +impl PartialEq for OnchainTxHandler { fn eq(&self, other: &Self) -> bool { // `signer`, `secp_ctx`, and `pending_claim_events` are excluded on purpose. self.channel_value_satoshis == other.channel_value_satoshis && @@ -303,7 +303,7 @@ impl PartialEq for OnchainTxHandler OnchainTxHandler { +impl OnchainTxHandler { pub(crate) fn write(&self, writer: &mut W) -> Result<(), io::Error> { write_ver_prefix!(writer, SERIALIZATION_VERSION, MIN_SERIALIZATION_VERSION); @@ -443,10 +443,10 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP } } -impl OnchainTxHandler { +impl OnchainTxHandler { pub(crate) fn new( channel_value_satoshis: u64, channel_keys_id: [u8; 32], destination_script: ScriptBuf, - signer: ChannelSigner, channel_parameters: ChannelTransactionParameters, + signer: Signer, channel_parameters: ChannelTransactionParameters, holder_commitment: HolderCommitmentTransaction, secp_ctx: Secp256k1 ) -> Self { OnchainTxHandler { @@ -665,8 +665,8 @@ impl OnchainTxHandler { } // We'll locate an anchor output we can spend within the commitment transaction. - let funding_pubkey = &self.channel_transaction_parameters.holder_pubkeys.funding_pubkey; - match chan_utils::get_anchor_output(&tx.0, funding_pubkey) { + let anchor_spk = self.signer.get_anchor_txout(true, true).expect("signer does not support anchors").script_pubkey; + match tx.0.output.iter().enumerate().find(|(_, txout)| anchor_spk == txout.script_pubkey).map(|(idx, txout)| (idx as u32, txout)) { // An anchor output was found, so we should yield a funding event externally. Some((idx, _)) => { // TODO: Use a lower confirmation target when both our and the @@ -1187,17 +1187,17 @@ impl OnchainTxHandler { &self.holder_commitment.trust().built_transaction().transaction } - pub(crate) fn get_maybe_signed_holder_tx(&mut self, funding_redeemscript: &Script) -> MaybeSignedTransaction { + pub(crate) fn get_maybe_signed_holder_tx(&mut self) -> MaybeSignedTransaction { let tx = self.signer.sign_holder_commitment(&self.holder_commitment, &self.secp_ctx) - .map(|sig| self.holder_commitment.add_holder_sig(funding_redeemscript, sig)) + .map(|witness| self.holder_commitment.extract_tx(witness)) .unwrap_or_else(|_| self.get_unsigned_holder_commitment_tx().clone()); MaybeSignedTransaction(tx) } #[cfg(any(test, feature="unsafe_revoked_tx_signing"))] - pub(crate) fn get_fully_signed_copy_holder_tx(&mut self, funding_redeemscript: &Script) -> Transaction { - let sig = self.signer.unsafe_sign_holder_commitment(&self.holder_commitment, &self.secp_ctx).expect("sign holder commitment"); - self.holder_commitment.add_holder_sig(funding_redeemscript, sig) + pub(crate) fn get_fully_signed_copy_holder_tx(&mut self) -> Transaction { + let witness = self.signer.unsafe_sign_holder_commitment(&self.holder_commitment, &self.secp_ctx).expect("sign holder commitment"); + self.holder_commitment.extract_tx(witness) } pub(crate) fn get_maybe_signed_htlc_tx(&mut self, outp: &::bitcoin::OutPoint, preimage: &Option) -> Option { @@ -1210,8 +1210,9 @@ impl OnchainTxHandler { .find(|(_, htlc)| htlc.transaction_output_index.unwrap() == outp.vout) .unwrap(); let counterparty_htlc_sig = holder_commitment.counterparty_htlc_sigs[htlc_idx]; + let revokeable_spk = self.signer.get_revokeable_spk(true, holder_commitment.commitment_number(), &holder_commitment.per_commitment_point(), &self.secp_ctx); let mut htlc_tx = trusted_tx.build_unsigned_htlc_tx( - &self.channel_transaction_parameters.as_holder_broadcastable(), htlc_idx, preimage, + htlc_idx, preimage, revokeable_spk, ); let htlc_descriptor = HTLCDescriptor { @@ -1228,10 +1229,8 @@ impl OnchainTxHandler { preimage: preimage.clone(), counterparty_sig: counterparty_htlc_sig.clone(), }; - if let Ok(htlc_sig) = self.signer.sign_holder_htlc_transaction(&htlc_tx, 0, &htlc_descriptor, &self.secp_ctx) { - htlc_tx.input[0].witness = trusted_tx.build_htlc_input_witness( - htlc_idx, &counterparty_htlc_sig, &htlc_sig, preimage, - ); + if let Ok(witness) = self.signer.sign_holder_htlc_transaction(&htlc_tx, 0, &htlc_descriptor, &self.secp_ctx) { + htlc_tx.input[0].witness = witness; } Some(MaybeSignedTransaction(htlc_tx)) }; @@ -1273,7 +1272,7 @@ impl OnchainTxHandler { } pub(crate) fn channel_type_features(&self) -> &ChannelTypeFeatures { - &self.channel_transaction_parameters.channel_type_features + self.channel_transaction_parameters.channel_type_features() } } @@ -1295,7 +1294,7 @@ mod tests { }; use crate::ln::channel_keys::{DelayedPaymentBasepoint, HtlcBasepoint, RevocationBasepoint}; use crate::ln::functional_test_utils::create_dummy_block; - use crate::sign::InMemorySigner; + use crate::sign::{ChannelSigner, InMemorySigner}; use crate::types::payment::{PaymentHash, PaymentPreimage}; use crate::util::test_utils::{TestBroadcaster, TestFeeEstimator, TestLogger}; @@ -1307,7 +1306,7 @@ mod tests { #[test] fn test_broadcast_height() { let secp_ctx = Secp256k1::new(); - let signer = InMemorySigner::new( + let mut signer = InMemorySigner::new( &secp_ctx, SecretKey::from_slice(&[41; 32]).unwrap(), SecretKey::from_slice(&[41; 32]).unwrap(), @@ -1356,6 +1355,7 @@ mod tests { funding_outpoint: Some(funding_outpoint), channel_type_features: ChannelTypeFeatures::only_static_remote_key(), }; + signer.provide_channel_parameters(&chan_params); // Create an OnchainTxHandler for a commitment containing HTLCs with CLTV expiries of 0, 1, // and 2 blocks. diff --git a/lightning/src/chain/package.rs b/lightning/src/chain/package.rs index 53bba3a754b..9491dbbc46f 100644 --- a/lightning/src/chain/package.rs +++ b/lightning/src/chain/package.rs @@ -21,11 +21,10 @@ use bitcoin::transaction::OutPoint as BitcoinOutPoint; use bitcoin::script::{Script, ScriptBuf}; use bitcoin::hash_types::Txid; use bitcoin::secp256k1::{SecretKey,PublicKey}; -use bitcoin::sighash::EcdsaSighashType; use bitcoin::transaction::Version; use crate::types::payment::PaymentPreimage; -use crate::ln::chan_utils::{self, TxCreationKeys, HTLCOutputInCommitment}; +use crate::ln::chan_utils::{self, HTLCOutputInCommitment}; use crate::types::features::ChannelTypeFeatures; use crate::ln::channel_keys::{DelayedPaymentBasepoint, HtlcBasepoint}; use crate::ln::channelmanager::MIN_CLTV_EXPIRY_DELTA; @@ -33,7 +32,7 @@ use crate::ln::msgs::DecodeError; use crate::chain::channelmonitor::COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE; 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::chain::transaction::MaybeSignedTransaction; -use crate::sign::ecdsa::EcdsaChannelSigner; +use crate::sign::ChannelSigner; use crate::chain::onchaintx::{FeerateStrategy, ExternalHTLCClaim, OnchainTxHandler}; use crate::util::logger::Logger; use crate::util::ser::{Readable, Writer, Writeable, RequiredWrapper}; @@ -125,25 +124,25 @@ const HIGH_FREQUENCY_BUMP_INTERVAL: u32 = 1; #[derive(Clone, PartialEq, Eq)] pub(crate) struct RevokedOutput { per_commitment_point: PublicKey, - counterparty_delayed_payment_base_key: DelayedPaymentBasepoint, - counterparty_htlc_base_key: HtlcBasepoint, + counterparty_delayed_payment_base_key: Option, + counterparty_htlc_base_key: Option, per_commitment_key: SecretKey, weight: u64, amount: Amount, - on_counterparty_tx_csv: u16, + on_counterparty_tx_csv: Option, is_counterparty_balance_on_anchors: Option<()>, } impl RevokedOutput { - pub(crate) fn build(per_commitment_point: PublicKey, counterparty_delayed_payment_base_key: DelayedPaymentBasepoint, counterparty_htlc_base_key: HtlcBasepoint, per_commitment_key: SecretKey, amount: Amount, on_counterparty_tx_csv: u16, is_counterparty_balance_on_anchors: bool) -> Self { + pub(crate) fn build(per_commitment_point: PublicKey, per_commitment_key: SecretKey, amount: Amount, is_counterparty_balance_on_anchors: bool, weight: u64) -> Self { RevokedOutput { per_commitment_point, - counterparty_delayed_payment_base_key, - counterparty_htlc_base_key, + counterparty_delayed_payment_base_key: None, + counterparty_htlc_base_key: None, per_commitment_key, - weight: WEIGHT_REVOKED_OUTPUT, + weight, amount, - on_counterparty_tx_csv, + on_counterparty_tx_csv: None, is_counterparty_balance_on_anchors: if is_counterparty_balance_on_anchors { Some(()) } else { None } } } @@ -151,12 +150,12 @@ impl RevokedOutput { impl_writeable_tlv_based!(RevokedOutput, { (0, per_commitment_point, required), - (2, counterparty_delayed_payment_base_key, required), - (4, counterparty_htlc_base_key, required), + (2, counterparty_delayed_payment_base_key, option), + (4, counterparty_htlc_base_key, option), (6, per_commitment_key, required), (8, weight, required), (10, amount, required), - (12, on_counterparty_tx_csv, required), + (12, on_counterparty_tx_csv, option), (14, is_counterparty_balance_on_anchors, option) }); @@ -171,8 +170,8 @@ impl_writeable_tlv_based!(RevokedOutput, { #[derive(Clone, PartialEq, Eq)] pub(crate) struct RevokedHTLCOutput { per_commitment_point: PublicKey, - counterparty_delayed_payment_base_key: DelayedPaymentBasepoint, - counterparty_htlc_base_key: HtlcBasepoint, + counterparty_delayed_payment_base_key: Option, + counterparty_htlc_base_key: Option, per_commitment_key: SecretKey, weight: u64, amount: u64, @@ -180,12 +179,11 @@ pub(crate) struct RevokedHTLCOutput { } impl RevokedHTLCOutput { - 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) }; + pub(crate) fn build(per_commitment_point: PublicKey, per_commitment_key: SecretKey, amount: u64, htlc: HTLCOutputInCommitment, weight: u64) -> Self { RevokedHTLCOutput { per_commitment_point, - counterparty_delayed_payment_base_key, - counterparty_htlc_base_key, + counterparty_delayed_payment_base_key: None, + counterparty_htlc_base_key: None, per_commitment_key, weight, amount, @@ -196,8 +194,8 @@ impl RevokedHTLCOutput { impl_writeable_tlv_based!(RevokedHTLCOutput, { (0, per_commitment_point, required), - (2, counterparty_delayed_payment_base_key, required), - (4, counterparty_htlc_base_key, required), + (2, counterparty_delayed_payment_base_key, option), + (4, counterparty_htlc_base_key, option), (6, per_commitment_key, required), (8, weight, required), (10, amount, required), @@ -215,22 +213,24 @@ impl_writeable_tlv_based!(RevokedHTLCOutput, { #[derive(Clone, PartialEq, Eq)] pub(crate) struct CounterpartyOfferedHTLCOutput { per_commitment_point: PublicKey, - counterparty_delayed_payment_base_key: DelayedPaymentBasepoint, - counterparty_htlc_base_key: HtlcBasepoint, + counterparty_delayed_payment_base_key: Option, + counterparty_htlc_base_key: Option, preimage: PaymentPreimage, htlc: HTLCOutputInCommitment, channel_type_features: ChannelTypeFeatures, + weight: u64, } impl CounterpartyOfferedHTLCOutput { - 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 { + pub(crate) fn build(per_commitment_point: PublicKey, preimage: PaymentPreimage, htlc: HTLCOutputInCommitment, channel_type_features: ChannelTypeFeatures, weight: u64) -> Self { CounterpartyOfferedHTLCOutput { per_commitment_point, - counterparty_delayed_payment_base_key, - counterparty_htlc_base_key, + counterparty_delayed_payment_base_key: None, + counterparty_htlc_base_key: None, preimage, htlc, channel_type_features, + weight, } } } @@ -240,12 +240,13 @@ impl Writeable for CounterpartyOfferedHTLCOutput { let legacy_deserialization_prevention_marker = chan_utils::legacy_deserialization_prevention_marker_for_channel_type_features(&self.channel_type_features); write_tlv_fields!(writer, { (0, self.per_commitment_point, required), - (2, self.counterparty_delayed_payment_base_key, required), - (4, self.counterparty_htlc_base_key, required), + (2, self.counterparty_delayed_payment_base_key, option), + (4, self.counterparty_htlc_base_key, option), (6, self.preimage, required), (8, self.htlc, required), (10, legacy_deserialization_prevention_marker, option), (11, self.channel_type_features, required), + (13, self.weight, required), }); Ok(()) } @@ -254,32 +255,37 @@ impl Writeable for CounterpartyOfferedHTLCOutput { impl Readable for CounterpartyOfferedHTLCOutput { fn read(reader: &mut R) -> Result { let mut per_commitment_point = RequiredWrapper(None); - let mut counterparty_delayed_payment_base_key = RequiredWrapper(None); - let mut counterparty_htlc_base_key = RequiredWrapper(None); + let mut counterparty_delayed_payment_base_key = None; + let mut counterparty_htlc_base_key = None; let mut preimage = RequiredWrapper(None); let mut htlc = RequiredWrapper(None); let mut _legacy_deserialization_prevention_marker: Option<()> = None; let mut channel_type_features = None; + let mut weight = RequiredWrapper(None); read_tlv_fields!(reader, { (0, per_commitment_point, required), - (2, counterparty_delayed_payment_base_key, required), - (4, counterparty_htlc_base_key, required), + (2, counterparty_delayed_payment_base_key, option), + (4, counterparty_htlc_base_key, option), (6, preimage, required), (8, htlc, required), (10, _legacy_deserialization_prevention_marker, option), (11, channel_type_features, option), + (13, weight, (default_value, weight_offered_htlc(channel_type_features.as_ref().unwrap_or(&ChannelTypeFeatures::only_static_remote_key())))), }); verify_channel_type_features(&channel_type_features, None)?; + let channel_type_features = channel_type_features.unwrap_or(ChannelTypeFeatures::only_static_remote_key()); + Ok(Self { per_commitment_point: per_commitment_point.0.unwrap(), - counterparty_delayed_payment_base_key: counterparty_delayed_payment_base_key.0.unwrap(), - counterparty_htlc_base_key: counterparty_htlc_base_key.0.unwrap(), + counterparty_delayed_payment_base_key, + counterparty_htlc_base_key, preimage: preimage.0.unwrap(), htlc: htlc.0.unwrap(), - channel_type_features: channel_type_features.unwrap_or(ChannelTypeFeatures::only_static_remote_key()) + channel_type_features, + weight: weight.0.unwrap(), }) } } @@ -293,20 +299,22 @@ impl Readable for CounterpartyOfferedHTLCOutput { #[derive(Clone, PartialEq, Eq)] pub(crate) struct CounterpartyReceivedHTLCOutput { per_commitment_point: PublicKey, - counterparty_delayed_payment_base_key: DelayedPaymentBasepoint, - counterparty_htlc_base_key: HtlcBasepoint, + counterparty_delayed_payment_base_key: Option, + counterparty_htlc_base_key: Option, htlc: HTLCOutputInCommitment, channel_type_features: ChannelTypeFeatures, + weight: u64, } impl CounterpartyReceivedHTLCOutput { - 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 { + pub(crate) fn build(per_commitment_point: PublicKey, htlc: HTLCOutputInCommitment, channel_type_features: ChannelTypeFeatures, weight: u64) -> Self { CounterpartyReceivedHTLCOutput { per_commitment_point, - counterparty_delayed_payment_base_key, - counterparty_htlc_base_key, + counterparty_delayed_payment_base_key: None, + counterparty_htlc_base_key: None, htlc, - channel_type_features + channel_type_features, + weight, } } } @@ -316,11 +324,12 @@ impl Writeable for CounterpartyReceivedHTLCOutput { let legacy_deserialization_prevention_marker = chan_utils::legacy_deserialization_prevention_marker_for_channel_type_features(&self.channel_type_features); write_tlv_fields!(writer, { (0, self.per_commitment_point, required), - (2, self.counterparty_delayed_payment_base_key, required), - (4, self.counterparty_htlc_base_key, required), + (2, self.counterparty_delayed_payment_base_key, option), + (4, self.counterparty_htlc_base_key, option), (6, self.htlc, required), (8, legacy_deserialization_prevention_marker, option), (9, self.channel_type_features, required), + (11, self.weight, required), }); Ok(()) } @@ -329,29 +338,34 @@ impl Writeable for CounterpartyReceivedHTLCOutput { impl Readable for CounterpartyReceivedHTLCOutput { fn read(reader: &mut R) -> Result { let mut per_commitment_point = RequiredWrapper(None); - let mut counterparty_delayed_payment_base_key = RequiredWrapper(None); - let mut counterparty_htlc_base_key = RequiredWrapper(None); + let mut counterparty_delayed_payment_base_key = None; + let mut counterparty_htlc_base_key = None; let mut htlc = RequiredWrapper(None); let mut _legacy_deserialization_prevention_marker: Option<()> = None; let mut channel_type_features = None; + let mut weight = RequiredWrapper(None); read_tlv_fields!(reader, { (0, per_commitment_point, required), - (2, counterparty_delayed_payment_base_key, required), - (4, counterparty_htlc_base_key, required), + (2, counterparty_delayed_payment_base_key, option), + (4, counterparty_htlc_base_key, option), (6, htlc, required), (8, _legacy_deserialization_prevention_marker, option), (9, channel_type_features, option), + (11, weight, (default_value, weight_received_htlc(channel_type_features.as_ref().unwrap_or(&ChannelTypeFeatures::only_static_remote_key())))), }); verify_channel_type_features(&channel_type_features, None)?; + let channel_type_features = channel_type_features.unwrap_or(ChannelTypeFeatures::only_static_remote_key()); + Ok(Self { per_commitment_point: per_commitment_point.0.unwrap(), - counterparty_delayed_payment_base_key: counterparty_delayed_payment_base_key.0.unwrap(), - counterparty_htlc_base_key: counterparty_htlc_base_key.0.unwrap(), + counterparty_delayed_payment_base_key, + counterparty_htlc_base_key, htlc: htlc.0.unwrap(), - channel_type_features: channel_type_features.unwrap_or(ChannelTypeFeatures::only_static_remote_key()) + channel_type_features, + weight: weight.0.unwrap(), }) } } @@ -439,16 +453,14 @@ impl Readable for HolderHTLCOutput { /// Note that on upgrades, some features of existing outputs may be missed. #[derive(Clone, PartialEq, Eq)] pub(crate) struct HolderFundingOutput { - funding_redeemscript: ScriptBuf, pub(crate) funding_amount: Option, channel_type_features: ChannelTypeFeatures, } impl HolderFundingOutput { - pub(crate) fn build(funding_redeemscript: ScriptBuf, funding_amount: u64, channel_type_features: ChannelTypeFeatures) -> Self { + pub(crate) fn build(funding_amount: u64, channel_type_features: ChannelTypeFeatures) -> Self { HolderFundingOutput { - funding_redeemscript, funding_amount: Some(funding_amount), channel_type_features, } @@ -459,7 +471,7 @@ impl Writeable for HolderFundingOutput { fn write(&self, writer: &mut W) -> Result<(), io::Error> { let legacy_deserialization_prevention_marker = chan_utils::legacy_deserialization_prevention_marker_for_channel_type_features(&self.channel_type_features); write_tlv_fields!(writer, { - (0, self.funding_redeemscript, required), + (0, None::, option), (1, self.channel_type_features, required), (2, legacy_deserialization_prevention_marker, option), (3, self.funding_amount, option), @@ -470,13 +482,13 @@ impl Writeable for HolderFundingOutput { impl Readable for HolderFundingOutput { fn read(reader: &mut R) -> Result { - let mut funding_redeemscript = RequiredWrapper(None); + let mut _funding_redeemscript: Option = None; let mut _legacy_deserialization_prevention_marker: Option<()> = None; let mut channel_type_features = None; let mut funding_amount = None; read_tlv_fields!(reader, { - (0, funding_redeemscript, required), + (0, _funding_redeemscript, option), (1, channel_type_features, option), (2, _legacy_deserialization_prevention_marker, option), (3, funding_amount, option), @@ -485,7 +497,6 @@ impl Readable for HolderFundingOutput { verify_channel_type_features(&channel_type_features, None)?; Ok(Self { - funding_redeemscript: funding_redeemscript.0.unwrap(), channel_type_features: channel_type_features.unwrap_or(ChannelTypeFeatures::only_static_remote_key()), funding_amount }) @@ -528,16 +539,11 @@ impl PackageSolvingData { match self { PackageSolvingData::RevokedOutput(ref outp) => outp.weight as usize, PackageSolvingData::RevokedHTLCOutput(ref outp) => outp.weight as usize, - PackageSolvingData::CounterpartyOfferedHTLCOutput(ref outp) => weight_offered_htlc(&outp.channel_type_features) as usize, - PackageSolvingData::CounterpartyReceivedHTLCOutput(ref outp) => weight_received_htlc(&outp.channel_type_features) as usize, - PackageSolvingData::HolderHTLCOutput(ref outp) => { - debug_assert!(outp.channel_type_features.supports_anchors_zero_fee_htlc_tx()); - if outp.preimage.is_none() { - weight_offered_htlc(&outp.channel_type_features) as usize - } else { - weight_received_htlc(&outp.channel_type_features) as usize - } - }, + PackageSolvingData::CounterpartyOfferedHTLCOutput(ref outp) => outp.weight as usize, + PackageSolvingData::CounterpartyReceivedHTLCOutput(ref outp) => outp.weight as usize, + // Since HolderHLTCOutput requires external funding, we never inquire the witness + // weight of the HTLC transaction here. See OnchainTxHandler::generate_claim. + PackageSolvingData::HolderHTLCOutput(ref _outp) => unreachable!(), // Since HolderFundingOutput maps to an untractable package that is already signed, its // weight can be determined from the transaction itself. PackageSolvingData::HolderFundingOutput(..) => unreachable!(), @@ -601,69 +607,42 @@ impl PackageSolvingData { witness: Witness::new(), } } - fn finalize_input(&self, bumped_tx: &mut Transaction, i: usize, onchain_handler: &mut OnchainTxHandler) -> bool { + fn finalize_input(&self, bumped_tx: &mut Transaction, i: usize, onchain_handler: &mut OnchainTxHandler) -> bool { match self { PackageSolvingData::RevokedOutput(ref outp) => { - let chan_keys = TxCreationKeys::derive_new(&onchain_handler.secp_ctx, &outp.per_commitment_point, &outp.counterparty_delayed_payment_base_key, &outp.counterparty_htlc_base_key, &onchain_handler.signer.pubkeys().revocation_basepoint, &onchain_handler.signer.pubkeys().htlc_basepoint); - let witness_script = chan_utils::get_revokeable_redeemscript(&chan_keys.revocation_key, outp.on_counterparty_tx_csv, &chan_keys.broadcaster_delayed_payment_key); //TODO: should we panic on signer failure ? - if let Ok(sig) = onchain_handler.signer.sign_justice_revoked_output(&bumped_tx, i, outp.amount.to_sat(), &outp.per_commitment_key, &onchain_handler.secp_ctx) { - 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(vec!(1)); - bumped_tx.input[i].witness.push(witness_script.clone().into_bytes()); + if let Ok(witness) = onchain_handler.signer.punish_revokeable_output(bumped_tx, i, outp.amount.to_sat(), &outp.per_commitment_key, &onchain_handler.secp_ctx, &outp.per_commitment_point) { + bumped_tx.input[i].witness = witness; } else { return false; } }, PackageSolvingData::RevokedHTLCOutput(ref outp) => { - let chan_keys = TxCreationKeys::derive_new(&onchain_handler.secp_ctx, &outp.per_commitment_point, &outp.counterparty_delayed_payment_base_key, &outp.counterparty_htlc_base_key, &onchain_handler.signer.pubkeys().revocation_basepoint, &onchain_handler.signer.pubkeys().htlc_basepoint); - let witness_script = chan_utils::get_htlc_redeemscript_with_explicit_keys(&outp.htlc, &onchain_handler.channel_type_features(), &chan_keys.broadcaster_htlc_key, &chan_keys.countersignatory_htlc_key, &chan_keys.revocation_key); //TODO: should we panic on signer failure ? - if let Ok(sig) = onchain_handler.signer.sign_justice_revoked_htlc(&bumped_tx, i, outp.amount, &outp.per_commitment_key, &outp.htlc, &onchain_handler.secp_ctx) { - 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.to_public_key().serialize().to_vec()); - bumped_tx.input[i].witness.push(witness_script.clone().into_bytes()); + if let Ok(witness) = onchain_handler.signer.punish_htlc_output(&bumped_tx, i, outp.amount, &outp.per_commitment_key, &onchain_handler.secp_ctx, &outp.per_commitment_point, &outp.htlc) { + bumped_tx.input[i].witness = witness; } else { return false; } }, PackageSolvingData::CounterpartyOfferedHTLCOutput(ref outp) => { - let chan_keys = TxCreationKeys::derive_new(&onchain_handler.secp_ctx, &outp.per_commitment_point, &outp.counterparty_delayed_payment_base_key, &outp.counterparty_htlc_base_key, &onchain_handler.signer.pubkeys().revocation_basepoint, &onchain_handler.signer.pubkeys().htlc_basepoint); - let witness_script = chan_utils::get_htlc_redeemscript_with_explicit_keys(&outp.htlc, &onchain_handler.channel_type_features(), &chan_keys.broadcaster_htlc_key, &chan_keys.countersignatory_htlc_key, &chan_keys.revocation_key); - - if let Ok(sig) = onchain_handler.signer.sign_counterparty_htlc_transaction(&bumped_tx, i, &outp.htlc.amount_msat / 1000, &outp.per_commitment_point, &outp.htlc, &onchain_handler.secp_ctx) { - 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(outp.preimage.0.to_vec()); - bumped_tx.input[i].witness.push(witness_script.clone().into_bytes()); + if let Ok(witness) = onchain_handler.signer.sweep_counterparty_htlc_output(&bumped_tx, i, outp.htlc.amount_msat / 1000, &onchain_handler.secp_ctx, &outp.per_commitment_point, &outp.htlc, Some(&outp.preimage)) { + bumped_tx.input[i].witness = witness; } }, PackageSolvingData::CounterpartyReceivedHTLCOutput(ref outp) => { - let chan_keys = TxCreationKeys::derive_new(&onchain_handler.secp_ctx, &outp.per_commitment_point, &outp.counterparty_delayed_payment_base_key, &outp.counterparty_htlc_base_key, &onchain_handler.signer.pubkeys().revocation_basepoint, &onchain_handler.signer.pubkeys().htlc_basepoint); - let witness_script = chan_utils::get_htlc_redeemscript_with_explicit_keys(&outp.htlc, &onchain_handler.channel_type_features(), &chan_keys.broadcaster_htlc_key, &chan_keys.countersignatory_htlc_key, &chan_keys.revocation_key); - - if let Ok(sig) = onchain_handler.signer.sign_counterparty_htlc_transaction(&bumped_tx, i, &outp.htlc.amount_msat / 1000, &outp.per_commitment_point, &outp.htlc, &onchain_handler.secp_ctx) { - let mut ser_sig = sig.serialize_der().to_vec(); - ser_sig.push(EcdsaSighashType::All as u8); - bumped_tx.input[i].witness.push(ser_sig); - // Due to BIP146 (MINIMALIF) this must be a zero-length element to relay. - bumped_tx.input[i].witness.push(vec![]); - bumped_tx.input[i].witness.push(witness_script.clone().into_bytes()); + if let Ok(witness) = onchain_handler.signer.sweep_counterparty_htlc_output(&bumped_tx, i, outp.htlc.amount_msat / 1000, &onchain_handler.secp_ctx, &outp.per_commitment_point, &outp.htlc, None) { + bumped_tx.input[i].witness = witness; } }, _ => { panic!("API Error!"); } } true } - fn get_maybe_finalized_tx(&self, outpoint: &BitcoinOutPoint, onchain_handler: &mut OnchainTxHandler) -> Option { + fn get_maybe_finalized_tx(&self, outpoint: &BitcoinOutPoint, onchain_handler: &mut OnchainTxHandler) -> Option { match self { PackageSolvingData::HolderHTLCOutput(ref outp) => { debug_assert!(!outp.channel_type_features.supports_anchors_zero_fee_htlc_tx()); onchain_handler.get_maybe_signed_htlc_tx(outpoint, &outp.preimage) } - PackageSolvingData::HolderFundingOutput(ref outp) => { - Some(onchain_handler.get_maybe_signed_holder_tx(&outp.funding_redeemscript)) + PackageSolvingData::HolderFundingOutput(ref _outp) => { + Some(onchain_handler.get_maybe_signed_holder_tx()) } _ => { panic!("API Error!"); } } @@ -969,7 +948,7 @@ impl PackageTemplate { let output_weight = (8 + 1 + destination_script.len()) * WITNESS_SCALE_FACTOR; (inputs_weight + witnesses_weight + transaction_weight + output_weight) as u64 } - pub(crate) fn construct_malleable_package_with_external_funding( + pub(crate) fn construct_malleable_package_with_external_funding( &self, onchain_handler: &mut OnchainTxHandler, ) -> Option> { debug_assert!(self.requires_external_funding()); @@ -987,7 +966,7 @@ impl PackageTemplate { } htlcs } - pub(crate) fn maybe_finalize_malleable_package( + pub(crate) fn maybe_finalize_malleable_package( &self, current_height: u32, onchain_handler: &mut OnchainTxHandler, value: Amount, destination_script: ScriptBuf, logger: &L ) -> Option { @@ -1010,7 +989,7 @@ impl PackageTemplate { } Some(MaybeSignedTransaction(bumped_tx)) } - pub(crate) fn maybe_finalize_untractable_package( + pub(crate) fn maybe_finalize_untractable_package( &self, onchain_handler: &mut OnchainTxHandler, logger: &L, ) -> Option { debug_assert!(!self.is_malleable()); @@ -1331,11 +1310,10 @@ where #[cfg(test)] mod tests { - use crate::chain::package::{CounterpartyOfferedHTLCOutput, CounterpartyReceivedHTLCOutput, HolderFundingOutput, HolderHTLCOutput, PackageTemplate, PackageSolvingData, RevokedHTLCOutput, RevokedOutput, WEIGHT_REVOKED_OUTPUT, weight_offered_htlc, weight_received_htlc}; + use crate::chain::package::{CounterpartyOfferedHTLCOutput, CounterpartyReceivedHTLCOutput, HolderFundingOutput, HolderHTLCOutput, PackageTemplate, PackageSolvingData, RevokedHTLCOutput, RevokedOutput, WEIGHT_REVOKED_OUTPUT, weight_offered_htlc, weight_received_htlc, weight_revoked_received_htlc}; use crate::chain::Txid; use crate::ln::chan_utils::HTLCOutputInCommitment; use crate::types::payment::{PaymentPreimage, PaymentHash}; - use crate::ln::channel_keys::{DelayedPaymentBasepoint, HtlcBasepoint}; use bitcoin::absolute::LockTime; use bitcoin::amount::Amount; @@ -1369,7 +1347,7 @@ mod tests { let secp_ctx = Secp256k1::new(); 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, DelayedPaymentBasepoint::from(dumb_point), HtlcBasepoint::from(dumb_point), dumb_scalar, Amount::ZERO, 0, $is_counterparty_balance_on_anchors)) + PackageSolvingData::RevokedOutput(RevokedOutput::build(dumb_point, dumb_scalar, Amount::ZERO, $is_counterparty_balance_on_anchors, WEIGHT_REVOKED_OUTPUT)) } } } @@ -1382,7 +1360,9 @@ mod tests { let dumb_point = PublicKey::from_secret_key(&secp_ctx, &dumb_scalar); let hash = PaymentHash([1; 32]); let htlc = HTLCOutputInCommitment { offered: false, amount_msat: 1_000_000, cltv_expiry: 0, payment_hash: hash, transaction_output_index: None }; - PackageSolvingData::RevokedHTLCOutput(RevokedHTLCOutput::build(dumb_point, DelayedPaymentBasepoint::from(dumb_point), HtlcBasepoint::from(dumb_point), dumb_scalar, 1_000_000 / 1_000, htlc, &ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies())) + let features = &ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies(); + let witness_weight = weight_revoked_received_htlc(features); + PackageSolvingData::RevokedHTLCOutput(RevokedHTLCOutput::build(dumb_point, dumb_scalar, 1_000_000 / 1_000, htlc, witness_weight)) } } } @@ -1395,7 +1375,8 @@ 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: $expiry, payment_hash: hash, transaction_output_index: None }; - PackageSolvingData::CounterpartyReceivedHTLCOutput(CounterpartyReceivedHTLCOutput::build(dumb_point, DelayedPaymentBasepoint::from(dumb_point), HtlcBasepoint::from(dumb_point), htlc, $features)) + let weight = weight_received_htlc(&$features); + PackageSolvingData::CounterpartyReceivedHTLCOutput(CounterpartyReceivedHTLCOutput::build(dumb_point, htlc, $features, weight)) } } } @@ -1409,7 +1390,8 @@ mod tests { let hash = PaymentHash([1; 32]); let preimage = PaymentPreimage([2;32]); let htlc = HTLCOutputInCommitment { offered: false, amount_msat: $amt, cltv_expiry: 0, payment_hash: hash, transaction_output_index: None }; - PackageSolvingData::CounterpartyOfferedHTLCOutput(CounterpartyOfferedHTLCOutput::build(dumb_point, DelayedPaymentBasepoint::from(dumb_point), HtlcBasepoint::from(dumb_point), preimage, htlc, $features)) + let weight = weight_offered_htlc(&$features); + PackageSolvingData::CounterpartyOfferedHTLCOutput(CounterpartyOfferedHTLCOutput::build(dumb_point, preimage, htlc, $features, weight)) } } } @@ -1433,7 +1415,7 @@ mod tests { macro_rules! dumb_funding_output { () => { - PackageSolvingData::HolderFundingOutput(HolderFundingOutput::build(ScriptBuf::new(), 0, ChannelTypeFeatures::only_static_remote_key())) + PackageSolvingData::HolderFundingOutput(HolderFundingOutput::build(0, ChannelTypeFeatures::only_static_remote_key())) } } diff --git a/lightning/src/events/bump_transaction.rs b/lightning/src/events/bump_transaction.rs index 3acb2145e5b..6af3ec2b527 100644 --- a/lightning/src/events/bump_transaction.rs +++ b/lightning/src/events/bump_transaction.rs @@ -20,13 +20,10 @@ use crate::io_extras::sink; use crate::ln::channel::ANCHOR_OUTPUT_VALUE_SATOSHI; use crate::ln::types::ChannelId; use crate::ln::chan_utils; -use crate::ln::chan_utils::{ - ANCHOR_INPUT_WITNESS_WEIGHT, HTLC_SUCCESS_INPUT_ANCHOR_WITNESS_WEIGHT, - HTLC_TIMEOUT_INPUT_ANCHOR_WITNESS_WEIGHT, HTLCOutputInCommitment -}; +use crate::ln::chan_utils::HTLCOutputInCommitment; use crate::prelude::*; use crate::sign::{ - ChannelDerivationParameters, HTLCDescriptor, SignerProvider, P2WPKH_WITNESS_WEIGHT + ChannelDerivationParameters, ChannelSigner, HTLCDescriptor, SignerProvider, P2WPKH_WITNESS_WEIGHT, }; use crate::sign::ecdsa::EcdsaChannelSigner; use crate::sync::Mutex; @@ -122,11 +119,9 @@ pub enum BumpTransactionEvent { /// broadcast first, as the child anchor transaction depends on it. /// /// The consumer should be able to sign for any of the additional inputs included within the - /// child anchor transaction. To sign its anchor input, an [`EcdsaChannelSigner`] should be - /// re-derived through [`AnchorDescriptor::derive_channel_signer`]. The anchor input signature - /// can be computed with [`EcdsaChannelSigner::sign_holder_anchor_input`], which can then be - /// provided to [`build_anchor_input_witness`] along with the `funding_pubkey` to obtain the - /// full witness required to spend. + /// child anchor transaction. To sign its anchor input, a [`ChannelSigner`] should be + /// re-derived through [`AnchorDescriptor::derive_channel_signer`]. The anchor input witness + /// can be computed with [`ChannelSigner::spend_holder_anchor_output`]. /// /// It is possible to receive more than one instance of this event if a valid child anchor /// transaction is never broadcast or is but not with a sufficient fee to be mined. Care should @@ -145,9 +140,8 @@ pub enum BumpTransactionEvent { /// an empty `pending_htlcs`), confirmation of the commitment transaction can be considered to /// be not urgent. /// - /// [`EcdsaChannelSigner`]: crate::sign::ecdsa::EcdsaChannelSigner - /// [`EcdsaChannelSigner::sign_holder_anchor_input`]: crate::sign::ecdsa::EcdsaChannelSigner::sign_holder_anchor_input - /// [`build_anchor_input_witness`]: crate::ln::chan_utils::build_anchor_input_witness + /// [`ChannelSigner`]: crate::sign::ChannelSigner + /// [`ChannelSigner::spend_holder_anchor_output`]: crate::sign::ChannelSigner::spend_holder_anchor_output ChannelClose { /// The `channel_id` of the channel which has been closed. channel_id: ChannelId, @@ -188,10 +182,8 @@ pub enum BumpTransactionEvent { /// /// The consumer should be able to sign for any of the non-HTLC inputs added to the resulting /// HTLC transaction. To sign HTLC inputs, an [`EcdsaChannelSigner`] should be re-derived - /// through [`HTLCDescriptor::derive_channel_signer`]. Each HTLC input's signature can be - /// computed with [`EcdsaChannelSigner::sign_holder_htlc_transaction`], which can then be - /// provided to [`HTLCDescriptor::tx_input_witness`] to obtain the fully signed witness required - /// to spend. + /// through [`HTLCDescriptor::derive_channel_signer`]. Each HTLC input's witness can be + /// computed with [`ChannelSigner::sign_holder_htlc_transaction`]. /// /// It is possible to receive more than one instance of this event if a valid HTLC transaction /// is never broadcast or is but not with a sufficient fee to be mined. Care should be taken by @@ -204,7 +196,7 @@ pub enum BumpTransactionEvent { /// to the HTLC transaction is greater in value than the HTLCs being claimed. /// /// [`EcdsaChannelSigner`]: crate::sign::ecdsa::EcdsaChannelSigner - /// [`EcdsaChannelSigner::sign_holder_htlc_transaction`]: crate::sign::ecdsa::EcdsaChannelSigner::sign_holder_htlc_transaction + /// [`ChannelSigner::sign_holder_htlc_transaction`]: crate::sign::ChannelSigner::sign_holder_htlc_transaction HTLCResolution { /// The `channel_id` of the channel which has been closed. channel_id: ChannelId, @@ -611,6 +603,9 @@ where &self, claim_id: ClaimId, package_target_feerate_sat_per_1000_weight: u32, commitment_tx: &Transaction, commitment_tx_fee_sat: u64, anchor_descriptor: &AnchorDescriptor, ) -> Result<(), ()> { + let signer = anchor_descriptor.derive_channel_signer(&self.signer_provider); + let anchor_input_witness_weight = signer.get_holder_anchor_input_witness_weight(); + // Our commitment transaction already has fees allocated to it, so we should take them into // account. We do so by pretending the commitment transaction's fee and weight are part of // the anchor input. @@ -618,7 +613,7 @@ where let commitment_tx_fee_sat = Amount::from_sat(commitment_tx_fee_sat); anchor_utxo.value += commitment_tx_fee_sat; let starting_package_and_fixed_input_satisfaction_weight = - commitment_tx.weight().to_wu() + ANCHOR_INPUT_WITNESS_WEIGHT + EMPTY_SCRIPT_SIG_WEIGHT; + commitment_tx.weight().to_wu() + anchor_input_witness_weight + EMPTY_SCRIPT_SIG_WEIGHT; let mut package_and_fixed_input_satisfaction_weight = starting_package_and_fixed_input_satisfaction_weight; @@ -643,7 +638,7 @@ where output: vec![], }; - let total_satisfaction_weight = ANCHOR_INPUT_WITNESS_WEIGHT + EMPTY_SCRIPT_SIG_WEIGHT + + let total_satisfaction_weight = anchor_input_witness_weight + EMPTY_SCRIPT_SIG_WEIGHT + coin_selection.confirmed_utxos.iter().map(|utxo| utxo.satisfaction_weight).sum::(); let total_input_amount = must_spend_amount + coin_selection.confirmed_utxos.iter().map(|utxo| utxo.output.value).sum(); @@ -689,9 +684,8 @@ where log_debug!(self.logger, "Signing anchor transaction {}", anchor_txid); anchor_tx = self.utxo_source.sign_psbt(anchor_psbt)?; - let signer = anchor_descriptor.derive_channel_signer(&self.signer_provider); - let anchor_sig = signer.sign_holder_anchor_input(&anchor_tx, 0, &self.secp)?; - anchor_tx.input[0].witness = anchor_descriptor.tx_input_witness(&anchor_sig); + let anchor_witness = signer.spend_holder_anchor_output(&anchor_tx, 0, &self.secp)?; + anchor_tx.input[0].witness = anchor_witness; #[cfg(debug_assertions)] { let signed_tx_weight = anchor_tx.weight().to_wu(); @@ -728,19 +722,27 @@ where output: vec![], }; let mut must_spend = Vec::with_capacity(htlc_descriptors.len()); + let mut signers_and_revokeable_spks = BTreeMap::new(); for htlc_descriptor in htlc_descriptors { + // TODO: avoid having mutable references flying around + let (_, revokeable_spk, witness_weight, htlc_spk) = signers_and_revokeable_spks.entry(htlc_descriptor.channel_derivation_parameters.keys_id) + .or_insert_with(|| { + let signer = htlc_descriptor.derive_channel_signer(&self.signer_provider); + let revokeable_spk = signer.get_revokeable_spk(true, htlc_descriptor.per_commitment_number, &htlc_descriptor.per_commitment_point, &self.secp); + // TODO: cache this, it does not change throughout the lifetime of the channel + let witness_weight = signer.get_holder_htlc_transaction_witness_weight(htlc_descriptor.htlc.offered); + let htlc_spk = signer.get_htlc_spk(&htlc_descriptor.htlc, true, &htlc_descriptor.per_commitment_point, &self.secp); + (signer, revokeable_spk, witness_weight, htlc_spk) + }); + let htlc_input = htlc_descriptor.unsigned_tx_input(); must_spend.push(Input { outpoint: htlc_input.previous_output.clone(), - previous_utxo: htlc_descriptor.previous_utxo(&self.secp), - satisfaction_weight: EMPTY_SCRIPT_SIG_WEIGHT + if htlc_descriptor.preimage.is_some() { - HTLC_SUCCESS_INPUT_ANCHOR_WITNESS_WEIGHT - } else { - HTLC_TIMEOUT_INPUT_ANCHOR_WITNESS_WEIGHT - }, + previous_utxo: htlc_descriptor.previous_utxo(htlc_spk.clone()), + satisfaction_weight: EMPTY_SCRIPT_SIG_WEIGHT + *witness_weight, }); htlc_tx.input.push(htlc_input); - let htlc_output = htlc_descriptor.tx_output(&self.secp); + let htlc_output = htlc_descriptor.tx_output(revokeable_spk.clone()); htlc_tx.output.push(htlc_output); } @@ -771,7 +773,9 @@ where // add witness_utxo to htlc inputs for (i, htlc_descriptor) in htlc_descriptors.iter().enumerate() { debug_assert_eq!(htlc_psbt.unsigned_tx.input[i].previous_output, htlc_descriptor.outpoint()); - htlc_psbt.inputs[i].witness_utxo = Some(htlc_descriptor.previous_utxo(&self.secp)); + // Unwrap because we derived the corresponding script pubkeys for all the htlc descriptors further above + let htlc_spk = signers_and_revokeable_spks.get(&htlc_descriptor.channel_derivation_parameters.keys_id).unwrap().3.clone(); + htlc_psbt.inputs[i].witness_utxo = Some(htlc_descriptor.previous_utxo(htlc_spk)); } // add witness_utxo to remaining inputs for (idx, utxo) in coin_selection.confirmed_utxos.into_iter().enumerate() { @@ -789,13 +793,11 @@ where log_debug!(self.logger, "Signing HTLC transaction {}", htlc_psbt.unsigned_tx.compute_txid()); htlc_tx = self.utxo_source.sign_psbt(htlc_psbt)?; - let mut signers = BTreeMap::new(); for (idx, htlc_descriptor) in htlc_descriptors.iter().enumerate() { - let signer = signers.entry(htlc_descriptor.channel_derivation_parameters.keys_id) - .or_insert_with(|| htlc_descriptor.derive_channel_signer(&self.signer_provider)); - let htlc_sig = signer.sign_holder_htlc_transaction(&htlc_tx, idx, htlc_descriptor, &self.secp)?; - let witness_script = htlc_descriptor.witness_script(&self.secp); - htlc_tx.input[idx].witness = htlc_descriptor.tx_input_witness(&htlc_sig, &witness_script); + // Unwrap because we derived the corresponding signers for all htlc descriptors further above + let signer = &signers_and_revokeable_spks.get(&htlc_descriptor.channel_derivation_parameters.keys_id).unwrap().0; + let witness = signer.sign_holder_htlc_transaction(&htlc_tx, idx, htlc_descriptor, &self.secp)?; + htlc_tx.input[idx].witness = witness; } #[cfg(debug_assertions)] { @@ -857,7 +859,7 @@ mod tests { use super::*; use crate::io::Cursor; - use crate::ln::chan_utils::ChannelTransactionParameters; + use crate::ln::chan_utils::{ANCHOR_INPUT_WITNESS_WEIGHT, ChannelTransactionParameters}; use crate::util::ser::Readable; use crate::util::test_utils::{TestBroadcaster, TestLogger}; use crate::sign::KeysManager; diff --git a/lightning/src/ln/chan_utils.rs b/lightning/src/ln/chan_utils.rs index ae76308f0ce..6e02e6c459c 100644 --- a/lightning/src/ln/chan_utils.rs +++ b/lightning/src/ln/chan_utils.rs @@ -20,14 +20,13 @@ use bitcoin::sighash::EcdsaSighashType; use bitcoin::transaction::Version; use bitcoin::hashes::{Hash, HashEngine}; -use bitcoin::hashes::hash160::Hash as Hash160; use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::hashes::ripemd160::Hash as Ripemd160; use bitcoin::hash_types::Txid; use crate::chain::chaininterface::fee_for_weight; use crate::chain::package::WEIGHT_REVOKED_OUTPUT; -use crate::sign::EntropySource; +use crate::sign::{ChannelSigner, EntropySource}; use crate::types::payment::{PaymentHash, PaymentPreimage}; use crate::ln::msgs::DecodeError; use crate::util::ser::{Readable, RequiredWrapper, Writeable, Writer}; @@ -41,8 +40,6 @@ use bitcoin::{secp256k1, Sequence, Witness}; use crate::io; use core::cmp; -use crate::util::transaction_utils::sort_outputs; -use crate::ln::channel::{INITIAL_COMMITMENT_NUMBER, ANCHOR_OUTPUT_VALUE_SATOSHI}; use core::ops::Deref; use crate::chain; use crate::types::features::ChannelTypeFeatures; @@ -699,19 +696,17 @@ pub(crate) fn make_funding_redeemscript_from_slices(broadcaster_funding_key: &[u } /// Builds an unsigned HTLC-Success or HTLC-Timeout transaction from the given channel and HTLC -/// parameters. This is used by [`TrustedCommitmentTransaction::get_htlc_sigs`] to fetch the -/// transaction which needs signing, and can be used to construct an HTLC transaction which is -/// broadcastable given a counterparty HTLC signature. +/// parameters. This is can be used to construct an HTLC transaction which is broadcastable given a +/// counterparty HTLC signature. /// /// 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: &DelayedPaymentKey, revocation_key: &RevocationKey) -> Transaction { - let txins= vec![build_htlc_input(commitment_txid, htlc, channel_type_features)]; +pub fn build_htlc_transaction(commitment_txid: &Txid, feerate_per_kw: u32, htlc: &HTLCOutputInCommitment, channel_type_features: &ChannelTypeFeatures, revokeable_spk: ScriptBuf) -> Transaction { + let txins = vec![build_htlc_input(commitment_txid, htlc, channel_type_features)]; let mut txouts: Vec = Vec::new(); txouts.push(build_htlc_output( - feerate_per_kw, contest_delay, htlc, channel_type_features, - broadcaster_delayed_payment_key, revocation_key + feerate_per_kw, htlc, channel_type_features, revokeable_spk, )); Transaction { @@ -735,7 +730,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: &DelayedPaymentKey, revocation_key: &RevocationKey + feerate_per_kw: u32, htlc: &HTLCOutputInCommitment, channel_type_features: &ChannelTypeFeatures, revokeable_spk: ScriptBuf ) -> TxOut { let weight = if htlc.offered { htlc_timeout_tx_weight(channel_type_features) @@ -750,7 +745,7 @@ pub(crate) fn build_htlc_output( }; TxOut { - script_pubkey: get_revokeable_redeemscript(revocation_key, contest_delay, broadcaster_delayed_payment_key).to_p2wsh(), + script_pubkey: revokeable_spk, value: output_value, } } @@ -843,14 +838,6 @@ pub fn get_anchor_redeemscript(funding_pubkey: &PublicKey) -> ScriptBuf { .into_script() } -/// Locates the output with an anchor script paying to `funding_pubkey` within `commitment_tx`. -pub(crate) fn get_anchor_output<'a>(commitment_tx: &'a Transaction, funding_pubkey: &PublicKey) -> Option<(u32, &'a TxOut)> { - let anchor_script = get_anchor_redeemscript(funding_pubkey).to_p2wsh(); - commitment_tx.output.iter().enumerate() - .find(|(_, txout)| txout.script_pubkey == anchor_script) - .map(|(idx, txout)| (idx as u32, txout)) -} - /// Returns the witness required to satisfy and spend an anchor input. pub fn build_anchor_input_witness(funding_key: &PublicKey, funding_sig: &Signature) -> Witness { let anchor_redeem_script = get_anchor_redeemscript(funding_key); @@ -951,6 +938,10 @@ impl ChannelTransactionParameters { channel_type_features: ChannelTypeFeatures::empty(), } } + + pub(crate) fn channel_type_features(&self) -> &ChannelTypeFeatures { + &self.channel_type_features + } } impl_writeable_tlv_based!(CounterpartyChannelTransactionParameters, { @@ -1105,24 +1096,25 @@ impl_writeable_tlv_based!(HolderCommitmentTransaction, { impl HolderCommitmentTransaction { #[cfg(test)] pub fn dummy(htlcs: &mut Vec<(HTLCOutputInCommitment, ())>) -> Self { + use crate::sign::InMemorySigner; let secp_ctx = Secp256k1::new(); - let dummy_key = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap()); + let dummy_sec = SecretKey::from_slice(&[42; 32]).unwrap(); + let dummy_key = PublicKey::from_secret_key(&secp_ctx, &dummy_sec); let dummy_sig = sign(&secp_ctx, &secp256k1::Message::from_digest([42; 32]), &SecretKey::from_slice(&[42; 32]).unwrap()); - let keys = TxCreationKeys { - per_commitment_point: 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: RevocationBasepoint::from(dummy_key), - payment_point: dummy_key.clone(), - delayed_payment_basepoint: DelayedPaymentBasepoint::from(dummy_key.clone()), - htlc_basepoint: HtlcBasepoint::from(dummy_key.clone()) - }; + let mut signer = InMemorySigner::new( + &secp_ctx, + dummy_sec, + dummy_sec, + dummy_sec, + dummy_sec, + dummy_sec, + [42; 32], + 0, + [42; 32], + [42; 32], + ); + let channel_pubkeys = signer.pubkeys(); let channel_parameters = ChannelTransactionParameters { holder_pubkeys: channel_pubkeys.clone(), holder_selected_contest_delay: 0, @@ -1131,11 +1123,13 @@ impl HolderCommitmentTransaction { funding_outpoint: Some(chain::transaction::OutPoint { txid: Txid::all_zeros(), index: 0 }), channel_type_features: ChannelTypeFeatures::only_static_remote_key(), }; + signer.provide_channel_parameters(&channel_parameters); + let keys = TxCreationKeys::from_channel_static_keys(&dummy_key.clone(), &signer.pubkeys(), signer.counterparty_pubkeys().unwrap(), &secp_ctx); let mut counterparty_htlc_sigs = Vec::new(); for _ in 0..htlcs.len() { counterparty_htlc_sigs.push(dummy_sig); } - let inner = CommitmentTransaction::new_with_auxiliary_htlc_data(0, 0, 0, dummy_key.clone(), dummy_key.clone(), keys, 0, htlcs, &channel_parameters.as_counterparty_broadcastable()); + let inner = CommitmentTransaction::new_with_auxiliary_htlc_data(0, 0, 0, keys, 0, htlcs, &channel_parameters.as_counterparty_broadcastable(), &signer, &secp_ctx, false); htlcs.sort_by_key(|htlc| htlc.0.transaction_output_index); HolderCommitmentTransaction { inner, @@ -1156,20 +1150,26 @@ impl HolderCommitmentTransaction { } } - pub(crate) fn add_holder_sig(&self, funding_redeemscript: &Script, holder_sig: Signature) -> Transaction { + pub(crate) fn finalize_witness(&self, funding_redeemscript: &Script, holder_sig: Signature) -> Witness { // First push the multisig dummy, note that due to BIP147 (NULLDUMMY) it must be a zero-length element. - let mut tx = self.inner.built.transaction.clone(); - tx.input[0].witness.push(Vec::new()); + let mut witness = Witness::new(); + witness.push(Vec::new()); if self.holder_sig_first { - tx.input[0].witness.push_ecdsa_signature(&BitcoinSignature::sighash_all(holder_sig)); - tx.input[0].witness.push_ecdsa_signature(&BitcoinSignature::sighash_all(self.counterparty_sig)); + witness.push_ecdsa_signature(&BitcoinSignature::sighash_all(holder_sig)); + witness.push_ecdsa_signature(&BitcoinSignature::sighash_all(self.counterparty_sig)); } else { - tx.input[0].witness.push_ecdsa_signature(&BitcoinSignature::sighash_all(self.counterparty_sig)); - tx.input[0].witness.push_ecdsa_signature(&BitcoinSignature::sighash_all(holder_sig)); + witness.push_ecdsa_signature(&BitcoinSignature::sighash_all(self.counterparty_sig)); + witness.push_ecdsa_signature(&BitcoinSignature::sighash_all(holder_sig)); } - tx.input[0].witness.push(funding_redeemscript.as_bytes().to_vec()); + witness.push(funding_redeemscript.to_bytes()); + witness + } + + pub(crate) fn extract_tx(&self, witness: Witness) -> Transaction { + let mut tx = self.inner.built.transaction.clone(); + tx.input[0].witness = witness; tx } } @@ -1445,15 +1445,14 @@ impl CommitmentTransaction { /// Only include HTLCs that are above the dust limit for the channel. /// /// This is not exported to bindings users due to the generic though we likely should expose a version without - pub fn new_with_auxiliary_htlc_data(commitment_number: u64, to_broadcaster_value_sat: u64, to_countersignatory_value_sat: u64, broadcaster_funding_key: PublicKey, countersignatory_funding_key: PublicKey, keys: TxCreationKeys, feerate_per_kw: u32, htlcs_with_aux: &mut Vec<(HTLCOutputInCommitment, T)>, channel_parameters: &DirectedChannelTransactionParameters) -> CommitmentTransaction { + pub fn new_with_auxiliary_htlc_data(commitment_number: u64, to_broadcaster_value_sat: u64, to_countersignatory_value_sat: u64, keys: TxCreationKeys, feerate_per_kw: u32, htlcs_with_aux: &mut Vec<(HTLCOutputInCommitment, T)>, channel_parameters: &DirectedChannelTransactionParameters, signer: &Signer, secp_ctx: &Secp256k1, is_holder_tx: bool) -> CommitmentTransaction { let to_broadcaster_value_sat = Amount::from_sat(to_broadcaster_value_sat); let to_countersignatory_value_sat = Amount::from_sat(to_countersignatory_value_sat); // Sort outputs and populate output indices while keeping track of the auxiliary data - let (outputs, htlcs) = Self::internal_build_outputs(&keys, to_broadcaster_value_sat, to_countersignatory_value_sat, htlcs_with_aux, channel_parameters, &broadcaster_funding_key, &countersignatory_funding_key).unwrap(); + let htlcs: Vec<&mut HTLCOutputInCommitment> = htlcs_with_aux.iter_mut().map(|(htlc, _)| htlc).collect(); + let (transaction, sorted_htlcs) = signer.build_transaction(&keys.per_commitment_point, to_broadcaster_value_sat, to_countersignatory_value_sat, htlcs, secp_ctx, is_holder_tx, commitment_number); - let (obscured_commitment_transaction_number, txins) = Self::internal_build_inputs(commitment_number, channel_parameters); - let transaction = Self::make_transaction(obscured_commitment_transaction_number, txins, outputs); let txid = transaction.compute_txid(); CommitmentTransaction { commitment_number, @@ -1461,7 +1460,7 @@ impl CommitmentTransaction { to_countersignatory_value_sat, to_broadcaster_delay: Some(channel_parameters.contest_delay()), feerate_per_kw, - htlcs, + htlcs: sorted_htlcs, channel_type_features: channel_parameters.channel_type_features().clone(), keys, built: BuiltCommitmentTransaction { @@ -1479,13 +1478,11 @@ impl CommitmentTransaction { self } - fn internal_rebuild_transaction(&self, keys: &TxCreationKeys, channel_parameters: &DirectedChannelTransactionParameters, broadcaster_funding_key: &PublicKey, countersignatory_funding_key: &PublicKey) -> Result { - let (obscured_commitment_transaction_number, txins) = Self::internal_build_inputs(self.commitment_number, channel_parameters); - - let mut htlcs_with_aux = self.htlcs.iter().map(|h| (h.clone(), ())).collect(); - let (outputs, _) = Self::internal_build_outputs(keys, self.to_broadcaster_value_sat, self.to_countersignatory_value_sat, &mut htlcs_with_aux, channel_parameters, broadcaster_funding_key, countersignatory_funding_key)?; + fn internal_rebuild_transaction(&self, per_commitment_point: &PublicKey, signer: &Signer, secp_ctx: &Secp256k1, is_holder_tx: bool) -> Result { + let mut htlcs: Vec<_> = self.htlcs.iter().map(|h| h.clone()).collect(); + let htlcs: Vec<&mut HTLCOutputInCommitment> = htlcs.iter_mut().collect(); + let (transaction, _) = signer.build_transaction(per_commitment_point, self.to_broadcaster_value_sat, self.to_countersignatory_value_sat, htlcs, secp_ctx, is_holder_tx, self.commitment_number); - let transaction = Self::make_transaction(obscured_commitment_transaction_number, txins, outputs); let txid = transaction.compute_txid(); let built_transaction = BuiltCommitmentTransaction { transaction, @@ -1494,141 +1491,6 @@ impl CommitmentTransaction { Ok(built_transaction) } - fn make_transaction(obscured_commitment_transaction_number: u64, txins: Vec, outputs: Vec) -> Transaction { - Transaction { - version: Version::TWO, - lock_time: LockTime::from_consensus(((0x20 as u32) << 8 * 3) | ((obscured_commitment_transaction_number & 0xffffffu64) as u32)), - input: txins, - output: outputs, - } - } - - // This is used in two cases: - // - initial sorting of outputs / HTLCs in the constructor, in which case T is auxiliary data the - // caller needs to have sorted together with the HTLCs so it can keep track of the output index - // - building of a bitcoin transaction during a verify() call, in which case T is just () - fn internal_build_outputs(keys: &TxCreationKeys, to_broadcaster_value_sat: Amount, to_countersignatory_value_sat: Amount, htlcs_with_aux: &mut Vec<(HTLCOutputInCommitment, T)>, channel_parameters: &DirectedChannelTransactionParameters, broadcaster_funding_key: &PublicKey, countersignatory_funding_key: &PublicKey) -> Result<(Vec, Vec), ()> { - let countersignatory_pubkeys = channel_parameters.countersignatory_pubkeys(); - let contest_delay = channel_parameters.contest_delay(); - - let mut txouts: Vec<(TxOut, Option<&mut HTLCOutputInCommitment>)> = Vec::new(); - - if to_countersignatory_value_sat > Amount::ZERO { - let script = if channel_parameters.channel_type_features().supports_anchors_zero_fee_htlc_tx() { - get_to_countersignatory_with_anchors_redeemscript(&countersignatory_pubkeys.payment_point).to_p2wsh() - } else { - ScriptBuf::new_p2wpkh(&Hash160::hash(&countersignatory_pubkeys.payment_point.serialize()).into()) - }; - txouts.push(( - TxOut { - script_pubkey: script.clone(), - value: to_countersignatory_value_sat, - }, - None, - )) - } - - if to_broadcaster_value_sat > Amount::ZERO { - let redeem_script = get_revokeable_redeemscript( - &keys.revocation_key, - contest_delay, - &keys.broadcaster_delayed_payment_key, - ); - txouts.push(( - TxOut { - script_pubkey: redeem_script.to_p2wsh(), - value: to_broadcaster_value_sat, - }, - None, - )); - } - - if channel_parameters.channel_type_features().supports_anchors_zero_fee_htlc_tx() { - if to_broadcaster_value_sat > Amount::ZERO || !htlcs_with_aux.is_empty() { - let anchor_script = get_anchor_redeemscript(broadcaster_funding_key); - txouts.push(( - TxOut { - script_pubkey: anchor_script.to_p2wsh(), - value: Amount::from_sat(ANCHOR_OUTPUT_VALUE_SATOSHI), - }, - None, - )); - } - - if to_countersignatory_value_sat > Amount::ZERO || !htlcs_with_aux.is_empty() { - let anchor_script = get_anchor_redeemscript(countersignatory_funding_key); - txouts.push(( - TxOut { - script_pubkey: anchor_script.to_p2wsh(), - value: Amount::from_sat(ANCHOR_OUTPUT_VALUE_SATOSHI), - }, - None, - )); - } - } - - let mut htlcs = Vec::with_capacity(htlcs_with_aux.len()); - for (htlc, _) in htlcs_with_aux { - let script = get_htlc_redeemscript(&htlc, &channel_parameters.channel_type_features(), &keys); - let txout = TxOut { - script_pubkey: script.to_p2wsh(), - value: htlc.to_bitcoin_amount(), - }; - txouts.push((txout, Some(htlc))); - } - - // Sort output in BIP-69 order (amount, scriptPubkey). Tie-breaks based on HTLC - // CLTV expiration height. - sort_outputs(&mut txouts, |a, b| { - if let &Some(ref a_htlcout) = a { - if let &Some(ref b_htlcout) = b { - a_htlcout.cltv_expiry.cmp(&b_htlcout.cltv_expiry) - // Note that due to hash collisions, we have to have a fallback comparison - // here for fuzzing mode (otherwise at least chanmon_fail_consistency - // may fail)! - .then(a_htlcout.payment_hash.0.cmp(&b_htlcout.payment_hash.0)) - // For non-HTLC outputs, if they're copying our SPK we don't really care if we - // close the channel due to mismatches - they're doing something dumb: - } else { cmp::Ordering::Equal } - } else { cmp::Ordering::Equal } - }); - - let mut outputs = Vec::with_capacity(txouts.len()); - for (idx, out) in txouts.drain(..).enumerate() { - if let Some(htlc) = out.1 { - htlc.transaction_output_index = Some(idx as u32); - htlcs.push(htlc.clone()); - } - outputs.push(out.0); - } - Ok((outputs, htlcs)) - } - - fn internal_build_inputs(commitment_number: u64, channel_parameters: &DirectedChannelTransactionParameters) -> (u64, Vec) { - let broadcaster_pubkeys = channel_parameters.broadcaster_pubkeys(); - let countersignatory_pubkeys = channel_parameters.countersignatory_pubkeys(); - let commitment_transaction_number_obscure_factor = get_commitment_transaction_number_obscure_factor( - &broadcaster_pubkeys.payment_point, - &countersignatory_pubkeys.payment_point, - channel_parameters.is_outbound(), - ); - - let obscured_commitment_transaction_number = - commitment_transaction_number_obscure_factor ^ (INITIAL_COMMITMENT_NUMBER - commitment_number); - - let txins = { - let ins: Vec = vec![TxIn { - previous_output: channel_parameters.funding_outpoint(), - script_sig: ScriptBuf::new(), - sequence: Sequence(((0x80 as u32) << 8 * 3) - | ((obscured_commitment_transaction_number >> 3 * 8) as u32)), - witness: Witness::new(), - }]; - ins - }; - (obscured_commitment_transaction_number, txins) - } - /// The backwards-counting commitment number pub fn commitment_number(&self) -> u64 { self.commitment_number @@ -1680,14 +1542,10 @@ impl CommitmentTransaction { /// /// An external validating signer must call this method before signing /// or using the built transaction. - pub fn verify(&self, channel_parameters: &DirectedChannelTransactionParameters, broadcaster_keys: &ChannelPublicKeys, countersignatory_keys: &ChannelPublicKeys, secp_ctx: &Secp256k1) -> Result { + pub fn verify(&self, secp_ctx: &Secp256k1, signer: &Signer, is_holder_tx: bool) -> Result { // This is the only field of the key cache that we trust let per_commitment_point = self.keys.per_commitment_point; - let keys = TxCreationKeys::from_channel_static_keys(&per_commitment_point, broadcaster_keys, countersignatory_keys, secp_ctx); - if keys != self.keys { - return Err(()); - } - let tx = self.internal_rebuild_transaction(&keys, channel_parameters, &broadcaster_keys.funding_pubkey, &countersignatory_keys.funding_pubkey)?; + let tx = self.internal_rebuild_transaction(&per_commitment_point, signer, secp_ctx, is_holder_tx)?; if self.built.transaction != tx.transaction || self.built.txid != tx.txid { return Err(()); } @@ -1732,40 +1590,8 @@ impl<'a> TrustedCommitmentTransaction<'a> { &self.inner.channel_type_features } - /// Get a signature for each HTLC which was included in the commitment transaction (ie for - /// which HTLCOutputInCommitment::transaction_output_index.is_some()). - /// - /// The returned Vec has one entry for each HTLC, and in the same order. - /// - /// This function is only valid in the holder commitment context, it always uses EcdsaSighashType::All. - pub fn get_htlc_sigs( - &self, htlc_base_key: &SecretKey, channel_parameters: &DirectedChannelTransactionParameters, - entropy_source: &ES, secp_ctx: &Secp256k1, - ) -> Result, ()> where ES::Target: EntropySource { - let inner = self.inner; - let keys = &inner.keys; - let txid = inner.built.txid; - let mut ret = Vec::with_capacity(inner.htlcs.len()); - let holder_htlc_key = derive_private_key(secp_ctx, &inner.keys.per_commitment_point, htlc_base_key); - - for this_htlc in inner.htlcs.iter() { - assert!(this_htlc.transaction_output_index.is_some()); - let htlc_tx = build_htlc_transaction(&txid, inner.feerate_per_kw, channel_parameters.contest_delay(), &this_htlc, &self.channel_type_features, &keys.broadcaster_delayed_payment_key, &keys.revocation_key); - - let htlc_redeemscript = get_htlc_redeemscript_with_explicit_keys(&this_htlc, &self.channel_type_features, &keys.broadcaster_htlc_key, &keys.countersignatory_htlc_key, &keys.revocation_key); - - let sighash = hash_to_message!(&sighash::SighashCache::new(&htlc_tx).p2wsh_signature_hash(0, &htlc_redeemscript, this_htlc.to_bitcoin_amount(), EcdsaSighashType::All).unwrap()[..]); - ret.push(sign_with_aux_rand(secp_ctx, &sighash, &holder_htlc_key, entropy_source)); - } - Ok(ret) - } - /// Builds the second-level holder HTLC transaction for the HTLC with index `htlc_index`. - pub(crate) fn build_unsigned_htlc_tx( - &self, channel_parameters: &DirectedChannelTransactionParameters, htlc_index: usize, - preimage: &Option, - ) -> Transaction { - let keys = &self.inner.keys; + pub(crate) fn build_unsigned_htlc_tx(&self, htlc_index: usize, preimage: &Option, revokeable_spk: ScriptBuf) -> Transaction { let this_htlc = &self.inner.htlcs[htlc_index]; assert!(this_htlc.transaction_output_index.is_some()); // if we don't have preimage for an HTLC-Success, we can't generate an HTLC transaction. @@ -1774,25 +1600,8 @@ impl<'a> TrustedCommitmentTransaction<'a> { if this_htlc.offered && preimage.is_some() { unreachable!(); } build_htlc_transaction( - &self.inner.built.txid, self.inner.feerate_per_kw, channel_parameters.contest_delay(), &this_htlc, - &self.channel_type_features, &keys.broadcaster_delayed_payment_key, &keys.revocation_key - ) - } - - - /// Builds the witness required to spend the input for the HTLC with index `htlc_index` in a - /// second-level holder HTLC transaction. - pub(crate) fn build_htlc_input_witness( - &self, htlc_index: usize, counterparty_signature: &Signature, signature: &Signature, - preimage: &Option - ) -> Witness { - let keys = &self.inner.keys; - let htlc_redeemscript = get_htlc_redeemscript_with_explicit_keys( - &self.inner.htlcs[htlc_index], &self.channel_type_features, &keys.broadcaster_htlc_key, - &keys.countersignatory_htlc_key, &keys.revocation_key - ); - build_htlc_input_witness( - signature, counterparty_signature, preimage, &htlc_redeemscript, &self.channel_type_features, + &self.inner.built.txid, self.inner.feerate_per_kw, &this_htlc, + &self.channel_type_features, revokeable_spk, ) } @@ -1895,7 +1704,7 @@ mod tests { use super::{CounterpartyCommitmentSecrets, ChannelPublicKeys}; use crate::chain; use crate::ln::chan_utils::{get_htlc_redeemscript, get_to_countersignatory_with_anchors_redeemscript, CommitmentTransaction, TxCreationKeys, ChannelTransactionParameters, CounterpartyChannelTransactionParameters, HTLCOutputInCommitment}; - use bitcoin::secp256k1::{PublicKey, SecretKey, Secp256k1}; + use bitcoin::secp256k1::{self, PublicKey, SecretKey, Secp256k1}; use crate::util::test_utils; use crate::sign::{ChannelSigner, SignerProvider}; use bitcoin::{Network, Txid, ScriptBuf, CompressedPublicKey}; @@ -1904,19 +1713,20 @@ mod tests { use crate::types::payment::PaymentHash; use bitcoin::PublicKey as BitcoinPublicKey; use crate::types::features::ChannelTypeFeatures; + use crate::util::test_channel_signer::TestChannelSigner; #[allow(unused_imports)] use crate::prelude::*; struct TestCommitmentTxBuilder { commitment_number: u64, - holder_funding_pubkey: PublicKey, - counterparty_funding_pubkey: PublicKey, keys: TxCreationKeys, feerate_per_kw: u32, htlcs_with_aux: Vec<(HTLCOutputInCommitment, ())>, channel_parameters: ChannelTransactionParameters, counterparty_pubkeys: ChannelPublicKeys, + signer: TestChannelSigner, + secp_ctx: Secp256k1::, } impl TestCommitmentTxBuilder { @@ -1925,15 +1735,13 @@ mod tests { let seed = [42; 32]; let network = Network::Testnet; let keys_provider = test_utils::TestKeysInterface::new(&seed, network); - let signer = keys_provider.derive_channel_signer(3000, keys_provider.generate_channel_keys_id(false, 1_000_000, 0)); + let mut signer = keys_provider.derive_channel_signer(3000, keys_provider.generate_channel_keys_id(false, 1_000_000, 0)); let counterparty_signer = keys_provider.derive_channel_signer(3000, keys_provider.generate_channel_keys_id(true, 1_000_000, 1)); - let delayed_payment_base = &signer.pubkeys().delayed_payment_basepoint; let per_commitment_secret = SecretKey::from_slice(&>::from_hex("1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100").unwrap()[..]).unwrap(); let per_commitment_point = PublicKey::from_secret_key(&secp_ctx, &per_commitment_secret); - let htlc_basepoint = &signer.pubkeys().htlc_basepoint; - let holder_pubkeys = signer.pubkeys(); + let holder_pubkeys = signer.pubkeys().clone(); let counterparty_pubkeys = counterparty_signer.pubkeys().clone(); - let keys = TxCreationKeys::derive_new(&secp_ctx, &per_commitment_point, delayed_payment_base, htlc_basepoint, &counterparty_pubkeys.revocation_basepoint, &counterparty_pubkeys.htlc_basepoint); + let keys = TxCreationKeys::from_channel_static_keys(&per_commitment_point, &holder_pubkeys, &counterparty_pubkeys, &secp_ctx); let channel_parameters = ChannelTransactionParameters { holder_pubkeys: holder_pubkeys.clone(), holder_selected_contest_delay: 0, @@ -1942,29 +1750,38 @@ mod tests { funding_outpoint: Some(chain::transaction::OutPoint { txid: Txid::all_zeros(), index: 0 }), channel_type_features: ChannelTypeFeatures::only_static_remote_key(), }; + signer.provide_channel_parameters(&channel_parameters); let htlcs_with_aux = Vec::new(); Self { commitment_number: 0, - holder_funding_pubkey: holder_pubkeys.funding_pubkey, - counterparty_funding_pubkey: counterparty_pubkeys.funding_pubkey, keys, feerate_per_kw: 1, htlcs_with_aux, channel_parameters, counterparty_pubkeys, + signer, + secp_ctx, } } fn build(&mut self, to_broadcaster_sats: u64, to_countersignatory_sats: u64) -> CommitmentTransaction { CommitmentTransaction::new_with_auxiliary_htlc_data( - self.commitment_number, to_broadcaster_sats, to_countersignatory_sats, - self.holder_funding_pubkey.clone(), - self.counterparty_funding_pubkey.clone(), + self.commitment_number, + to_broadcaster_sats, + to_countersignatory_sats, self.keys.clone(), self.feerate_per_kw, - &mut self.htlcs_with_aux, &self.channel_parameters.as_holder_broadcastable() + &mut self.htlcs_with_aux, &self.channel_parameters.as_holder_broadcastable(), + &self.signer, + &self.secp_ctx, + true, ) } + + fn overwrite_channel_features(&mut self, channel_type_features: ChannelTypeFeatures) { + self.channel_parameters.channel_type_features = channel_type_features; + self.signer.overwrite_channel_parameters(&self.channel_parameters); + } } #[test] @@ -1977,7 +1794,7 @@ mod tests { assert_eq!(tx.built.transaction.output[1].script_pubkey, bitcoin::address::Address::p2wpkh(&CompressedPublicKey(builder.counterparty_pubkeys.payment_point), Network::Testnet).script_pubkey()); // Generate broadcaster and counterparty outputs as well as two anchors - builder.channel_parameters.channel_type_features = ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies(); + builder.overwrite_channel_features(ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies()); let tx = builder.build(1000, 2000); assert_eq!(tx.built.transaction.output.len(), 4); assert_eq!(tx.built.transaction.output[3].script_pubkey, get_to_countersignatory_with_anchors_redeemscript(&builder.counterparty_pubkeys.payment_point).to_p2wsh()); @@ -2007,7 +1824,7 @@ mod tests { }; // Generate broadcaster output and received and offered HTLC outputs, w/o anchors - builder.channel_parameters.channel_type_features = ChannelTypeFeatures::only_static_remote_key(); + builder.overwrite_channel_features(ChannelTypeFeatures::only_static_remote_key()); builder.htlcs_with_aux = vec![(received_htlc.clone(), ()), (offered_htlc.clone(), ())]; let tx = builder.build(3000, 0); let keys = &builder.keys.clone(); @@ -2020,7 +1837,7 @@ mod tests { "0020215d61bba56b19e9eadb6107f5a85d7f99c40f65992443f69229c290165bc00d"); // Generate broadcaster output and received and offered HTLC outputs, with anchors - builder.channel_parameters.channel_type_features = ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies(); + builder.overwrite_channel_features(ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies()); builder.htlcs_with_aux = vec![(received_htlc.clone(), ()), (offered_htlc.clone(), ())]; let tx = builder.build(3000, 0); assert_eq!(tx.built.transaction.output.len(), 5); diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index b092a437fe9..b7c2f32582c 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -11,7 +11,6 @@ use bitcoin::amount::Amount; use bitcoin::constants::ChainHash; use bitcoin::script::{Script, ScriptBuf, Builder, WScriptHash}; use bitcoin::transaction::{Transaction, TxIn}; -use bitcoin::sighash; use bitcoin::sighash::EcdsaSighashType; use bitcoin::consensus::encode; use bitcoin::absolute::LockTime; @@ -1771,26 +1770,6 @@ trait InitialRemoteCommitmentReceiver where SP::Target: SignerProvide fn received_msg(&self) -> &'static str; - fn check_counterparty_commitment_signature( - &self, sig: &Signature, holder_commitment_point: &mut HolderCommitmentPoint, logger: &L - ) -> Result where L::Target: Logger { - let funding_script = self.context().get_funding_redeemscript(); - - let keys = self.context().build_holder_transaction_keys(holder_commitment_point.current_point()); - let initial_commitment_tx = self.context().build_commitment_transaction(holder_commitment_point.transaction_number(), &keys, true, false, logger).tx; - let trusted_tx = initial_commitment_tx.trust(); - let initial_commitment_bitcoin_tx = trusted_tx.built_transaction(); - let sighash = initial_commitment_bitcoin_tx.get_sighash_all(&funding_script, self.context().channel_value_satoshis); - // They sign the holder commitment transaction... - log_trace!(logger, "Checking {} tx signature {} by key {} against tx {} (sighash {}) with redeemscript {} for channel {}.", - self.received_msg(), log_bytes!(sig.serialize_compact()[..]), log_bytes!(self.context().counterparty_funding_pubkey().serialize()), - encode::serialize_hex(&initial_commitment_bitcoin_tx.transaction), log_bytes!(sighash[..]), - encode::serialize_hex(&funding_script), &self.context().channel_id()); - secp_check!(self.context().secp_ctx.verify_ecdsa(&sighash, sig, self.context().counterparty_funding_pubkey()), format!("Invalid {} signature from peer", self.received_msg())); - - Ok(initial_commitment_tx) - } - fn initial_commitment_signed( &mut self, channel_id: ChannelId, counterparty_signature: Signature, holder_commitment_point: &mut HolderCommitmentPoint, counterparty_commitment_number: u64, best_block: BestBlock, signer_provider: &SP, logger: &L, @@ -1798,30 +1777,9 @@ trait InitialRemoteCommitmentReceiver where SP::Target: SignerProvide where L::Target: Logger { - let initial_commitment_tx = match self.check_counterparty_commitment_signature(&counterparty_signature, holder_commitment_point, logger) { - Ok(res) => res, - Err(ChannelError::Close(e)) => { - // TODO(dual_funding): Update for V2 established channels. - if !self.context().is_outbound() { - self.context_mut().channel_transaction_parameters.funding_outpoint = None; - } - return Err(ChannelError::Close(e)); - }, - Err(e) => { - // The only error we know how to handle is ChannelError::Close, so we fall over here - // to make sure we don't continue with an inconsistent state. - panic!("unexpected error type from check_counterparty_commitment_signature {:?}", e); - } - }; - let context = self.context_mut(); - let counterparty_keys = context.build_remote_transaction_keys(); - let counterparty_initial_commitment_tx = context.build_commitment_transaction(context.cur_counterparty_commitment_transaction_number, &counterparty_keys, false, false, logger).tx; - let counterparty_trusted_tx = counterparty_initial_commitment_tx.trust(); - let counterparty_initial_bitcoin_tx = counterparty_trusted_tx.built_transaction(); - - log_trace!(logger, "Initial counterparty tx for channel {} is: txid {} tx {}", - &context.channel_id(), counterparty_initial_bitcoin_tx.txid, encode::serialize_hex(&counterparty_initial_bitcoin_tx.transaction)); - + let context = self.context(); + let keys = context.build_holder_transaction_keys(holder_commitment_point.current_point()); + let initial_commitment_tx = context.build_commitment_transaction(holder_commitment_point.transaction_number(), &keys, true, false, logger).tx; let holder_commitment_tx = HolderCommitmentTransaction::new( initial_commitment_tx, counterparty_signature, @@ -1830,11 +1788,24 @@ trait InitialRemoteCommitmentReceiver where SP::Target: SignerProvide context.counterparty_funding_pubkey() ); - if context.holder_signer.as_ref().validate_holder_commitment(&holder_commitment_tx, Vec::new()).is_err() { + if context.holder_signer.as_ref().validate_holder_commitment(&holder_commitment_tx, Vec::new(), &context.secp_ctx).is_err() { + // TODO(dual_funding): Update for V2 established channels. + if !self.context().is_outbound() { + self.context_mut().channel_transaction_parameters.funding_outpoint = None; + } return Err(ChannelError::close("Failed to validate our commitment".to_owned())); } + let counterparty_keys = context.build_remote_transaction_keys(); + let counterparty_initial_commitment_tx = context.build_commitment_transaction(context.cur_counterparty_commitment_transaction_number, &counterparty_keys, false, false, logger).tx; + let counterparty_trusted_tx = counterparty_initial_commitment_tx.trust(); + let counterparty_initial_bitcoin_tx = counterparty_trusted_tx.built_transaction(); + + log_trace!(logger, "Initial counterparty tx for channel {} is: txid {} tx {}", + &context.channel_id(), counterparty_initial_bitcoin_tx.txid, encode::serialize_hex(&counterparty_initial_bitcoin_tx.transaction)); + // Now that we're past error-generating stuff, update our local state: + let context = self.context_mut(); context.channel_id = channel_id; @@ -1853,18 +1824,16 @@ trait InitialRemoteCommitmentReceiver where SP::Target: SignerProvide return Err(ChannelError::close("Failed to advance holder commitment point".to_owned())); } - let funding_redeemscript = context.get_funding_redeemscript(); + let funding_txo_script = context.get_funding_redeemscript().to_p2wsh(); let funding_txo = context.get_funding_txo().unwrap(); - let funding_txo_script = funding_redeemscript.to_p2wsh(); let obscure_factor = get_commitment_transaction_number_obscure_factor(&context.get_holder_pubkeys().payment_point, &context.get_counterparty_pubkeys().payment_point, context.is_outbound()); let shutdown_script = context.shutdown_scriptpubkey.clone().map(|script| script.into_inner()); - let mut monitor_signer = signer_provider.derive_channel_signer(context.channel_value_satoshis, context.channel_keys_id); - monitor_signer.provide_channel_parameters(&context.channel_transaction_parameters); + let monitor_signer = signer_provider.derive_channel_signer(context.channel_value_satoshis, context.channel_keys_id); let channel_monitor = ChannelMonitor::new(context.secp_ctx.clone(), monitor_signer, shutdown_script, context.get_holder_selected_contest_delay(), &context.destination_script, (funding_txo, funding_txo_script), &context.channel_transaction_parameters, context.is_outbound(), - funding_redeemscript.clone(), context.channel_value_satoshis, + context.channel_value_satoshis, obscure_factor, holder_commitment_tx, best_block, context.counterparty_node_id, context.channel_id()); channel_monitor.provide_initial_counterparty_commitment_tx( @@ -3338,8 +3307,8 @@ impl ChannelContext where SP::Target: SignerProvider { broadcaster_max_commitment_tx_output.1 = cmp::max(broadcaster_max_commitment_tx_output.1, value_to_remote_msat as u64); } - let total_fee_sat = commit_tx_fee_sat(feerate_per_kw, included_non_dust_htlcs.len(), &self.channel_transaction_parameters.channel_type_features); - let anchors_val = if self.channel_transaction_parameters.channel_type_features.supports_anchors_zero_fee_htlc_tx() { ANCHOR_OUTPUT_VALUE_SATOSHI * 2 } else { 0 } as i64; + let total_fee_sat = commit_tx_fee_sat(feerate_per_kw, included_non_dust_htlcs.len(), self.channel_transaction_parameters.channel_type_features()); + let anchors_val = if self.channel_transaction_parameters.channel_type_features().supports_anchors_zero_fee_htlc_tx() { ANCHOR_OUTPUT_VALUE_SATOSHI * 2 } else { 0 } as i64; let (value_to_self, value_to_remote) = if self.is_outbound() { (value_to_self_msat / 1000 - anchors_val - total_fee_sat as i64, value_to_remote_msat / 1000) } else { @@ -3348,11 +3317,6 @@ impl ChannelContext where SP::Target: SignerProvider { let mut value_to_a = if local { value_to_self } else { value_to_remote }; let mut value_to_b = if local { value_to_remote } else { value_to_self }; - let (funding_pubkey_a, funding_pubkey_b) = if local { - (self.get_holder_pubkeys().funding_pubkey, self.get_counterparty_pubkeys().funding_pubkey) - } else { - (self.get_counterparty_pubkeys().funding_pubkey, self.get_holder_pubkeys().funding_pubkey) - }; if value_to_a >= (broadcaster_dust_limit_satoshis as i64) { log_trace!(logger, " ...including {} output with value {}", if local { "to_local" } else { "to_remote" }, value_to_a); @@ -3374,12 +3338,13 @@ impl ChannelContext where SP::Target: SignerProvider { let tx = CommitmentTransaction::new_with_auxiliary_htlc_data(commitment_number, value_to_a as u64, value_to_b as u64, - funding_pubkey_a, - funding_pubkey_b, keys.clone(), feerate_per_kw, &mut included_non_dust_htlcs, - &channel_parameters + &channel_parameters, + self.holder_signer.as_ecdsa().unwrap(), + &self.secp_ctx, + local, ); let mut htlcs_included = included_non_dust_htlcs; // The unwrap is safe, because all non-dust HTLCs have been assigned an output index @@ -4197,7 +4162,7 @@ impl ChannelContext where SP::Target: SignerProvider { if self.channel_type.supports_anchors_zero_fee_htlc_tx() { self.channel_type.clear_anchors_zero_fee_htlc_tx(); self.feerate_per_kw = fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::NonAnchorChannelFee); - assert!(!self.channel_transaction_parameters.channel_type_features.supports_anchors_nonzero_fee_htlc_tx()); + assert!(!self.channel_transaction_parameters.channel_type_features().supports_anchors_nonzero_fee_htlc_tx()); } else if self.channel_type.supports_scid_privacy() { self.channel_type.clear_scid_privacy(); } else { @@ -5251,25 +5216,25 @@ impl FundedChannel where return Err(ChannelError::close("Peer sent commitment_signed after we'd started exchanging closing_signeds".to_owned())); } - let funding_script = self.context.get_funding_redeemscript(); - let keys = self.context.build_holder_transaction_keys(self.holder_commitment_point.current_point()); let commitment_stats = self.context.build_commitment_transaction(self.holder_commitment_point.transaction_number(), &keys, true, false, logger); - let commitment_txid = { - let trusted_tx = commitment_stats.tx.trust(); - let bitcoin_tx = trusted_tx.built_transaction(); - let sighash = bitcoin_tx.get_sighash_all(&funding_script, self.context.channel_value_satoshis); - - log_trace!(logger, "Checking commitment tx signature {} by key {} against tx {} (sighash {}) with redeemscript {} in channel {}", - log_bytes!(msg.signature.serialize_compact()[..]), - log_bytes!(self.context.counterparty_funding_pubkey().serialize()), encode::serialize_hex(&bitcoin_tx.transaction), - log_bytes!(sighash[..]), encode::serialize_hex(&funding_script), &self.context.channel_id()); - if let Err(_) = self.context.secp_ctx.verify_ecdsa(&sighash, &msg.signature, &self.context.counterparty_funding_pubkey()) { - return Err(ChannelError::close("Invalid commitment tx signature from peer".to_owned())); - } - bitcoin_tx.txid - }; + + if msg.htlc_signatures.len() != commitment_stats.num_nondust_htlcs { + return Err(ChannelError::close(format!("Got wrong number of HTLC signatures ({}) from remote. It must be {}", msg.htlc_signatures.len(), commitment_stats.num_nondust_htlcs))); + } + + let holder_commitment_tx = HolderCommitmentTransaction::new( + commitment_stats.tx, + msg.signature, + msg.htlc_signatures.clone(), + &self.context.get_holder_pubkeys().funding_pubkey, + self.context.counterparty_funding_pubkey() + ); + + self.context.holder_signer.as_ref().validate_holder_commitment(&holder_commitment_tx, commitment_stats.outbound_htlc_preimages, &self.context.secp_ctx) + .map_err(|_| ChannelError::close("Failed to validate our commitment".to_owned()))?; + let mut htlcs_cloned: Vec<_> = commitment_stats.htlcs_included.iter().map(|htlc| (htlc.0.clone(), htlc.1.map(|h| h.clone()))).collect(); // If our counterparty updated the channel fee in this commitment transaction, check that @@ -5302,10 +5267,6 @@ impl FundedChannel where } } - if msg.htlc_signatures.len() != commitment_stats.num_nondust_htlcs { - return Err(ChannelError::close(format!("Got wrong number of HTLC signatures ({}) from remote. It must be {}", msg.htlc_signatures.len(), commitment_stats.num_nondust_htlcs))); - } - // Up to LDK 0.0.115, HTLC information was required to be duplicated in the // `htlcs_and_sigs` vec and in the `holder_commitment_tx` itself, both of which were passed // in the `ChannelMonitorUpdate`. In 0.0.115, support for having a separate set of @@ -5325,19 +5286,6 @@ impl FundedChannel where let mut htlcs_and_sigs = Vec::with_capacity(htlcs_cloned.len()); for (idx, (htlc, mut source_opt)) in htlcs_cloned.drain(..).enumerate() { if let Some(_) = htlc.transaction_output_index { - let htlc_tx = chan_utils::build_htlc_transaction(&commitment_txid, commitment_stats.feerate_per_kw, - self.context.get_counterparty_selected_contest_delay().unwrap(), &htlc, &self.context.channel_type, - &keys.broadcaster_delayed_payment_key, &keys.revocation_key); - - let htlc_redeemscript = chan_utils::get_htlc_redeemscript(&htlc, &self.context.channel_type, &keys); - 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).p2wsh_signature_hash(0, &htlc_redeemscript, htlc.to_bitcoin_amount(), 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.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.to_public_key()) { - return Err(ChannelError::close("Invalid HTLC tx signature from peer".to_owned())); - } if !separate_nondust_htlc_sources { htlcs_and_sigs.push((htlc, Some(msg.htlc_signatures[idx]), source_opt.take())); } @@ -5352,17 +5300,6 @@ impl FundedChannel where debug_assert!(source_opt.is_none(), "HTLCSource should have been put somewhere"); } - let holder_commitment_tx = HolderCommitmentTransaction::new( - commitment_stats.tx, - msg.signature, - msg.htlc_signatures.clone(), - &self.context.get_holder_pubkeys().funding_pubkey, - self.context.counterparty_funding_pubkey() - ); - - self.context.holder_signer.as_ref().validate_holder_commitment(&holder_commitment_tx, commitment_stats.outbound_htlc_preimages) - .map_err(|_| ChannelError::close("Failed to validate our commitment".to_owned()))?; - // Update state now that we've passed all the can-fail calls... let mut need_commitment = false; if let &mut Some((_, ref mut update_state)) = &mut self.context.pending_update_fee { @@ -5661,17 +5598,8 @@ impl FundedChannel where *self.context.next_remote_commitment_tx_fee_info_cached.lock().unwrap() = None; } - match &self.context.holder_signer { - ChannelSignerType::Ecdsa(ecdsa) => { - ecdsa.validate_counterparty_revocation( - self.context.cur_counterparty_commitment_transaction_number + 1, - &secret - ).map_err(|_| ChannelError::close("Failed to validate revocation from peer".to_owned()))?; - }, - // TODO (taproot|arik) - #[cfg(taproot)] - _ => todo!() - }; + self.context.holder_signer.as_ref().validate_counterparty_revocation(self.context.cur_counterparty_commitment_transaction_number + 1, &secret) + .map_err(|_| ChannelError::close("Failed to validate revocation from peer".to_owned()))?; self.context.commitment_secrets.provide_secret(self.context.cur_counterparty_commitment_transaction_number + 1, msg.per_commitment_secret) .map_err(|_| ChannelError::close("Previous secrets did not match new one".to_owned()))?; @@ -8285,6 +8213,7 @@ impl FundedChannel where ).map_err(|_| ChannelError::Ignore("Failed to get signatures for new commitment_signed".to_owned()))?; signature = res.0; htlc_signatures = res.1; + let revokeable_spk = ecdsa.get_revokeable_spk(false, commitment_stats.tx.commitment_number(), &commitment_stats.tx.per_commitment_point(), &self.context.secp_ctx); log_trace!(logger, "Signed remote commitment tx {} (txid {}) with redeemscript {} -> {} in channel {}", encode::serialize_hex(&commitment_stats.tx.trust().built_transaction().transaction), @@ -8293,7 +8222,7 @@ impl FundedChannel where for (ref htlc_sig, ref htlc) in htlc_signatures.iter().zip(htlcs) { 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::build_htlc_transaction(&counterparty_commitment_txid, commitment_stats.feerate_per_kw, htlc, &self.context.channel_type, revokeable_spk.clone())), encode::serialize_hex(&chan_utils::get_htlc_redeemscript(&htlc, &self.context.channel_type, &counterparty_keys)), log_bytes!(counterparty_keys.broadcaster_htlc_key.to_public_key().serialize()), log_bytes!(htlc_sig.serialize_compact()[..]), &self.context.channel_id()); @@ -10538,6 +10467,14 @@ mod tests { } } + #[cfg(ldk_test_vectors)] + impl crate::ln::channel::ChannelContext<&Keys> { + fn overwrite_channel_features(&mut self, channel_type_features: ChannelTypeFeatures) { + self.channel_transaction_parameters.channel_type_features = channel_type_features; + self.holder_signer.as_mut_ecdsa().unwrap().overwrite_channel_parameters(&self.channel_transaction_parameters); + } + } + #[cfg(ldk_test_vectors)] fn public_from_secret_hex(secp_ctx: &Secp256k1, hex: &str) -> PublicKey { assert!(cfg!(not(feature = "grind_signatures"))); @@ -11129,7 +11066,7 @@ mod tests { use bitcoin::hash_types::Txid; use bitcoin::hex::DisplayHex; use bitcoin::secp256k1::Message; - use crate::sign::{ChannelDerivationParameters, HTLCDescriptor, ecdsa::EcdsaChannelSigner}; + use crate::sign::{ChannelDerivationParameters, HTLCDescriptor}; use crate::types::payment::PaymentPreimage; use crate::ln::channel::{HTLCOutputInCommitment ,TxCreationKeys}; use crate::ln::channel_keys::{DelayedPaymentBasepoint, HtlcBasepoint}; @@ -11143,7 +11080,7 @@ mod tests { let logger : Arc = Arc::new(test_utils::TestLogger::new()); let secp_ctx = Secp256k1::new(); - let mut signer = InMemorySigner::new( + let signer = InMemorySigner::new( &secp_ctx, SecretKey::from_slice(&>::from_hex("30ff4956bbdd3222d44cc5e8a1261dab1e07957bdac5ae88fe3261ef321f3749").unwrap()[..]).unwrap(), SecretKey::from_slice(&>::from_hex("0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap()[..]).unwrap(), @@ -11160,7 +11097,7 @@ mod tests { assert_eq!(signer.pubkeys().funding_pubkey.serialize()[..], >::from_hex("023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb").unwrap()[..]); - let keys_provider = Keys { signer: signer.clone() }; + let keys_provider = Keys { signer }; let counterparty_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap()); let mut config = UserConfig::default(); @@ -11184,7 +11121,6 @@ mod tests { selected_contest_delay: 144 }); chan.context.channel_transaction_parameters.funding_outpoint = Some(funding_info); - signer.provide_channel_parameters(&chan.context.channel_transaction_parameters); assert_eq!(counterparty_pubkeys.payment_point.serialize()[..], >::from_hex("032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991").unwrap()[..]); @@ -11206,14 +11142,14 @@ mod tests { macro_rules! test_commitment { ( $counterparty_sig_hex: expr, $sig_hex: expr, $tx_hex: expr, $($remain:tt)* ) => { - chan.context.channel_transaction_parameters.channel_type_features = ChannelTypeFeatures::only_static_remote_key(); + chan.context.overwrite_channel_features(ChannelTypeFeatures::only_static_remote_key()); test_commitment_common!($counterparty_sig_hex, $sig_hex, $tx_hex, &ChannelTypeFeatures::only_static_remote_key(), $($remain)*); }; } macro_rules! test_commitment_with_anchors { ( $counterparty_sig_hex: expr, $sig_hex: expr, $tx_hex: expr, $($remain:tt)* ) => { - chan.context.channel_transaction_parameters.channel_type_features = ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies(); + chan.context.overwrite_channel_features(ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies()); test_commitment_common!($counterparty_sig_hex, $sig_hex, $tx_hex, &ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies(), $($remain)*); }; } @@ -11256,11 +11192,8 @@ mod tests { &chan.context.holder_signer.as_ref().pubkeys().funding_pubkey, chan.context.counterparty_funding_pubkey() ); - let holder_sig = signer.sign_holder_commitment(&holder_commitment_tx, &secp_ctx).unwrap(); - assert_eq!(Signature::from_der(&>::from_hex($sig_hex).unwrap()[..]).unwrap(), holder_sig, "holder_sig"); - - let funding_redeemscript = chan.context.get_funding_redeemscript(); - let tx = holder_commitment_tx.add_holder_sig(&funding_redeemscript, holder_sig); + let witness = chan.context.holder_signer.as_ref().sign_holder_commitment(&holder_commitment_tx, &secp_ctx).unwrap(); + let tx = holder_commitment_tx.extract_tx(witness); assert_eq!(serialize(&tx)[..], >::from_hex($tx_hex).unwrap()[..], "tx"); // ((htlc, counterparty_sig), (index, holder_sig)) @@ -11271,9 +11204,8 @@ mod tests { let remote_signature = Signature::from_der(&>::from_hex($counterparty_htlc_sig_hex).unwrap()[..]).unwrap(); let ref htlc = htlcs[$htlc_idx]; - let mut htlc_tx = chan_utils::build_htlc_transaction(&unsigned_tx.txid, chan.context.feerate_per_kw, - chan.context.get_counterparty_selected_contest_delay().unwrap(), - &htlc, $opt_anchors, &keys.broadcaster_delayed_payment_key, &keys.revocation_key); + let revokeable_spk = chan.context.holder_signer.as_ref().get_revokeable_spk(true, holder_commitment_tx.commitment_number(), &holder_commitment_tx.per_commitment_point(), &secp_ctx); + let mut htlc_tx = chan_utils::build_htlc_transaction(&unsigned_tx.txid, chan.context.feerate_per_kw, &htlc, $opt_anchors, revokeable_spk); 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_digest(sighash::SighashCache::new(&htlc_tx).p2wsh_signature_hash(0, &htlc_redeemscript, htlc.to_bitcoin_amount(), htlc_sighashtype).unwrap().as_raw_hash().to_byte_array()); @@ -11291,8 +11223,12 @@ mod tests { assert!(preimage.is_some()); } + let num_anchors = if $opt_anchors.supports_anchors_zero_fee_htlc_tx() { 2 } else { 0 }; + assert_eq!(htlc.transaction_output_index, Some($htlc_idx + num_anchors), "output index"); + let htlc_counterparty_sig = htlc_counterparty_sig_iter.next().unwrap(); - let htlc_holder_sig = signer.sign_holder_htlc_transaction(&htlc_tx, 0, &HTLCDescriptor { + + htlc_tx.input[0].witness = chan.context.holder_signer.as_ref().sign_holder_htlc_transaction(&htlc_tx, 0, &HTLCDescriptor { channel_derivation_parameters: ChannelDerivationParameters { value_satoshis: chan.context.channel_value_satoshis, keys_id: chan.context.channel_keys_id, @@ -11306,13 +11242,7 @@ mod tests { preimage: preimage.clone(), counterparty_sig: *htlc_counterparty_sig, }, &secp_ctx).unwrap(); - let num_anchors = if $opt_anchors.supports_anchors_zero_fee_htlc_tx() { 2 } else { 0 }; - assert_eq!(htlc.transaction_output_index, Some($htlc_idx + num_anchors), "output index"); - let signature = Signature::from_der(&>::from_hex($htlc_sig_hex).unwrap()[..]).unwrap(); - assert_eq!(signature, htlc_holder_sig, "htlc sig"); - let trusted_tx = holder_commitment_tx.trust(); - htlc_tx.input[0].witness = trusted_tx.build_htlc_input_witness($htlc_idx, htlc_counterparty_sig, &htlc_holder_sig, &preimage); log_trace!(logger, "htlc_tx = {}", serialize(&htlc_tx).as_hex()); assert_eq!(serialize(&htlc_tx)[..], >::from_hex($htlc_tx_hex).unwrap()[..], "htlc tx"); })* diff --git a/lightning/src/ln/functional_tests.rs b/lightning/src/ln/functional_tests.rs index 5a7f870078a..7a2a91c6637 100644 --- a/lightning/src/ln/functional_tests.rs +++ b/lightning/src/ln/functional_tests.rs @@ -731,24 +731,22 @@ fn test_update_fee_that_funder_cannot_afford() { // Get the TestChannelSigner for each channel, which will be used to (1) get the keys // needed to sign the new commitment tx and (2) sign the new commitment tx. - let (local_revocation_basepoint, local_htlc_basepoint, local_funding) = { + let (local_revocation_basepoint, local_htlc_basepoint) = { let per_peer_state = nodes[0].node.per_peer_state.read().unwrap(); let chan_lock = per_peer_state.get(&nodes[1].node.get_our_node_id()).unwrap().lock().unwrap(); let local_chan = chan_lock.channel_by_id.get(&chan.2).and_then(Channel::as_funded).unwrap(); let chan_signer = local_chan.get_signer(); let pubkeys = chan_signer.as_ref().pubkeys(); - (pubkeys.revocation_basepoint, pubkeys.htlc_basepoint, - pubkeys.funding_pubkey) + (pubkeys.revocation_basepoint, pubkeys.htlc_basepoint) }; - let (remote_delayed_payment_basepoint, remote_htlc_basepoint, remote_point, remote_funding) = { + let (remote_delayed_payment_basepoint, remote_htlc_basepoint, remote_point) = { let per_peer_state = nodes[1].node.per_peer_state.read().unwrap(); let chan_lock = per_peer_state.get(&nodes[0].node.get_our_node_id()).unwrap().lock().unwrap(); let remote_chan = chan_lock.channel_by_id.get(&chan.2).and_then(Channel::as_funded).unwrap(); let chan_signer = remote_chan.get_signer(); let pubkeys = chan_signer.as_ref().pubkeys(); (pubkeys.delayed_payment_basepoint, pubkeys.htlc_basepoint, - chan_signer.as_ref().get_per_commitment_point(INITIAL_COMMITMENT_NUMBER - 1, &secp_ctx).unwrap(), - pubkeys.funding_pubkey) + chan_signer.as_ref().get_per_commitment_point(INITIAL_COMMITMENT_NUMBER - 1, &secp_ctx).unwrap()) }; // Assemble the set of keys we can use for signatures for our commitment_signed message. @@ -765,11 +763,13 @@ fn test_update_fee_that_funder_cannot_afford() { INITIAL_COMMITMENT_NUMBER - 1, push_sats, channel_value - push_sats - commit_tx_fee_msat(non_buffer_feerate + 4, 0, &channel_type_features) / 1000, - local_funding, remote_funding, commit_tx_keys.clone(), non_buffer_feerate + 4, &mut htlcs, - &local_chan.context.channel_transaction_parameters.as_counterparty_broadcastable() + &local_chan.context.channel_transaction_parameters.as_counterparty_broadcastable(), + local_chan_signer.as_ecdsa().unwrap(), + &secp_ctx, + false, ); local_chan_signer.as_ecdsa().unwrap().sign_counterparty_commitment(&commitment_tx, Vec::new(), Vec::new(), &secp_ctx).unwrap() }; @@ -1461,7 +1461,7 @@ fn test_fee_spike_violation_fails_htlc() { // Get the TestChannelSigner for each channel, which will be used to (1) get the keys // needed to sign the new commitment tx and (2) sign the new commitment tx. - let (local_revocation_basepoint, local_htlc_basepoint, local_secret, next_local_point, local_funding) = { + let (local_revocation_basepoint, local_htlc_basepoint, local_secret, next_local_point) = { let per_peer_state = nodes[0].node.per_peer_state.read().unwrap(); let chan_lock = per_peer_state.get(&nodes[1].node.get_our_node_id()).unwrap().lock().unwrap(); let local_chan = chan_lock.channel_by_id.get(&chan.2).and_then(Channel::as_funded).unwrap(); @@ -1472,18 +1472,16 @@ fn test_fee_spike_violation_fails_htlc() { let pubkeys = chan_signer.as_ref().pubkeys(); (pubkeys.revocation_basepoint, pubkeys.htlc_basepoint, chan_signer.as_ref().release_commitment_secret(INITIAL_COMMITMENT_NUMBER).unwrap(), - chan_signer.as_ref().get_per_commitment_point(INITIAL_COMMITMENT_NUMBER - 2, &secp_ctx).unwrap(), - chan_signer.as_ref().pubkeys().funding_pubkey) + chan_signer.as_ref().get_per_commitment_point(INITIAL_COMMITMENT_NUMBER - 2, &secp_ctx).unwrap()) }; - let (remote_delayed_payment_basepoint, remote_htlc_basepoint, remote_point, remote_funding) = { + let (remote_delayed_payment_basepoint, remote_htlc_basepoint, remote_point) = { let per_peer_state = nodes[1].node.per_peer_state.read().unwrap(); let chan_lock = per_peer_state.get(&nodes[0].node.get_our_node_id()).unwrap().lock().unwrap(); let remote_chan = chan_lock.channel_by_id.get(&chan.2).and_then(Channel::as_funded).unwrap(); let chan_signer = remote_chan.get_signer(); let pubkeys = chan_signer.as_ref().pubkeys(); (pubkeys.delayed_payment_basepoint, pubkeys.htlc_basepoint, - chan_signer.as_ref().get_per_commitment_point(INITIAL_COMMITMENT_NUMBER - 1, &secp_ctx).unwrap(), - chan_signer.as_ref().pubkeys().funding_pubkey) + chan_signer.as_ref().get_per_commitment_point(INITIAL_COMMITMENT_NUMBER - 1, &secp_ctx).unwrap()) }; // Assemble the set of keys we can use for signatures for our commitment_signed message. @@ -1513,11 +1511,13 @@ fn test_fee_spike_violation_fails_htlc() { commitment_number, 95000, local_chan_balance, - local_funding, remote_funding, commit_tx_keys.clone(), feerate_per_kw, &mut vec![(accepted_htlc_info, ())], - &local_chan.context.channel_transaction_parameters.as_counterparty_broadcastable() + &local_chan.context.channel_transaction_parameters.as_counterparty_broadcastable(), + local_chan_signer.as_ecdsa().unwrap(), + &secp_ctx, + false, ); local_chan_signer.as_ecdsa().unwrap().sign_counterparty_commitment(&commitment_tx, Vec::new(), Vec::new(), &secp_ctx).unwrap() }; @@ -9113,7 +9113,7 @@ fn test_duplicate_funding_err_in_funding() { funding_created_msg.funding_output_index += 10; nodes[1].node.handle_funding_created(nodes[2].node.get_our_node_id(), &funding_created_msg); get_err_msg(&nodes[1], &nodes[2].node.get_our_node_id()); - let err = "Invalid funding_created signature from peer".to_owned(); + let err = "Failed to validate our commitment".to_owned(); let reason = ClosureReason::ProcessingError { err }; let expected_closing = ExpectedCloseEvent::from_id_reason(real_channel_id, false, reason); check_closed_events(&nodes[1], &[expected_closing]); diff --git a/lightning/src/ln/monitor_tests.rs b/lightning/src/ln/monitor_tests.rs index 9556e988b4e..324256b02b1 100644 --- a/lightning/src/ln/monitor_tests.rs +++ b/lightning/src/ln/monitor_tests.rs @@ -9,7 +9,9 @@ //! Further functional tests which test blockchain reorganizations. -use crate::sign::{ecdsa::EcdsaChannelSigner, OutputSpender, SpendableOutputDescriptor}; +use alloc::collections::BTreeMap; + +use crate::sign::{ChannelSigner, OutputSpender, SpendableOutputDescriptor}; use crate::chain::channelmonitor::{ANTI_REORG_DELAY, LATENCY_GRACE_PERIOD_BLOCKS, Balance, BalanceSource, ChannelMonitorUpdateStep}; use crate::chain::transaction::OutPoint; use crate::chain::chaininterface::{ConfirmationTarget, LowerBoundedFeeEstimator, compute_feerate_sat_per_1000_weight}; @@ -2901,6 +2903,7 @@ fn test_anchors_aggregated_revoked_htlc_tx() { }], }; let mut descriptors = Vec::with_capacity(4); + let mut revokeable_spks = BTreeMap::new(); for event in events { // We don't use the `BumpTransactionEventHandler` here because it does not support // creating one transaction from multiple `HTLCResolution` events. @@ -2909,7 +2912,12 @@ fn test_anchors_aggregated_revoked_htlc_tx() { for htlc_descriptor in &htlc_descriptors { assert!(!htlc_descriptor.htlc.offered); htlc_tx.input.push(htlc_descriptor.unsigned_tx_input()); - htlc_tx.output.push(htlc_descriptor.tx_output(&secp)); + let revokeable_spk = revokeable_spks.entry(htlc_descriptor.channel_derivation_parameters.keys_id) + .or_insert_with(|| { + let signer = htlc_descriptor.derive_channel_signer(&nodes[1].keys_manager); + signer.get_revokeable_spk(true, htlc_descriptor.per_commitment_number, &htlc_descriptor.per_commitment_point, &secp) + }).clone(); + htlc_tx.output.push(htlc_descriptor.tx_output(revokeable_spk)); } descriptors.append(&mut htlc_descriptors); htlc_tx.lock_time = tx_lock_time; @@ -2920,9 +2928,8 @@ fn test_anchors_aggregated_revoked_htlc_tx() { for (idx, htlc_descriptor) in descriptors.into_iter().enumerate() { let htlc_input_idx = idx + 1; let signer = htlc_descriptor.derive_channel_signer(&nodes[1].keys_manager); - let our_sig = signer.sign_holder_htlc_transaction(&htlc_tx, htlc_input_idx, &htlc_descriptor, &secp).unwrap(); - let witness_script = htlc_descriptor.witness_script(&secp); - htlc_tx.input[htlc_input_idx].witness = htlc_descriptor.tx_input_witness(&our_sig, &witness_script); + let witness = signer.sign_holder_htlc_transaction(&htlc_tx, htlc_input_idx, &htlc_descriptor, &secp).unwrap(); + htlc_tx.input[htlc_input_idx].witness = witness; } let fee_utxo_sig = { let witness_script = ScriptBuf::new_p2pkh(&public_key.pubkey_hash()); diff --git a/lightning/src/sign/ecdsa.rs b/lightning/src/sign/ecdsa.rs index 2f42d332962..a950319d629 100644 --- a/lightning/src/sign/ecdsa.rs +++ b/lightning/src/sign/ecdsa.rs @@ -4,18 +4,16 @@ use bitcoin::transaction::Transaction; use bitcoin::secp256k1; use bitcoin::secp256k1::ecdsa::Signature; -use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey}; +use bitcoin::secp256k1::Secp256k1; -use crate::ln::chan_utils::{ - ClosingTransaction, CommitmentTransaction, HTLCOutputInCommitment, HolderCommitmentTransaction, -}; +use crate::ln::chan_utils::{ClosingTransaction, CommitmentTransaction}; use crate::ln::msgs::UnsignedChannelAnnouncement; use crate::types::payment::PaymentPreimage; #[allow(unused_imports)] use crate::prelude::*; -use crate::sign::{ChannelSigner, HTLCDescriptor}; +use crate::sign::ChannelSigner; /// A trait to sign Lightning channel transactions as described in /// [BOLT 3](https://github.com/lightning/bolts/blob/master/03-transactions.md). @@ -56,142 +54,6 @@ pub trait EcdsaChannelSigner: ChannelSigner { &self, commitment_tx: &CommitmentTransaction, inbound_htlc_preimages: Vec, outbound_htlc_preimages: Vec, secp_ctx: &Secp256k1, ) -> Result<(Signature, Vec), ()>; - /// Creates a signature for a holder's commitment transaction. - /// - /// This will be called - /// - with a non-revoked `commitment_tx`. - /// - with the latest `commitment_tx` when we initiate a force-close. - /// - /// This may be called multiple times for the same transaction. - /// - /// An external signer implementation should check that the commitment has not been revoked. - /// - /// An `Err` can be returned to signal that the signer is unavailable/cannot produce a valid - /// signature and should be retried later. Once the signer is ready to provide a signature after - /// previously returning an `Err`, [`ChannelMonitor::signer_unblocked`] must be called on its - /// monitor or [`ChainMonitor::signer_unblocked`] called to attempt unblocking all monitors. - /// - /// [`ChannelMonitor::signer_unblocked`]: crate::chain::channelmonitor::ChannelMonitor::signer_unblocked - /// [`ChainMonitor::signer_unblocked`]: crate::chain::chainmonitor::ChainMonitor::signer_unblocked - fn sign_holder_commitment( - &self, commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1, - ) -> Result; - /// Same as [`sign_holder_commitment`], but exists only for tests to get access to holder - /// commitment transactions which will be broadcasted later, after the channel has moved on to a - /// newer state. Thus, needs its own method as [`sign_holder_commitment`] may enforce that we - /// only ever get called once. - /// - /// This method is *not* async as it is intended only for testing purposes. - #[cfg(any(test, feature = "unsafe_revoked_tx_signing"))] - fn unsafe_sign_holder_commitment( - &self, commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1, - ) -> Result; - /// Create a signature for the given input in a transaction spending an HTLC transaction output - /// or a commitment transaction `to_local` output when our counterparty broadcasts an old state. - /// - /// A justice transaction may claim multiple outputs at the same time if timelocks are - /// similar, but only a signature for the input at index `input` should be signed for here. - /// It may be called multiple times for same output(s) if a fee-bump is needed with regards - /// to an upcoming timelock expiration. - /// - /// Amount is value of the output spent by this input, committed to in the BIP 143 signature. - /// - /// `per_commitment_key` is revocation secret which was provided by our counterparty when they - /// revoked the state which they eventually broadcast. It's not a _holder_ secret key and does - /// not allow the spending of any funds by itself (you need our holder `revocation_secret` to do - /// so). - /// - /// An `Err` can be returned to signal that the signer is unavailable/cannot produce a valid - /// signature and should be retried later. Once the signer is ready to provide a signature after - /// previously returning an `Err`, [`ChannelMonitor::signer_unblocked`] must be called on its - /// monitor or [`ChainMonitor::signer_unblocked`] called to attempt unblocking all monitors. - /// - /// [`ChannelMonitor::signer_unblocked`]: crate::chain::channelmonitor::ChannelMonitor::signer_unblocked - /// [`ChainMonitor::signer_unblocked`]: crate::chain::chainmonitor::ChainMonitor::signer_unblocked - fn sign_justice_revoked_output( - &self, justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey, - secp_ctx: &Secp256k1, - ) -> Result; - /// Create a signature for the given input in a transaction spending a commitment transaction - /// HTLC output when our counterparty broadcasts an old state. - /// - /// A justice transaction may claim multiple outputs at the same time if timelocks are - /// similar, but only a signature for the input at index `input` should be signed for here. - /// It may be called multiple times for same output(s) if a fee-bump is needed with regards - /// to an upcoming timelock expiration. - /// - /// `amount` is the value of the output spent by this input, committed to in the BIP 143 - /// signature. - /// - /// `per_commitment_key` is revocation secret which was provided by our counterparty when they - /// revoked the state which they eventually broadcast. It's not a _holder_ secret key and does - /// not allow the spending of any funds by itself (you need our holder revocation_secret to do - /// so). - /// - /// `htlc` holds HTLC elements (hash, timelock), thus changing the format of the witness script - /// (which is committed to in the BIP 143 signatures). - /// - /// An `Err` can be returned to signal that the signer is unavailable/cannot produce a valid - /// signature and should be retried later. Once the signer is ready to provide a signature after - /// previously returning an `Err`, [`ChannelMonitor::signer_unblocked`] must be called on its - /// monitor or [`ChainMonitor::signer_unblocked`] called to attempt unblocking all monitors. - /// - /// [`ChannelMonitor::signer_unblocked`]: crate::chain::channelmonitor::ChannelMonitor::signer_unblocked - /// [`ChainMonitor::signer_unblocked`]: crate::chain::chainmonitor::ChainMonitor::signer_unblocked - fn sign_justice_revoked_htlc( - &self, justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey, - htlc: &HTLCOutputInCommitment, secp_ctx: &Secp256k1, - ) -> Result; - /// Computes the signature for a commitment transaction's HTLC output used as an input within - /// `htlc_tx`, which spends the commitment transaction at index `input`. The signature returned - /// must be be computed using [`EcdsaSighashType::All`]. - /// - /// Note that this may be called for HTLCs in the penultimate commitment transaction if a - /// [`ChannelMonitor`] [replica](https://github.com/lightningdevkit/rust-lightning/blob/main/GLOSSARY.md#monitor-replicas) - /// broadcasts it before receiving the update for the latest commitment transaction. - /// - /// An `Err` can be returned to signal that the signer is unavailable/cannot produce a valid - /// signature and should be retried later. Once the signer is ready to provide a signature after - /// previously returning an `Err`, [`ChannelMonitor::signer_unblocked`] must be called on its - /// monitor or [`ChainMonitor::signer_unblocked`] called to attempt unblocking all monitors. - /// - /// [`EcdsaSighashType::All`]: bitcoin::sighash::EcdsaSighashType::All - /// [`ChannelMonitor`]: crate::chain::channelmonitor::ChannelMonitor - /// [`ChannelMonitor::signer_unblocked`]: crate::chain::channelmonitor::ChannelMonitor::signer_unblocked - /// [`ChainMonitor::signer_unblocked`]: crate::chain::chainmonitor::ChainMonitor::signer_unblocked - fn sign_holder_htlc_transaction( - &self, htlc_tx: &Transaction, input: usize, htlc_descriptor: &HTLCDescriptor, - secp_ctx: &Secp256k1, - ) -> Result; - /// Create a signature for a claiming transaction for a HTLC output on a counterparty's commitment - /// transaction, either offered or received. - /// - /// Such a transaction may claim multiples offered outputs at same time if we know the - /// preimage for each when we create it, but only the input at index `input` should be - /// signed for here. It may be called multiple times for same output(s) if a fee-bump is - /// needed with regards to an upcoming timelock expiration. - /// - /// `witness_script` is either an offered or received script as defined in BOLT3 for HTLC - /// outputs. - /// - /// `amount` is value of the output spent by this input, committed to in the BIP 143 signature. - /// - /// `per_commitment_point` is the dynamic point corresponding to the channel state - /// detected onchain. It has been generated by our counterparty and is used to derive - /// channel state keys, which are then included in the witness script and committed to in the - /// BIP 143 signature. - /// - /// An `Err` can be returned to signal that the signer is unavailable/cannot produce a valid - /// signature and should be retried later. Once the signer is ready to provide a signature after - /// previously returning an `Err`, [`ChannelMonitor::signer_unblocked`] must be called on its - /// monitor or [`ChainMonitor::signer_unblocked`] called to attempt unblocking all monitors. - /// - /// [`ChannelMonitor::signer_unblocked`]: crate::chain::channelmonitor::ChannelMonitor::signer_unblocked - /// [`ChainMonitor::signer_unblocked`]: crate::chain::chainmonitor::ChainMonitor::signer_unblocked - fn sign_counterparty_htlc_transaction( - &self, htlc_tx: &Transaction, input: usize, amount: u64, per_commitment_point: &PublicKey, - htlc: &HTLCOutputInCommitment, secp_ctx: &Secp256k1, - ) -> Result; /// Create a signature for a (proposed) closing transaction. /// /// Note that, due to rounding, there may be one "missing" satoshi, and either party may have @@ -205,19 +67,6 @@ pub trait EcdsaChannelSigner: ChannelSigner { fn sign_closing_transaction( &self, closing_tx: &ClosingTransaction, secp_ctx: &Secp256k1, ) -> Result; - /// Computes the signature for a commitment transaction's anchor output used as an - /// input within `anchor_tx`, which spends the commitment transaction, at index `input`. - /// - /// An `Err` can be returned to signal that the signer is unavailable/cannot produce a valid - /// signature and should be retried later. Once the signer is ready to provide a signature after - /// previously returning an `Err`, [`ChannelMonitor::signer_unblocked`] must be called on its - /// monitor or [`ChainMonitor::signer_unblocked`] called to attempt unblocking all monitors. - /// - /// [`ChannelMonitor::signer_unblocked`]: crate::chain::channelmonitor::ChannelMonitor::signer_unblocked - /// [`ChainMonitor::signer_unblocked`]: crate::chain::chainmonitor::ChainMonitor::signer_unblocked - fn sign_holder_anchor_input( - &self, anchor_tx: &Transaction, input: usize, secp_ctx: &Secp256k1, - ) -> Result; /// Signs a channel announcement message with our funding key proving it comes from one of the /// channel participants. /// diff --git a/lightning/src/sign/mod.rs b/lightning/src/sign/mod.rs index 2be0cb39f4f..4a1b1d7fd58 100644 --- a/lightning/src/sign/mod.rs +++ b/lightning/src/sign/mod.rs @@ -18,7 +18,7 @@ use bitcoin::ecdsa::Signature as EcdsaSignature; use bitcoin::locktime::absolute::LockTime; use bitcoin::network::Network; use bitcoin::opcodes; -use bitcoin::script::{Builder, Script, ScriptBuf}; +use bitcoin::script::{Builder, ScriptBuf}; use bitcoin::sighash; use bitcoin::sighash::EcdsaSighashType; use bitcoin::transaction::Version; @@ -38,17 +38,22 @@ use bitcoin::{secp256k1, Psbt, Sequence, Txid, WPubkeyHash, Witness}; use lightning_invoice::RawBolt11Invoice; +use crate::chain::package::{ + weight_offered_htlc, weight_received_htlc, weight_revoked_offered_htlc, + weight_revoked_received_htlc, WEIGHT_REVOKED_OUTPUT, +}; use crate::chain::transaction::OutPoint; use crate::crypto::utils::{hkdf_extract_expand_twice, sign, sign_with_aux_rand}; use crate::ln::chan_utils; use crate::ln::chan_utils::{ - get_revokeable_redeemscript, make_funding_redeemscript, ChannelPublicKeys, - ChannelTransactionParameters, ClosingTransaction, CommitmentTransaction, - HTLCOutputInCommitment, HolderCommitmentTransaction, + get_anchor_redeemscript, get_counterparty_payment_script, get_revokeable_redeemscript, + make_funding_redeemscript, ChannelPublicKeys, ChannelTransactionParameters, ClosingTransaction, + CommitmentTransaction, HTLCOutputInCommitment, HolderCommitmentTransaction, + ANCHOR_INPUT_WITNESS_WEIGHT, }; use crate::ln::channel::ANCHOR_OUTPUT_VALUE_SATOSHI; use crate::ln::channel_keys::{ - add_public_key_tweak, DelayedPaymentBasepoint, DelayedPaymentKey, HtlcBasepoint, HtlcKey, + add_public_key_tweak, DelayedPaymentBasepoint, DelayedPaymentKey, HtlcBasepoint, RevocationBasepoint, RevocationKey, }; use crate::ln::inbound_payment::ExpandedKey; @@ -57,19 +62,23 @@ use crate::ln::msgs::PartialSignatureWithNonce; use crate::ln::msgs::{UnsignedChannelAnnouncement, UnsignedGossipMessage}; use crate::ln::script::ShutdownScript; use crate::offers::invoice::UnsignedBolt12Invoice; +use crate::sign::transaction_utils::sort_outputs; use crate::types::payment::PaymentPreimage; use crate::util::ser::{Readable, ReadableArgs, Writeable, Writer}; use crate::util::transaction_utils; use crate::crypto::chacha20::ChaCha20; use crate::io::{self, Error}; +use crate::ln::channel::INITIAL_COMMITMENT_NUMBER; use crate::ln::msgs::DecodeError; use crate::prelude::*; +use crate::sign::chan_utils::{get_commitment_transaction_number_obscure_factor, TxCreationKeys}; use crate::sign::ecdsa::EcdsaChannelSigner; #[cfg(taproot)] use crate::sign::taproot::TaprootChannelSigner; use crate::types::features::ChannelTypeFeatures; use crate::util::atomic_counter::AtomicCounter; +use core::cmp; use core::convert::TryInto; use core::ops::Deref; use core::sync::atomic::{AtomicUsize, Ordering}; @@ -109,16 +118,8 @@ pub struct DelayedPaymentOutputDescriptor { /// /// Added as optional, but always `Some` if the descriptor was produced in v0.0.123 or later. pub channel_transaction_parameters: Option, -} - -impl DelayedPaymentOutputDescriptor { - /// The maximum length a well-formed witness spending one of these should have. - /// Note: If you have the grind_signatures feature enabled, this will be at least 1 byte - /// shorter. - // Calculated as 1 byte length + 73 byte signature, 1 byte empty vec push, 1 byte length plus - // redeemscript push length. - pub const MAX_WITNESS_LENGTH: u64 = - 1 + 73 + 1 + chan_utils::REVOKEABLE_REDEEMSCRIPT_MAX_LENGTH as u64 + 1; + /// Witness weight + pub witness_weight: u64, } impl_writeable_tlv_based!(DelayedPaymentOutputDescriptor, { @@ -130,6 +131,8 @@ impl_writeable_tlv_based!(DelayedPaymentOutputDescriptor, { (10, channel_keys_id, required), (12, channel_value_satoshis, required), (13, channel_transaction_parameters, option), + // Don't break downgrades ? + (15, witness_weight, (default_value, 1 + 73 + 1 + chan_utils::REVOKEABLE_REDEEMSCRIPT_MAX_LENGTH as u64 + 1)), }); pub(crate) const P2WPKH_WITNESS_WEIGHT: u64 = 1 /* num stack items */ + @@ -161,44 +164,25 @@ pub struct StaticPaymentOutputDescriptor { /// /// Added as optional, but always `Some` if the descriptor was produced in v0.0.117 or later. pub channel_transaction_parameters: Option, + /// Witness weight + pub witness_weight: u64, } -impl StaticPaymentOutputDescriptor { - /// Returns the `witness_script` of the spendable output. - /// - /// Note that this will only return `Some` for [`StaticPaymentOutputDescriptor`]s that - /// originated from an anchor outputs channel, as they take the form of a P2WSH script. - pub fn witness_script(&self) -> Option { - self.channel_transaction_parameters.as_ref().and_then(|channel_params| { - if channel_params.supports_anchors() { - let payment_point = channel_params.holder_pubkeys.payment_point; - Some(chan_utils::get_to_countersignatory_with_anchors_redeemscript(&payment_point)) - } else { - None - } - }) - } - - /// The maximum length a well-formed witness spending one of these should have. - /// Note: If you have the grind_signatures feature enabled, this will be at least 1 byte - /// shorter. - pub fn max_witness_length(&self) -> u64 { - if self.channel_transaction_parameters.as_ref().map_or(false, |p| p.supports_anchors()) { - let witness_script_weight = 1 /* pubkey push */ + 33 /* pubkey */ + - 1 /* OP_CHECKSIGVERIFY */ + 1 /* OP_1 */ + 1 /* OP_CHECKSEQUENCEVERIFY */; - 1 /* num witness items */ + 1 /* sig push */ + 73 /* sig including sighash flag */ + - 1 /* witness script push */ + witness_script_weight - } else { - P2WPKH_WITNESS_WEIGHT - } - } -} impl_writeable_tlv_based!(StaticPaymentOutputDescriptor, { (0, outpoint, required), (2, output, required), (4, channel_keys_id, required), (6, channel_value_satoshis, required), (7, channel_transaction_parameters, option), + // Don't break downgrades ? + (9, witness_weight, (default_value, + if channel_transaction_parameters.as_ref().map_or(false, |p: &ChannelTransactionParameters| p.supports_anchors()) { + let witness_script_weight = 1 /* pubkey push */ + 33 /* pubkey */ + 1 /* OP_CHECKSIGVERIFY */ + 1 /* OP_1 */ + 1 /* OP_CHECKSEQUENCEVERIFY */; + 1 /* num witness items */ + 1 /* sig push */ + 73 /* sig including sighash flag */ + 1 /* witness script push */ + witness_script_weight + } else { + P2WPKH_WITNESS_WEIGHT + }) + ), }); /// Describes the necessary information to spend a spendable output. @@ -408,10 +392,21 @@ impl SpendableOutputDescriptor { ..Default::default() } }, - SpendableOutputDescriptor::StaticPaymentOutput(descriptor) => bitcoin::psbt::Input { - witness_utxo: Some(descriptor.output.clone()), - witness_script: descriptor.witness_script(), - ..Default::default() + SpendableOutputDescriptor::StaticPaymentOutput(descriptor) => { + let witness_script = + descriptor.channel_transaction_parameters.as_ref().and_then(|channel_params| { + channel_params.supports_anchors().then(|| { + let payment_point = channel_params.holder_pubkeys.payment_point; + chan_utils::get_to_countersignatory_with_anchors_redeemscript( + &payment_point, + ) + }) + }); + bitcoin::psbt::Input { + witness_utxo: Some(descriptor.output.clone()), + witness_script, + ..Default::default() + } }, } } @@ -462,7 +457,7 @@ impl SpendableOutputDescriptor { sequence, witness: Witness::new(), }); - witness_weight += descriptor.max_witness_length(); + witness_weight += descriptor.witness_weight; #[cfg(feature = "grind_signatures")] { // Guarantees a low R signature @@ -480,7 +475,7 @@ impl SpendableOutputDescriptor { sequence: Sequence(descriptor.to_self_delay as u32), witness: Witness::new(), }); - witness_weight += DelayedPaymentOutputDescriptor::MAX_WITNESS_LENGTH; + witness_weight += descriptor.witness_weight; #[cfg(feature = "grind_signatures")] { // Guarantees a low R signature @@ -609,13 +604,8 @@ impl HTLCDescriptor { /// Returns the UTXO to be spent by the HTLC input, which can be obtained via /// [`Self::unsigned_tx_input`]. - pub fn previous_utxo( - &self, secp: &Secp256k1, - ) -> TxOut { - TxOut { - script_pubkey: self.witness_script(secp).to_p2wsh(), - value: self.htlc.to_bitcoin_amount(), - } + pub fn previous_utxo(&self, htlc_spk: ScriptBuf) -> TxOut { + TxOut { script_pubkey: htlc_spk, value: self.htlc.to_bitcoin_amount() } } /// Returns the unsigned transaction input spending the HTLC output in the commitment @@ -624,80 +614,18 @@ impl HTLCDescriptor { chan_utils::build_htlc_input( &self.commitment_txid, &self.htlc, - &self.channel_derivation_parameters.transaction_parameters.channel_type_features, + self.channel_derivation_parameters.transaction_parameters.channel_type_features(), ) } /// Returns the delayed output created as a result of spending the HTLC output in the commitment /// transaction. - pub fn tx_output( - &self, secp: &Secp256k1, - ) -> TxOut { - 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 = 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, - ); + pub fn tx_output(&self, revokeable_spk: ScriptBuf) -> TxOut { 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, - ) - } - - /// Returns the witness script of the HTLC output in the commitment transaction. - pub fn witness_script( - &self, secp: &Secp256k1, - ) -> ScriptBuf { - 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 = HtlcKey::from_basepoint( - secp, - &broadcaster_keys.htlc_basepoint, - &self.per_commitment_point, - ); - 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, - ) - } - - /// Returns the fully signed witness required to spend the HTLC output in the commitment - /// transaction. - pub fn tx_input_witness(&self, signature: &Signature, witness_script: &Script) -> Witness { - chan_utils::build_htlc_input_witness( - signature, - &self.counterparty_sig, - &self.preimage, - witness_script, - &self.channel_derivation_parameters.transaction_parameters.channel_type_features, + self.channel_derivation_parameters.transaction_parameters.channel_type_features(), + revokeable_spk, ) } @@ -772,8 +700,80 @@ pub trait ChannelSigner { /// and pause future signing operations until this validation completes. fn validate_holder_commitment( &self, holder_tx: &HolderCommitmentTransaction, - outbound_htlc_preimages: Vec, - ) -> Result<(), ()>; + _outbound_htlc_preimages: Vec, secp_ctx: &Secp256k1, + ) -> Result<(), ()> { + let channel_value_satoshis = self.get_channel_value_satoshis(); + let params = self.get_channel_parameters().unwrap(); + let holder_pubkey = params.holder_pubkeys.funding_pubkey; + let counterparty_pubkey = + params.counterparty_parameters.as_ref().unwrap().pubkeys.funding_pubkey; + let funding_redeemscript = make_funding_redeemscript(&holder_pubkey, &counterparty_pubkey); + let trusted_tx = holder_tx.trust(); + let bitcoin_tx = trusted_tx.built_transaction(); + let sighash = bitcoin_tx.get_sighash_all(&funding_redeemscript, channel_value_satoshis); + if let Err(_) = + secp_ctx.verify_ecdsa(&sighash, &holder_tx.counterparty_sig, &counterparty_pubkey) + { + return Err(()); + } + + if holder_tx.htlcs().len() != holder_tx.counterparty_htlc_sigs.len() { + return Err(()); + } + + let commitment_txid = bitcoin_tx.txid; + let revokeable_spk = self.get_revokeable_spk( + true, + holder_tx.commitment_number(), + &holder_tx.per_commitment_point(), + secp_ctx, + ); + let params = self.get_channel_parameters().unwrap().as_holder_broadcastable(); + let keys = TxCreationKeys::from_channel_static_keys( + &holder_tx.per_commitment_point(), + params.broadcaster_pubkeys(), + params.countersignatory_pubkeys(), + secp_ctx, + ); + for (idx, htlc) in holder_tx.htlcs().iter().enumerate() { + if let Some(_) = htlc.transaction_output_index { + let htlc_tx = chan_utils::build_htlc_transaction( + &commitment_txid, + holder_tx.feerate_per_kw(), + &htlc, + params.channel_type_features(), + revokeable_spk.clone(), + ); + let htlc_redeemscript = + chan_utils::get_htlc_redeemscript(&htlc, params.channel_type_features(), &keys); + let htlc_sighashtype = + if params.channel_type_features().supports_anchors_zero_fee_htlc_tx() { + EcdsaSighashType::SinglePlusAnyoneCanPay + } else { + EcdsaSighashType::All + }; + let htlc_sighash = hash_to_message!( + &sighash::SighashCache::new(&htlc_tx) + .p2wsh_signature_hash( + 0, + &htlc_redeemscript, + htlc.to_bitcoin_amount(), + htlc_sighashtype + ) + .unwrap()[..] + ); + secp_ctx + .verify_ecdsa( + &htlc_sighash, + &holder_tx.counterparty_htlc_sigs[idx], + &keys.countersignatory_htlc_key.to_public_key(), + ) + .map_err(|_| ())?; + } + } + + Ok(()) + } /// Validate the counterparty's revocation. /// @@ -807,6 +807,490 @@ pub trait ChannelSigner { /// /// channel_parameters.is_populated() MUST be true. fn provide_channel_parameters(&mut self, channel_parameters: &ChannelTransactionParameters); + + /// Returns the parameters of this signer + fn get_channel_parameters(&self) -> Option<&ChannelTransactionParameters>; + + /// Return the channel value + fn get_channel_value_satoshis(&self) -> u64; + + /// Returns the script pubkey that should be placed in the `to_remote` output of commitment + /// transactions. + /// + /// Assumes the signer has already been given the channel parameters via + /// `provide_channel_parameters`. + /// + /// If `is_holder_tx` is set, return the `to_remote` script pubkey for the local party's commitment + /// transaction, otherwise, for the remote party's. + fn get_counterparty_payment_script(&self, is_holder_tx: bool) -> ScriptBuf { + let params = if is_holder_tx { + self.get_channel_parameters().unwrap().as_holder_broadcastable() + } else { + self.get_channel_parameters().unwrap().as_counterparty_broadcastable() + }; + let payment_point = ¶ms.countersignatory_pubkeys().payment_point; + get_counterparty_payment_script(params.channel_type_features(), payment_point) + } + + /// Returns the script pubkey that should be placed in the `to_local` output of commitment + /// transactions, and in the output of second level HTLC transactions. + /// + /// Assumes the signer has already been given the channel parameters via + /// `provide_channel_parameters`. + /// + /// If `is_holder_tx` is set, return the revokeable script pubkey for local party's + /// commitment / htlc transaction, otherwise, for the remote party's. + fn get_revokeable_spk( + &self, is_holder_tx: bool, _commitment_number: u64, per_commitment_point: &PublicKey, + secp_ctx: &Secp256k1, + ) -> ScriptBuf { + let params = if is_holder_tx { + self.get_channel_parameters().unwrap().as_holder_broadcastable() + } else { + self.get_channel_parameters().unwrap().as_counterparty_broadcastable() + }; + let contest_delay = params.contest_delay(); + let keys = TxCreationKeys::from_channel_static_keys( + per_commitment_point, + params.broadcaster_pubkeys(), + params.countersignatory_pubkeys(), + secp_ctx, + ); + get_revokeable_redeemscript( + &keys.revocation_key, + contest_delay, + &keys.broadcaster_delayed_payment_key, + ) + .to_p2wsh() + } + + /// Finalize the given input in a transaction spending an HTLC transaction output + /// or a commitment transaction `to_local` output when our counterparty broadcasts an old state. + /// + /// A justice transaction may claim multiple outputs at the same time if timelocks are + /// similar, but only the input at index `input` should be finalized here. + /// It may be called multiple times for the same output(s) if a fee-bump is needed with regards + /// to an upcoming timelock expiration. + /// + /// Amount is the value of the output spent by this input, committed to in the BIP 143 signature. + /// + /// `per_commitment_key` is revocation secret which was provided by our counterparty when they + /// revoked the state which they eventually broadcast. It's not a _holder_ secret key and does + /// not allow the spending of any funds by itself (you need our holder `revocation_secret` to do + /// so). + /// + /// An `Err` can be returned to signal that the signer is unavailable/cannot produce a valid + /// signature and should be retried later. Once the signer is ready to provide a signature after + /// previously returning an `Err`, [`ChannelMonitor::signer_unblocked`] must be called on its + /// monitor or [`ChainMonitor::signer_unblocked`] called to attempt unblocking all monitors. + /// + /// [`ChannelMonitor::signer_unblocked`]: crate::chain::channelmonitor::ChannelMonitor::signer_unblocked + /// [`ChainMonitor::signer_unblocked`]: crate::chain::chainmonitor::ChainMonitor::signer_unblocked + /// + /// TODO(taproot): pass to the `ChannelSigner` all the `TxOut`'s spent by the justice transaction. + fn punish_revokeable_output( + &self, justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey, + secp_ctx: &Secp256k1, per_commitment_point: &PublicKey, + ) -> Result; + + /// Return the total weight of the witness required to spend the justice path of the revokeable + /// output. + fn get_punishment_witness_weight(&self) -> u64 { + WEIGHT_REVOKED_OUTPUT + } + + /// Finalize the given input in a transaction spending a revoked HTLC output in a commitment + /// transaction when our counterparty broadcasts an old state. + /// + /// A justice transaction may claim multiple outputs at the same time if timelocks are + /// similar, but only the input at index `input` should be finalized here. + /// It may be called multiple times for the same output(s) if a fee-bump is needed with regards + /// to an upcoming timelock expiration. + /// + /// Amount is the value of the output spent by this input, committed to in the BIP 143 signature. + /// + /// `per_commitment_key` is revocation secret which was provided by our counterparty when they + /// revoked the state which they eventually broadcast. It's not a _holder_ secret key and does + /// not allow the spending of any funds by itself (you need our holder `revocation_secret` to do + /// so). + /// + /// An `Err` can be returned to signal that the signer is unavailable/cannot produce a valid + /// signature and should be retried later. Once the signer is ready to provide a signature after + /// previously returning an `Err`, [`ChannelMonitor::signer_unblocked`] must be called on its + /// monitor or [`ChainMonitor::signer_unblocked`] called to attempt unblocking all monitors. + /// + /// [`ChannelMonitor::signer_unblocked`]: crate::chain::channelmonitor::ChannelMonitor::signer_unblocked + /// [`ChainMonitor::signer_unblocked`]: crate::chain::chainmonitor::ChainMonitor::signer_unblocked + /// + /// TODO(taproot): pass to the `ChannelSigner` all the `TxOut`'s spent by the justice transaction. + fn punish_htlc_output( + &self, justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey, + secp_ctx: &Secp256k1, per_commitment_point: &PublicKey, + htlc: &HTLCOutputInCommitment, + ) -> Result; + + /// Return the total weight of the witness required to spend the justice path of a HTLC output in a + /// commitment transaction. + fn get_htlc_punishment_witness_weight(&self, offered: bool) -> u64 { + let features = self.get_channel_parameters().unwrap().channel_type_features(); + if offered { + weight_revoked_offered_htlc(features) + } else { + weight_revoked_received_htlc(features) + } + } + + /// Create a signature for a claiming transaction for a HTLC output on a counterparty's commitment + /// transaction, either offered or received. + /// + /// Such a transaction may claim multiples offered outputs at same time if we know the + /// preimage for each when we create it, but only the input at index `input` should be + /// signed for here. It may be called multiple times for same output(s) if a fee-bump is + /// needed with regards to an upcoming timelock expiration. + /// + /// `witness_script` is either an offered or received script as defined in BOLT3 for HTLC + /// outputs. + /// + /// `amount` is value of the output spent by this input, committed to in the BIP 143 signature. + /// + /// `per_commitment_point` is the dynamic point corresponding to the channel state + /// detected onchain. It has been generated by our counterparty and is used to derive + /// channel state keys, which are then included in the witness script and committed to in the + /// BIP 143 signature. + /// + /// An `Err` can be returned to signal that the signer is unavailable/cannot produce a valid + /// signature and should be retried later. Once the signer is ready to provide a signature after + /// previously returning an `Err`, [`ChannelMonitor::signer_unblocked`] must be called on its + /// monitor or [`ChainMonitor::signer_unblocked`] called to attempt unblocking all monitors. + /// + /// [`ChannelMonitor::signer_unblocked`]: crate::chain::channelmonitor::ChannelMonitor::signer_unblocked + /// [`ChainMonitor::signer_unblocked`]: crate::chain::chainmonitor::ChainMonitor::signer_unblocked + fn sweep_counterparty_htlc_output( + &self, sweep_tx: &Transaction, input: usize, amount: u64, + secp_ctx: &Secp256k1, per_commitment_point: &PublicKey, + htlc: &HTLCOutputInCommitment, preimage: Option<&PaymentPreimage>, + ) -> Result; + + /// Weight of the witness that sweeps htlc outputs in counterparty commitment transactions + fn counterparty_htlc_output_witness_weight(&self, offered: bool) -> u64 { + let features = self.get_channel_parameters().unwrap().channel_type_features(); + if offered { + weight_offered_htlc(features) + } else { + weight_received_htlc(features) + } + } + + /// Creates a signature for a holder's commitment transaction. + /// + /// This will be called + /// - with a non-revoked `commitment_tx`. + /// - with the latest `commitment_tx` when we initiate a force-close. + /// + /// This may be called multiple times for the same transaction. + /// + /// An external signer implementation should check that the commitment has not been revoked. + /// + /// An `Err` can be returned to signal that the signer is unavailable/cannot produce a valid + /// signature and should be retried later. Once the signer is ready to provide a signature after + /// previously returning an `Err`, [`ChannelMonitor::signer_unblocked`] must be called on its + /// monitor or [`ChainMonitor::signer_unblocked`] called to attempt unblocking all monitors. + /// + /// [`ChannelMonitor::signer_unblocked`]: crate::chain::channelmonitor::ChannelMonitor::signer_unblocked + /// [`ChainMonitor::signer_unblocked`]: crate::chain::chainmonitor::ChainMonitor::signer_unblocked + fn sign_holder_commitment( + &self, commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1, + ) -> Result; + /// Same as [`sign_holder_commitment`], but exists only for tests to get access to holder + /// commitment transactions which will be broadcasted later, after the channel has moved on to a + /// newer state. Thus, needs its own method as [`sign_holder_commitment`] may enforce that we + /// only ever get called once. + /// + /// This method is *not* async as it is intended only for testing purposes. + #[cfg(any(test, feature = "unsafe_revoked_tx_signing"))] + fn unsafe_sign_holder_commitment( + &self, commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1, + ) -> Result; + /// Computes the signature for a commitment transaction's HTLC output used as an input within + /// `htlc_tx`, which spends the commitment transaction at index `input`. The signature returned + /// must be be computed using [`EcdsaSighashType::All`]. + /// + /// Note that this may be called for HTLCs in the penultimate commitment transaction if a + /// [`ChannelMonitor`] [replica](https://github.com/lightningdevkit/rust-lightning/blob/main/GLOSSARY.md#monitor-replicas) + /// broadcasts it before receiving the update for the latest commitment transaction. + /// + /// An `Err` can be returned to signal that the signer is unavailable/cannot produce a valid + /// signature and should be retried later. Once the signer is ready to provide a signature after + /// previously returning an `Err`, [`ChannelMonitor::signer_unblocked`] must be called on its + /// monitor or [`ChainMonitor::signer_unblocked`] called to attempt unblocking all monitors. + /// + /// [`EcdsaSighashType::All`]: bitcoin::sighash::EcdsaSighashType::All + /// [`ChannelMonitor`]: crate::chain::channelmonitor::ChannelMonitor + /// [`ChannelMonitor::signer_unblocked`]: crate::chain::channelmonitor::ChannelMonitor::signer_unblocked + /// [`ChainMonitor::signer_unblocked`]: crate::chain::chainmonitor::ChainMonitor::signer_unblocked + fn sign_holder_htlc_transaction( + &self, htlc_tx: &Transaction, input: usize, htlc_descriptor: &HTLCDescriptor, + secp_ctx: &Secp256k1, + ) -> Result; + + /// Gets the weight of the witness of the input that spends the htlc output of a + /// holder commitment transaction + fn get_holder_htlc_transaction_witness_weight(&self, offered: bool) -> u64 { + if offered { + chan_utils::HTLC_TIMEOUT_INPUT_ANCHOR_WITNESS_WEIGHT + } else { + chan_utils::HTLC_SUCCESS_INPUT_ANCHOR_WITNESS_WEIGHT + } + } + + /// Gets the script pubkey of a htlc output in a commitment transaction + fn get_htlc_spk( + &self, htlc: &HTLCOutputInCommitment, is_holder_tx: bool, per_commitment_point: &PublicKey, + secp_ctx: &Secp256k1, + ) -> ScriptBuf { + let params = if is_holder_tx { + self.get_channel_parameters().unwrap().as_holder_broadcastable() + } else { + self.get_channel_parameters().unwrap().as_counterparty_broadcastable() + }; + let keys = TxCreationKeys::from_channel_static_keys( + per_commitment_point, + params.broadcaster_pubkeys(), + params.countersignatory_pubkeys(), + secp_ctx, + ); + chan_utils::get_htlc_redeemscript(htlc, params.channel_type_features(), &keys).to_p2wsh() + } + + /// Get the anchor output of a commit tx + fn get_anchor_txout(&self, is_holder_tx: bool, is_broadcaster_anchor: bool) -> Option { + let channel_parameters = self.get_channel_parameters().unwrap(); + if channel_parameters.channel_type_features().supports_anchors_zero_fee_htlc_tx() { + let params = if is_holder_tx { + channel_parameters.as_holder_broadcastable() + } else { + channel_parameters.as_counterparty_broadcastable() + }; + let funding_key = if is_broadcaster_anchor { + params.broadcaster_pubkeys().funding_pubkey + } else { + params.countersignatory_pubkeys().funding_pubkey + }; + Some(TxOut { + script_pubkey: get_anchor_redeemscript(&funding_key).to_p2wsh(), + value: Amount::from_sat(ANCHOR_OUTPUT_VALUE_SATOSHI), + }) + } else { + None + } + } + + /// Computes the signature for a commitment transaction's anchor output used as an + /// input within `anchor_tx`, which spends the commitment transaction, at index `input`. + /// + /// An `Err` can be returned to signal that the signer is unavailable/cannot produce a valid + /// signature and should be retried later. Once the signer is ready to provide a signature after + /// previously returning an `Err`, [`ChannelMonitor::signer_unblocked`] must be called on its + /// monitor or [`ChainMonitor::signer_unblocked`] called to attempt unblocking all monitors. + /// + /// [`ChannelMonitor::signer_unblocked`]: crate::chain::channelmonitor::ChannelMonitor::signer_unblocked + /// [`ChainMonitor::signer_unblocked`]: crate::chain::chainmonitor::ChainMonitor::signer_unblocked + fn spend_holder_anchor_output( + &self, anchor_tx: &Transaction, input_idx: usize, secp_ctx: &Secp256k1, + ) -> Result; + + /// Get the weight of the witness to spend the holder anchor input + fn get_holder_anchor_input_witness_weight(&self) -> u64 { + ANCHOR_INPUT_WITNESS_WEIGHT + } + + /// Gets the weight of the witness that spends a `to_remote` output + fn get_to_remote_witness_weight(&self) -> u64 { + // The maximum length a well-formed witness spending one of these should have. + // Note: If you have the grind_signatures feature enabled, this will be at least 1 byte + // shorter. + if self.get_channel_parameters().as_ref().map_or(false, |p| p.supports_anchors()) { + let witness_script_weight = 1 /* pubkey push */ + 33 /* pubkey */ + + 1 /* OP_CHECKSIGVERIFY */ + 1 /* OP_1 */ + 1 /* OP_CHECKSEQUENCEVERIFY */; + 1 /* num witness items */ + 1 /* sig push */ + 73 /* sig including sighash flag */ + + 1 /* witness script push */ + witness_script_weight + } else { + P2WPKH_WITNESS_WEIGHT + } + } + + /// Gets the weight of the witness that spends a `to_local` output + fn get_to_local_witness_weight(&self) -> u64 { + // The maximum length a well-formed witness spending one of these should have. + // Note: If you have the grind_signatures feature enabled, this will be at least 1 byte + // shorter. + // Calculated as 1 byte length + 73 byte signature, 1 byte empty vec push, 1 byte length plus + // redeemscript push length. + 1 + 73 + 1 + chan_utils::REVOKEABLE_REDEEMSCRIPT_MAX_LENGTH as u64 + 1 + } + + /// Builds the outputs of a commitment transaction + /// + /// This is used in two cases: + /// - initial sorting of outputs / HTLCs in the constructor + /// - building of a bitcoin transaction during a `CommitmentTransaction::verify` call + fn build_outputs( + &self, per_commitment_point: &PublicKey, to_broadcaster_value_sat: Amount, + to_countersignatory_value_sat: Amount, htlcs: Vec<&mut HTLCOutputInCommitment>, + secp_ctx: &Secp256k1, is_holder_tx: bool, commitment_number: u64, + ) -> Result<(Vec, Vec), ()> { + let mut txouts: Vec<(TxOut, Option<&mut HTLCOutputInCommitment>)> = Vec::new(); + + if to_countersignatory_value_sat > Amount::ZERO { + txouts.push(( + TxOut { + script_pubkey: self.get_counterparty_payment_script(is_holder_tx), + value: to_countersignatory_value_sat, + }, + None, + )) + } + + if to_broadcaster_value_sat > Amount::ZERO { + txouts.push(( + TxOut { + script_pubkey: self.get_revokeable_spk( + is_holder_tx, + commitment_number, + per_commitment_point, + secp_ctx, + ), + value: to_broadcaster_value_sat, + }, + None, + )); + } + + if to_broadcaster_value_sat > Amount::ZERO || !htlcs.is_empty() { + if let Some(txout) = self.get_anchor_txout(is_holder_tx, true) { + txouts.push((txout, None)); + } + } + + if to_countersignatory_value_sat > Amount::ZERO || !htlcs.is_empty() { + if let Some(txout) = self.get_anchor_txout(is_holder_tx, false) { + txouts.push((txout, None)); + } + } + + let mut sorted_htlcs = Vec::with_capacity(htlcs.len()); + for htlc in htlcs { + let txout = TxOut { + script_pubkey: self.get_htlc_spk( + htlc, + is_holder_tx, + per_commitment_point, + secp_ctx, + ), + value: htlc.to_bitcoin_amount(), + }; + txouts.push((txout, Some(htlc))); + } + + // Sort output in BIP-69 order (amount, scriptPubkey). Tie-breaks based on HTLC + // CLTV expiration height. + sort_outputs(&mut txouts, |a, b| { + if let &Some(ref a_htlcout) = a { + if let &Some(ref b_htlcout) = b { + a_htlcout + .cltv_expiry + .cmp(&b_htlcout.cltv_expiry) + // Note that due to hash collisions, we have to have a fallback comparison + // here for fuzzing mode (otherwise at least chanmon_fail_consistency + // may fail)! + .then(a_htlcout.payment_hash.0.cmp(&b_htlcout.payment_hash.0)) + // For non-HTLC outputs, if they're copying our SPK we don't really care if we + // close the channel due to mismatches - they're doing something dumb: + } else { + cmp::Ordering::Equal + } + } else { + cmp::Ordering::Equal + } + }); + + let mut outputs = Vec::with_capacity(txouts.len()); + for (idx, out) in txouts.into_iter().enumerate() { + if let Some(htlc) = out.1 { + htlc.transaction_output_index = Some(idx as u32); + sorted_htlcs.push(htlc.clone()); + } + outputs.push(out.0); + } + Ok((outputs, sorted_htlcs)) + } + + /// Builds the input of a commitment transaction + fn build_inputs(&self, commitment_number: u64, is_holder_tx: bool) -> (u64, Vec) { + let params = if is_holder_tx { + self.get_channel_parameters().unwrap().as_holder_broadcastable() + } else { + self.get_channel_parameters().unwrap().as_counterparty_broadcastable() + }; + let broadcaster_pubkeys = params.broadcaster_pubkeys(); + let countersignatory_pubkeys = params.countersignatory_pubkeys(); + let commitment_transaction_number_obscure_factor = + get_commitment_transaction_number_obscure_factor( + &broadcaster_pubkeys.payment_point, + &countersignatory_pubkeys.payment_point, + params.is_outbound(), + ); + + let obscured_commitment_transaction_number = commitment_transaction_number_obscure_factor + ^ (INITIAL_COMMITMENT_NUMBER - commitment_number); + + let txins = { + let ins: Vec = vec![TxIn { + previous_output: params.funding_outpoint(), + script_sig: ScriptBuf::new(), + sequence: Sequence( + ((0x80 as u32) << 8 * 3) + | ((obscured_commitment_transaction_number >> 3 * 8) as u32), + ), + witness: Witness::new(), + }]; + ins + }; + (obscured_commitment_transaction_number, txins) + } + + /// Builds a commitment transaction + fn build_transaction( + &self, per_commitment_point: &PublicKey, to_broadcaster_value_sat: Amount, + to_countersignatory_value_sat: Amount, htlcs: Vec<&mut HTLCOutputInCommitment>, + secp_ctx: &Secp256k1, is_holder_tx: bool, commitment_number: u64, + ) -> (Transaction, Vec) { + let (obscured_commitment_transaction_number, txins) = + self.build_inputs(commitment_number, is_holder_tx); + let (txouts, sorted_htlcs) = self + .build_outputs( + per_commitment_point, + to_broadcaster_value_sat, + to_countersignatory_value_sat, + htlcs, + secp_ctx, + is_holder_tx, + commitment_number, + ) + .unwrap(); + ( + Transaction { + version: Version::TWO, + lock_time: LockTime::from_consensus( + ((0x20 as u32) << 8 * 3) + | ((obscured_commitment_transaction_number & 0xffffffu64) as u32), + ), + input: txins, + output: txouts, + }, + sorted_htlcs, + ) + } } /// Specifies the recipient of an invoice. @@ -1177,22 +1661,13 @@ impl InMemorySigner { self.get_channel_parameters().map(|params| params.funding_outpoint.as_ref()).flatten() } - /// Returns a [`ChannelTransactionParameters`] for this channel, to be used when verifying or - /// building transactions. - /// - /// Will return `None` if [`ChannelSigner::provide_channel_parameters`] has not been called. - /// In general, this is safe to `unwrap` only in [`ChannelSigner`] implementation. - pub fn get_channel_parameters(&self) -> Option<&ChannelTransactionParameters> { - self.channel_parameters.as_ref() - } - /// Returns the channel type features of the channel parameters. Should be helpful for /// determining a channel's category, i. e. legacy/anchors/taproot/etc. /// /// Will return `None` if [`ChannelSigner::provide_channel_parameters`] has not been called. /// In general, this is safe to `unwrap` only in [`ChannelSigner`] implementation. pub fn channel_type_features(&self) -> Option<&ChannelTypeFeatures> { - self.get_channel_parameters().map(|params| ¶ms.channel_type_features) + self.get_channel_parameters().map(|params| params.channel_type_features()) } /// Sign the single input of `spend_tx` at index `input_idx`, which spends the output described @@ -1338,6 +1813,14 @@ impl InMemorySigner { witness_script.as_bytes(), ])) } + + #[cfg(test)] + pub(crate) fn overwrite_channel_parameters( + &mut self, channel_parameters: &ChannelTransactionParameters, + ) { + assert!(channel_parameters.is_populated(), "Channel parameters must be fully populated"); + self.channel_parameters = Some(channel_parameters.clone()); + } } impl EntropySource for InMemorySigner { @@ -1360,13 +1843,6 @@ impl ChannelSigner for InMemorySigner { Ok(chan_utils::build_commitment_secret(&self.commitment_seed, idx)) } - fn validate_holder_commitment( - &self, _holder_tx: &HolderCommitmentTransaction, - _outbound_htlc_preimages: Vec, - ) -> Result<(), ()> { - Ok(()) - } - fn validate_counterparty_revocation(&self, _idx: u64, _secret: &SecretKey) -> Result<(), ()> { Ok(()) } @@ -1391,141 +1867,33 @@ impl ChannelSigner for InMemorySigner { assert!(channel_parameters.is_populated(), "Channel parameters must be fully populated"); self.channel_parameters = Some(channel_parameters.clone()); } -} - -const MISSING_PARAMS_ERR: &'static str = - "ChannelSigner::provide_channel_parameters must be called before signing operations"; - -impl EcdsaChannelSigner for InMemorySigner { - fn sign_counterparty_commitment( - &self, commitment_tx: &CommitmentTransaction, - _inbound_htlc_preimages: Vec, - _outbound_htlc_preimages: Vec, secp_ctx: &Secp256k1, - ) -> Result<(Signature, Vec), ()> { - let trusted_tx = commitment_tx.trust(); - let keys = trusted_tx.keys(); - let funding_pubkey = PublicKey::from_secret_key(secp_ctx, &self.funding_key); - let counterparty_keys = self.counterparty_pubkeys().expect(MISSING_PARAMS_ERR); - let channel_funding_redeemscript = - make_funding_redeemscript(&funding_pubkey, &counterparty_keys.funding_pubkey); + fn get_channel_parameters(&self) -> Option<&ChannelTransactionParameters> { + self.channel_parameters.as_ref() + } - let built_tx = trusted_tx.built_transaction(); - let commitment_sig = built_tx.sign_counterparty_commitment( - &self.funding_key, - &channel_funding_redeemscript, - self.channel_value_satoshis, + fn punish_revokeable_output( + &self, justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey, + secp_ctx: &Secp256k1, per_commitment_point: &PublicKey, + ) -> Result { + let params = self.channel_parameters.as_ref().unwrap().as_counterparty_broadcastable(); + let contest_delay = params.contest_delay(); + let keys = TxCreationKeys::from_channel_static_keys( + per_commitment_point, + params.broadcaster_pubkeys(), + params.countersignatory_pubkeys(), secp_ctx, ); - let commitment_txid = built_tx.txid; - - let mut htlc_sigs = Vec::with_capacity(commitment_tx.htlcs().len()); - for htlc in commitment_tx.htlcs() { - let channel_parameters = self.get_channel_parameters().expect(MISSING_PARAMS_ERR); - let holder_selected_contest_delay = - self.holder_selected_contest_delay().expect(MISSING_PARAMS_ERR); - let chan_type = &channel_parameters.channel_type_features; - 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, - ); - let htlc_redeemscript = chan_utils::get_htlc_redeemscript(&htlc, chan_type, &keys); - let htlc_sighashtype = if chan_type.supports_anchors_zero_fee_htlc_tx() { - EcdsaSighashType::SinglePlusAnyoneCanPay - } else { - EcdsaSighashType::All - }; - let htlc_sighash = hash_to_message!( - &sighash::SighashCache::new(&htlc_tx) - .p2wsh_signature_hash( - 0, - &htlc_redeemscript, - htlc.to_bitcoin_amount(), - htlc_sighashtype - ) - .unwrap()[..] - ); - let holder_htlc_key = chan_utils::derive_private_key( - &secp_ctx, - &keys.per_commitment_point, - &self.htlc_base_key, - ); - htlc_sigs.push(sign(secp_ctx, &htlc_sighash, &holder_htlc_key)); - } - - Ok((commitment_sig, htlc_sigs)) - } - - fn sign_holder_commitment( - &self, commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1, - ) -> Result { - let funding_pubkey = PublicKey::from_secret_key(secp_ctx, &self.funding_key); - let counterparty_keys = self.counterparty_pubkeys().expect(MISSING_PARAMS_ERR); - let funding_redeemscript = - make_funding_redeemscript(&funding_pubkey, &counterparty_keys.funding_pubkey); - let trusted_tx = commitment_tx.trust(); - Ok(trusted_tx.built_transaction().sign_holder_commitment( - &self.funding_key, - &funding_redeemscript, - self.channel_value_satoshis, - &self, - secp_ctx, - )) - } - - #[cfg(any(test, feature = "unsafe_revoked_tx_signing"))] - fn unsafe_sign_holder_commitment( - &self, commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1, - ) -> Result { - let funding_pubkey = PublicKey::from_secret_key(secp_ctx, &self.funding_key); - let counterparty_keys = self.counterparty_pubkeys().expect(MISSING_PARAMS_ERR); - let funding_redeemscript = - make_funding_redeemscript(&funding_pubkey, &counterparty_keys.funding_pubkey); - let trusted_tx = commitment_tx.trust(); - Ok(trusted_tx.built_transaction().sign_holder_commitment( - &self.funding_key, - &funding_redeemscript, - self.channel_value_satoshis, - &self, - secp_ctx, - )) - } - - fn sign_justice_revoked_output( - &self, justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey, - secp_ctx: &Secp256k1, - ) -> Result { + let witness_script = get_revokeable_redeemscript( + &keys.revocation_key, + contest_delay, + &keys.broadcaster_delayed_payment_key, + ); 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 = 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 = 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); let sighash = hash_to_message!( &sighash_parts @@ -1537,45 +1905,32 @@ impl EcdsaChannelSigner for InMemorySigner { ) .unwrap()[..] ); - return Ok(sign_with_aux_rand(secp_ctx, &sighash, &revocation_key, &self)); + let sig = sign_with_aux_rand(secp_ctx, &sighash, &revocation_key, &self); + let ecdsa_sig = EcdsaSignature::sighash_all(sig); + Ok(Witness::from( + &[ecdsa_sig.serialize().as_ref(), &[1][..], witness_script.as_bytes()][..], + )) } - fn sign_justice_revoked_htlc( + fn punish_htlc_output( &self, justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey, - htlc: &HTLCOutputInCommitment, secp_ctx: &Secp256k1, - ) -> Result { + secp_ctx: &Secp256k1, per_commitment_point: &PublicKey, + htlc: &HTLCOutputInCommitment, + ) -> Result { + let params = self.channel_parameters.as_ref().unwrap().as_counterparty_broadcastable(); + let keys = TxCreationKeys::from_channel_static_keys( + per_commitment_point, + params.broadcaster_pubkeys(), + params.countersignatory_pubkeys(), + secp_ctx, + ); + let witness_script = + chan_utils::get_htlc_redeemscript(htlc, params.channel_type_features(), &keys); 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 = 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 = 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, - ) - }; let mut sighash_parts = sighash::SighashCache::new(justice_tx); let sighash = hash_to_message!( &sighash_parts @@ -1587,14 +1942,106 @@ impl EcdsaChannelSigner for InMemorySigner { ) .unwrap()[..] ); - return Ok(sign_with_aux_rand(secp_ctx, &sighash, &revocation_key, &self)); + let sig = sign_with_aux_rand(secp_ctx, &sighash, &revocation_key, &self); + let ecdsa_sig = EcdsaSignature::sighash_all(sig); + Ok(Witness::from( + &[ + ecdsa_sig.serialize().as_ref(), + &keys.revocation_key.to_public_key().serialize()[..], + witness_script.as_bytes(), + ][..], + )) + } + + fn sweep_counterparty_htlc_output( + &self, sweep_tx: &Transaction, input: usize, amount: u64, + secp_ctx: &Secp256k1, per_commitment_point: &PublicKey, + htlc: &HTLCOutputInCommitment, preimage: Option<&PaymentPreimage>, + ) -> Result { + let params = self.channel_parameters.as_ref().unwrap().as_counterparty_broadcastable(); + let keys = TxCreationKeys::from_channel_static_keys( + per_commitment_point, + params.broadcaster_pubkeys(), + params.countersignatory_pubkeys(), + secp_ctx, + ); + let witness_script = + chan_utils::get_htlc_redeemscript(htlc, params.channel_type_features(), &keys); + let htlc_key = + chan_utils::derive_private_key(&secp_ctx, &per_commitment_point, &self.htlc_base_key); + let mut sighash_parts = sighash::SighashCache::new(sweep_tx); + let sighash = hash_to_message!( + &sighash_parts + .p2wsh_signature_hash( + input, + &witness_script, + Amount::from_sat(amount), + EcdsaSighashType::All + ) + .unwrap()[..] + ); + let sig = sign_with_aux_rand(secp_ctx, &sighash, &htlc_key, &self); + let ecdsa_sig = EcdsaSignature::sighash_all(sig); + let element = match preimage { + Some(ref p) => &p.0[..], + None => &[][..], + }; + Ok(Witness::from(&[ecdsa_sig.serialize().as_ref(), element, witness_script.as_bytes()][..])) + } + + fn sign_holder_commitment( + &self, commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1, + ) -> Result { + let funding_pubkey = PublicKey::from_secret_key(secp_ctx, &self.funding_key); + let counterparty_keys = self.counterparty_pubkeys().expect(MISSING_PARAMS_ERR); + let funding_redeemscript = + make_funding_redeemscript(&funding_pubkey, &counterparty_keys.funding_pubkey); + let trusted_tx = commitment_tx.trust(); + let sig = trusted_tx.built_transaction().sign_holder_commitment( + &self.funding_key, + &funding_redeemscript, + self.channel_value_satoshis, + &self, + secp_ctx, + ); + Ok(commitment_tx.finalize_witness(&funding_redeemscript, sig)) + } + + #[cfg(any(test, feature = "unsafe_revoked_tx_signing"))] + fn unsafe_sign_holder_commitment( + &self, commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1, + ) -> Result { + let funding_pubkey = PublicKey::from_secret_key(secp_ctx, &self.funding_key); + let counterparty_keys = self.counterparty_pubkeys().expect(MISSING_PARAMS_ERR); + let funding_redeemscript = + make_funding_redeemscript(&funding_pubkey, &counterparty_keys.funding_pubkey); + let trusted_tx = commitment_tx.trust(); + let sig = trusted_tx.built_transaction().sign_holder_commitment( + &self.funding_key, + &funding_redeemscript, + self.channel_value_satoshis, + &self, + secp_ctx, + ); + Ok(commitment_tx.finalize_witness(&funding_redeemscript, sig)) } fn sign_holder_htlc_transaction( &self, htlc_tx: &Transaction, input: usize, htlc_descriptor: &HTLCDescriptor, secp_ctx: &Secp256k1, - ) -> Result { - let witness_script = htlc_descriptor.witness_script(secp_ctx); + ) -> Result { + let params = self.get_channel_parameters().unwrap().as_holder_broadcastable(); + let keys = TxCreationKeys::from_channel_static_keys( + &htlc_descriptor.per_commitment_point, + params.broadcaster_pubkeys(), + params.countersignatory_pubkeys(), + secp_ctx, + ); + let witness_script = chan_utils::get_htlc_redeemscript( + &htlc_descriptor.htlc, + params.channel_type_features(), + &keys, + ); let sighash = &sighash::SighashCache::new(&*htlc_tx) .p2wsh_signature_hash( input, @@ -1609,48 +2056,109 @@ impl EcdsaChannelSigner for InMemorySigner { &self.htlc_base_key, ); let sighash = hash_to_message!(sighash.as_byte_array()); - Ok(sign_with_aux_rand(&secp_ctx, &sighash, &our_htlc_private_key, &self)) + let sig = sign_with_aux_rand(&secp_ctx, &sighash, &our_htlc_private_key, &self); + + let features = self.channel_parameters.as_ref().unwrap().channel_type_features(); + Ok(chan_utils::build_htlc_input_witness( + &sig, + &htlc_descriptor.counterparty_sig, + &htlc_descriptor.preimage, + &witness_script, + features, + )) } - 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 = RevocationKey::from_basepoint( - &secp_ctx, - &self.pubkeys().revocation_basepoint, - &per_commitment_point, - ); + fn spend_holder_anchor_output( + &self, anchor_tx: &Transaction, input_idx: usize, secp_ctx: &Secp256k1, + ) -> Result { + let funding_pubkey = &self.pubkeys().funding_pubkey; + let witness_script = chan_utils::get_anchor_redeemscript(funding_pubkey); + let sighash = sighash::SighashCache::new(anchor_tx) + .p2wsh_signature_hash( + input_idx, + &witness_script, + Amount::from_sat(ANCHOR_OUTPUT_VALUE_SATOSHI), + EcdsaSighashType::All, + ) + .unwrap(); + let sig = + sign_with_aux_rand(secp_ctx, &hash_to_message!(&sighash[..]), &self.funding_key, &self); + Ok(chan_utils::build_anchor_input_witness(funding_pubkey, &sig)) + } + + fn get_channel_value_satoshis(&self) -> u64 { + self.channel_value_satoshis + } +} + +const MISSING_PARAMS_ERR: &'static str = + "ChannelSigner::provide_channel_parameters must be called before signing operations"; + +impl EcdsaChannelSigner for InMemorySigner { + fn sign_counterparty_commitment( + &self, commitment_tx: &CommitmentTransaction, + _inbound_htlc_preimages: Vec, + _outbound_htlc_preimages: Vec, secp_ctx: &Secp256k1, + ) -> Result<(Signature, Vec), ()> { + let trusted_tx = commitment_tx.trust(); + let keys = trusted_tx.keys(); + + let funding_pubkey = PublicKey::from_secret_key(secp_ctx, &self.funding_key); let counterparty_keys = self.counterparty_pubkeys().expect(MISSING_PARAMS_ERR); - let counterparty_htlcpubkey = HtlcKey::from_basepoint( - &secp_ctx, - &counterparty_keys.htlc_basepoint, - &per_commitment_point, - ); - let htlc_basepoint = self.pubkeys().htlc_basepoint; - let htlcpubkey = HtlcKey::from_basepoint(&secp_ctx, &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 channel_funding_redeemscript = + make_funding_redeemscript(&funding_pubkey, &counterparty_keys.funding_pubkey); + + let built_tx = trusted_tx.built_transaction(); + let commitment_sig = built_tx.sign_counterparty_commitment( + &self.funding_key, + &channel_funding_redeemscript, + self.channel_value_satoshis, + secp_ctx, ); - let mut sighash_parts = sighash::SighashCache::new(htlc_tx); - let sighash = hash_to_message!( - &sighash_parts - .p2wsh_signature_hash( - input, - &witness_script, - Amount::from_sat(amount), - EcdsaSighashType::All - ) - .unwrap()[..] + let commitment_txid = built_tx.txid; + + let mut htlc_sigs = Vec::with_capacity(commitment_tx.htlcs().len()); + let revokeable_spk = self.get_revokeable_spk( + false, + trusted_tx.commitment_number(), + &trusted_tx.per_commitment_point(), + secp_ctx, ); - Ok(sign_with_aux_rand(secp_ctx, &sighash, &htlc_key, &self)) + for htlc in commitment_tx.htlcs() { + let channel_parameters = self.get_channel_parameters().expect(MISSING_PARAMS_ERR); + let chan_type = channel_parameters.channel_type_features(); + let htlc_tx = chan_utils::build_htlc_transaction( + &commitment_txid, + commitment_tx.feerate_per_kw(), + htlc, + chan_type, + revokeable_spk.clone(), + ); + let htlc_redeemscript = chan_utils::get_htlc_redeemscript(&htlc, chan_type, &keys); + let htlc_sighashtype = if chan_type.supports_anchors_zero_fee_htlc_tx() { + EcdsaSighashType::SinglePlusAnyoneCanPay + } else { + EcdsaSighashType::All + }; + let htlc_sighash = hash_to_message!( + &sighash::SighashCache::new(&htlc_tx) + .p2wsh_signature_hash( + 0, + &htlc_redeemscript, + htlc.to_bitcoin_amount(), + htlc_sighashtype + ) + .unwrap()[..] + ); + let holder_htlc_key = chan_utils::derive_private_key( + &secp_ctx, + &keys.per_commitment_point, + &self.htlc_base_key, + ); + htlc_sigs.push(sign(secp_ctx, &htlc_sighash, &holder_htlc_key)); + } + + Ok((commitment_sig, htlc_sigs)) } fn sign_closing_transaction( @@ -1669,22 +2177,6 @@ impl EcdsaChannelSigner for InMemorySigner { )) } - fn sign_holder_anchor_input( - &self, anchor_tx: &Transaction, input: usize, secp_ctx: &Secp256k1, - ) -> Result { - let witness_script = - chan_utils::get_anchor_redeemscript(&self.holder_channel_pubkeys.funding_pubkey); - let sighash = sighash::SighashCache::new(&*anchor_tx) - .p2wsh_signature_hash( - input, - &witness_script, - Amount::from_sat(ANCHOR_OUTPUT_VALUE_SATOSHI), - EcdsaSighashType::All, - ) - .unwrap(); - Ok(sign_with_aux_rand(secp_ctx, &hash_to_message!(&sighash[..]), &self.funding_key, &self)) - } - fn sign_channel_announcement_with_funding_key( &self, msg: &UnsignedChannelAnnouncement, secp_ctx: &Secp256k1, ) -> Result { @@ -1752,13 +2244,6 @@ impl TaprootChannelSigner for InMemorySigner { todo!() } - fn sign_holder_htlc_transaction( - &self, htlc_tx: &Transaction, input: usize, htlc_descriptor: &HTLCDescriptor, - secp_ctx: &Secp256k1, - ) -> Result { - todo!() - } - fn sign_counterparty_htlc_transaction( &self, htlc_tx: &Transaction, input: usize, amount: u64, per_commitment_point: &PublicKey, htlc: &HTLCOutputInCommitment, secp_ctx: &Secp256k1, diff --git a/lightning/src/sign/taproot.rs b/lightning/src/sign/taproot.rs index ebfaad26c85..a3b7e211261 100644 --- a/lightning/src/sign/taproot.rs +++ b/lightning/src/sign/taproot.rs @@ -105,22 +105,6 @@ pub trait TaprootChannelSigner: ChannelSigner { htlc: &HTLCOutputInCommitment, secp_ctx: &Secp256k1, ) -> Result; - /// Computes the signature for a commitment transaction's HTLC output used as an input within - /// `htlc_tx`, which spends the commitment transaction at index `input`. The signature returned - /// must be be computed using [`TapSighashType::Default`]. - /// - /// Note that this may be called for HTLCs in the penultimate commitment transaction if a - /// [`ChannelMonitor`] [replica](https://github.com/lightningdevkit/rust-lightning/blob/main/GLOSSARY.md#monitor-replicas) - /// broadcasts it before receiving the update for the latest commitment transaction. - /// - /// - /// [`TapSighashType::Default`]: bitcoin::sighash::TapSighashType::Default - /// [`ChannelMonitor`]: crate::chain::channelmonitor::ChannelMonitor - fn sign_holder_htlc_transaction( - &self, htlc_tx: &Transaction, input: usize, htlc_descriptor: &HTLCDescriptor, - secp_ctx: &Secp256k1, - ) -> Result; - /// Create a signature for a claiming transaction for a HTLC output on a counterparty's commitment /// transaction, either offered or received. /// diff --git a/lightning/src/util/persist.rs b/lightning/src/util/persist.rs index d0e9e01a482..1b4042f1413 100644 --- a/lightning/src/util/persist.rs +++ b/lightning/src/util/persist.rs @@ -26,7 +26,7 @@ use crate::chain::transaction::OutPoint; use crate::ln::channelmanager::AChannelManager; use crate::routing::gossip::NetworkGraph; use crate::routing::scoring::WriteableScore; -use crate::sign::{ecdsa::EcdsaChannelSigner, EntropySource, SignerProvider}; +use crate::sign::{ChannelSigner, EntropySource, SignerProvider}; use crate::util::logger::Logger; use crate::util::ser::{Readable, ReadableArgs, Writeable}; @@ -252,14 +252,14 @@ where } } -impl Persist for K { +impl Persist for K { // TODO: We really need a way for the persister to inform the user that its time to crash/shut // down once these start returning failure. // Then we should return InProgress rather than UnrecoverableError, implying we should probably // just shut down the node since we're not retrying persistence! fn persist_new_channel( - &self, funding_txo: OutPoint, monitor: &ChannelMonitor, + &self, funding_txo: OutPoint, monitor: &ChannelMonitor, ) -> chain::ChannelMonitorUpdateStatus { let key = format!("{}_{}", funding_txo.txid.to_string(), funding_txo.index); match self.write( @@ -275,7 +275,7 @@ impl Persist, - monitor: &ChannelMonitor, + monitor: &ChannelMonitor, ) -> chain::ChannelMonitorUpdateStatus { let key = format!("{}_{}", funding_txo.txid.to_string(), funding_txo.index); match self.write( @@ -702,15 +702,8 @@ where } } -impl< - ChannelSigner: EcdsaChannelSigner, - K: Deref, - L: Deref, - ES: Deref, - SP: Deref, - BI: Deref, - FE: Deref, - > Persist for MonitorUpdatingPersister +impl + Persist for MonitorUpdatingPersister where K::Target: KVStore, L::Target: Logger, @@ -722,7 +715,7 @@ where /// Persists a new channel. This means writing the entire monitor to the /// parametrized [`KVStore`]. fn persist_new_channel( - &self, funding_txo: OutPoint, monitor: &ChannelMonitor, + &self, funding_txo: OutPoint, monitor: &ChannelMonitor, ) -> chain::ChannelMonitorUpdateStatus { // Determine the proper key for this monitor let monitor_name = MonitorName::from(funding_txo); @@ -764,7 +757,7 @@ where /// - The update is at [`u64::MAX`], indicating an update generated by pre-0.1 LDK. fn update_persisted_channel( &self, funding_txo: OutPoint, update: Option<&ChannelMonitorUpdate>, - monitor: &ChannelMonitor, + monitor: &ChannelMonitor, ) -> chain::ChannelMonitorUpdateStatus { const LEGACY_CLOSED_CHANNEL_UPDATE_ID: u64 = u64::MAX; if let Some(update) = update { @@ -1458,9 +1451,9 @@ mod tests { .is_err()); } - fn persist_fn(_persist: P) -> bool + fn persist_fn(_persist: P) -> bool where - P::Target: Persist, + P::Target: Persist, { true } diff --git a/lightning/src/util/test_channel_signer.rs b/lightning/src/util/test_channel_signer.rs index f3ef4dc1557..d4e689e785c 100644 --- a/lightning/src/util/test_channel_signer.rs +++ b/lightning/src/util/test_channel_signer.rs @@ -7,8 +7,7 @@ // You may not use this file except in accordance with one or both of these // licenses. -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::{self, HTLCOutputInCommitment, ChannelPublicKeys, HolderCommitmentTransaction, CommitmentTransaction, ChannelTransactionParameters, TrustedCommitmentTransaction, ClosingTransaction, TxCreationKeys}; use crate::ln::channel_keys::{HtlcKey}; use crate::ln::msgs; use crate::types::payment::PaymentPreimage; @@ -26,6 +25,7 @@ use bitcoin::transaction::Transaction; use bitcoin::hashes::Hash; use bitcoin::sighash; use bitcoin::sighash::EcdsaSighashType; +use bitcoin::Witness; use bitcoin::secp256k1; #[cfg(taproot)] @@ -161,6 +161,11 @@ impl TestChannelSigner { fn is_signer_available(&self, signer_op: SignerOp) -> bool { !self.get_enforcement_state().disabled_signer_ops.contains(&signer_op) } + + #[cfg(test)] + pub(crate) fn overwrite_channel_parameters(&mut self, channel_parameters: &ChannelTransactionParameters) { + self.inner.overwrite_channel_parameters(channel_parameters) + } } impl ChannelSigner for TestChannelSigner { @@ -186,12 +191,12 @@ impl ChannelSigner for TestChannelSigner { self.inner.release_commitment_secret(idx) } - fn validate_holder_commitment(&self, holder_tx: &HolderCommitmentTransaction, _outbound_htlc_preimages: Vec) -> Result<(), ()> { + fn validate_holder_commitment(&self, holder_tx: &HolderCommitmentTransaction, outbound_htlc_preimages: Vec, secp_ctx: &Secp256k1) -> Result<(), ()> { let mut state = self.state.lock().unwrap(); let idx = holder_tx.commitment_number(); assert!(idx == state.last_holder_commitment || idx == state.last_holder_commitment - 1, "expecting to validate the current or next holder commitment - trying {}, current {}", idx, state.last_holder_commitment); state.last_holder_commitment = idx; - Ok(()) + self.inner.validate_holder_commitment(holder_tx, outbound_htlc_preimages, secp_ctx) } fn validate_counterparty_revocation(&self, idx: u64, _secret: &SecretKey) -> Result<(), ()> { @@ -212,33 +217,46 @@ impl ChannelSigner for TestChannelSigner { fn provide_channel_parameters(&mut self, channel_parameters: &ChannelTransactionParameters) { self.inner.provide_channel_parameters(channel_parameters) } -} -impl EcdsaChannelSigner for TestChannelSigner { - fn sign_counterparty_commitment(&self, commitment_tx: &CommitmentTransaction, inbound_htlc_preimages: Vec, outbound_htlc_preimages: Vec, secp_ctx: &Secp256k1) -> Result<(Signature, Vec), ()> { - self.verify_counterparty_commitment_tx(commitment_tx, secp_ctx); + fn get_channel_parameters(&self) -> Option<&ChannelTransactionParameters> { + self.inner.get_channel_parameters() + } - { - #[cfg(test)] - if !self.is_signer_available(SignerOp::SignCounterpartyCommitment) { - return Err(()); - } - let mut state = self.state.lock().unwrap(); - let actual_commitment_number = commitment_tx.commitment_number(); - let last_commitment_number = state.last_counterparty_commitment; - // These commitment numbers are backwards counting. We expect either the same as the previously encountered, - // or the next one. - assert!(last_commitment_number == actual_commitment_number || last_commitment_number - 1 == actual_commitment_number, "{} doesn't come after {}", actual_commitment_number, last_commitment_number); - // Ensure that the counterparty doesn't get more than two broadcastable commitments - - // the last and the one we are trying to sign - assert!(actual_commitment_number >= state.last_counterparty_revoked_commitment - 2, "cannot sign a commitment if second to last wasn't revoked - signing {} revoked {}", actual_commitment_number, state.last_counterparty_revoked_commitment); - state.last_counterparty_commitment = cmp::min(last_commitment_number, actual_commitment_number) + fn punish_revokeable_output( + &self, justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey, + secp_ctx: &Secp256k1, per_commitment_point: &PublicKey, + ) -> Result { + #[cfg(test)] + if !self.is_signer_available(SignerOp::SignJusticeRevokedOutput) { + return Err(()); } + self.inner.punish_revokeable_output(justice_tx, input, amount, per_commitment_key, secp_ctx, per_commitment_point) + } - Ok(self.inner.sign_counterparty_commitment(commitment_tx, inbound_htlc_preimages, outbound_htlc_preimages, secp_ctx).unwrap()) + fn punish_htlc_output( + &self, justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey, + secp_ctx: &Secp256k1, per_commitment_point: &PublicKey, htlc: &HTLCOutputInCommitment, + ) -> Result { + #[cfg(test)] + if !self.is_signer_available(SignerOp::SignJusticeRevokedHtlc) { + return Err(()); + } + self.inner.punish_htlc_output(justice_tx, input, amount, per_commitment_key, secp_ctx, per_commitment_point, htlc) } - fn sign_holder_commitment(&self, commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1) -> Result { + fn sweep_counterparty_htlc_output( + &self, sweep_tx: &Transaction, input: usize, amount: u64, + secp_ctx: &Secp256k1, per_commitment_point: &PublicKey, + htlc: &HTLCOutputInCommitment, preimage: Option<&PaymentPreimage>, + ) -> Result { + #[cfg(test)] + if !self.is_signer_available(SignerOp::SignCounterpartyHtlcTransaction) { + return Err(()); + } + self.inner.sweep_counterparty_htlc_output(sweep_tx, input, amount, secp_ctx, per_commitment_point, htlc, preimage) + } + + fn sign_holder_commitment(&self, commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1) -> Result { #[cfg(test)] if !self.is_signer_available(SignerOp::SignHolderCommitment) { return Err(()); @@ -256,30 +274,14 @@ impl EcdsaChannelSigner for TestChannelSigner { } #[cfg(any(test,feature = "unsafe_revoked_tx_signing"))] - fn unsafe_sign_holder_commitment(&self, commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1) -> Result { + fn unsafe_sign_holder_commitment(&self, commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1) -> Result { Ok(self.inner.unsafe_sign_holder_commitment(commitment_tx, secp_ctx).unwrap()) } - fn sign_justice_revoked_output(&self, justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey, secp_ctx: &Secp256k1) -> Result { - #[cfg(test)] - if !self.is_signer_available(SignerOp::SignJusticeRevokedOutput) { - return Err(()); - } - Ok(EcdsaChannelSigner::sign_justice_revoked_output(&self.inner, justice_tx, input, amount, per_commitment_key, secp_ctx).unwrap()) - } - - fn sign_justice_revoked_htlc(&self, justice_tx: &Transaction, input: usize, amount: u64, per_commitment_key: &SecretKey, htlc: &HTLCOutputInCommitment, secp_ctx: &Secp256k1) -> Result { - #[cfg(test)] - if !self.is_signer_available(SignerOp::SignJusticeRevokedHtlc) { - return Err(()); - } - Ok(EcdsaChannelSigner::sign_justice_revoked_htlc(&self.inner, justice_tx, input, amount, per_commitment_key, htlc, secp_ctx).unwrap()) - } - fn sign_holder_htlc_transaction( &self, htlc_tx: &Transaction, input: usize, htlc_descriptor: &HTLCDescriptor, secp_ctx: &Secp256k1 - ) -> Result { + ) -> Result { #[cfg(test)] if !self.is_signer_available(SignerOp::SignHolderHtlcTransaction) { return Err(()); @@ -294,9 +296,17 @@ impl EcdsaChannelSigner for TestChannelSigner { } } assert_eq!(htlc_tx.input[input], htlc_descriptor.unsigned_tx_input()); - assert_eq!(htlc_tx.output[input], htlc_descriptor.tx_output(secp_ctx)); + let revokeable_spk = self.get_revokeable_spk(true, htlc_descriptor.per_commitment_number, &htlc_descriptor.per_commitment_point, secp_ctx); + assert_eq!(htlc_tx.output[input], htlc_descriptor.tx_output(revokeable_spk)); { - let witness_script = htlc_descriptor.witness_script(secp_ctx); + let params = self.get_channel_parameters().unwrap().as_holder_broadcastable(); + let keys = TxCreationKeys::from_channel_static_keys( + &htlc_descriptor.per_commitment_point, + params.broadcaster_pubkeys(), + params.countersignatory_pubkeys(), + secp_ctx, + ); + let witness_script = chan_utils::get_htlc_redeemscript(&htlc_descriptor.htlc, params.channel_type_features(), &keys); let sighash_type = if self.channel_type_features().supports_anchors_zero_fee_htlc_tx() { EcdsaSighashType::SinglePlusAnyoneCanPay } else { @@ -313,15 +323,42 @@ impl EcdsaChannelSigner for TestChannelSigner { &hash_to_message!(sighash.as_byte_array()), &htlc_descriptor.counterparty_sig, &countersignatory_htlc_key.to_public_key() ).unwrap(); } - Ok(EcdsaChannelSigner::sign_holder_htlc_transaction(&self.inner, htlc_tx, input, htlc_descriptor, secp_ctx).unwrap()) + Ok(self.inner.sign_holder_htlc_transaction(htlc_tx, input, htlc_descriptor, secp_ctx).unwrap()) } - fn sign_counterparty_htlc_transaction(&self, htlc_tx: &Transaction, input: usize, amount: u64, per_commitment_point: &PublicKey, htlc: &HTLCOutputInCommitment, secp_ctx: &Secp256k1) -> Result { - #[cfg(test)] - if !self.is_signer_available(SignerOp::SignCounterpartyHtlcTransaction) { - return Err(()); + fn spend_holder_anchor_output( + &self, anchor_tx: &Transaction, input_idx: usize, secp_ctx: &Secp256k1, + ) -> Result { + self.inner.spend_holder_anchor_output(anchor_tx, input_idx, secp_ctx) + } + + fn get_channel_value_satoshis(&self) -> u64 { + self.inner.get_channel_value_satoshis() + } +} + +impl EcdsaChannelSigner for TestChannelSigner { + fn sign_counterparty_commitment(&self, commitment_tx: &CommitmentTransaction, inbound_htlc_preimages: Vec, outbound_htlc_preimages: Vec, secp_ctx: &Secp256k1) -> Result<(Signature, Vec), ()> { + self.verify_counterparty_commitment_tx(commitment_tx, secp_ctx); + + { + #[cfg(test)] + if !self.is_signer_available(SignerOp::SignCounterpartyCommitment) { + return Err(()); + } + let mut state = self.state.lock().unwrap(); + let actual_commitment_number = commitment_tx.commitment_number(); + let last_commitment_number = state.last_counterparty_commitment; + // These commitment numbers are backwards counting. We expect either the same as the previously encountered, + // or the next one. + assert!(last_commitment_number == actual_commitment_number || last_commitment_number - 1 == actual_commitment_number, "{} doesn't come after {}", actual_commitment_number, last_commitment_number); + // Ensure that the counterparty doesn't get more than two broadcastable commitments - + // the last and the one we are trying to sign + assert!(actual_commitment_number >= state.last_counterparty_revoked_commitment - 2, "cannot sign a commitment if second to last wasn't revoked - signing {} revoked {}", actual_commitment_number, state.last_counterparty_revoked_commitment); + state.last_counterparty_commitment = cmp::min(last_commitment_number, actual_commitment_number) } - Ok(EcdsaChannelSigner::sign_counterparty_htlc_transaction(&self.inner, htlc_tx, input, amount, per_commitment_point, htlc, secp_ctx).unwrap()) + + Ok(self.inner.sign_counterparty_commitment(commitment_tx, inbound_htlc_preimages, outbound_htlc_preimages, secp_ctx).unwrap()) } fn sign_closing_transaction(&self, closing_tx: &ClosingTransaction, secp_ctx: &Secp256k1) -> Result { @@ -334,20 +371,6 @@ impl EcdsaChannelSigner for TestChannelSigner { Ok(self.inner.sign_closing_transaction(closing_tx, secp_ctx).unwrap()) } - fn sign_holder_anchor_input( - &self, anchor_tx: &Transaction, input: usize, secp_ctx: &Secp256k1, - ) -> Result { - debug_assert!(MIN_CHAN_DUST_LIMIT_SATOSHIS > ANCHOR_OUTPUT_VALUE_SATOSHI); - // As long as our minimum dust limit is enforced and is greater than our anchor output - // value, an anchor output can only have an index within [0, 1]. - assert!(anchor_tx.input[input].previous_output.vout == 0 || anchor_tx.input[input].previous_output.vout == 1); - #[cfg(test)] - if !self.is_signer_available(SignerOp::SignHolderAnchorInput) { - return Err(()); - } - EcdsaChannelSigner::sign_holder_anchor_input(&self.inner, anchor_tx, input, secp_ctx) - } - fn sign_channel_announcement_with_funding_key( &self, msg: &msgs::UnsignedChannelAnnouncement, secp_ctx: &Secp256k1 ) -> Result { @@ -385,10 +408,6 @@ impl TaprootChannelSigner for TestChannelSigner { todo!() } - fn sign_holder_htlc_transaction(&self, htlc_tx: &Transaction, input: usize, htlc_descriptor: &HTLCDescriptor, secp_ctx: &Secp256k1) -> Result { - todo!() - } - fn sign_counterparty_htlc_transaction(&self, htlc_tx: &Transaction, input: usize, amount: u64, per_commitment_point: &PublicKey, htlc: &HTLCOutputInCommitment, secp_ctx: &Secp256k1) -> Result { todo!() } @@ -414,17 +433,19 @@ impl Writeable for TestChannelSigner { } impl TestChannelSigner { - fn verify_counterparty_commitment_tx<'a, T: secp256k1::Signing + secp256k1::Verification>(&self, commitment_tx: &'a CommitmentTransaction, secp_ctx: &Secp256k1) -> TrustedCommitmentTransaction<'a> { + fn verify_counterparty_commitment_tx<'a>(&self, commitment_tx: &'a CommitmentTransaction, secp_ctx: &Secp256k1) -> TrustedCommitmentTransaction<'a> { commitment_tx.verify( - &self.inner.get_channel_parameters().unwrap().as_counterparty_broadcastable(), - self.inner.counterparty_pubkeys().unwrap(), self.inner.pubkeys(), secp_ctx + secp_ctx, + self, + false, ).expect("derived different per-tx keys or built transaction") } - fn verify_holder_commitment_tx<'a, T: secp256k1::Signing + secp256k1::Verification>(&self, commitment_tx: &'a CommitmentTransaction, secp_ctx: &Secp256k1) -> TrustedCommitmentTransaction<'a> { + fn verify_holder_commitment_tx<'a>(&self, commitment_tx: &'a CommitmentTransaction, secp_ctx: &Secp256k1) -> TrustedCommitmentTransaction<'a> { commitment_tx.verify( - &self.inner.get_channel_parameters().unwrap().as_holder_broadcastable(), - self.inner.pubkeys(), self.inner.counterparty_pubkeys().unwrap(), secp_ctx + secp_ctx, + self, + true, ).expect("derived different per-tx keys or built transaction") } } diff --git a/lightning/src/util/test_utils.rs b/lightning/src/util/test_utils.rs index 07b2b19b0d6..8895337db09 100644 --- a/lightning/src/util/test_utils.rs +++ b/lightning/src/util/test_utils.rs @@ -517,7 +517,7 @@ impl chainmonitor::Persist for while let Some(JusticeTxData { justice_tx, value, commitment_number }) = channel_state.front() { let input_idx = 0; let commitment_txid = justice_tx.input[input_idx].previous_output.txid; - match data.sign_to_local_justice_tx(justice_tx.clone(), input_idx, value.to_sat(), *commitment_number) { + match data.punish_revokeable_output(justice_tx.clone(), input_idx, value.to_sat(), *commitment_number) { Ok(signed_justice_tx) => { let dup = self.watchtower_state.lock().unwrap() .get_mut(&funding_txo).unwrap()