bitcoin_hashes/
sha256t.rs1use 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
15pub trait Tag {
17 fn engine() -> sha256::HashEngine;
19}
20
21#[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_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#[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 let midstate = sha256::Midstate::from_byte_array(TEST_MIDSTATE);
179 sha256::HashEngine::from_midstate(midstate, 64)
180 }
181 }
182
183 #[cfg(feature = "alloc")]
185 pub type TestHash = sha256t::Hash<TestHashTag>;
186
187 sha256t_hash_newtype! {
188 struct NewTypeTag = raw(TEST_MIDSTATE, 64);
190
191 #[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}