Add inherent functions to hashes

Currently we have a trait `Hash` that is required for `Hmac`, `Hkdf`,
and other use cases. However, it is unegonomic for users who just want
to do a simple hash to have to import the trait.

Add inherent functions to all hash types including those created with
the new wrapper type macros.

This patch introduces some duplicate code but we are trying to make
progress in the hashes API re-write. We can come back and de-dublicate
later.

Includes making `to_byte_array`,`from_byte_array`, `as_byte_array`, and
`all_zeros` const where easily possible.
This commit is contained in:
Tobin C. Harding 2024-06-11 12:39:49 +10:00
parent cf870bbd7b
commit 6b7d02e5ae
No known key found for this signature in database
GPG Key ID: 40BF9E4C269D6607
45 changed files with 248 additions and 131 deletions

View File

@ -35,7 +35,7 @@ use core::{fmt, str};
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub use std::{string::String, vec::Vec}; pub use std::{string::String, vec::Vec};
use hashes::{sha256d, Hash}; use hashes::sha256d;
use crate::error::{IncorrectChecksumError, TooShortError}; use crate::error::{IncorrectChecksumError, TooShortError};

View File

@ -26,7 +26,6 @@ use std::collections::BTreeMap;
use std::str::FromStr; use std::str::FromStr;
use bitcoin::bip32::{ChildNumber, DerivationPath, Fingerprint, IntoDerivationPath, Xpriv, Xpub}; use bitcoin::bip32::{ChildNumber, DerivationPath, Fingerprint, IntoDerivationPath, Xpriv, Xpub};
use bitcoin::hashes::Hash;
use bitcoin::locktime::absolute; use bitcoin::locktime::absolute;
use bitcoin::psbt::Input; use bitcoin::psbt::Input;
use bitcoin::secp256k1::{Secp256k1, Signing}; use bitcoin::secp256k1::{Secp256k1, Signing};

View File

@ -4,7 +4,6 @@
use std::str::FromStr; use std::str::FromStr;
use bitcoin::hashes::Hash;
use bitcoin::locktime::absolute; use bitcoin::locktime::absolute;
use bitcoin::secp256k1::{rand, Message, Secp256k1, SecretKey, Signing}; use bitcoin::secp256k1::{rand, Message, Secp256k1, SecretKey, Signing};
use bitcoin::sighash::{EcdsaSighashType, SighashCache}; use bitcoin::sighash::{EcdsaSighashType, SighashCache};

View File

@ -4,7 +4,6 @@
use std::str::FromStr; use std::str::FromStr;
use bitcoin::hashes::Hash;
use bitcoin::key::{Keypair, TapTweak, TweakedKeypair, UntweakedPublicKey}; use bitcoin::key::{Keypair, TapTweak, TweakedKeypair, UntweakedPublicKey};
use bitcoin::locktime::absolute; use bitcoin::locktime::absolute;
use bitcoin::secp256k1::{rand, Message, Secp256k1, SecretKey, Signing, Verification}; use bitcoin::secp256k1::{rand, Message, Secp256k1, SecretKey, Signing, Verification};

View File

@ -24,7 +24,6 @@ use std::collections::BTreeMap;
use std::str::FromStr; use std::str::FromStr;
use bitcoin::bip32::{ChildNumber, DerivationPath, Fingerprint, IntoDerivationPath, Xpriv, Xpub}; use bitcoin::bip32::{ChildNumber, DerivationPath, Fingerprint, IntoDerivationPath, Xpriv, Xpub};
use bitcoin::hashes::Hash;
use bitcoin::key::UntweakedPublicKey; use bitcoin::key::UntweakedPublicKey;
use bitcoin::locktime::absolute; use bitcoin::locktime::absolute;
use bitcoin::psbt::Input; use bitcoin::psbt::Input;

View File

@ -33,7 +33,7 @@ use core::marker::PhantomData;
use core::str::FromStr; use core::str::FromStr;
use bech32::primitives::hrp::Hrp; use bech32::primitives::hrp::Hrp;
use hashes::{sha256, Hash, HashEngine}; use hashes::{sha256, HashEngine};
use secp256k1::{Secp256k1, Verification, XOnlyPublicKey}; use secp256k1::{Secp256k1, Verification, XOnlyPublicKey};
use crate::blockdata::constants::{ use crate::blockdata::constants::{

View File

@ -8,7 +8,7 @@ use core::{convert, fmt, mem};
#[cfg(feature = "std")] #[cfg(feature = "std")]
use std::error; use std::error;
use hashes::{sha256, siphash24, Hash}; use hashes::{sha256, siphash24};
use internals::impl_array_newtype; use internals::impl_array_newtype;
use io::{BufRead, Write}; use io::{BufRead, Write};

View File

@ -40,7 +40,7 @@
use core::cmp::{self, Ordering}; use core::cmp::{self, Ordering};
use core::fmt::{self, Display, Formatter}; use core::fmt::{self, Display, Formatter};
use hashes::{sha256d, siphash24, Hash}; use hashes::{sha256d, siphash24};
use internals::write_err; use internals::write_err;
use io::{BufRead, Write}; use io::{BufRead, Write};

View File

@ -9,7 +9,7 @@
use core::fmt; use core::fmt;
use hashes::{sha256d, Hash, HashEngine}; use hashes::{sha256d, HashEngine};
use io::{BufRead, Write}; use io::{BufRead, Write};
use super::Weight; use super::Weight;

View File

@ -6,7 +6,7 @@
//! consensus code. In particular, it defines the genesis block and its //! consensus code. In particular, it defines the genesis block and its
//! single transaction. //! single transaction.
use hashes::{sha256d, Hash}; use hashes::sha256d;
use internals::impl_array_newtype; use internals::impl_array_newtype;
use crate::blockdata::block::{self, Block}; use crate::blockdata::block::{self, Block};

View File

@ -5,7 +5,6 @@ use core::ops::{
Bound, Index, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive, Bound, Index, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive,
}; };
use hashes::Hash;
use secp256k1::{Secp256k1, Verification}; use secp256k1::{Secp256k1, Verification};
use super::PushBytes; use super::PushBytes;

View File

@ -2,7 +2,6 @@
use core::str::FromStr; use core::str::FromStr;
use hashes::Hash;
use hex_lit::hex; use hex_lit::hex;
use super::*; use super::*;

View File

@ -9,7 +9,6 @@
use core::fmt; use core::fmt;
use hashes::Hash as _;
use internals::array_vec::ArrayVec; use internals::array_vec::ArrayVec;
use secp256k1::{Secp256k1, Verification}; use secp256k1::{Secp256k1, Verification};

View File

@ -12,7 +12,7 @@
use core::{cmp, fmt, str}; use core::{cmp, fmt, str};
use hashes::{sha256d, Hash}; use hashes::sha256d;
use internals::write_err; use internals::write_err;
use io::{BufRead, Write}; use io::{BufRead, Write};
use units::parse::{self, PrefixedHexError, UnprefixedHexError}; use units::parse::{self, PrefixedHexError, UnprefixedHexError};

View File

@ -9,7 +9,7 @@ use core::fmt::{self, Write as _};
use core::ops; use core::ops;
use core::str::FromStr; use core::str::FromStr;
use hashes::{hash160, Hash}; use hashes::hash160;
use hex::{FromHex, HexToArrayError}; use hex::{FromHex, HexToArrayError};
use internals::array_vec::ArrayVec; use internals::array_vec::ArrayVec;
use internals::write_err; use internals::write_err;

View File

@ -13,7 +13,7 @@
use core::{fmt, str}; use core::{fmt, str};
use hashes::{hash_newtype, sha256, sha256d, sha256t_hash_newtype, Hash}; use hashes::{hash_newtype, sha256, sha256d, sha256t_hash_newtype};
use internals::write_err; use internals::write_err;
use io::Write; use io::Write;

View File

@ -13,7 +13,6 @@ pub use crate::{
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::hashes::Hash;
use crate::{ use crate::{
LegacySighash, PubkeyHash, ScriptHash, SegwitV0Sighash, TapSighash, WPubkeyHash, LegacySighash, PubkeyHash, ScriptHash, SegwitV0Sighash, TapSighash, WPubkeyHash,
WScriptHash, XKeyIdentifier, WScriptHash, XKeyIdentifier,

View File

@ -185,7 +185,6 @@ macro_rules! impl_hashencode {
impl $crate::consensus::Decodable for $hashtype { impl $crate::consensus::Decodable for $hashtype {
fn consensus_decode<R: $crate::io::BufRead + ?Sized>(r: &mut R) -> core::result::Result<Self, $crate::consensus::encode::Error> { fn consensus_decode<R: $crate::io::BufRead + ?Sized>(r: &mut R) -> core::result::Result<Self, $crate::consensus::encode::Error> {
use $crate::hashes::Hash;
Ok(Self::from_byte_array(<<$hashtype as $crate::hashes::Hash>::Bytes>::consensus_decode(r)?)) Ok(Self::from_byte_array(<<$hashtype as $crate::hashes::Hash>::Bytes>::consensus_decode(r)?))
} }
} }
@ -199,14 +198,12 @@ macro_rules! impl_asref_push_bytes {
$( $(
impl AsRef<$crate::blockdata::script::PushBytes> for $hashtype { impl AsRef<$crate::blockdata::script::PushBytes> for $hashtype {
fn as_ref(&self) -> &$crate::blockdata::script::PushBytes { fn as_ref(&self) -> &$crate::blockdata::script::PushBytes {
use $crate::hashes::Hash;
self.as_byte_array().into() self.as_byte_array().into()
} }
} }
impl From<$hashtype> for $crate::blockdata::script::PushBytesBuf { impl From<$hashtype> for $crate::blockdata::script::PushBytesBuf {
fn from(hash: $hashtype) -> Self { fn from(hash: $hashtype) -> Self {
use $crate::hashes::Hash;
hash.as_byte_array().into() hash.as_byte_array().into()
} }
} }

View File

@ -40,7 +40,6 @@
use core::fmt; use core::fmt;
use hashes::Hash;
use io::{BufRead, Write}; use io::{BufRead, Write};
use self::MerkleBlockError::*; use self::MerkleBlockError::*;

View File

@ -7,7 +7,7 @@
use core::{fmt, iter}; use core::{fmt, iter};
use hashes::{sha256d, Hash}; use hashes::sha256d;
use io::{BufRead, Write}; use io::{BufRead, Write};
use crate::blockdata::{block, transaction}; use crate::blockdata::{block, transaction};

View File

@ -5,7 +5,7 @@
//! This module describes network messages which are used for passing //! This module describes network messages which are used for passing
//! Bitcoin data (blocks and transactions) around. //! Bitcoin data (blocks and transactions) around.
use hashes::{sha256d, Hash as _}; use hashes::sha256d;
use io::{BufRead, Write}; use io::{BufRead, Write};
use crate::blockdata::block::BlockHash; use crate::blockdata::block::BlockHash;
@ -144,7 +144,6 @@ impl_consensus_encoding!(GetHeadersMessage, version, locator_hashes, stop_hash);
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use hashes::Hash;
use hex::test_hex_unwrap as hex; use hex::test_hex_unwrap as hex;
use super::*; use super::*;

View File

@ -217,7 +217,6 @@ impl Target {
/// to the target. /// to the target.
#[cfg_attr(all(test, mutate), mutate)] #[cfg_attr(all(test, mutate), mutate)]
pub fn is_met_by(&self, hash: BlockHash) -> bool { pub fn is_met_by(&self, hash: BlockHash) -> bool {
use hashes::Hash;
let hash = U256::from_le_bytes(hash.to_byte_array()); let hash = U256::from_le_bytes(hash.to_byte_array());
hash <= self.0 hash <= self.0
} }
@ -1784,8 +1783,6 @@ mod tests {
#[test] #[test]
fn compact_target_from_upwards_difficulty_adjustment_using_headers() { fn compact_target_from_upwards_difficulty_adjustment_using_headers() {
use hashes::Hash;
use crate::block::Version; use crate::block::Version;
use crate::constants::genesis_block; use crate::constants::genesis_block;
use crate::TxMerkleNode; use crate::TxMerkleNode;
@ -1809,8 +1806,6 @@ mod tests {
#[test] #[test]
fn compact_target_from_downwards_difficulty_adjustment_using_headers() { fn compact_target_from_downwards_difficulty_adjustment_using_headers() {
use hashes::Hash;
use crate::block::Version; use crate::block::Version;
use crate::TxMerkleNode; use crate::TxMerkleNode;
let params = Params::new(crate::Network::Signet); let params = Params::new(crate::Network::Signet);
@ -1896,8 +1891,6 @@ mod tests {
fn target_is_met_by_for_target_equals_hash() { fn target_is_met_by_for_target_equals_hash() {
use std::str::FromStr; use std::str::FromStr;
use hashes::Hash;
let hash = let hash =
BlockHash::from_str("ef537f25c895bfa782526529a9b63d97aa631564d5d789c2b765448c8635fb6c") BlockHash::from_str("ef537f25c895bfa782526529a9b63d97aa631564d5d789c2b765448c8635fb6c")
.expect("failed to parse block hash"); .expect("failed to parse block hash");

View File

@ -1205,7 +1205,7 @@ pub use self::display_from_str::PsbtParseError;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use hashes::{hash160, ripemd160, sha256, Hash}; use hashes::{hash160, ripemd160, sha256};
use hex::{test_hex_unwrap as hex, FromHex}; use hex::{test_hex_unwrap as hex, FromHex};
#[cfg(feature = "rand-std")] #[cfg(feature = "rand-std")]
use secp256k1::{All, SecretKey}; use secp256k1::{All, SecretKey};

View File

@ -5,7 +5,7 @@
//! Traits to serialize PSBT values to and from raw bytes //! Traits to serialize PSBT values to and from raw bytes
//! according to the BIP-174 specification. //! according to the BIP-174 specification.
use hashes::{hash160, ripemd160, sha256, sha256d, Hash}; use hashes::{hash160, ripemd160, sha256, sha256d};
use secp256k1::XOnlyPublicKey; use secp256k1::XOnlyPublicKey;
use super::map::{Input, Map, Output, PsbtSighashType}; use super::map::{Input, Map, Output, PsbtSighashType};

View File

@ -5,7 +5,7 @@
//! This module provides signature related functions including secp256k1 signature recovery when //! This module provides signature related functions including secp256k1 signature recovery when
//! library is used with the `secp-recovery` feature. //! library is used with the `secp-recovery` feature.
use hashes::{sha256d, Hash, HashEngine}; use hashes::{sha256d, HashEngine};
use crate::consensus::{encode, Encodable}; use crate::consensus::{encode, Encodable};
@ -21,7 +21,7 @@ pub const BITCOIN_SIGNED_MSG_PREFIX: &[u8] = b"\x18Bitcoin Signed Message:\n";
mod message_signing { mod message_signing {
use core::fmt; use core::fmt;
use hashes::{sha256d, Hash}; use hashes::sha256d;
use internals::write_err; use internals::write_err;
use secp256k1::ecdsa::{RecoverableSignature, RecoveryId}; use secp256k1::ecdsa::{RecoverableSignature, RecoveryId};

View File

@ -11,7 +11,7 @@ use core::cmp::Reverse;
use core::fmt; use core::fmt;
use core::iter::FusedIterator; use core::iter::FusedIterator;
use hashes::{sha256t_hash_newtype, Hash, HashEngine}; use hashes::{sha256t_hash_newtype, HashEngine};
use internals::write_err; use internals::write_err;
use io::Write; use io::Write;
use secp256k1::{Scalar, Secp256k1}; use secp256k1::{Scalar, Secp256k1};

View File

@ -30,7 +30,7 @@ use bitcoin::bip32::{ChildNumber, KeySource, Xpriv, Xpub};
use bitcoin::blockdata::locktime::{absolute, relative}; use bitcoin::blockdata::locktime::{absolute, relative};
use bitcoin::blockdata::witness::Witness; use bitcoin::blockdata::witness::Witness;
use bitcoin::consensus::encode::deserialize; use bitcoin::consensus::encode::deserialize;
use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d, Hash}; use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d};
use bitcoin::hex::FromHex; use bitcoin::hex::FromHex;
use bitcoin::psbt::raw::{self, Key, Pair, ProprietaryKey}; use bitcoin::psbt::raw::{self, Key, Pair, ProprietaryKey};
use bitcoin::psbt::{Input, Output, Psbt, PsbtSighashType}; use bitcoin::psbt::{Input, Output, Psbt, PsbtSighashType};

View File

@ -1,4 +1,4 @@
use bitcoin::hashes::{ripemd160, Hash, HashEngine}; use bitcoin::hashes::{ripemd160, HashEngine};
use honggfuzz::fuzz; use honggfuzz::fuzz;
fn do_test(data: &[u8]) { fn do_test(data: &[u8]) {

View File

@ -1,4 +1,4 @@
use bitcoin::hashes::{sha1, Hash, HashEngine}; use bitcoin::hashes::{sha1, HashEngine};
use honggfuzz::fuzz; use honggfuzz::fuzz;
fn do_test(data: &[u8]) { fn do_test(data: &[u8]) {

View File

@ -1,4 +1,4 @@
use bitcoin::hashes::{sha256, Hash, HashEngine}; use bitcoin::hashes::{sha256, HashEngine};
use honggfuzz::fuzz; use honggfuzz::fuzz;
fn do_test(data: &[u8]) { fn do_test(data: &[u8]) {

View File

@ -1,4 +1,4 @@
use bitcoin::hashes::{sha512, Hash, HashEngine}; use bitcoin::hashes::{sha512, HashEngine};
use honggfuzz::fuzz; use honggfuzz::fuzz;
fn do_test(data: &[u8]) { fn do_test(data: &[u8]) {

View File

@ -1,4 +1,4 @@
use bitcoin::hashes::{sha512_256, Hash, HashEngine}; use bitcoin::hashes::{sha512_256, HashEngine};
use honggfuzz::fuzz; use honggfuzz::fuzz;
fn do_test(data: &[u8]) { fn do_test(data: &[u8]) {

View File

@ -21,8 +21,6 @@ crate::internal_macros::hash_type! {
type HashEngine = sha256::HashEngine; type HashEngine = sha256::HashEngine;
fn from_engine(e: HashEngine) -> Hash { fn from_engine(e: HashEngine) -> Hash {
use crate::Hash as _;
let sha2 = sha256::Hash::from_engine(e); let sha2 = sha256::Hash::from_engine(e);
let rmd = ripemd160::Hash::hash(&sha2[..]); let rmd = ripemd160::Hash::hash(&sha2[..]);
@ -92,7 +90,7 @@ mod tests {
fn ripemd_serde() { fn ripemd_serde() {
use serde_test::{assert_tokens, Configure, Token}; use serde_test::{assert_tokens, Configure, Token};
use crate::{hash160, Hash}; use crate::hash160;
#[rustfmt::skip] #[rustfmt::skip]
static HASH_BYTES: [u8; 20] = [ static HASH_BYTES: [u8; 20] = [

View File

@ -63,17 +63,14 @@ pub(crate) use arr_newtype_fmt_impl;
/// ///
/// * There must be a free-standing `fn from_engine(HashEngine) -> Hash` in the scope /// * There must be a free-standing `fn from_engine(HashEngine) -> Hash` in the scope
/// * `fn internal_new([u8; $bits / 8]) -> Self` must exist on `Hash` /// * `fn internal_new([u8; $bits / 8]) -> Self` must exist on `Hash`
/// * `fn internal_engine() -> HashEngine` must exist on `Hash`
/// ///
/// `from_engine` obviously implements the finalization algorithm. /// `from_engine` obviously implements the finalization algorithm.
/// `internal_new` is required so that types with more than one field are constructible.
/// `internal_engine` is required to initialize the engine for given hash type.
macro_rules! hash_trait_impls { macro_rules! hash_trait_impls {
($bits:expr, $reverse:expr $(, $gen:ident: $gent:ident)*) => { ($bits:expr, $reverse:expr $(, $gen:ident: $gent:ident)*) => {
impl<$($gen: $gent),*> $crate::_export::_core::str::FromStr for Hash<$($gen),*> { impl<$($gen: $gent),*> $crate::_export::_core::str::FromStr for Hash<$($gen),*> {
type Err = $crate::hex::HexToArrayError; type Err = $crate::hex::HexToArrayError;
fn from_str(s: &str) -> $crate::_export::_core::result::Result<Self, Self::Err> { fn from_str(s: &str) -> $crate::_export::_core::result::Result<Self, Self::Err> {
use $crate::{Hash, hex::{FromHex}}; use $crate::{hex::{FromHex}};
let mut bytes = <[u8; $bits / 8]>::from_hex(s)?; let mut bytes = <[u8; $bits / 8]>::from_hex(s)?;
if $reverse { if $reverse {
@ -109,39 +106,21 @@ macro_rules! hash_trait_impls {
const LEN: usize = $bits / 8; const LEN: usize = $bits / 8;
const DISPLAY_BACKWARD: bool = $reverse; const DISPLAY_BACKWARD: bool = $reverse;
fn engine() -> Self::Engine { fn engine() -> HashEngine { Self::engine() }
Self::internal_engine()
}
fn from_engine(e: HashEngine) -> Hash<$($gen),*> { fn from_engine(e: HashEngine) -> Hash<$($gen),*> { Self::from_engine(e) }
from_engine(e)
}
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> {
if sl.len() != $bits / 8 { Self::from_slice(sl)
Err(FromSliceError{expected: Self::LEN, got: sl.len()})
} else {
let mut ret = [0; $bits / 8];
ret.copy_from_slice(sl);
Ok(Self::internal_new(ret))
}
} }
fn to_byte_array(self) -> Self::Bytes { fn to_byte_array(self) -> Self::Bytes { self.to_byte_array() }
self.0
}
fn as_byte_array(&self) -> &Self::Bytes { fn as_byte_array(&self) -> &Self::Bytes { self.as_byte_array() }
&self.0
}
fn from_byte_array(bytes: Self::Bytes) -> Self { fn from_byte_array(bytes: Self::Bytes) -> Self { Self::from_byte_array(bytes) }
Self::internal_new(bytes)
}
fn all_zeros() -> Self { fn all_zeros() -> Self { Self::all_zeros() }
Hash::internal_new([0x00; $bits / 8])
}
} }
} }
} }
@ -149,8 +128,8 @@ pub(crate) use hash_trait_impls;
/// Creates a type called `Hash` and implements standard interface for it. /// Creates a type called `Hash` and implements standard interface for it.
/// ///
/// The created type will have all standard derives, `Hash` impl and implementation of /// The created type has a single field and will have all standard derives as well as an
/// `internal_engine` returning default. The created type has a single field. /// implementation of [`crate::Hash`].
/// ///
/// Arguments: /// Arguments:
/// ///
@ -169,9 +148,7 @@ macro_rules! hash_type {
pub struct Hash([u8; $bits / 8]); pub struct Hash([u8; $bits / 8]);
impl Hash { impl Hash {
fn internal_new(arr: [u8; $bits / 8]) -> Self { Hash(arr) } const fn internal_new(arr: [u8; $bits / 8]) -> Self { Hash(arr) }
fn internal_engine() -> HashEngine { Default::default() }
/// Zero cost conversion between a fixed length byte array shared reference and /// Zero cost conversion between a fixed length byte array shared reference and
/// a shared reference to this Hash type. /// a shared reference to this Hash type.
@ -186,6 +163,68 @@ macro_rules! hash_type {
// Safety: Sound because Self is #[repr(transparent)] containing [u8; $bits / 8] // Safety: Sound because Self is #[repr(transparent)] containing [u8; $bits / 8]
unsafe { &mut *(bytes as *mut _ as *mut Self) } unsafe { &mut *(bytes as *mut _ as *mut Self) }
} }
/// Constructs a new engine.
pub fn engine() -> HashEngine { Default::default() }
/// Produces a hash from the current state of a given engine.
pub fn from_engine(e: HashEngine) -> Hash { from_engine(e) }
/// Copies a byte slice into a hash object.
pub fn from_slice(sl: &[u8]) -> $crate::_export::_core::result::Result<Hash, FromSliceError> {
if sl.len() != $bits / 8 {
Err(FromSliceError{expected: $bits / 8, got: sl.len()})
} else {
let mut ret = [0; $bits / 8];
ret.copy_from_slice(sl);
Ok(Self::internal_new(ret))
}
}
/// Hashes some bytes.
#[allow(clippy::self_named_constructors)] // Hash is a noun and a verb.
pub fn hash(data: &[u8]) -> Self {
use $crate::HashEngine;
let mut engine = Self::engine();
engine.input(data);
Self::from_engine(engine)
}
/// Hashes all the byte slices retrieved from the iterator together.
pub fn hash_byte_chunks<B, I>(byte_slices: I) -> Self
where
B: AsRef<[u8]>,
I: IntoIterator<Item = B>,
{
use $crate::HashEngine;
let mut engine = Self::engine();
for slice in byte_slices {
engine.input(slice.as_ref());
}
Self::from_engine(engine)
}
/// Returns the underlying byte array.
pub const fn to_byte_array(self) -> [u8; $bits / 8] { self.0 }
/// Returns a reference to the underlying byte array.
pub const fn as_byte_array(&self) -> &[u8; $bits / 8] { &self.0 }
/// Constructs a hash from the underlying byte array.
pub const fn from_byte_array(bytes: [u8; $bits / 8]) -> Self {
Self::internal_new(bytes)
}
/// Returns an all zero hash.
///
/// An all zeros hash is a made up construct because there is not a known input that can create
/// it, however it is used in various places in Bitcoin e.g., the Bitcoin genesis block's
/// previous blockhash and the coinbase transaction's outpoint txid.
pub const fn all_zeros() -> Self {
Hash::internal_new([0x00; $bits / 8])
}
} }
#[cfg(feature = "schemars")] #[cfg(feature = "schemars")]

View File

@ -249,7 +249,7 @@ impl std::error::Error for FromSliceError {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{sha256d, Hash}; use crate::sha256d;
hash_newtype! { hash_newtype! {
/// A test newtype /// A test newtype

View File

@ -412,7 +412,7 @@ mod tests {
fn test() { fn test() {
use std::convert::TryFrom; use std::convert::TryFrom;
use crate::{ripemd160, Hash, HashEngine}; use crate::{ripemd160, HashEngine};
#[derive(Clone)] #[derive(Clone)]
struct Test { struct Test {
@ -507,7 +507,7 @@ mod tests {
fn ripemd_serde() { fn ripemd_serde() {
use serde_test::{assert_tokens, Configure, Token}; use serde_test::{assert_tokens, Configure, Token};
use crate::{ripemd160, Hash}; use crate::ripemd160;
#[rustfmt::skip] #[rustfmt::skip]
static HASH_BYTES: [u8; 20] = [ static HASH_BYTES: [u8; 20] = [

View File

@ -129,7 +129,7 @@ mod tests {
#[test] #[test]
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
fn test() { fn test() {
use crate::{sha1, Hash, HashEngine}; use crate::{sha1, HashEngine};
#[derive(Clone)] #[derive(Clone)]
struct Test { struct Test {
@ -199,7 +199,7 @@ mod tests {
fn sha1_serde() { fn sha1_serde() {
use serde_test::{assert_tokens, Configure, Token}; use serde_test::{assert_tokens, Configure, Token};
use crate::{sha1, Hash}; use crate::sha1;
#[rustfmt::skip] #[rustfmt::skip]
static HASH_BYTES: [u8; 20] = [ static HASH_BYTES: [u8; 20] = [

View File

@ -154,7 +154,7 @@ impl Midstate {
} }
/// Unwraps the [`Midstate`] and returns the underlying byte array. /// Unwraps the [`Midstate`] and returns the underlying byte array.
pub fn to_byte_array(self) -> [u8; 32] { self.0 } pub const fn to_byte_array(self) -> [u8; 32] { self.0 }
/// Creates midstate for tagged hashes. /// Creates midstate for tagged hashes.
/// ///
@ -815,7 +815,7 @@ impl HashEngine {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::{sha256, Hash as _, HashEngine}; use crate::{sha256, HashEngine};
#[test] #[test]
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]

View File

@ -16,8 +16,6 @@ crate::internal_macros::hash_type! {
type HashEngine = sha256::HashEngine; type HashEngine = sha256::HashEngine;
fn from_engine(e: sha256::HashEngine) -> Hash { fn from_engine(e: sha256::HashEngine) -> Hash {
use crate::Hash as _;
let sha2 = sha256::Hash::from_engine(e); let sha2 = sha256::Hash::from_engine(e);
let sha2d = sha256::Hash::hash(&sha2[..]); let sha2d = sha256::Hash::hash(&sha2[..]);
@ -28,7 +26,7 @@ fn from_engine(e: sha256::HashEngine) -> Hash {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{sha256d, Hash as _}; use crate::sha256d;
#[test] #[test]
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]

View File

@ -7,7 +7,7 @@ use core::marker::PhantomData;
use core::ops::Index; use core::ops::Index;
use core::slice::SliceIndex; use core::slice::SliceIndex;
use crate::{sha256, FromSliceError}; use crate::{sha256, FromSliceError, HashEngine as _};
type HashEngine = sha256::HashEngine; type HashEngine = sha256::HashEngine;
@ -39,8 +39,6 @@ impl<T: Tag> schemars::JsonSchema for Hash<T> {
impl<T: Tag> Hash<T> { impl<T: Tag> Hash<T> {
fn internal_new(arr: [u8; 32]) -> Self { Hash(arr, Default::default()) } fn internal_new(arr: [u8; 32]) -> Self { Hash(arr, Default::default()) }
fn internal_engine() -> HashEngine { T::engine() }
/// Zero cost conversion between a fixed length byte array shared reference and /// Zero cost conversion between a fixed length byte array shared reference and
/// a shared reference to this Hash type. /// a shared reference to this Hash type.
pub fn from_bytes_ref(bytes: &[u8; 32]) -> &Self { pub fn from_bytes_ref(bytes: &[u8; 32]) -> &Self {
@ -54,6 +52,68 @@ impl<T: Tag> Hash<T> {
// Safety: Sound because Self is #[repr(transparent)] containing [u8; 32] // Safety: Sound because Self is #[repr(transparent)] containing [u8; 32]
unsafe { &mut *(bytes as *mut _ as *mut Self) } unsafe { &mut *(bytes as *mut _ as *mut Self) }
} }
/// Constructs a new engine.
pub fn engine() -> HashEngine { T::engine() }
/// Produces a hash from the current state of a given engine.
pub fn from_engine(e: HashEngine) -> Hash<T> {
from_engine(e)
}
/// Copies a byte slice into a hash object.
pub fn from_slice(sl: &[u8]) -> Result<Hash<T>, FromSliceError> {
if sl.len() != 32 {
Err(FromSliceError{expected: 32, got: sl.len()})
} else {
let mut ret = [0; 32];
ret.copy_from_slice(sl);
Ok(Self::internal_new(ret))
}
}
/// Hashes some bytes.
#[allow(clippy::self_named_constructors)] // Hash is a noun and a verb.
pub fn hash(data: &[u8]) -> Self {
use crate::HashEngine;
let mut engine = Self::engine();
engine.input(data);
Self::from_engine(engine)
}
/// Hashes all the byte slices retrieved from the iterator together.
pub fn hash_byte_chunks<B, I>(byte_slices: I) -> Self
where
B: AsRef<[u8]>,
I: IntoIterator<Item = B>,
{
let mut engine = Self::engine();
for slice in byte_slices {
engine.input(slice.as_ref());
}
Self::from_engine(engine)
}
/// Returns the underlying byte array.
pub fn to_byte_array(self) -> [u8; 32] { self.0 }
/// Returns a reference to the underlying byte array.
pub fn as_byte_array(&self) -> &[u8; 32] { &self.0 }
/// Constructs a hash from the underlying byte array.
pub fn from_byte_array(bytes: [u8; 32]) -> Self {
Self::internal_new(bytes)
}
/// Returns an all zero hash.
///
/// An all zeros hash is a made up construct because there is not a known input that can create
/// it, however it is used in various places in Bitcoin e.g., the Bitcoin genesis block's
/// previous blockhash and the coinbase transaction's outpoint txid.
pub fn all_zeros() -> Self {
Hash::internal_new([0x00; 32])
}
} }
impl<T: Tag> Copy for Hash<T> {} impl<T: Tag> Copy for Hash<T> {}
@ -82,8 +142,6 @@ impl<T: Tag> core::hash::Hash for Hash<T> {
crate::internal_macros::hash_trait_impls!(256, false, T: Tag); crate::internal_macros::hash_trait_impls!(256, false, T: Tag);
fn from_engine<T: Tag>(e: sha256::HashEngine) -> Hash<T> { fn from_engine<T: Tag>(e: sha256::HashEngine) -> Hash<T> {
use crate::Hash as _;
Hash::from_byte_array(sha256::Hash::from_engine(e).to_byte_array()) Hash::from_byte_array(sha256::Hash::from_engine(e).to_byte_array())
} }
@ -176,8 +234,6 @@ macro_rules! sha256t_hash_newtype_tag_constructor {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
#[cfg(feature = "alloc")]
use crate::Hash;
use crate::{sha256, sha256t}; use crate::{sha256, sha256t};
const TEST_MIDSTATE: [u8; 32] = [ const TEST_MIDSTATE: [u8; 32] = [

View File

@ -47,7 +47,7 @@ mod tests {
#[test] #[test]
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
fn test() { fn test() {
use crate::{sha384, Hash, HashEngine}; use crate::{sha384, HashEngine};
#[derive(Clone)] #[derive(Clone)]
struct Test { struct Test {

View File

@ -307,7 +307,7 @@ mod tests {
#[test] #[test]
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
fn test() { fn test() {
use crate::{sha512, Hash, HashEngine}; use crate::{sha512, HashEngine};
#[derive(Clone)] #[derive(Clone)]
struct Test { struct Test {
@ -386,7 +386,7 @@ mod tests {
fn sha512_serde() { fn sha512_serde() {
use serde_test::{assert_tokens, Configure, Token}; use serde_test::{assert_tokens, Configure, Token};
use crate::{sha512, Hash}; use crate::sha512;
#[rustfmt::skip] #[rustfmt::skip]
static HASH_BYTES: [u8; 64] = [ static HASH_BYTES: [u8; 64] = [

View File

@ -57,7 +57,7 @@ mod tests {
#[test] #[test]
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
fn test() { fn test() {
use crate::{sha512_256, Hash, HashEngine}; use crate::{sha512_256, HashEngine};
#[derive(Clone)] #[derive(Clone)]
struct Test { struct Test {

View File

@ -6,7 +6,7 @@ use core::ops::Index;
use core::slice::SliceIndex; use core::slice::SliceIndex;
use core::{cmp, mem, ptr}; use core::{cmp, mem, ptr};
use crate::{FromSliceError, Hash as _, HashEngine as _}; use crate::{FromSliceError, HashEngine as _};
crate::internal_macros::hash_type! { crate::internal_macros::hash_type! {
64, 64,

View File

@ -192,24 +192,88 @@ macro_rules! hash_newtype {
$crate::serde_impl!($newtype, <$newtype as $crate::Hash>::LEN); $crate::serde_impl!($newtype, <$newtype as $crate::Hash>::LEN);
$crate::borrow_slice_impl!($newtype); $crate::borrow_slice_impl!($newtype);
#[allow(unused)] // Private wrapper types may not need all functions.
impl $newtype { impl $newtype {
/// Creates this wrapper type from the inner hash type. /// Creates this wrapper type from the inner hash type.
#[allow(unused)] // the user of macro may not need this
pub fn from_raw_hash(inner: $hash) -> $newtype { pub fn from_raw_hash(inner: $hash) -> $newtype {
$newtype(inner) $newtype(inner)
} }
/// Returns the inner hash (sha256, sh256d etc.). /// Returns the inner hash (sha256, sh256d etc.).
#[allow(unused)] // the user of macro may not need this
pub fn to_raw_hash(self) -> $hash { pub fn to_raw_hash(self) -> $hash {
self.0 self.0
} }
/// Returns a reference to the inner hash (sha256, sh256d etc.). /// Returns a reference to the inner hash (sha256, sh256d etc.).
#[allow(unused)] // the user of macro may not need this
pub fn as_raw_hash(&self) -> &$hash { pub fn as_raw_hash(&self) -> &$hash {
&self.0 &self.0
} }
/// Constructs a new engine.
pub fn engine() -> <$hash as $crate::Hash>::Engine {
<$hash as $crate::Hash>::engine()
}
/// Produces a hash from the current state of a given engine.
pub fn from_engine(e: <$hash as $crate::Hash>::Engine) -> Self {
Self::from(<$hash as $crate::Hash>::from_engine(e))
}
/// Copies a byte slice into a hash object.
pub fn from_slice(sl: &[u8]) -> $crate::_export::_core::result::Result<$newtype, $crate::FromSliceError> {
Ok($newtype(<$hash as $crate::Hash>::from_slice(sl)?))
}
/// Hashes some bytes.
#[allow(unused)] // the user of macro may not need this
pub fn hash(data: &[u8]) -> Self {
use $crate::HashEngine;
let mut engine = Self::engine();
engine.input(data);
Self::from_engine(engine)
}
/// Hashes all the byte slices retrieved from the iterator together.
pub fn hash_byte_chunks<B, I>(byte_slices: I) -> Self
where
B: AsRef<[u8]>,
I: IntoIterator<Item = B>,
{
use $crate::HashEngine;
let mut engine = Self::engine();
for slice in byte_slices {
engine.input(slice.as_ref());
}
Self::from_engine(engine)
}
/// Returns the underlying byte array.
pub fn to_byte_array(self) -> <$hash as $crate::Hash>::Bytes {
self.0.to_byte_array()
}
/// Returns a reference to the underlying byte array.
pub fn as_byte_array(&self) -> &<$hash as $crate::Hash>::Bytes {
self.0.as_byte_array()
}
/// Constructs a hash from the underlying byte array.
pub fn from_byte_array(bytes: <$hash as $crate::Hash>::Bytes) -> Self {
$newtype(<$hash as $crate::Hash>::from_byte_array(bytes))
}
/// Returns an all zero hash.
///
/// An all zeros hash is a made up construct because there is not a known input that can create
/// it, however it is used in various places in Bitcoin e.g., the Bitcoin genesis block's
/// previous blockhash and the coinbase transaction's outpoint txid.
pub fn all_zeros() -> Self {
let zeros = <$hash>::all_zeros();
$newtype(zeros)
}
} }
impl $crate::_export::_core::convert::From<$hash> for $newtype { impl $crate::_export::_core::convert::From<$hash> for $newtype {
@ -232,45 +296,27 @@ macro_rules! hash_newtype {
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() -> Self::Engine { fn engine() -> <$hash as $crate::Hash>::Engine { Self::engine() }
<$hash as $crate::Hash>::engine()
}
fn from_engine(e: Self::Engine) -> Self { fn from_engine(e: <$hash as $crate::Hash>::Engine) -> $newtype { Self::from_engine(e) }
Self::from(<$hash as $crate::Hash>::from_engine(e))
}
#[inline]
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> {
Ok($newtype(<$hash as $crate::Hash>::from_slice(sl)?)) Self::from_slice(sl)
} }
#[inline] fn to_byte_array(self) -> Self::Bytes { self.to_byte_array() }
fn from_byte_array(bytes: Self::Bytes) -> Self {
$newtype(<$hash as $crate::Hash>::from_byte_array(bytes))
}
#[inline] fn as_byte_array(&self) -> &Self::Bytes { self.as_byte_array() }
fn to_byte_array(self) -> Self::Bytes {
self.0.to_byte_array()
}
#[inline] fn from_byte_array(bytes: Self::Bytes) -> Self { Self::from_byte_array(bytes) }
fn as_byte_array(&self) -> &Self::Bytes {
self.0.as_byte_array()
}
#[inline] fn all_zeros() -> Self { Self::all_zeros() }
fn all_zeros() -> Self {
let zeros = <$hash>::all_zeros();
$newtype(zeros)
}
} }
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> {
use $crate::{Hash, hex::FromHex}; use $crate::{hex::FromHex};
let mut bytes = <[u8; <Self as $crate::Hash>::LEN]>::from_hex(s)?; let mut bytes = <[u8; <Self as $crate::Hash>::LEN]>::from_hex(s)?;
if <Self as $crate::Hash>::DISPLAY_BACKWARD { if <Self as $crate::Hash>::DISPLAY_BACKWARD {
@ -386,7 +432,7 @@ macro_rules! hash_newtype_known_attrs {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::{sha256, Hash}; use crate::sha256;
#[test] #[test]
fn hash_as_ref_array() { fn hash_as_ref_array() {