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