hashes: split Hash trait into two

This commit is contained in:
Andrew Poelstra 2024-06-16 13:40:51 +00:00
parent 1fe4c63986
commit 73dcc79763
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
12 changed files with 97 additions and 74 deletions

View File

@ -9,7 +9,7 @@ use core::ops::Index;
use core::str::FromStr; use core::str::FromStr;
use core::{fmt, slice}; use core::{fmt, slice};
use hashes::{hash160, hash_newtype, sha512, Hash, HashEngine, Hmac, HmacEngine}; use hashes::{hash160, hash_newtype, sha512, GeneralHash, HashEngine, Hmac, HmacEngine};
use internals::{impl_array_newtype, write_err}; use internals::{impl_array_newtype, write_err};
use io::Write; use io::Write;
use secp256k1::{Secp256k1, XOnlyPublicKey}; use secp256k1::{Secp256k1, XOnlyPublicKey};

View File

@ -16,7 +16,7 @@
use core::{fmt, mem}; use core::{fmt, mem};
use hashes::{sha256, sha256d, Hash}; use hashes::{sha256, sha256d, GeneralHash, Hash};
use hex::error::{InvalidCharError, OddLengthStringError}; use hex::error::{InvalidCharError, OddLengthStringError};
use internals::write_err; use internals::write_err;
use io::{BufRead, Cursor, Read, Write}; use io::{BufRead, Cursor, Read, Write};
@ -768,7 +768,7 @@ impl Decodable for Box<[u8]> {
/// Does a double-SHA256 on `data` and returns the first 4 bytes. /// Does a double-SHA256 on `data` and returns the first 4 bytes.
fn sha2_checksum(data: &[u8]) -> [u8; 4] { fn sha2_checksum(data: &[u8]) -> [u8; 4] {
let checksum = <sha256d::Hash as Hash>::hash(data); let checksum = <sha256d::Hash as GeneralHash>::hash(data);
[checksum[0], checksum[1], checksum[2], checksum[3]] [checksum[0], checksum[1], checksum[2], checksum[3]]
} }

View File

@ -121,7 +121,7 @@ macro_rules! psbt_insert_hash_pair {
match $slf.$map.entry(key_val) { match $slf.$map.entry(key_val) {
btree_map::Entry::Vacant(empty_key) => { btree_map::Entry::Vacant(empty_key) => {
let val: Vec<u8> = Deserialize::deserialize(&$raw_value)?; let val: Vec<u8> = Deserialize::deserialize(&$raw_value)?;
if <$hash as hashes::Hash>::hash(&val) != key_val { if <$hash as hashes::GeneralHash>::hash(&val) != key_val {
return Err(psbt::Error::InvalidPreimageHashPair { return Err(psbt::Error::InvalidPreimageHashPair {
preimage: val.into_boxed_slice(), preimage: val.into_boxed_slice(),
hash: Box::from(key_val.borrow()), hash: Box::from(key_val.borrow()),

View File

@ -34,7 +34,8 @@ mod tests {
#[test] #[test]
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
fn test() { fn test() {
use crate::{hash160, Hash, HashEngine}; use super::Hash;
use crate::{hash160, HashEngine};
#[derive(Clone)] #[derive(Clone)]
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
@ -111,7 +112,7 @@ mod tests {
mod benches { mod benches {
use test::Bencher; use test::Bencher;
use crate::{hash160, Hash, HashEngine}; use crate::{hash160, GeneralHash as _, Hash as _, HashEngine};
#[bench] #[bench]
pub fn hash160_10(bh: &mut Bencher) { pub fn hash160_10(bh: &mut Bencher) {

View File

@ -11,7 +11,7 @@ use alloc::vec;
use alloc::vec::Vec; use alloc::vec::Vec;
use core::fmt; use core::fmt;
use crate::{Hash, HashEngine, Hmac, HmacEngine}; use crate::{GeneralHash, HashEngine, Hmac, HmacEngine};
/// Output keying material max length multiple. /// Output keying material max length multiple.
const MAX_OUTPUT_BLOCKS: usize = 255; const MAX_OUTPUT_BLOCKS: usize = 255;
@ -32,12 +32,12 @@ impl fmt::Display for MaxLengthError {
impl std::error::Error for MaxLengthError {} impl std::error::Error for MaxLengthError {}
/// HMAC-based Extract-and-Expand Key Derivation Function (HKDF). /// HMAC-based Extract-and-Expand Key Derivation Function (HKDF).
pub struct Hkdf<T: Hash> { pub struct Hkdf<T: GeneralHash> {
/// Pseudorandom key based on the extract step. /// Pseudorandom key based on the extract step.
prk: Hmac<T>, prk: Hmac<T>,
} }
impl<T: Hash> Hkdf<T> { impl<T: GeneralHash> Hkdf<T> {
/// Initialize a HKDF by performing the extract step. /// Initialize a HKDF by performing the extract step.
pub fn new(salt: &[u8], ikm: &[u8]) -> Self { pub fn new(salt: &[u8], ikm: &[u8]) -> Self {
let mut hmac_engine: HmacEngine<T> = HmacEngine::new(salt); let mut hmac_engine: HmacEngine<T> = HmacEngine::new(salt);

View File

@ -12,15 +12,15 @@ use core::{convert, fmt, str};
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde::{Deserialize, Deserializer, Serialize, Serializer};
use crate::{FromSliceError, Hash, HashEngine}; use crate::{FromSliceError, GeneralHash, Hash, HashEngine};
/// A hash computed from a RFC 2104 HMAC. Parameterized by the underlying hash function. /// A hash computed from a RFC 2104 HMAC. Parameterized by the underlying hash function.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)] #[repr(transparent)]
pub struct Hmac<T: Hash>(T); pub struct Hmac<T: GeneralHash>(T);
#[cfg(feature = "schemars")] #[cfg(feature = "schemars")]
impl<T: Hash + schemars::JsonSchema> schemars::JsonSchema for Hmac<T> { impl<T: GeneralHash + schemars::JsonSchema> schemars::JsonSchema for Hmac<T> {
fn is_referenceable() -> bool { <T as schemars::JsonSchema>::is_referenceable() } fn is_referenceable() -> bool { <T as schemars::JsonSchema>::is_referenceable() }
fn schema_name() -> std::string::String { <T as schemars::JsonSchema>::schema_name() } fn schema_name() -> std::string::String { <T as schemars::JsonSchema>::schema_name() }
@ -30,13 +30,13 @@ impl<T: Hash + schemars::JsonSchema> schemars::JsonSchema for Hmac<T> {
} }
} }
impl<T: Hash + str::FromStr> str::FromStr for Hmac<T> { impl<T: GeneralHash + str::FromStr> str::FromStr for Hmac<T> {
type Err = <T as str::FromStr>::Err; type Err = <T as str::FromStr>::Err;
fn from_str(s: &str) -> Result<Self, Self::Err> { Ok(Hmac(str::FromStr::from_str(s)?)) } fn from_str(s: &str) -> Result<Self, Self::Err> { Ok(Hmac(str::FromStr::from_str(s)?)) }
} }
/// Pair of underlying hash midstates which represent the current state of an `HmacEngine`. /// Pair of underlying hash midstates which represent the current state of an `HmacEngine`.
pub struct HmacMidState<T: Hash> { pub struct HmacMidState<T: GeneralHash> {
/// Midstate of the inner hash engine /// Midstate of the inner hash engine
pub inner: <T::Engine as HashEngine>::MidState, pub inner: <T::Engine as HashEngine>::MidState,
/// Midstate of the outer hash engine /// Midstate of the outer hash engine
@ -45,16 +45,16 @@ pub struct HmacMidState<T: Hash> {
/// Pair of underlying hash engines, used for the inner and outer hash of HMAC. /// Pair of underlying hash engines, used for the inner and outer hash of HMAC.
#[derive(Clone)] #[derive(Clone)]
pub struct HmacEngine<T: Hash> { pub struct HmacEngine<T: GeneralHash> {
iengine: T::Engine, iengine: T::Engine,
oengine: T::Engine, oengine: T::Engine,
} }
impl<T: Hash> Default for HmacEngine<T> { impl<T: GeneralHash> Default for HmacEngine<T> {
fn default() -> Self { HmacEngine::new(&[]) } fn default() -> Self { HmacEngine::new(&[]) }
} }
impl<T: Hash> HmacEngine<T> { impl<T: GeneralHash> HmacEngine<T> {
/// Constructs a new keyed HMAC from `key`. /// Constructs a new keyed HMAC from `key`.
/// ///
/// We only support underlying hashes whose block sizes are ≤ 128 bytes. /// We only support underlying hashes whose block sizes are ≤ 128 bytes.
@ -67,10 +67,13 @@ impl<T: Hash> HmacEngine<T> {
let mut ipad = [0x36u8; 128]; let mut ipad = [0x36u8; 128];
let mut opad = [0x5cu8; 128]; let mut opad = [0x5cu8; 128];
let mut ret = HmacEngine { iengine: <T as Hash>::engine(), oengine: <T as Hash>::engine() }; let mut ret = HmacEngine {
iengine: <T as GeneralHash>::engine(),
oengine: <T as GeneralHash>::engine(),
};
if key.len() > T::Engine::BLOCK_SIZE { if key.len() > T::Engine::BLOCK_SIZE {
let hash = <T as Hash>::hash(key); let hash = <T as GeneralHash>::hash(key);
for (b_i, b_h) in ipad.iter_mut().zip(hash.as_ref()) { for (b_i, b_h) in ipad.iter_mut().zip(hash.as_ref()) {
*b_i ^= *b_h; *b_i ^= *b_h;
} }
@ -97,7 +100,7 @@ impl<T: Hash> HmacEngine<T> {
} }
} }
impl<T: Hash> HashEngine for HmacEngine<T> { impl<T: GeneralHash> HashEngine for HmacEngine<T> {
type MidState = HmacMidState<T>; type MidState = HmacMidState<T>;
fn midstate(&self) -> Self::MidState { fn midstate(&self) -> Self::MidState {
@ -111,25 +114,24 @@ impl<T: Hash> HashEngine for HmacEngine<T> {
fn input(&mut self, buf: &[u8]) { self.iengine.input(buf) } fn input(&mut self, buf: &[u8]) { self.iengine.input(buf) }
} }
impl<T: Hash> fmt::Debug for Hmac<T> { impl<T: GeneralHash> fmt::Debug for Hmac<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(&self.0, f) } fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(&self.0, f) }
} }
impl<T: Hash> fmt::Display for Hmac<T> { impl<T: GeneralHash> fmt::Display for Hmac<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) } fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
} }
impl<T: Hash> fmt::LowerHex for Hmac<T> { impl<T: GeneralHash> fmt::LowerHex for Hmac<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(&self.0, f) } fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(&self.0, f) }
} }
impl<T: Hash> convert::AsRef<[u8]> for Hmac<T> { impl<T: GeneralHash> convert::AsRef<[u8]> for Hmac<T> {
fn as_ref(&self) -> &[u8] { self.0.as_ref() } fn as_ref(&self) -> &[u8] { self.0.as_ref() }
} }
impl<T: Hash> Hash for Hmac<T> { impl<T: GeneralHash> GeneralHash for Hmac<T> {
type Engine = HmacEngine<T>; type Engine = HmacEngine<T>;
type Bytes = T::Bytes;
fn from_engine(mut e: HmacEngine<T>) -> Hmac<T> { fn from_engine(mut e: HmacEngine<T>) -> Hmac<T> {
let ihash = T::from_engine(e.iengine); let ihash = T::from_engine(e.iengine);
@ -137,7 +139,10 @@ impl<T: Hash> Hash for Hmac<T> {
let ohash = T::from_engine(e.oengine); let ohash = T::from_engine(e.oengine);
Hmac(ohash) Hmac(ohash)
} }
}
impl<T: GeneralHash> Hash for Hmac<T> {
type Bytes = T::Bytes;
const LEN: usize = T::LEN; const LEN: usize = T::LEN;
fn from_slice(sl: &[u8]) -> Result<Hmac<T>, FromSliceError> { T::from_slice(sl).map(Hmac) } fn from_slice(sl: &[u8]) -> Result<Hmac<T>, FromSliceError> { T::from_slice(sl).map(Hmac) }
@ -150,14 +155,14 @@ impl<T: Hash> Hash for Hmac<T> {
} }
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
impl<T: Hash + Serialize> Serialize for Hmac<T> { impl<T: GeneralHash + Serialize> Serialize for Hmac<T> {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> { fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
Serialize::serialize(&self.0, s) Serialize::serialize(&self.0, s)
} }
} }
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
impl<'de, T: Hash + Deserialize<'de>> Deserialize<'de> for Hmac<T> { impl<'de, T: GeneralHash + Deserialize<'de>> Deserialize<'de> for Hmac<T> {
fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Hmac<T>, D::Error> { fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Hmac<T>, D::Error> {
let bytes = Deserialize::deserialize(d)?; let bytes = Deserialize::deserialize(d)?;
Ok(Hmac(bytes)) Ok(Hmac(bytes))
@ -169,7 +174,7 @@ mod tests {
#[test] #[test]
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
fn test() { fn test() {
use crate::{sha256, Hash, HashEngine, Hmac, HmacEngine}; use crate::{sha256, GeneralHash as _, Hash as _, HashEngine, Hmac, HmacEngine};
#[derive(Clone)] #[derive(Clone)]
struct Test { struct Test {
@ -297,7 +302,7 @@ mod tests {
fn hmac_sha512_serde() { fn hmac_sha512_serde() {
use serde_test::{assert_tokens, Configure, Token}; use serde_test::{assert_tokens, Configure, Token};
use crate::{sha512, Hash, Hmac}; use crate::{sha512, Hash as _, Hmac};
#[rustfmt::skip] #[rustfmt::skip]
static HASH_BYTES: [u8; 64] = [ static HASH_BYTES: [u8; 64] = [
@ -327,7 +332,7 @@ mod tests {
mod benches { mod benches {
use test::Bencher; use test::Bencher;
use crate::{sha256, Hash, HashEngine, Hmac}; use crate::{sha256, GeneralHash as _, HashEngine, Hmac};
#[bench] #[bench]
pub fn hmac_sha256_10(bh: &mut Bencher) { pub fn hmac_sha256_10(bh: &mut Bencher) {

View File

@ -60,14 +60,16 @@ impl_write!(
Ok(buf.len()) Ok(buf.len())
}, },
|_us| { Ok(()) }, |_us| { Ok(()) },
T: crate::Hash T: crate::GeneralHash
); );
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use bitcoin_io::Write; use bitcoin_io::Write;
use crate::{hash160, hmac, ripemd160, sha1, sha256, sha256d, sha512, siphash24, Hash}; use crate::{
hash160, hmac, ripemd160, sha1, sha256, sha256d, sha512, siphash24, GeneralHash as _,
};
macro_rules! write_test { macro_rules! write_test {
($mod:ident, $exp_empty:expr, $exp_256:expr, $exp_64k:expr,) => { ($mod:ident, $exp_empty:expr, $exp_256:expr, $exp_64k:expr,) => {

View File

@ -99,16 +99,19 @@ macro_rules! hash_trait_impls {
} }
} }
impl<$($gen: $gent),*> crate::Hash for Hash<$($gen),*> { impl<$($gen: $gent),*> crate::GeneralHash for Hash<$($gen),*> {
type Engine = HashEngine; type Engine = HashEngine;
type Bytes = [u8; $bits / 8];
const LEN: usize = $bits / 8;
const DISPLAY_BACKWARD: bool = $reverse;
fn engine() -> HashEngine { Self::engine() } fn engine() -> HashEngine { Self::engine() }
fn from_engine(e: HashEngine) -> Hash<$($gen),*> { Self::from_engine(e) } fn from_engine(e: HashEngine) -> Hash<$($gen),*> { Self::from_engine(e) }
}
impl<$($gen: $gent),*> crate::Hash for Hash<$($gen),*> {
type Bytes = [u8; $bits / 8];
const LEN: usize = $bits / 8;
const DISPLAY_BACKWARD: bool = $reverse;
fn from_slice(sl: &[u8]) -> $crate::_export::_core::result::Result<Hash<$($gen),*>, FromSliceError> { fn from_slice(sl: &[u8]) -> $crate::_export::_core::result::Result<Hash<$($gen),*>, FromSliceError> {
Self::from_slice(sl) Self::from_slice(sl)

View File

@ -12,7 +12,7 @@
//! Hashing a single byte slice or a string: //! Hashing a single byte slice or a string:
//! //!
//! ```rust //! ```rust
//! use bitcoin_hashes::{sha256, Hash as _}; //! use bitcoin_hashes::{sha256, GeneralHash as _};
//! //!
//! let bytes = [0u8; 5]; //! let bytes = [0u8; 5];
//! let hash_of_bytes = sha256::Hash::hash(&bytes); //! let hash_of_bytes = sha256::Hash::hash(&bytes);
@ -23,7 +23,7 @@
//! Hashing content from a reader: //! Hashing content from a reader:
//! //!
//! ```rust //! ```rust
//! use bitcoin_hashes::{sha256, Hash as _}; //! use bitcoin_hashes::{sha256, GeneralHash as _};
//! //!
//! #[cfg(std)] //! #[cfg(std)]
//! # fn main() -> std::io::Result<()> { //! # fn main() -> std::io::Result<()> {
@ -147,40 +147,19 @@ pub trait HashEngine: Clone + Default {
fn n_bytes_hashed(&self) -> usize; fn n_bytes_hashed(&self) -> usize;
} }
/// Trait which applies to hashes of all types. /// Trait describing hash digests which can be constructed by hashing arbitrary data.
pub trait Hash: pub trait GeneralHash: Hash {
Copy
+ Clone
+ PartialEq
+ Eq
+ PartialOrd
+ Ord
+ hash::Hash
+ fmt::Debug
+ fmt::Display
+ fmt::LowerHex
+ convert::AsRef<[u8]>
{
/// A hashing engine which bytes can be serialized into. It is expected /// A hashing engine which bytes can be serialized into. It is expected
/// to implement the `io::Write` trait, and to never return errors under /// to implement the `io::Write` trait, and to never return errors under
/// any conditions. /// any conditions.
type Engine: HashEngine; type Engine: HashEngine;
/// The byte array that represents the hash internally.
type Bytes: hex::FromHex + Copy;
/// Constructs a new engine. /// Constructs a new engine.
fn engine() -> Self::Engine { Self::Engine::default() } fn engine() -> Self::Engine { Self::Engine::default() }
/// Produces a hash from the current state of a given engine. /// Produces a hash from the current state of a given engine.
fn from_engine(e: Self::Engine) -> Self; fn from_engine(e: Self::Engine) -> Self;
/// Length of the hash, in bytes.
const LEN: usize;
/// Copies a byte slice into a hash object.
fn from_slice(sl: &[u8]) -> Result<Self, FromSliceError>;
/// Hashes some bytes. /// Hashes some bytes.
fn hash(data: &[u8]) -> Self { fn hash(data: &[u8]) -> Self {
let mut engine = Self::engine(); let mut engine = Self::engine();
@ -200,6 +179,30 @@ pub trait Hash:
} }
Self::from_engine(engine) Self::from_engine(engine)
} }
}
/// Trait which applies to hashes of all types.
pub trait Hash:
Copy
+ Clone
+ PartialEq
+ Eq
+ PartialOrd
+ Ord
+ hash::Hash
+ fmt::Debug
+ fmt::Display
+ fmt::LowerHex
+ convert::AsRef<[u8]>
{
/// The byte array that represents the hash internally.
type Bytes: hex::FromHex + Copy;
/// Length of the hash, in bytes.
const LEN: usize;
/// Copies a byte slice into a hash object.
fn from_slice(sl: &[u8]) -> Result<Self, FromSliceError>;
/// Flag indicating whether user-visible serializations of this hash /// Flag indicating whether user-visible serializations of this hash
/// should be backward. For some reason Satoshi decided this should be /// should be backward. For some reason Satoshi decided this should be

View File

@ -101,7 +101,7 @@ impl crate::HashEngine for HashEngine {
impl Hash { impl Hash {
/// Iterate the sha256 algorithm to turn a sha256 hash into a sha256d hash /// Iterate the sha256 algorithm to turn a sha256 hash into a sha256d hash
pub fn hash_again(&self) -> sha256d::Hash { pub fn hash_again(&self) -> sha256d::Hash {
crate::Hash::from_byte_array(<Self as crate::Hash>::hash(&self.0).0) crate::Hash::from_byte_array(<Self as crate::GeneralHash>::hash(&self.0).0)
} }
/// Computes hash from `bytes` in `const` context. /// Computes hash from `bytes` in `const` context.

View File

@ -210,13 +210,13 @@ macro_rules! hash_newtype {
} }
/// Constructs a new engine. /// Constructs a new engine.
pub fn engine() -> <$hash as $crate::Hash>::Engine { pub fn engine() -> <$hash as $crate::GeneralHash>::Engine {
<$hash as $crate::Hash>::engine() <$hash as $crate::GeneralHash>::engine()
} }
/// Produces a hash from the current state of a given engine. /// Produces a hash from the current state of a given engine.
pub fn from_engine(e: <$hash as $crate::Hash>::Engine) -> Self { pub fn from_engine(e: <$hash as $crate::GeneralHash>::Engine) -> Self {
Self::from(<$hash as $crate::Hash>::from_engine(e)) Self::from(<$hash as $crate::GeneralHash>::from_engine(e))
} }
/// Copies a byte slice into a hash object. /// Copies a byte slice into a hash object.
@ -279,16 +279,12 @@ macro_rules! hash_newtype {
} }
impl $crate::Hash for $newtype { impl $crate::Hash for $newtype {
type Engine = <$hash as $crate::Hash>::Engine;
type Bytes = <$hash as $crate::Hash>::Bytes; type Bytes = <$hash as $crate::Hash>::Bytes;
const LEN: usize = <$hash as $crate::Hash>::LEN; const LEN: usize = <$hash as $crate::Hash>::LEN;
const DISPLAY_BACKWARD: bool = $crate::hash_newtype_get_direction!($hash, $(#[$($type_attrs)*])*); const DISPLAY_BACKWARD: bool = $crate::hash_newtype_get_direction!($hash, $(#[$($type_attrs)*])*);
fn engine() -> <$hash as $crate::Hash>::Engine { Self::engine() } #[inline]
fn from_engine(e: <$hash as $crate::Hash>::Engine) -> $newtype { Self::from_engine(e) }
fn from_slice(sl: &[u8]) -> $crate::_export::_core::result::Result<$newtype, $crate::FromSliceError> { fn from_slice(sl: &[u8]) -> $crate::_export::_core::result::Result<$newtype, $crate::FromSliceError> {
Self::from_slice(sl) Self::from_slice(sl)
} }
@ -300,6 +296,19 @@ macro_rules! hash_newtype {
fn from_byte_array(bytes: Self::Bytes) -> Self { Self::from_byte_array(bytes) } fn from_byte_array(bytes: Self::Bytes) -> Self { Self::from_byte_array(bytes) }
} }
// To be dropped in the next commit
impl $crate::GeneralHash for $newtype {
type Engine = <$hash as $crate::GeneralHash>::Engine;
fn engine() -> Self::Engine {
<$hash as $crate::GeneralHash>::engine()
}
fn from_engine(e: <$hash as $crate::GeneralHash>::Engine) -> $newtype {
Self::from_engine(e)
}
}
impl $crate::_export::_core::str::FromStr for $newtype { impl $crate::_export::_core::str::FromStr for $newtype {
type Err = $crate::hex::HexToArrayError; type Err = $crate::hex::HexToArrayError;
fn from_str(s: &str) -> $crate::_export::_core::result::Result<$newtype, Self::Err> { fn from_str(s: &str) -> $crate::_export::_core::result::Result<$newtype, Self::Err> {

View File

@ -2,7 +2,7 @@
use bitcoin_hashes::{ use bitcoin_hashes::{
hash160, ripemd160, sha1, sha256, sha256d, sha256t, sha384, sha512, sha512_256, siphash24, hash160, ripemd160, sha1, sha256, sha256d, sha256t, sha384, sha512, sha512_256, siphash24,
Hash as _, HashEngine as _, Hmac, HmacEngine, GeneralHash as _, HashEngine as _, Hmac, HmacEngine,
}; };
const DATA: &str = "arbitrary data to hash as a regression test"; const DATA: &str = "arbitrary data to hash as a regression test";