From 613fddc82be3b17696eaf4589d30995ba29f529b Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Fri, 7 Feb 2025 07:00:01 +1100 Subject: [PATCH 1/6] Delete deprecated sha256t_hash_newtype macro This macro is a maintenance burden. We would like to put a tag on the hash engine but doing so would require breaking this macro anyway so lets just delete it. --- hashes/src/sha256t/mod.rs | 99 --------------------------------------- 1 file changed, 99 deletions(-) diff --git a/hashes/src/sha256t/mod.rs b/hashes/src/sha256t/mod.rs index 6c62846c8..2e7ddf89d 100644 --- a/hashes/src/sha256t/mod.rs +++ b/hashes/src/sha256t/mod.rs @@ -140,105 +140,6 @@ where Hash::from_byte_array(sha256::Hash::from_engine(e).to_byte_array()) } -/// Macro used to define a newtype tagged hash. -/// -/// This macro creates two types: -/// -/// * a tag struct -/// * a hash wrapper -/// -/// The syntax is: -/// -/// ``` -/// # #[allow(deprecated)] { -/// # use bitcoin_hashes::sha256t_hash_newtype; -/// sha256t_hash_newtype! { -/// /// Optional documentation details here. -/// /// Summary is always generated. -/// pub struct FooTag = hash_str("foo"); -/// -/// /// A foo hash. -/// // Direction works just like the hash_newtype! macro. -/// #[hash_newtype(backward)] -/// pub struct FooHash(_); -/// } -/// # } -/// ``` -/// -/// The structs must be defined in this order - tag first, then hash type. `hash_str` marker -/// says the midstate should be generated by hashing the supplied string in a way described in -/// BIP-341. Alternatively, you can supply `hash_bytes` to hash raw bytes. If you have the midstate -/// already pre-computed and prefer **compiler** performance to readability you may use -/// `raw(MIDSTATE_BYTES, HASHED_BYTES_LENGTH)` instead. -/// -/// Both visibility modifiers and attributes are optional and passed to inner structs (excluding -/// `#[hash_newtype(...)]`). The attributes suffer same compiler performance limitations as in -/// [`hash_newtype`] macro. -/// -/// [`hash_newtype`]: crate::hash_newtype -#[macro_export] -#[deprecated(since = "0.15.0", note = "use `sha256_tag!` combined with `hash_newtype!` instead")] -macro_rules! sha256t_hash_newtype { - ($(#[$($tag_attr:tt)*])* $tag_vis:vis struct $tag:ident = $constructor:tt($($tag_value:tt)+); $(#[$($hash_attr:tt)*])* $hash_vis:vis struct $hash_name:ident($(#[$($field_attr:tt)*])* _);) => { - $crate::sha256t_tag_struct!($tag_vis, $tag, stringify!($hash_name), $(#[$($tag_attr)*])*); - - impl $crate::sha256t::Tag for $tag { - #[inline] - fn engine() -> $crate::sha256::HashEngine { - const MIDSTATE: $crate::sha256::Midstate = $crate::sha256t_tag_constructor!($constructor, $($tag_value)+); - $crate::sha256::HashEngine::from_midstate(MIDSTATE) - } - } - - $crate::hash_newtype! { - $(#[$($hash_attr)*])* - $hash_vis struct $hash_name($(#[$($field_attr)*])* $crate::sha256t::Hash<$tag>); - } - - impl $hash_name { - /// Constructs a new engine. - #[allow(unused)] // the user of macro may not need this - pub fn engine() -> <$hash_name as $crate::GeneralHash>::Engine { - <$hash_name as $crate::GeneralHash>::engine() - } - - /// Produces a hash from the current state of a given engine. - #[allow(unused)] // the user of macro may not need this - pub fn from_engine(e: <$hash_name as $crate::GeneralHash>::Engine) -> Self { - <$hash_name as $crate::GeneralHash>::from_engine(e) - } - - /// Hashes some bytes. - #[allow(unused)] // the user of macro may not need this - pub fn hash(data: &[u8]) -> Self { - <$hash_name as $crate::GeneralHash>::hash(data) - } - - /// Hashes all the byte slices retrieved from the iterator together. - #[allow(unused)] // the user of macro may not need this - pub fn hash_byte_chunks(byte_slices: I) -> Self - where - B: AsRef<[u8]>, - I: IntoIterator, - { - <$hash_name as $crate::GeneralHash>::hash_byte_chunks(byte_slices) - } - } - - impl $crate::GeneralHash for $hash_name { - type Engine = <$crate::sha256t::Hash<$tag> as $crate::GeneralHash>::Engine; - - fn engine() -> Self::Engine { - <$crate::sha256t::Hash<$tag> as $crate::GeneralHash>::engine() - } - - fn from_engine(e: Self::Engine) -> $hash_name { - Self::from(<$crate::sha256t::Hash<$tag> as $crate::GeneralHash>::from_engine(e)) - } - } - } -} - // Workaround macros being unavailable in attributes. #[doc(hidden)] #[macro_export] From ba6425947fa9b9a4f43c4977cb3b4bf477d4ce1e Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Fri, 7 Feb 2025 07:05:30 +1100 Subject: [PATCH 2/6] hashes: Use associated cost for pre-tagging Instead of requiring users of the `Tag` trait to implement the `engine` method we can have an associated const and provide an `engine` method with a default implementation. Use an associated const for the pre-tagged hash engine. Fro now keep the `engine` trait method but have a default impl that returns the const. We will remove it as a separate patch to assist review. --- hashes/src/macros.rs | 6 +----- hashes/src/sha256t/mod.rs | 13 +++++++------ hashes/tests/regression.rs | 5 +---- 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/hashes/src/macros.rs b/hashes/src/macros.rs index dc134577a..12ca01520 100644 --- a/hashes/src/macros.rs +++ b/hashes/src/macros.rs @@ -33,11 +33,7 @@ macro_rules! sha256t_tag { $crate::sha256t_tag_struct!($tag_vis, $tag, stringify!($hash_name), $(#[$($tag_attr)*])*); impl $crate::sha256t::Tag for $tag { - #[inline] - fn engine() -> $crate::sha256::HashEngine { - const MIDSTATE: $crate::sha256::Midstate = $crate::sha256t_tag_constructor!($constructor, $($tag_value)+); - $crate::sha256::HashEngine::from_midstate(MIDSTATE) - } + const MIDSTATE: $crate::sha256::Midstate = $crate::sha256t_tag_constructor!($constructor, $($tag_value)+); } } } diff --git a/hashes/src/sha256t/mod.rs b/hashes/src/sha256t/mod.rs index 2e7ddf89d..4a4ce84ee 100644 --- a/hashes/src/sha256t/mod.rs +++ b/hashes/src/sha256t/mod.rs @@ -11,8 +11,13 @@ type HashEngine = sha256::HashEngine; /// Trait representing a tag that can be used as a context for SHA256t hashes. pub trait Tag { + /// The [`Midstate`] after pre-tagging the hash engine. + const MIDSTATE: sha256::Midstate; + /// Returns a hash engine that is pre-tagged and is ready to be used for the data. - fn engine() -> sha256::HashEngine; + fn engine() -> sha256::HashEngine { + sha256::HashEngine::from_midstate(Self::MIDSTATE) + } } /// Output of the SHA256t hash function. @@ -190,11 +195,7 @@ mod tests { pub struct TestHashTag; impl sha256t::Tag for TestHashTag { - fn engine() -> sha256::HashEngine { - // The TapRoot TapLeaf midstate. - let midstate = sha256::Midstate::new(TEST_MIDSTATE, 64); - sha256::HashEngine::from_midstate(midstate) - } + const MIDSTATE: sha256::Midstate = sha256::Midstate::new(TEST_MIDSTATE, 64); } // We support manually implementing `Tag` and creating a tagged hash from it. diff --git a/hashes/tests/regression.rs b/hashes/tests/regression.rs index 0b6856794..a4ffddc2f 100644 --- a/hashes/tests/regression.rs +++ b/hashes/tests/regression.rs @@ -40,10 +40,7 @@ impl_regression_test! { pub struct RegHashTag; impl sha256t::Tag for RegHashTag { - fn engine() -> sha256::HashEngine { - let midstate = sha256::Midstate::new([0xab; 32], 64); - sha256::HashEngine::from_midstate(midstate) - } + const MIDSTATE: sha256::Midstate = sha256::Midstate::new([0xab; 32], 64); } type RegHash = sha256t::Hash; From 5ce8781162cdf75515a26ceab4209cd0c89c0001 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Fri, 7 Feb 2025 07:14:04 +1100 Subject: [PATCH 3/6] Remove the Tag::engine method Now we have an associated const we can do away with the `engine` trait method all together. Users can call `Hash::engine` instead. This is better because its an API more similar to the other hash types and therefor easier to discover and remember. --- bitcoin/src/taproot/mod.rs | 19 +++++++++---------- hashes/src/sha256t/mod.rs | 9 +++------ 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/bitcoin/src/taproot/mod.rs b/bitcoin/src/taproot/mod.rs index 50ea6c257..d9842ec28 100644 --- a/bitcoin/src/taproot/mod.rs +++ b/bitcoin/src/taproot/mod.rs @@ -1564,7 +1564,6 @@ mod sealed { #[cfg(test)] mod test { use hashes::sha256; - use hashes::sha256t::Tag; use hex::{DisplayHex, FromHex}; use secp256k1::VerifyOnly; @@ -1590,12 +1589,12 @@ mod test { #[test] fn midstates() { - use sha256t::Hash; + use sha256t::{Hash, Tag}; // test that engine creation roundtrips - assert_eq!(tag_engine("TapLeaf").midstate(), TapLeafTag::engine().midstate()); - assert_eq!(tag_engine("TapBranch").midstate(), TapBranchTag::engine().midstate()); - assert_eq!(tag_engine("TapTweak").midstate(), TapTweakTag::engine().midstate()); - assert_eq!(tag_engine("TapSighash").midstate(), TapSighashTag::engine().midstate()); + assert_eq!(tag_engine("TapLeaf").midstate().unwrap(), TapLeafTag::MIDSTATE); + assert_eq!(tag_engine("TapBranch").midstate().unwrap(), TapBranchTag::MIDSTATE); + assert_eq!(tag_engine("TapTweak").midstate().unwrap(), TapTweakTag::MIDSTATE); + assert_eq!(tag_engine("TapSighash").midstate().unwrap(), TapSighashTag::MIDSTATE); // check that hash creation is the same as building into the same engine fn empty_hash(tag_name: &str) -> [u8; 32] { @@ -1618,19 +1617,19 @@ mod test { // CHashWriter writer = HasherTapLeaf; // writer.GetSHA256().GetHex() assert_eq!( - Hash::::from_engine(TapLeafTag::engine()).to_string(), + Hash::::from_engine(Hash::::engine()).to_string(), "5212c288a377d1f8164962a5a13429f9ba6a7b84e59776a52c6637df2106facb" ); assert_eq!( - Hash::::from_engine(TapBranchTag::engine()).to_string(), + Hash::::from_engine(Hash::::engine()).to_string(), "53c373ec4d6f3c53c1f5fb2ff506dcefe1a0ed74874f93fa93c8214cbe9ffddf" ); assert_eq!( - Hash::::from_engine(TapTweakTag::engine()).to_string(), + Hash::::from_engine(Hash::::engine()).to_string(), "8aa4229474ab0100b2d6f0687f031d1fc9d8eef92a042ad97d279bff456b15e4" ); assert_eq!( - Hash::::from_engine(TapSighashTag::engine()).to_string(), + Hash::::from_engine(Hash::::engine()).to_string(), "dabc11914abcd8072900042a2681e52f8dba99ce82e224f97b5fdb7cd4b9c803" ); diff --git a/hashes/src/sha256t/mod.rs b/hashes/src/sha256t/mod.rs index 4a4ce84ee..cb91f6ab0 100644 --- a/hashes/src/sha256t/mod.rs +++ b/hashes/src/sha256t/mod.rs @@ -13,11 +13,6 @@ type HashEngine = sha256::HashEngine; pub trait Tag { /// The [`Midstate`] after pre-tagging the hash engine. const MIDSTATE: sha256::Midstate; - - /// Returns a hash engine that is pre-tagged and is ready to be used for the data. - fn engine() -> sha256::HashEngine { - sha256::HashEngine::from_midstate(Self::MIDSTATE) - } } /// Output of the SHA256t hash function. @@ -65,7 +60,9 @@ where pub fn from_engine(e: HashEngine) -> Hash { from_engine(e) } /// Constructs a new engine. - pub fn engine() -> HashEngine { T::engine() } + pub fn engine() -> HashEngine { + sha256::HashEngine::from_midstate(T::MIDSTATE) + } /// Hashes some bytes. #[allow(clippy::self_named_constructors)] // Hash is a noun and a verb. From a0211906fe265eb848823c8fdbd407a3ea372770 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Fri, 7 Feb 2025 07:22:00 +1100 Subject: [PATCH 4/6] sha256t: Remove standalone from_engine function This function is meant to be used in conjunction with the `general_hash_type` macro but the `sha256t` module does not use that macro. Inline the function. Internal change only. --- hashes/src/sha256t/mod.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/hashes/src/sha256t/mod.rs b/hashes/src/sha256t/mod.rs index cb91f6ab0..f355ac82e 100644 --- a/hashes/src/sha256t/mod.rs +++ b/hashes/src/sha256t/mod.rs @@ -57,7 +57,9 @@ where } /// Produces a hash from the current state of a given engine. - pub fn from_engine(e: HashEngine) -> Hash { from_engine(e) } + pub fn from_engine(e: HashEngine) -> Hash { + Hash::from_byte_array(sha256::Hash::from_engine(e).to_byte_array()) + } /// Constructs a new engine. pub fn engine() -> HashEngine { @@ -135,13 +137,6 @@ impl core::hash::Hash for Hash { crate::internal_macros::hash_trait_impls!(256, false, T: Tag); -fn from_engine(e: sha256::HashEngine) -> Hash -where - T: Tag, -{ - Hash::from_byte_array(sha256::Hash::from_engine(e).to_byte_array()) -} - // Workaround macros being unavailable in attributes. #[doc(hidden)] #[macro_export] From 3e8e2e46bf7a2c093b832047aaadacf93d40a4ec Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Fri, 7 Feb 2025 07:25:45 +1100 Subject: [PATCH 5/6] Use Self::Engine in GeneralHash impl Match the trait method signature. Internal change only. --- hashes/src/internal_macros.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hashes/src/internal_macros.rs b/hashes/src/internal_macros.rs index 959ccdb47..3c6818bd4 100644 --- a/hashes/src/internal_macros.rs +++ b/hashes/src/internal_macros.rs @@ -29,7 +29,7 @@ macro_rules! hash_trait_impls { impl<$($gen: $gent),*> $crate::GeneralHash for Hash<$($gen),*> { type Engine = HashEngine; - fn from_engine(e: HashEngine) -> Hash<$($gen),*> { Self::from_engine(e) } + fn from_engine(e: Self::Engine) -> Hash<$($gen),*> { Self::from_engine(e) } } #[cfg(feature = "serde")] From 6002ccdc567b26c69f6b55173f5d11b95b4d6966 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Fri, 7 Feb 2025 07:27:29 +1100 Subject: [PATCH 6/6] Add a tagged sha256t hash engine We would like it if two different pre-tagged engines were considered different types so it is not possible to mix them up. Add a new `sha256t::HashEngine` where `T` is a tag the same as on `sha256t::Hash`. --- bitcoin/src/taproot/mod.rs | 2 +- hashes/src/internal_macros.rs | 2 +- hashes/src/sha256t/mod.rs | 41 ++++++++++++++++++++++++++++------- 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/bitcoin/src/taproot/mod.rs b/bitcoin/src/taproot/mod.rs index d9842ec28..775357f07 100644 --- a/bitcoin/src/taproot/mod.rs +++ b/bitcoin/src/taproot/mod.rs @@ -1600,7 +1600,7 @@ mod test { fn empty_hash(tag_name: &str) -> [u8; 32] { let mut e = tag_engine(tag_name); e.input(&[]); - Hash::::from_engine(e).to_byte_array() + sha256::Hash::from_engine(e).to_byte_array() } assert_eq!(empty_hash("TapLeaf"), Hash::::hash(&[]).to_byte_array()); assert_eq!(empty_hash("TapBranch"), Hash::::hash(&[]).to_byte_array()); diff --git a/hashes/src/internal_macros.rs b/hashes/src/internal_macros.rs index 3c6818bd4..5ae79f119 100644 --- a/hashes/src/internal_macros.rs +++ b/hashes/src/internal_macros.rs @@ -27,7 +27,7 @@ macro_rules! hash_trait_impls { $crate::impl_debug_only!(Hash, { $bits / 8 }, $reverse $(, $gen: $gent)*); impl<$($gen: $gent),*> $crate::GeneralHash for Hash<$($gen),*> { - type Engine = HashEngine; + type Engine = HashEngine<$($gen),*>; fn from_engine(e: Self::Engine) -> Hash<$($gen),*> { Self::from_engine(e) } } diff --git a/hashes/src/sha256t/mod.rs b/hashes/src/sha256t/mod.rs index f355ac82e..5d28b9d93 100644 --- a/hashes/src/sha256t/mod.rs +++ b/hashes/src/sha256t/mod.rs @@ -5,12 +5,12 @@ use core::cmp; use core::marker::PhantomData; +#[cfg(doc)] +use crate::sha256::Midstate; use crate::{sha256, FromSliceError, HashEngine as _}; -type HashEngine = sha256::HashEngine; - /// Trait representing a tag that can be used as a context for SHA256t hashes. -pub trait Tag { +pub trait Tag: Clone { /// The [`Midstate`] after pre-tagging the hash engine. const MIDSTATE: sha256::Midstate; } @@ -57,14 +57,12 @@ where } /// Produces a hash from the current state of a given engine. - pub fn from_engine(e: HashEngine) -> Hash { - Hash::from_byte_array(sha256::Hash::from_engine(e).to_byte_array()) + pub fn from_engine(e: HashEngine) -> Hash { + Hash::from_byte_array(sha256::Hash::from_engine(e.0).to_byte_array()) } /// Constructs a new engine. - pub fn engine() -> HashEngine { - sha256::HashEngine::from_midstate(T::MIDSTATE) - } + pub fn engine() -> HashEngine { HashEngine::default() } /// Hashes some bytes. #[allow(clippy::self_named_constructors)] // Hash is a noun and a verb. @@ -137,6 +135,33 @@ impl core::hash::Hash for Hash { crate::internal_macros::hash_trait_impls!(256, false, T: Tag); +/// Engine to compute SHA256t hash function. +#[derive(Debug, Clone)] +pub struct HashEngine(sha256::HashEngine, PhantomData); + +impl Default for HashEngine { + fn default() -> Self { + let tagged = sha256::HashEngine::from_midstate(T::MIDSTATE); + HashEngine(tagged, PhantomData) + } +} + +impl crate::HashEngine for HashEngine { + const BLOCK_SIZE: usize = 64; // Same as sha256::HashEngine::BLOCK_SIZE; + fn input(&mut self, data: &[u8]) { self.0.input(data) } + fn n_bytes_hashed(&self) -> u64 { self.0.n_bytes_hashed() } +} + +crate::internal_macros::impl_io_write!( + HashEngine, + |us: &mut HashEngine, buf| { + us.input(buf); + Ok(buf.len()) + }, + |_us| { Ok(()) }, + T: crate::sha256t::Tag +); + // Workaround macros being unavailable in attributes. #[doc(hidden)] #[macro_export]