bitcoin_hashes/
sha256t.rs

1// SPDX-License-Identifier: CC0-1.0
2
3//! SHA256t implementation (tagged SHA256).
4//!
5
6use core::marker::PhantomData;
7use core::ops::Index;
8use core::slice::SliceIndex;
9use core::{cmp, str};
10
11#[cfg(feature = "schemars")]
12use alloc::{borrow::ToOwned, boxed::Box, string::String};
13
14use crate::{sha256, FromSliceError};
15
16type HashEngine = sha256::HashEngine;
17
18/// Trait representing a tag that can be used as a context for SHA256t hashes.
19pub trait Tag {
20    /// Returns a hash engine that is pre-tagged and is ready to be used for the data.
21    fn engine() -> sha256::HashEngine;
22}
23
24/// Output of the SHA256t hash function.
25#[repr(transparent)]
26pub struct Hash<T: Tag>([u8; 32], PhantomData<T>);
27
28#[cfg(feature = "schemars")]
29impl<T: Tag> schemars::JsonSchema for Hash<T> {
30    fn schema_name() -> String { "Hash".to_owned() }
31
32    fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
33        let mut schema: schemars::schema::SchemaObject = <String>::json_schema(gen).into();
34        schema.string = Some(Box::new(schemars::schema::StringValidation {
35            max_length: Some(32 * 2),
36            min_length: Some(32 * 2),
37            pattern: Some("[0-9a-fA-F]+".to_owned()),
38        }));
39        schema.into()
40    }
41}
42
43impl<T: Tag> Hash<T> {
44    fn internal_new(arr: [u8; 32]) -> Self { Hash(arr, Default::default()) }
45
46    fn internal_engine() -> HashEngine { T::engine() }
47}
48
49impl<T: Tag> Copy for Hash<T> {}
50impl<T: Tag> Clone for Hash<T> {
51    fn clone(&self) -> Self { *self }
52}
53impl<T: Tag> PartialEq for Hash<T> {
54    fn eq(&self, other: &Hash<T>) -> bool { self.0 == other.0 }
55}
56impl<T: Tag> Eq for Hash<T> {}
57impl<T: Tag> Default for Hash<T> {
58    fn default() -> Self { Hash([0; 32], PhantomData) }
59}
60impl<T: Tag> PartialOrd for Hash<T> {
61    fn partial_cmp(&self, other: &Hash<T>) -> Option<cmp::Ordering> {
62        Some(cmp::Ord::cmp(self, other))
63    }
64}
65impl<T: Tag> Ord for Hash<T> {
66    fn cmp(&self, other: &Hash<T>) -> cmp::Ordering { cmp::Ord::cmp(&self.0, &other.0) }
67}
68impl<T: Tag> core::hash::Hash for Hash<T> {
69    fn hash<H: core::hash::Hasher>(&self, h: &mut H) { self.0.hash(h) }
70}
71
72crate::internal_macros::hash_trait_impls!(256, true, T: Tag);
73
74fn from_engine<T: Tag>(e: sha256::HashEngine) -> Hash<T> {
75    use crate::Hash as _;
76
77    Hash::from_byte_array(sha256::Hash::from_engine(e).to_byte_array())
78}
79
80/// Macro used to define a newtype tagged hash.
81///
82/// This macro creates two types:
83///
84/// * a tag struct
85/// * a hash wrapper
86///
87/// The syntax is:
88///
89/// ```
90/// # use bitcoin_hashes::sha256t_hash_newtype;
91/// sha256t_hash_newtype! {
92///     /// Optional documentation details here.
93///     /// Summary is always generated.
94///     pub struct FooTag = hash_str("foo");
95///
96///     /// A foo hash.
97///     // Direction works just like in case of hash_newtype! macro.
98///     #[hash_newtype(forward)]
99///     pub struct FooHash(_);
100/// }
101/// ```
102///
103/// The structs must be defined in this order - tag first, then hash type. `hash_str` marker
104/// says the midstate should be generated by hashing the supplied string in a way described in
105/// BIP-341. Alternatively, you can supply `hash_bytes` to hash raw bytes. If you have the midstate
106/// already pre-computed and prefer **compiler** performance to readability you may use
107/// `raw(MIDSTATE_BYTES, HASHED_BYTES_LENGTH)` instead.
108///
109/// Both visibility modifiers and attributes are optional and passed to inner structs (excluding
110/// `#[hash_newtype(...)]`). The attributes suffer same compiler performance limitations as in
111/// [`hash_newtype`] macro.
112///
113/// The macro accepts multiple inputs so you can define multiple hash newtypes in one macro call.
114/// Just make sure to enter the structs in order `Tag0`, `Hash0`, `Tag1`, `Hash1`...
115///
116/// [`hash_newtype`]: crate::hash_newtype
117#[macro_export]
118macro_rules! sha256t_hash_newtype {
119    ($($(#[$($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)*])* _);)+) => {
120        $(
121        $crate::sha256t_hash_newtype_tag!($tag_vis, $tag, stringify!($hash_name), $(#[$($tag_attr)*])*);
122
123        impl $crate::sha256t::Tag for $tag {
124            #[inline]
125            fn engine() -> $crate::sha256::HashEngine {
126                const MIDSTATE: ($crate::sha256::Midstate, usize) = $crate::sha256t_hash_newtype_tag_constructor!($constructor, $($tag_value)+);
127                #[allow(unused)]
128                const _LENGTH_CHECK: () = [(); 1][MIDSTATE.1 % 64];
129                $crate::sha256::HashEngine::from_midstate(MIDSTATE.0, MIDSTATE.1)
130            }
131        }
132
133        $crate::hash_newtype! {
134            $(#[$($hash_attr)*])*
135            $hash_vis struct $hash_name($(#[$($field_attr)*])* $crate::sha256t::Hash<$tag>);
136        }
137        )+
138    }
139}
140
141// Workaround macros being unavailable in attributes.
142#[doc(hidden)]
143#[macro_export]
144macro_rules! sha256t_hash_newtype_tag {
145    ($vis:vis, $tag:ident, $name:expr, $(#[$($attr:meta)*])*) => {
146        #[doc = "The tag used for [`"]
147        #[doc = $name]
148        #[doc = "`]\n\n"]
149        $(#[$($attr)*])*
150        #[derive(Copy, Clone, PartialEq, Eq, Default, PartialOrd, Ord, Hash)]
151        $vis struct $tag;
152    };
153}
154
155#[doc(hidden)]
156#[macro_export]
157macro_rules! sha256t_hash_newtype_tag_constructor {
158    (hash_str, $value:expr) => {
159        ($crate::sha256::Midstate::hash_tag($value.as_bytes()), 64)
160    };
161    (hash_bytes, $value:expr) => {
162        ($crate::sha256::Midstate::hash_tag($value), 64)
163    };
164    (raw, $bytes:expr, $len:expr) => {
165        ($crate::sha256::Midstate::from_byte_array($bytes), $len)
166    };
167}
168
169#[cfg(test)]
170mod tests {
171    #[cfg(feature = "schemars")]
172    use alloc::{borrow::ToOwned, boxed::Box, string::String};
173
174    #[cfg(feature = "alloc")]
175    use crate::Hash;
176    #[cfg(feature = "alloc")]
177    use crate::{sha256, sha256t};
178
179    const TEST_MIDSTATE: [u8; 32] = [
180        156, 224, 228, 230, 124, 17, 108, 57, 56, 179, 202, 242, 195, 15, 80, 137, 211, 243, 147,
181        108, 71, 99, 110, 96, 125, 179, 62, 234, 221, 198, 240, 201,
182    ];
183
184    #[cfg(feature = "alloc")]
185    #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Hash)]
186    pub struct TestHashTag;
187
188    #[cfg(feature = "alloc")]
189    impl sha256t::Tag for TestHashTag {
190        fn engine() -> sha256::HashEngine {
191            // The TapRoot TapLeaf midstate.
192            let midstate = sha256::Midstate::from_byte_array(TEST_MIDSTATE);
193            sha256::HashEngine::from_midstate(midstate, 64)
194        }
195    }
196
197    #[cfg(feature = "schemars")]
198    impl schemars::JsonSchema for TestHashTag {
199        fn schema_name() -> String { "Hash".to_owned() }
200
201        fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
202            let mut schema: schemars::schema::SchemaObject = <String>::json_schema(gen).into();
203            schema.string = Some(Box::new(schemars::schema::StringValidation {
204                max_length: Some(64 * 2),
205                min_length: Some(64 * 2),
206                pattern: Some("[0-9a-fA-F]+".to_owned()),
207            }));
208            schema.into()
209        }
210    }
211
212    /// A hash tagged with `$name`.
213    #[cfg(feature = "alloc")]
214    pub type TestHash = sha256t::Hash<TestHashTag>;
215
216    sha256t_hash_newtype! {
217        /// Test detailed explanation.
218        struct NewTypeTag = raw(TEST_MIDSTATE, 64);
219
220        /// A test hash.
221        #[hash_newtype(backward)]
222        struct NewTypeHash(_);
223    }
224
225    #[test]
226    #[cfg(feature = "alloc")]
227    fn test_sha256t() {
228        assert_eq!(
229            TestHash::hash(&[0]).to_string(),
230            "29589d5122ec666ab5b4695070b6debc63881a4f85d88d93ddc90078038213ed"
231        );
232        assert_eq!(
233            NewTypeHash::hash(&[0]).to_string(),
234            "29589d5122ec666ab5b4695070b6debc63881a4f85d88d93ddc90078038213ed"
235        );
236    }
237}