diff --git a/bitcoin/src/crypto/sighash.rs b/bitcoin/src/crypto/sighash.rs index de1767a4..de6170ff 100644 --- a/bitcoin/src/crypto/sighash.rs +++ b/bitcoin/src/crypto/sighash.rs @@ -582,8 +582,8 @@ impl> SighashCache { annex: Option, leaf_hash_code_separator: Option<(TapLeafHash, u32)>, sighash_type: TapSighashType, - ) -> Result<(), TaprootError> { - prevouts.check_all(self.tx.borrow())?; + ) -> Result<(), SigningDataError> { + prevouts.check_all(self.tx.borrow()).map_err(SigningDataError::sighash)?; let (sighash, anyone_can_pay) = sighash_type.split_anyonecanpay_flag(); @@ -608,8 +608,8 @@ impl> SighashCache { // sha_sequences (32): the SHA256 of the serialization of all input nSequence. if !anyone_can_pay { self.common_cache().prevouts.consensus_encode(writer)?; - self.taproot_cache(prevouts.get_all()?).amounts.consensus_encode(writer)?; - self.taproot_cache(prevouts.get_all()?).script_pubkeys.consensus_encode(writer)?; + self.taproot_cache(prevouts.get_all().map_err(SigningDataError::sighash)?).amounts.consensus_encode(writer)?; + self.taproot_cache(prevouts.get_all().map_err(SigningDataError::sighash)?).script_pubkeys.consensus_encode(writer)?; self.common_cache().sequences.consensus_encode(writer)?; } @@ -637,8 +637,8 @@ impl> SighashCache { // scriptPubKey (35): scriptPubKey of the previous output spent by this input, serialized as script inside CTxOut. Its size is always 35 bytes. // nSequence (4): nSequence of this input. if anyone_can_pay { - let txin = &self.tx.borrow().tx_in(input_index)?; - let previous_output = prevouts.get(input_index)?; + let txin = &self.tx.borrow().tx_in(input_index).map_err(SigningDataError::sighash)?; + let previous_output = prevouts.get(input_index).map_err(SigningDataError::sighash)?; txin.previous_output.consensus_encode(writer)?; previous_output.value.consensus_encode(writer)?; previous_output.script_pubkey.consensus_encode(writer)?; @@ -669,7 +669,7 @@ impl> SighashCache { .ok_or(TaprootError::SingleMissingOutput(SingleMissingOutputError { input_index, outputs_length: self.tx.borrow().output.len(), - }))? + })).map_err(SigningDataError::Sighash)? .consensus_encode(&mut enc)?; let hash = sha256::Hash::from_engine(enc); hash.consensus_encode(writer)?; @@ -705,7 +705,7 @@ impl> SighashCache { annex, leaf_hash_code_separator, sighash_type, - )?; + ).map_err(SigningDataError::unwrap_sighash)?; Ok(TapSighash::from_engine(enc)) } @@ -724,7 +724,7 @@ impl> SighashCache { None, None, sighash_type, - )?; + ).map_err(SigningDataError::unwrap_sighash)?; Ok(TapSighash::from_engine(enc)) } @@ -747,7 +747,7 @@ impl> SighashCache { None, Some((leaf_hash.into(), 0xFFFFFFFF)), sighash_type, - )?; + ).map_err(SigningDataError::unwrap_sighash)?; Ok(TapSighash::from_engine(enc)) } @@ -764,7 +764,7 @@ impl> SighashCache { script_code: &Script, value: Amount, sighash_type: EcdsaSighashType, - ) -> Result<(), SegwitV0Error> { + ) -> Result<(), SigningDataError> { let zero_hash = sha256d::Hash::all_zeros(); let (sighash, anyone_can_pay) = sighash_type.split_anyonecanpay_flag(); @@ -787,7 +787,7 @@ impl> SighashCache { } { - let txin = &self.tx.borrow().tx_in(input_index)?; + let txin = &self.tx.borrow().tx_in(input_index).map_err(SigningDataError::sighash)?; txin.previous_output.consensus_encode(writer)?; script_code.consensus_encode(writer)?; value.consensus_encode(writer)?; @@ -831,7 +831,7 @@ impl> SighashCache { &script_code, value, sighash_type, - )?; + ).map_err(SigningDataError::unwrap_sighash)?; Ok(SegwitV0Sighash::from_engine(enc)) } @@ -842,7 +842,7 @@ impl> SighashCache { witness_script: &Script, value: Amount, sighash_type: EcdsaSighashType, - ) -> Result { + ) -> Result { let mut enc = SegwitV0Sighash::engine(); self.segwit_v0_encode_signing_data_to( &mut enc, @@ -850,7 +850,7 @@ impl> SighashCache { witness_script, value, sighash_type, - )?; + ).map_err(SigningDataError::unwrap_sighash)?; Ok(SegwitV0Sighash::from_engine(enc)) } @@ -882,10 +882,10 @@ impl> SighashCache { input_index: usize, script_pubkey: &Script, sighash_type: U, - ) -> EncodeSigningDataResult { + ) -> EncodeSigningDataResult> { // Validate input_index. if let Err(e) = self.tx.borrow().tx_in(input_index) { - return EncodeSigningDataResult::WriteResult(Err(e.into())); + return EncodeSigningDataResult::WriteResult(Err(SigningDataError::Sighash(e))); } let sighash_type: u32 = sighash_type.into(); @@ -977,7 +977,7 @@ impl> SighashCache { script_pubkey, sighash_type, ) - .map_err(|e| LegacyError::Io(e.kind())), + .map_err(Into::into), ) } @@ -1013,10 +1013,7 @@ impl> SighashCache { { Ok(true) => Ok(LegacySighash::from_byte_array(UINT256_ONE)), Ok(false) => Ok(LegacySighash::from_engine(engine)), - Err(e) => match e { - LegacyError::InputsIndex(e) => Err(e), - LegacyError::Io(_) => unreachable!("engines don't error"), - }, + Err(e) => Err(e.unwrap_sighash()), } } @@ -1145,9 +1142,6 @@ impl<'a> Encodable for Annex<'a> { pub enum TaprootError { /// Index out of bounds when accessing transaction input vector. InputsIndex(transaction::InputsIndexError), - /// Can happen only when using `*_encode_signing_*` methods with custom writers, engines - /// like those used in `*_signature_hash` methods do not error. - Io(io::ErrorKind), /// Using `SIGHASH_SINGLE` requires an output at the same index is the input. SingleMissingOutput(SingleMissingOutputError), /// Prevouts size error. @@ -1166,7 +1160,6 @@ impl fmt::Display for TaprootError { match *self { InputsIndex(ref e) => write_err!(f, "inputs index"; e), - Io(error_kind) => write!(f, "write failed: {:?}", error_kind), SingleMissingOutput(ref e) => write_err!(f, "sighash single"; e), PrevoutsSize(ref e) => write_err!(f, "prevouts size"; e), PrevoutsIndex(ref e) => write_err!(f, "prevouts index"; e), @@ -1187,7 +1180,7 @@ impl std::error::Error for TaprootError { PrevoutsSize(ref e) => Some(e), PrevoutsIndex(ref e) => Some(e), PrevoutsKind(ref e) => Some(e), - Io(_) | InvalidSighashType(_) => None, + InvalidSighashType(_) => None, } } } @@ -1196,10 +1189,6 @@ impl From for TaprootError { fn from(e: transaction::InputsIndexError) -> Self { Self::InputsIndex(e) } } -impl From for TaprootError { - fn from(e: io::Error) -> Self { Self::Io(e.kind()) } -} - impl From for TaprootError { fn from(e: PrevoutsSizeError) -> Self { Self::PrevoutsSize(e) } } @@ -1217,11 +1206,17 @@ impl From for TaprootError { #[non_exhaustive] pub enum P2wpkhError { /// Error computing the sighash. - Sighash(SegwitV0Error), + Sighash(transaction::InputsIndexError), /// Script is not a witness program for a p2wpkh output. NotP2wpkhScript, } +impl From for P2wpkhError { + fn from(value: transaction::InputsIndexError) -> Self { + P2wpkhError::Sighash(value) + } +} + impl fmt::Display for P2wpkhError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use P2wpkhError::*; @@ -1245,52 +1240,6 @@ impl std::error::Error for P2wpkhError { } } -impl From for P2wpkhError { - fn from(e: SegwitV0Error) -> Self { Self::Sighash(e) } -} - -/// Error computing the segwit sighash. -#[derive(Debug, Clone, PartialEq, Eq)] -#[non_exhaustive] -pub enum SegwitV0Error { - /// Index out of bounds when accessing transaction input vector. - InputsIndex(transaction::InputsIndexError), - /// Writer errored during consensus encoding. - Io(io::ErrorKind), -} - -impl fmt::Display for SegwitV0Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use SegwitV0Error::*; - - match *self { - InputsIndex(ref e) => write_err!(f, "inputs index"; e), - Io(error_kind) => write!(f, "write failed: {:?}", error_kind), - } - } -} - - -#[cfg(feature = "std")] -impl std::error::Error for SegwitV0Error { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - use SegwitV0Error::*; - - match *self { - InputsIndex(ref e) => Some(e), - Io(_) => None, - } - } -} - -impl From for SegwitV0Error { - fn from(e: transaction::InputsIndexError) -> Self { Self::InputsIndex(e) } -} - -impl From for SegwitV0Error { - fn from(e: io::Error) -> Self { Self::Io(e.kind()) } -} - /// Using `SIGHASH_SINGLE` requires an output at the same index as the input. #[derive(Debug, Clone, PartialEq, Eq)] #[non_exhaustive] @@ -1350,47 +1299,6 @@ impl std::error::Error for AnnexError { } } -/// Error computing the legacy sighash. -#[derive(Debug, Clone, PartialEq, Eq)] -#[non_exhaustive] -pub enum LegacyError { - /// Index out of bounds when accessing transaction input vector. - InputsIndex(transaction::InputsIndexError), - /// Writer errored during consensus encoding. - Io(io::ErrorKind), -} - -impl fmt::Display for LegacyError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use LegacyError::*; - - match *self { - InputsIndex(ref e) => write_err!(f, "inputs index"; e), - Io(error_kind) => write!(f, "write failed: {:?}", error_kind), - } - } -} - -#[cfg(feature = "std")] -impl std::error::Error for LegacyError { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - use LegacyError::*; - - match *self { - InputsIndex(ref e) => Some(e), - Io(_) => None, - } - } -} - -impl From for LegacyError { - fn from(e: transaction::InputsIndexError) -> Self { Self::InputsIndex(e) } -} - -impl From for LegacyError { - fn from(e: io::Error) -> Self { Self::Io(e.kind()) } -} - fn is_invalid_use_of_sighash_single(sighash: u32, input_index: usize, outputs_len: usize) -> bool { let ty = EcdsaSighashType::from_consensus(sighash); ty == EcdsaSighashType::Single && input_index >= outputs_len @@ -1464,6 +1372,59 @@ impl EncodeSigningDataResult { } } +/// Error returned when writing signing data fails. +#[derive(Debug)] +pub enum SigningDataError { + /// Can happen only when using `*_encode_signing_*` methods with custom writers, engines + /// like those used in `*_signature_hash` methods do not error. + Io(io::Error), + /// An argument to the called sighash function was invalid. + Sighash(E), +} + +impl SigningDataError { + /// Returns the sighash variant, panicking if it's IO. + /// + /// This is used when encoding to hash engine when we know that IO doesn't fail. + fn unwrap_sighash(self) -> E { + match self { + Self::Sighash(error) => error, + Self::Io(error) => panic!("hash engine error {}", error), + } + } + + fn sighash>(error: E2) -> Self { + Self::Sighash(error.into()) + } +} + +// We cannot simultaneously impl `From`. it was determined that this alternative requires less +// manual `map_err` calls. +impl From for SigningDataError { + fn from(value: io::Error) -> Self { + Self::Io(value) + } +} + +impl fmt::Display for SigningDataError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::Io(error) => write_err!(f, "failed to write sighash data"; error), + Self::Sighash(error) => write_err!(f, "failed to compute sighash data"; error), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for SigningDataError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + SigningDataError::Io(error) => Some(error), + SigningDataError::Sighash(error) => Some(error), + } + } +} + #[cfg(test)] mod tests { use std::str::FromStr; diff --git a/bitcoin/src/psbt/mod.rs b/bitcoin/src/psbt/mod.rs index 23df1b0e..22c8f49a 100644 --- a/bitcoin/src/psbt/mod.rs +++ b/bitcoin/src/psbt/mod.rs @@ -23,7 +23,7 @@ use internals::write_err; use secp256k1::{Message, Secp256k1, Signing}; use crate::bip32::{self, KeySource, Xpriv, Xpub}; -use crate::blockdata::transaction::{Transaction, TxOut}; +use crate::blockdata::transaction::{self, Transaction, TxOut}; use crate::crypto::ecdsa; use crate::crypto::key::{PrivateKey, PublicKey}; use crate::prelude::*; @@ -436,7 +436,7 @@ impl Psbt { let witness_script = input.witness_script.as_ref().ok_or(SignError::MissingWitnessScript)?; let sighash = - cache.p2wsh_signature_hash(input_index, witness_script, utxo.value, hash_ty)?; + cache.p2wsh_signature_hash(input_index, witness_script, utxo.value, hash_ty).map_err(SignError::SegwitV0Sighash)?; Ok((Message::from_digest(sighash.to_byte_array()), hash_ty)) } Tr => { @@ -771,7 +771,7 @@ pub enum SignError { /// The `scriptPubkey` is not a P2WPKH script. NotWpkh, /// Sighash computation error (segwit v0 input). - SegwitV0Sighash(sighash::SegwitV0Error), + SegwitV0Sighash(transaction::InputsIndexError), /// Sighash computation error (p2wpkh input). P2wpkhSighash(sighash::P2wpkhError), /// Unable to determine the output type. @@ -834,10 +834,6 @@ impl std::error::Error for SignError { } } -impl From for SignError { - fn from(e: sighash::SegwitV0Error) -> Self { Self::SegwitV0Sighash(e) } -} - impl From for SignError { fn from(e: sighash::P2wpkhError) -> Self { Self::P2wpkhSighash(e) } }