X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Futil%2Ftest_channel_signer.rs;h=9c6a1e30224a14b21eff915b3bc3666f96e7b2d5;hb=74078c4bef90bbc4906bf8ed112ed9246f7df078;hp=2fb1c494f93f4141210225817fdf5f07ebc49644;hpb=e38916d83898adfa2184596b984c1131699ab7c6;p=rust-lightning diff --git a/lightning/src/util/test_channel_signer.rs b/lightning/src/util/test_channel_signer.rs index 2fb1c494..9c6a1e30 100644 --- a/lightning/src/util/test_channel_signer.rs +++ b/lightning/src/util/test_channel_signer.rs @@ -9,7 +9,8 @@ use crate::ln::channel::{ANCHOR_OUTPUT_VALUE_SATOSHI, MIN_CHAN_DUST_LIMIT_SATOSHIS}; use crate::ln::chan_utils::{HTLCOutputInCommitment, ChannelPublicKeys, HolderCommitmentTransaction, CommitmentTransaction, ChannelTransactionParameters, TrustedCommitmentTransaction, ClosingTransaction}; -use crate::ln::{chan_utils, msgs, PaymentPreimage}; +use crate::ln::channel_keys::{HtlcKey}; +use crate::ln::{msgs, PaymentPreimage}; use crate::sign::{WriteableEcdsaChannelSigner, InMemorySigner, ChannelSigner, EcdsaChannelSigner}; use crate::prelude::*; @@ -17,13 +18,15 @@ use core::cmp; use crate::sync::{Mutex, Arc}; #[cfg(test)] use crate::sync::MutexGuard; -use bitcoin::blockdata::transaction::{Transaction, EcdsaSighashType}; -use bitcoin::util::sighash; +use bitcoin::blockdata::transaction::Transaction; +use bitcoin::hashes::Hash; +use bitcoin::sighash; +use bitcoin::sighash::EcdsaSighashType; use bitcoin::secp256k1; use bitcoin::secp256k1::{SecretKey, PublicKey}; use bitcoin::secp256k1::{Secp256k1, ecdsa::Signature}; -use crate::events::bump_transaction::HTLCDescriptor; +use crate::sign::HTLCDescriptor; use crate::util::ser::{Writeable, Writer}; use crate::io::Error; use crate::ln::features::ChannelTypeFeatures; @@ -56,6 +59,9 @@ pub struct TestChannelSigner { /// Channel state used for policy enforcement pub state: Arc>, pub disable_revocation_policy_check: bool, + /// When `true` (the default), the signer will respond immediately with signatures. When `false`, + /// the signer will return an error indicating that it is unavailable. + pub available: Arc>, } impl PartialEq for TestChannelSigner { @@ -71,7 +77,8 @@ impl TestChannelSigner { Self { inner, state, - disable_revocation_policy_check: false + disable_revocation_policy_check: false, + available: Arc::new(Mutex::new(true)), } } @@ -84,16 +91,27 @@ impl TestChannelSigner { Self { inner, state, - disable_revocation_policy_check + disable_revocation_policy_check, + available: Arc::new(Mutex::new(true)), } } - pub fn channel_type_features(&self) -> &ChannelTypeFeatures { self.inner.channel_type_features() } + pub fn channel_type_features(&self) -> &ChannelTypeFeatures { self.inner.channel_type_features().unwrap() } #[cfg(test)] pub fn get_enforcement_state(&self) -> MutexGuard { self.state.lock().unwrap() } + + /// Marks the signer's availability. + /// + /// When `true`, methods are forwarded to the underlying signer as normal. When `false`, some + /// methods will return `Err` indicating that the signer is unavailable. Intended to be used for + /// testing asynchronous signing. + #[cfg(test)] + pub fn set_available(&self, available: bool) { + *self.available.lock().unwrap() = available; + } } impl ChannelSigner for TestChannelSigner { @@ -133,6 +151,9 @@ impl EcdsaChannelSigner for TestChannelSigner { self.verify_counterparty_commitment_tx(commitment_tx, secp_ctx); { + if !*self.available.lock().unwrap() { + 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; @@ -149,17 +170,20 @@ impl EcdsaChannelSigner for TestChannelSigner { } fn validate_counterparty_revocation(&self, idx: u64, _secret: &SecretKey) -> Result<(), ()> { + if !*self.available.lock().unwrap() { + return Err(()); + } let mut state = self.state.lock().unwrap(); assert!(idx == state.last_counterparty_revoked_commitment || idx == state.last_counterparty_revoked_commitment - 1, "expecting to validate the current or next counterparty revocation - trying {}, current {}", idx, state.last_counterparty_revoked_commitment); state.last_counterparty_revoked_commitment = idx; Ok(()) } - fn sign_holder_commitment_and_htlcs(&self, commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1) -> Result<(Signature, Vec), ()> { + fn sign_holder_commitment(&self, commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1) -> Result { + if !*self.available.lock().unwrap() { + return Err(()); + } let trusted_tx = self.verify_holder_commitment_tx(commitment_tx, secp_ctx); - let commitment_txid = trusted_tx.txid(); - let holder_csv = self.inner.counterparty_selected_contest_delay(); - let state = self.state.lock().unwrap(); let commitment_number = trusted_tx.commitment_number(); if state.last_holder_revoked_commitment - 1 != commitment_number && state.last_holder_revoked_commitment - 2 != commitment_number { @@ -168,33 +192,12 @@ impl EcdsaChannelSigner for TestChannelSigner { state.last_holder_revoked_commitment, commitment_number, self.inner.commitment_seed[0]) } } - - for (this_htlc, sig) in trusted_tx.htlcs().iter().zip(&commitment_tx.counterparty_htlc_sigs) { - assert!(this_htlc.transaction_output_index.is_some()); - let keys = trusted_tx.keys(); - let htlc_tx = chan_utils::build_htlc_transaction(&commitment_txid, trusted_tx.feerate_per_kw(), holder_csv, &this_htlc, self.channel_type_features(), &keys.broadcaster_delayed_payment_key, &keys.revocation_key); - - let htlc_redeemscript = chan_utils::get_htlc_redeemscript(&this_htlc, self.channel_type_features(), &keys); - - let sighash_type = if self.channel_type_features().supports_anchors_zero_fee_htlc_tx() { - EcdsaSighashType::SinglePlusAnyoneCanPay - } else { - EcdsaSighashType::All - }; - let sighash = hash_to_message!( - &sighash::SighashCache::new(&htlc_tx).segwit_signature_hash( - 0, &htlc_redeemscript, this_htlc.amount_msat / 1000, sighash_type, - ).unwrap()[..] - ); - secp_ctx.verify_ecdsa(&sighash, sig, &keys.countersignatory_htlc_key).unwrap(); - } - - Ok(self.inner.sign_holder_commitment_and_htlcs(commitment_tx, secp_ctx).unwrap()) + Ok(self.inner.sign_holder_commitment(commitment_tx, secp_ctx).unwrap()) } #[cfg(any(test,feature = "unsafe_revoked_tx_signing"))] - fn unsafe_sign_holder_commitment_and_htlcs(&self, commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1) -> Result<(Signature, Vec), ()> { - Ok(self.inner.unsafe_sign_holder_commitment_and_htlcs(commitment_tx, secp_ctx).unwrap()) + 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 { @@ -209,8 +212,35 @@ impl EcdsaChannelSigner for TestChannelSigner { &self, htlc_tx: &Transaction, input: usize, htlc_descriptor: &HTLCDescriptor, secp_ctx: &Secp256k1 ) -> Result { + let state = self.state.lock().unwrap(); + if state.last_holder_revoked_commitment - 1 != htlc_descriptor.per_commitment_number && + state.last_holder_revoked_commitment - 2 != htlc_descriptor.per_commitment_number + { + if !self.disable_revocation_policy_check { + panic!("can only sign the next two unrevoked commitment numbers, revoked={} vs requested={} for {}", + state.last_holder_revoked_commitment, htlc_descriptor.per_commitment_number, self.inner.commitment_seed[0]) + } + } assert_eq!(htlc_tx.input[input], htlc_descriptor.unsigned_tx_input()); assert_eq!(htlc_tx.output[input], htlc_descriptor.tx_output(secp_ctx)); + { + let witness_script = htlc_descriptor.witness_script(secp_ctx); + let sighash_type = if self.channel_type_features().supports_anchors_zero_fee_htlc_tx() { + EcdsaSighashType::SinglePlusAnyoneCanPay + } else { + EcdsaSighashType::All + }; + let sighash = &sighash::SighashCache::new(&*htlc_tx).segwit_signature_hash( + input, &witness_script, htlc_descriptor.htlc.amount_msat / 1000, sighash_type + ).unwrap(); + let countersignatory_htlc_key = HtlcKey::from_basepoint( + &secp_ctx, &self.inner.counterparty_pubkeys().unwrap().htlc_basepoint, &htlc_descriptor.per_commitment_point, + ); + + secp_ctx.verify_ecdsa( + &hash_to_message!(sighash.as_byte_array()), &htlc_descriptor.counterparty_sig, &countersignatory_htlc_key.to_public_key() + ).unwrap(); + } Ok(self.inner.sign_holder_htlc_transaction(htlc_tx, input, htlc_descriptor, secp_ctx).unwrap()) } @@ -219,7 +249,7 @@ impl EcdsaChannelSigner for TestChannelSigner { } fn sign_closing_transaction(&self, closing_tx: &ClosingTransaction, secp_ctx: &Secp256k1) -> Result { - closing_tx.verify(self.inner.funding_outpoint().into_bitcoin_outpoint()) + closing_tx.verify(self.inner.funding_outpoint().unwrap().into_bitcoin_outpoint()) .expect("derived different closing transaction"); Ok(self.inner.sign_closing_transaction(closing_tx, secp_ctx).unwrap()) } @@ -256,15 +286,17 @@ 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> { - commitment_tx.verify(&self.inner.get_channel_parameters().as_counterparty_broadcastable(), - self.inner.counterparty_pubkeys(), self.inner.pubkeys(), secp_ctx) - .expect("derived different per-tx keys or built transaction") + commitment_tx.verify( + &self.inner.get_channel_parameters().unwrap().as_counterparty_broadcastable(), + self.inner.counterparty_pubkeys().unwrap(), self.inner.pubkeys(), secp_ctx + ).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> { - commitment_tx.verify(&self.inner.get_channel_parameters().as_holder_broadcastable(), - self.inner.pubkeys(), self.inner.counterparty_pubkeys(), secp_ctx) - .expect("derived different per-tx keys or built transaction") + commitment_tx.verify( + &self.inner.get_channel_parameters().unwrap().as_holder_broadcastable(), + self.inner.pubkeys(), self.inner.counterparty_pubkeys().unwrap(), secp_ctx + ).expect("derived different per-tx keys or built transaction") } }