]> git.bitcoin.ninja Git - rust-lightning/commitdiff
Merge pull request #3096 from alecchendev/2024-06-drop-one-signer-call
authorArik <arik-so@users.noreply.github.com>
Fri, 7 Jun 2024 16:26:39 +0000 (09:26 -0700)
committerGitHub <noreply@github.com>
Fri, 7 Jun 2024 16:26:39 +0000 (09:26 -0700)
Cache commitment point on ExternalHTLCClaim to drop a signer call

1  2 
lightning/src/chain/channelmonitor.rs
lightning/src/chain/onchaintx.rs

index 6992eb192abf98b35c7e569646893e27a0abe158,3a289226907e3f8d9d659f2731bcab7c3fe25ee2..6291006fc9cb1c6a6fc99a96f7f6561e355d8ab4
@@@ -20,7 -20,6 +20,7 @@@
  //! security-domain-separated system design, you should consider having multiple paths for
  //! ChannelMonitors to get out of the HSM and onto monitoring devices.
  
 +use bitcoin::amount::Amount;
  use bitcoin::blockdata::block::Header;
  use bitcoin::blockdata::transaction::{OutPoint as BitcoinOutPoint, TxOut, Transaction};
  use bitcoin::blockdata::script::{Script, ScriptBuf};
@@@ -29,10 -28,10 +29,10 @@@ 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::{Secp256k1, ecdsa::Signature};
  use bitcoin::secp256k1::{SecretKey, PublicKey};
  use bitcoin::secp256k1;
 -use bitcoin::sighash::EcdsaSighashType;
  
  use crate::ln::channel::INITIAL_COMMITMENT_NUMBER;
  use crate::ln::types::{PaymentHash, PaymentPreimage, ChannelId};
@@@ -412,7 -411,7 +412,7 @@@ impl OnchainEventEntry 
  /// The (output index, sats value) for the counterparty's output in a commitment transaction.
  ///
  /// This was added as an `Option` in 0.0.110.
 -type CommitmentTxCounterpartyOutputInfo = Option<(u32, u64)>;
 +type CommitmentTxCounterpartyOutputInfo = Option<(u32, Amount)>;
  
  /// Upon discovering of some classes of onchain tx by ChannelMonitor, we may have to take actions on it
  /// once they mature to enough confirmations (ANTI_REORG_DELAY)
@@@ -2172,7 -2171,7 +2172,7 @@@ impl<Signer: EcdsaChannelSigner> Channe
                                                } else { None }
                                        }) {
                                                res.push(Balance::ClaimableAwaitingConfirmations {
 -                                                      amount_satoshis: value,
 +                                                      amount_satoshis: value.to_sat(),
                                                        confirmation_height: conf_thresh,
                                                });
                                        } else {
                                                        descriptor: SpendableOutputDescriptor::StaticOutput { output, .. }
                                                } = &event.event {
                                                        res.push(Balance::ClaimableAwaitingConfirmations {
 -                                                              amount_satoshis: output.value,
 +                                                              amount_satoshis: output.value.to_sat(),
                                                                confirmation_height: event.confirmation_threshold(),
                                                        });
                                                        if let Some(confirmed_to_self_idx) = confirmed_counterparty_output.map(|(idx, _)| idx) {
                                                        .is_output_spend_pending(&BitcoinOutPoint::new(txid, confirmed_to_self_idx));
                                                if output_spendable {
                                                        res.push(Balance::CounterpartyRevokedOutputClaimable {
 -                                                              amount_satoshis: amt,
 +                                                              amount_satoshis: amt.to_sat(),
                                                        });
                                                }
                                        } else {
@@@ -2431,8 -2430,8 +2431,8 @@@ macro_rules! fail_unbroadcast_htlcs 
                debug_assert_eq!($commitment_tx_confirmed.txid(), $commitment_txid_confirmed);
  
                macro_rules! check_htlc_fails {
 -                      ($txid: expr, $commitment_tx: expr) => {
 -                              if let Some(ref latest_outpoints) = $self.counterparty_claimable_outpoints.get($txid) {
 +                      ($txid: expr, $commitment_tx: expr, $per_commitment_outpoints: expr) => {
 +                              if let Some(ref latest_outpoints) = $per_commitment_outpoints {
                                        for &(ref htlc, ref source_option) in latest_outpoints.iter() {
                                                if let &Some(ref source) = source_option {
                                                        // Check if the HTLC is present in the commitment transaction that was
                        }
                }
                if let Some(ref txid) = $self.current_counterparty_commitment_txid {
 -                      check_htlc_fails!(txid, "current");
 +                      check_htlc_fails!(txid, "current", $self.counterparty_claimable_outpoints.get(txid));
                }
                if let Some(ref txid) = $self.prev_counterparty_commitment_txid {
 -                      check_htlc_fails!(txid, "previous");
 +                      check_htlc_fails!(txid, "previous", $self.counterparty_claimable_outpoints.get(txid));
                }
        } }
  }
@@@ -2764,15 -2763,15 +2764,15 @@@ impl<Signer: EcdsaChannelSigner> Channe
                // If the channel is force closed, try to claim the output from this preimage.
                // First check if a counterparty commitment transaction has been broadcasted:
                macro_rules! claim_htlcs {
 -                      ($commitment_number: expr, $txid: expr) => {
 -                              let (htlc_claim_reqs, _) = self.get_counterparty_output_claim_info($commitment_number, $txid, None);
 +                      ($commitment_number: expr, $txid: expr, $htlcs: expr) => {
 +                              let (htlc_claim_reqs, _) = self.get_counterparty_output_claim_info($commitment_number, $txid, None, $htlcs);
                                self.onchain_tx_handler.update_claims_view_from_requests(htlc_claim_reqs, self.best_block.height, self.best_block.height, broadcaster, fee_estimator, logger);
                        }
                }
                if let Some(txid) = self.current_counterparty_commitment_txid {
                        if txid == confirmed_spend_txid {
                                if let Some(commitment_number) = self.counterparty_commitment_txn_on_chain.get(&txid) {
 -                                      claim_htlcs!(*commitment_number, txid);
 +                                      claim_htlcs!(*commitment_number, txid, self.counterparty_claimable_outpoints.get(&txid));
                                } else {
                                        debug_assert!(false);
                                        log_error!(logger, "Detected counterparty commitment tx on-chain without tracking commitment number");
                if let Some(txid) = self.prev_counterparty_commitment_txid {
                        if txid == confirmed_spend_txid {
                                if let Some(commitment_number) = self.counterparty_commitment_txn_on_chain.get(&txid) {
 -                                      claim_htlcs!(*commitment_number, txid);
 +                                      claim_htlcs!(*commitment_number, txid, self.counterparty_claimable_outpoints.get(&txid));
                                } else {
                                        debug_assert!(false);
                                        log_error!(logger, "Detected counterparty commitment tx on-chain without tracking commitment number");
                                        debug_assert_eq!(self.current_holder_commitment_tx.txid, commitment_txid);
                                        let pending_htlcs = self.current_holder_commitment_tx.non_dust_htlcs();
                                        let commitment_tx_fee_satoshis = self.channel_value_satoshis -
 -                                              commitment_tx.output.iter().fold(0u64, |sum, output| sum + output.value);
 +                                              commitment_tx.output.iter().fold(0u64, |sum, output| sum + output.value.to_sat());
                                        ret.push(Event::BumpTransaction(BumpTransactionEvent::ChannelClose {
                                                channel_id,
                                                counterparty_node_id,
                                                        },
                                                        commitment_txid: htlc.commitment_txid,
                                                        per_commitment_number: htlc.per_commitment_number,
-                                                       per_commitment_point: self.onchain_tx_handler.signer.get_per_commitment_point(
-                                                               htlc.per_commitment_number, &self.onchain_tx_handler.secp_ctx,
-                                                       ),
+                                                       per_commitment_point: htlc.per_commitment_point,
                                                        feerate_per_kw: 0,
                                                        htlc: htlc.htlc,
                                                        preimage: htlc.preimage,
  
                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_bitcoin_signature(&sig.serialize_der(), EcdsaSighashType::All);
 +              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());
                Ok(justice_tx)
        /// height > height + CLTV_SHARED_CLAIM_BUFFER. In any case, will install monitoring for
        /// HTLC-Success/HTLC-Timeout transactions.
        ///
 -      /// Returns packages to claim the revoked output(s), as well as additional outputs to watch and
 -      /// general information about the output that is to the counterparty in the commitment
 -      /// transaction.
 +      /// Returns packages to claim the revoked output(s) and general information about the output that
 +      /// is to the counterparty in the commitment transaction.
        fn check_spend_counterparty_transaction<L: Deref>(&mut self, tx: &Transaction, height: u32, block_hash: &BlockHash, logger: &L)
 -              -> (Vec<PackageTemplate>, TransactionOutputs, CommitmentTxCounterpartyOutputInfo)
 +              -> (Vec<PackageTemplate>, CommitmentTxCounterpartyOutputInfo)
        where L::Target: Logger {
                // Most secp and related errors trying to create keys means we have no hope of constructing
                // a spend transaction...so we return no transactions to broadcast
                let mut claimable_outpoints = Vec::new();
 -              let mut watch_outputs = Vec::new();
                let mut to_counterparty_output_info = None;
  
                let commitment_txid = tx.txid(); //TODO: This is gonna be a performance bottleneck for watchtowers!
                        ( $thing : expr ) => {
                                match $thing {
                                        Ok(a) => a,
 -                                      Err(_) => return (claimable_outpoints, (commitment_txid, watch_outputs), to_counterparty_output_info)
 +                                      Err(_) => return (claimable_outpoints, to_counterparty_output_info)
                                }
                        };
                }
                        let delayed_key = DelayedPaymentKey::from_basepoint(&self.onchain_tx_handler.secp_ctx, &self.counterparty_commitment_params.counterparty_delayed_payment_base_key, &PublicKey::from_secret_key(&self.onchain_tx_handler.secp_ctx, &per_commitment_key));
  
                        let revokeable_redeemscript = chan_utils::get_revokeable_redeemscript(&revocation_pubkey, self.counterparty_commitment_params.on_counterparty_tx_csv, &delayed_key);
 -                      let revokeable_p2wsh = revokeable_redeemscript.to_v0_p2wsh();
 +                      let revokeable_p2wsh = revokeable_redeemscript.to_p2wsh();
  
                        // First, process non-htlc outputs (to_holder & to_counterparty)
                        for (idx, outp) in tx.output.iter().enumerate() {
                        }
  
                        // Then, try to find revoked htlc outputs
 -                      if let Some(ref per_commitment_data) = per_commitment_option {
 -                              for (_, &(ref htlc, _)) in per_commitment_data.iter().enumerate() {
 +                      if let Some(per_commitment_claimable_data) = per_commitment_option {
 +                              for (htlc, _) in per_commitment_claimable_data {
                                        if let Some(transaction_output_index) = htlc.transaction_output_index {
                                                if transaction_output_index as usize >= tx.output.len() ||
 -                                                              tx.output[transaction_output_index as usize].value != htlc.amount_msat / 1000 {
 +                                                              tx.output[transaction_output_index as usize].value != htlc.to_bitcoin_amount() {
                                                        // per_commitment_data is corrupt or our commitment signing key leaked!
 -                                                      return (claimable_outpoints, (commitment_txid, watch_outputs),
 -                                                              to_counterparty_output_info);
 +                                                      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 justice_package = PackageTemplate::build_package(commitment_txid, transaction_output_index, PackageSolvingData::RevokedHTLCOutput(revk_htlc_outp), htlc.cltv_expiry, height);
                        if !claimable_outpoints.is_empty() || per_commitment_option.is_some() { // ie we're confident this is actually ours
                                // We're definitely a counterparty commitment transaction!
                                log_error!(logger, "Got broadcast of revoked counterparty commitment transaction, going to generate general spend tx with {} inputs", claimable_outpoints.len());
 -                              for (idx, outp) in tx.output.iter().enumerate() {
 -                                      watch_outputs.push((idx as u32, outp.clone()));
 -                              }
                                self.counterparty_commitment_txn_on_chain.insert(commitment_txid, commitment_number);
  
 -                              if let Some(per_commitment_data) = per_commitment_option {
 +                              if let Some(per_commitment_claimable_data) = per_commitment_option {
                                        fail_unbroadcast_htlcs!(self, "revoked_counterparty", commitment_txid, tx, height,
 -                                              block_hash, per_commitment_data.iter().map(|(htlc, htlc_source)|
 +                                              block_hash, per_commitment_claimable_data.iter().map(|(htlc, htlc_source)|
                                                        (htlc, htlc_source.as_ref().map(|htlc_source| htlc_source.as_ref()))
                                                ), logger);
                                } else {
                                                block_hash, [].iter().map(|reference| *reference), logger);
                                }
                        }
 -              } else if let Some(per_commitment_data) = per_commitment_option {
 +              } else if let Some(per_commitment_claimable_data) = per_commitment_option {
                        // While this isn't useful yet, there is a potential race where if a counterparty
                        // revokes a state at the same time as the commitment transaction for that state is
                        // confirmed, and the watchtower receives the block before the user, the user could
                        // already processed the block, resulting in the counterparty_commitment_txn_on_chain entry
                        // not being generated by the above conditional. Thus, to be safe, we go ahead and
                        // insert it here.
 -                      for (idx, outp) in tx.output.iter().enumerate() {
 -                              watch_outputs.push((idx as u32, outp.clone()));
 -                      }
                        self.counterparty_commitment_txn_on_chain.insert(commitment_txid, commitment_number);
  
                        log_info!(logger, "Got broadcast of non-revoked counterparty commitment transaction {}", commitment_txid);
                        fail_unbroadcast_htlcs!(self, "counterparty", commitment_txid, tx, height, block_hash,
 -                              per_commitment_data.iter().map(|(htlc, htlc_source)|
 +                              per_commitment_claimable_data.iter().map(|(htlc, htlc_source)|
                                        (htlc, htlc_source.as_ref().map(|htlc_source| htlc_source.as_ref()))
                                ), logger);
 -
                        let (htlc_claim_reqs, counterparty_output_info) =
 -                              self.get_counterparty_output_claim_info(commitment_number, commitment_txid, Some(tx));
 +                              self.get_counterparty_output_claim_info(commitment_number, commitment_txid, Some(tx), per_commitment_option);
                        to_counterparty_output_info = counterparty_output_info;
                        for req in htlc_claim_reqs {
                                claimable_outpoints.push(req);
                        }
  
                }
 -              (claimable_outpoints, (commitment_txid, watch_outputs), to_counterparty_output_info)
 +              (claimable_outpoints, to_counterparty_output_info)
        }
  
        /// Returns the HTLC claim package templates and the counterparty output info
 -      fn get_counterparty_output_claim_info(&self, commitment_number: u64, commitment_txid: Txid, tx: Option<&Transaction>)
 +      fn get_counterparty_output_claim_info(&self, commitment_number: u64, commitment_txid: Txid, tx: Option<&Transaction>, per_commitment_option: Option<&Vec<(HTLCOutputInCommitment, Option<Box<HTLCSource>>)>>)
        -> (Vec<PackageTemplate>, CommitmentTxCounterpartyOutputInfo) {
                let mut claimable_outpoints = Vec::new();
                let mut to_counterparty_output_info: CommitmentTxCounterpartyOutputInfo = None;
  
 -              let htlc_outputs = match self.counterparty_claimable_outpoints.get(&commitment_txid) {
 +              let per_commitment_claimable_data = match per_commitment_option {
                        Some(outputs) => outputs,
                        None => return (claimable_outpoints, to_counterparty_output_info),
                };
  
                        let revokeable_p2wsh = chan_utils::get_revokeable_redeemscript(&revocation_pubkey,
                                self.counterparty_commitment_params.on_counterparty_tx_csv,
 -                              &delayed_key).to_v0_p2wsh();
 +                              &delayed_key).to_p2wsh();
                        for (idx, outp) in transaction.output.iter().enumerate() {
                                if outp.script_pubkey == revokeable_p2wsh {
                                        to_counterparty_output_info =
                        }
                }
  
 -              for (_, &(ref htlc, _)) in htlc_outputs.iter().enumerate() {
 +              for  &(ref htlc, _) in per_commitment_claimable_data.iter() {
                        if let Some(transaction_output_index) = htlc.transaction_output_index {
                                if let Some(transaction) = tx {
                                        if transaction_output_index as usize >= transaction.output.len() ||
 -                                              transaction.output[transaction_output_index as usize].value != htlc.amount_msat / 1000 {
 +                                              transaction.output[transaction_output_index as usize].value != htlc.to_bitcoin_amount() {
                                                        // per_commitment_data is corrupt or our commitment signing key leaked!
                                                        return (claimable_outpoints, to_counterparty_output_info);
                                                }
                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_v0_p2wsh(), holder_tx.per_commitment_point.clone(), holder_tx.revocation_key.clone()));
 +              let broadcasted_holder_revokable_script = Some((redeemscript.to_p2wsh(), 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 {
                        if counterparty_commitment_txid == confirmed_commitment_txid {
                                continue;
                        }
 +                      // If we have generated claims for counterparty_commitment_txid earlier, we can rely on always
 +                      // having claim related htlcs for counterparty_commitment_txid in counterparty_claimable_outpoints.
                        for (htlc, _) in self.counterparty_claimable_outpoints.get(counterparty_commitment_txid).unwrap_or(&vec![]) {
                                log_trace!(logger, "Canceling claims for previously confirmed counterparty commitment {}",
                                        counterparty_commitment_txid);
        {
                let txn_matched = self.filter_block(txdata);
                for tx in &txn_matched {
 -                      let mut output_val = 0;
 +                      let mut output_val = Amount::ZERO;
                        for out in tx.output.iter() {
 -                              if out.value > 21_000_000_0000_0000 { panic!("Value-overflowing transaction provided to block connected"); }
 +                              if out.value > Amount::MAX_MONEY { panic!("Value-overflowing transaction provided to block connected"); }
                                output_val += out.value;
 -                              if output_val > 21_000_000_0000_0000 { panic!("Value-overflowing transaction provided to block connected"); }
 +                              if output_val > Amount::MAX_MONEY { panic!("Value-overflowing transaction provided to block connected"); }
                        }
                }
  
                                        self.funding_spend_seen = true;
                                        let mut commitment_tx_to_counterparty_output = None;
                                        if (tx.input[0].sequence.0 >> 8*3) as u8 == 0x80 && (tx.lock_time.to_consensus_u32() >> 8*3) as u8 == 0x20 {
 -                                              let (mut new_outpoints, new_outputs, counterparty_output_idx_sats) =
 -                                                      self.check_spend_counterparty_transaction(&tx, height, &block_hash, &logger);
 -                                              commitment_tx_to_counterparty_output = counterparty_output_idx_sats;
 -                                              if !new_outputs.1.is_empty() {
 -                                                      watch_outputs.push(new_outputs);
 -                                              }
 -                                              claimable_outpoints.append(&mut new_outpoints);
 -                                              if new_outpoints.is_empty() {
 -                                                      if let Some((mut new_outpoints, new_outputs)) = self.check_spend_holder_transaction(&tx, height, &block_hash, &logger) {
 -                                                              #[cfg(not(fuzzing))]
 -                                                              debug_assert!(commitment_tx_to_counterparty_output.is_none(),
 -                                                                      "A commitment transaction matched as both a counterparty and local commitment tx?");
 -                                                              if !new_outputs.1.is_empty() {
 -                                                                      watch_outputs.push(new_outputs);
 -                                                              }
 -                                                              claimable_outpoints.append(&mut new_outpoints);
 -                                                              balance_spendable_csv = Some(self.on_holder_tx_csv);
 +                                              if let Some((mut new_outpoints, new_outputs)) = self.check_spend_holder_transaction(&tx, height, &block_hash, &logger) {
 +                                                      if !new_outputs.1.is_empty() {
 +                                                              watch_outputs.push(new_outputs);
 +                                                      }
 +
 +                                                      claimable_outpoints.append(&mut new_outpoints);
 +                                                      balance_spendable_csv = Some(self.on_holder_tx_csv);
 +                                              } else {
 +                                                      let mut new_watch_outputs = Vec::new();
 +                                                      for (idx, outp) in tx.output.iter().enumerate() {
 +                                                              new_watch_outputs.push((idx as u32, outp.clone()));
                                                        }
 +                                                      watch_outputs.push((txid, new_watch_outputs));
 +
 +                                                      let (mut new_outpoints, counterparty_output_idx_sats) =
 +                                                              self.check_spend_counterparty_transaction(&tx, height, &block_hash, &logger);
 +                                                      commitment_tx_to_counterparty_output = counterparty_output_idx_sats;
 +
 +                                                      claimable_outpoints.append(&mut new_outpoints);
                                                }
                                        }
                                        self.onchain_events_awaiting_threshold_conf.push(OnchainEventEntry {
                                                        // If the expected script is a known type, check that the witness
                                                        // appears to be spending the correct type (ie that the match would
                                                        // actually succeed in BIP 158/159-style filters).
 -                                                      if _script_pubkey.is_v0_p2wsh() {
 +                                                      if _script_pubkey.is_p2wsh() {
                                                                if input.witness.last().unwrap().to_vec() == deliberately_bogus_accepted_htlc_witness_program() {
                                                                        // In at least one test we use a deliberately bogus witness
                                                                        // script which hit an old panic. Thus, we check for that here
                                                                }
  
                                                                assert_eq!(&bitcoin::Address::p2wsh(&ScriptBuf::from(input.witness.last().unwrap().to_vec()), bitcoin::Network::Bitcoin).script_pubkey(), _script_pubkey);
 -                                                      } else if _script_pubkey.is_v0_p2wpkh() {
 +                                                      } else if _script_pubkey.is_p2wpkh() {
                                                                assert_eq!(&bitcoin::Address::p2wpkh(&bitcoin::PublicKey::from_slice(&input.witness.last().unwrap()).unwrap(), bitcoin::Network::Bitcoin).unwrap().script_pubkey(), _script_pubkey);
                                                        } else { panic!(); }
                                                }
                        }
  
                        macro_rules! check_htlc_valid_counterparty {
 -                              ($counterparty_txid: expr, $htlc_output: expr) => {
 -                                      if let Some(txid) = $counterparty_txid {
 -                                              for &(ref pending_htlc, ref pending_source) in self.counterparty_claimable_outpoints.get(&txid).unwrap() {
 +                              ($htlc_output: expr, $per_commitment_data: expr) => {
 +                                              for &(ref pending_htlc, ref pending_source) in $per_commitment_data {
                                                        if pending_htlc.payment_hash == $htlc_output.payment_hash && pending_htlc.amount_msat == $htlc_output.amount_msat {
                                                                if let &Some(ref source) = pending_source {
                                                                        log_claim!("revoked counterparty commitment tx", false, pending_htlc, true);
                                                                }
                                                        }
                                                }
 -                                      }
                                }
                        }
  
                                                                // resolve the source HTLC with the original sender.
                                                                payment_data = Some(((*source).clone(), htlc_output.payment_hash, htlc_output.amount_msat));
                                                        } else if !$holder_tx {
 -                                                              check_htlc_valid_counterparty!(self.current_counterparty_commitment_txid, htlc_output);
 +                                                              if let Some(current_counterparty_commitment_txid) = &self.current_counterparty_commitment_txid {
 +                                                                      check_htlc_valid_counterparty!(htlc_output, self.counterparty_claimable_outpoints.get(current_counterparty_commitment_txid).unwrap());
 +                                                              }
                                                                if payment_data.is_none() {
 -                                                                      check_htlc_valid_counterparty!(self.prev_counterparty_commitment_txid, htlc_output);
 +                                                                      if let Some(prev_counterparty_commitment_txid) = &self.prev_counterparty_commitment_txid {
 +                                                                              check_htlc_valid_counterparty!(htlc_output, self.counterparty_claimable_outpoints.get(prev_counterparty_commitment_txid).unwrap());
 +                                                                      }
                                                                }
                                                        }
                                                        if payment_data.is_none() {
                                }
                        }
                        if let Some(ref htlc_outputs) = self.counterparty_claimable_outpoints.get(&input.previous_output.txid) {
 -                              scan_commitment!(htlc_outputs.iter().map(|&(ref a, ref b)| (a, (b.as_ref().clone()).map(|boxed| &**boxed))),
 +                              scan_commitment!(htlc_outputs.iter().map(|&(ref a, ref b)| (a, b.as_ref().map(|boxed| &**boxed))),
                                        "counterparty commitment tx", false);
                        }
  
@@@ -4715,11 -4717,11 +4713,11 @@@ impl<'a, 'b, ES: EntropySource, SP: Sig
                // wrong `counterparty_payment_script` was being tracked. Fix it now on deserialization to
                // give them a chance to recognize the spendable output.
                if onchain_tx_handler.channel_type_features().supports_anchors_zero_fee_htlc_tx() &&
 -                      counterparty_payment_script.is_v0_p2wpkh()
 +                      counterparty_payment_script.is_p2wpkh()
                {
                        let payment_point = onchain_tx_handler.channel_transaction_parameters.holder_pubkeys.payment_point;
                        counterparty_payment_script =
 -                              chan_utils::get_to_countersignatory_with_anchors_redeemscript(&payment_point).to_v0_p2wsh();
 +                              chan_utils::get_to_countersignatory_with_anchors_redeemscript(&payment_point).to_p2wsh();
                }
  
                Ok((best_block.block_hash, ChannelMonitor::from_impl(ChannelMonitorImpl {
  
  #[cfg(test)]
  mod tests {
 +      use bitcoin::amount::Amount;
        use bitcoin::blockdata::locktime::absolute::LockTime;
        use bitcoin::blockdata::script::{ScriptBuf, Builder};
        use bitcoin::blockdata::opcodes;
 -      use bitcoin::blockdata::transaction::{Transaction, TxIn, TxOut};
 +      use bitcoin::blockdata::transaction::{Transaction, TxIn, TxOut, Version};
        use bitcoin::blockdata::transaction::OutPoint as BitcoinOutPoint;
        use bitcoin::sighash;
        use bitcoin::sighash::EcdsaSighashType;
        use bitcoin::hashes::sha256::Hash as Sha256;
        use bitcoin::hashes::hex::FromHex;
        use bitcoin::hash_types::{BlockHash, Txid};
 -      use bitcoin::network::constants::Network;
 +      use bitcoin::network::Network;
        use bitcoin::secp256k1::{SecretKey,PublicKey};
        use bitcoin::secp256k1::Secp256k1;
        use bitcoin::{Sequence, Witness};
                        }
                }
                let dummy_sig = crate::crypto::utils::sign(&secp_ctx,
 -                      &bitcoin::secp256k1::Message::from_slice(&[42; 32]).unwrap(),
 +                      &bitcoin::secp256k1::Message::from_digest([42; 32]),
                        &SecretKey::from_slice(&[42; 32]).unwrap());
  
                macro_rules! test_preimages_exist {
                                        transaction_output_index: Some($idx as u32),
                                };
                                let redeem_script = if *$weight == WEIGHT_REVOKED_OUTPUT { chan_utils::get_revokeable_redeemscript(&RevocationKey::from_basepoint(&secp_ctx, &RevocationBasepoint::from(pubkey), &pubkey), 256, &DelayedPaymentKey::from_basepoint(&secp_ctx, &DelayedPaymentBasepoint::from(pubkey), &pubkey)) } else { chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, $opt_anchors, &HtlcKey::from_basepoint(&secp_ctx, &HtlcBasepoint::from(pubkey), &pubkey), &HtlcKey::from_basepoint(&secp_ctx, &HtlcBasepoint::from(pubkey), &pubkey), &RevocationKey::from_basepoint(&secp_ctx, &RevocationBasepoint::from(pubkey), &pubkey)) };
 -                              let sighash = hash_to_message!(&$sighash_parts.segwit_signature_hash($idx, &redeem_script, $amount, EcdsaSighashType::All).unwrap()[..]);
 +                              let sighash = hash_to_message!(&$sighash_parts.p2wsh_signature_hash($idx, &redeem_script, $amount, EcdsaSighashType::All).unwrap()[..]);
                                let sig = secp_ctx.sign_ecdsa(&sighash, &privkey);
                                let mut ser_sig = sig.serialize_der().to_vec();
                                ser_sig.push(EcdsaSighashType::All as u8);
  
                // Justice tx with 1 to_holder, 2 revoked offered HTLCs, 1 revoked received HTLCs
                for channel_type_features in [ChannelTypeFeatures::only_static_remote_key(), ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies()].iter() {
 -                      let mut claim_tx = Transaction { version: 0, lock_time: LockTime::ZERO, input: Vec::new(), output: Vec::new() };
 +                      let mut claim_tx = Transaction { version: Version(0), lock_time: LockTime::ZERO, input: Vec::new(), output: Vec::new() };
                        let mut sum_actual_sigs = 0;
                        for i in 0..4 {
                                claim_tx.input.push(TxIn {
                        }
                        claim_tx.output.push(TxOut {
                                script_pubkey: script_pubkey.clone(),
 -                              value: 0,
 +                              value: Amount::ZERO,
                        });
                        let base_weight = claim_tx.weight().to_wu();
                        let inputs_weight = vec![WEIGHT_REVOKED_OUTPUT, weight_revoked_offered_htlc(channel_type_features), weight_revoked_offered_htlc(channel_type_features), weight_revoked_received_htlc(channel_type_features)];
                        {
                                let mut sighash_parts = sighash::SighashCache::new(&mut claim_tx);
                                for (idx, inp) in inputs_weight.iter().enumerate() {
 -                                      sign_input!(sighash_parts, idx, 0, inp, sum_actual_sigs, channel_type_features);
 +                                      sign_input!(sighash_parts, idx, Amount::ZERO, inp, sum_actual_sigs, channel_type_features);
                                        inputs_total_weight += inp;
                                }
                        }
  
                // Claim tx with 1 offered HTLCs, 3 received HTLCs
                for channel_type_features in [ChannelTypeFeatures::only_static_remote_key(), ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies()].iter() {
 -                      let mut claim_tx = Transaction { version: 0, lock_time: LockTime::ZERO, input: Vec::new(), output: Vec::new() };
 +                      let mut claim_tx = Transaction { version: Version(0), lock_time: LockTime::ZERO, input: Vec::new(), output: Vec::new() };
                        let mut sum_actual_sigs = 0;
                        for i in 0..4 {
                                claim_tx.input.push(TxIn {
                        }
                        claim_tx.output.push(TxOut {
                                script_pubkey: script_pubkey.clone(),
 -                              value: 0,
 +                              value: Amount::ZERO,
                        });
                        let base_weight = claim_tx.weight().to_wu();
                        let inputs_weight = vec![weight_offered_htlc(channel_type_features), weight_received_htlc(channel_type_features), weight_received_htlc(channel_type_features), weight_received_htlc(channel_type_features)];
                        {
                                let mut sighash_parts = sighash::SighashCache::new(&mut claim_tx);
                                for (idx, inp) in inputs_weight.iter().enumerate() {
 -                                      sign_input!(sighash_parts, idx, 0, inp, sum_actual_sigs, channel_type_features);
 +                                      sign_input!(sighash_parts, idx, Amount::ZERO, inp, sum_actual_sigs, channel_type_features);
                                        inputs_total_weight += inp;
                                }
                        }
  
                // Justice tx with 1 revoked HTLC-Success tx output
                for channel_type_features in [ChannelTypeFeatures::only_static_remote_key(), ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies()].iter() {
 -                      let mut claim_tx = Transaction { version: 0, lock_time: LockTime::ZERO, input: Vec::new(), output: Vec::new() };
 +                      let mut claim_tx = Transaction { version: Version(0), lock_time: LockTime::ZERO, input: Vec::new(), output: Vec::new() };
                        let mut sum_actual_sigs = 0;
                        claim_tx.input.push(TxIn {
                                previous_output: BitcoinOutPoint {
                        });
                        claim_tx.output.push(TxOut {
                                script_pubkey: script_pubkey.clone(),
 -                              value: 0,
 +                              value: Amount::ZERO,
                        });
                        let base_weight = claim_tx.weight().to_wu();
                        let inputs_weight = vec![WEIGHT_REVOKED_OUTPUT];
                        {
                                let mut sighash_parts = sighash::SighashCache::new(&mut claim_tx);
                                for (idx, inp) in inputs_weight.iter().enumerate() {
 -                                      sign_input!(sighash_parts, idx, 0, inp, sum_actual_sigs, channel_type_features);
 +                                      sign_input!(sighash_parts, idx, Amount::ZERO, inp, sum_actual_sigs, channel_type_features);
                                        inputs_total_weight += inp;
                                }
                        }
index be097b637d0a4eb69974b712bb090931feaec81d,6bbd2439e507257a7acaa6c115167e18e8345bd8..e5757cfd1e52f5b1da358f4fd88a706052447147
@@@ -12,7 -12,6 +12,7 @@@
  //! OnchainTxHandler objects are fully-part of ChannelMonitor and encapsulates all
  //! building, tracking, bumping and notifications functions.
  
 +use bitcoin::amount::Amount;
  use bitcoin::blockdata::locktime::absolute::LockTime;
  use bitcoin::blockdata::transaction::Transaction;
  use bitcoin::blockdata::transaction::OutPoint as BitcoinOutPoint;
@@@ -20,6 -19,7 +20,7 @@@ use bitcoin::blockdata::script::{Script
  use bitcoin::hashes::{Hash, HashEngine};
  use bitcoin::hashes::sha256::Hash as Sha256;
  use bitcoin::hash_types::{Txid, BlockHash};
+ use bitcoin::secp256k1::PublicKey;
  use bitcoin::secp256k1::{Secp256k1, ecdsa::Signature};
  use bitcoin::secp256k1;
  
@@@ -180,6 -180,7 +181,7 @@@ pub(crate) struct ExternalHTLCClaim 
        pub(crate) htlc: HTLCOutputInCommitment,
        pub(crate) preimage: Option<PaymentPreimage>,
        pub(crate) counterparty_sig: Signature,
+       pub(crate) per_commitment_point: PublicKey,
  }
  
  // Represents the different types of claims for which events are yielded externally to satisfy said
@@@ -616,7 -617,7 +618,7 @@@ impl<ChannelSigner: EcdsaChannelSigner
                                assert!(new_feerate != 0);
  
                                let transaction = cached_request.maybe_finalize_malleable_package(
 -                                      cur_height, self, output_value, self.destination_script.clone(), logger
 +                                      cur_height, self, Amount::from_sat(output_value), self.destination_script.clone(), logger
                                ).unwrap();
                                assert!(predicted_weight >= transaction.0.weight().to_wu());
                                return Some((new_timer, new_feerate, OnchainClaim::Tx(transaction)));
                                        let package_target_feerate_sat_per_1000_weight = cached_request
                                                .compute_package_feerate(fee_estimator, conf_target, feerate_strategy);
                                        if let Some(input_amount_sat) = output.funding_amount {
 -                                              let fee_sat = input_amount_sat - tx.0.output.iter().map(|output| output.value).sum::<u64>();
 +                                              let fee_sat = input_amount_sat - tx.0.output.iter().map(|output| output.value.to_sat()).sum::<u64>();
                                                let commitment_tx_feerate_sat_per_1000_weight =
                                                        compute_feerate_sat_per_1000_weight(fee_sat, tx.0.weight().to_wu());
                                                if commitment_tx_feerate_sat_per_1000_weight >= package_target_feerate_sat_per_1000_weight {
                                                htlc: htlc.clone(),
                                                preimage: *preimage,
                                                counterparty_sig: counterparty_htlc_sig,
+                                               per_commitment_point: trusted_tx.per_commitment_point(),
                                        }
                                })
                };