// SPDX-License-Identifier: CC0-1.0 //! Public macros. //! //! - [`sha256t_tag`](crate::sha256t_tag) //! - [`hash_newtype`](crate::hash_newtype) //! - [`impl_hex_for_newtype`](crate::impl_hex_for_newtype) //! - [`impl_serde_for_newtype`](crate::impl_serde_for_newtype) /// Macro used to define a tag. /// /// Defines new struct and implements `Tag` for it. /// /// The syntax is: /// /// ``` /// # use bitcoin_hashes::sha256t_tag; /// sha256t_tag! { /// /// Optional documentation details here. /// /// Summary is always generated. /// pub struct FooTag = hash_str("foo"); /// } /// ``` /// /// The `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, note that HASHED_BYTES_LENGTH must /// be a multiple of 64. #[macro_export] macro_rules! sha256t_tag { ($(#[$($tag_attr:tt)*])* $tag_vis:vis struct $tag:ident = $constructor:tt($($tag_value:tt)+);) => { $crate::sha256t_tag_struct!($tag_vis, $tag, stringify!($hash_name), $(#[$($tag_attr)*])*); impl $crate::sha256t::Tag for $tag { const MIDSTATE: $crate::sha256::Midstate = $crate::sha256t_tag_constructor!($constructor, $($tag_value)+); } } } /// Creates a new newtype around a [`Hash`] type. /// /// The syntax is similar to the usual tuple struct syntax: /// /// ``` /// # use bitcoin_hashes::{hash_newtype, sha256}; /// hash_newtype! { /// /// Hash of `Foo`. /// pub struct MyNewtype(pub sha256::Hash); /// } /// ``` /// /// You can use any valid visibility specifier in place of `pub` or you can omit either or both, if /// you want the type or its field to be private. /// /// Whether the hash is reversed or not when displaying depends on the inner type. However you can /// override it like this: /// /// ``` /// # use bitcoin_hashes::{hash_newtype, sha256}; /// hash_newtype! { /// #[hash_newtype(backward)] /// struct MyNewtype(sha256::Hash); /// } /// ``` /// /// This will display the hash backwards regardless of what the inner type does. Use `forward` /// instead of `backward` to force displaying forward. /// /// You can add arbitrary doc comments or other attributes to the struct or it's field. Note that /// the macro already derives [`Copy`], [`Clone`], [`Eq`], [`PartialEq`], /// [`Hash`](core::hash::Hash), [`Ord`], [`PartialOrd`]. /// /// You can also define multiple newtypes within one macro call: /// /// ``` /// # use bitcoin_hashes::{hash_newtype, sha256, hash160}; /// /// hash_newtype! { /// /// My custom type 1 /// pub struct Newtype1(sha256::Hash); /// /// /// My custom type 2 /// struct Newtype2(hash160::Hash); /// } /// ``` /// /// Note: the macro is internally recursive. If you use too many attributes (> 256 tokens) you may /// hit recursion limit. If you have so many attributes for a good reason, just raising the limit /// should be OK. Note however that attribute-processing part has to use [TT muncher] which has /// quadratic complexity, so having many attributes may blow up compile time. This should be rare. /// /// [TT muncher]: https://danielkeep.github.io/tlborm/book/pat-incremental-tt-munchers.html /// // Ever heard of legendary comments warning developers to not touch the code? Yep, here's another // one. The following code is written the way it is for some specific reasons. If you think you can // simplify it, I suggest spending your time elsewhere. // // If you looks at the code carefully you might ask these questions: // // * Why are attributes using `tt` and not `meta`?! // * Why are the macros split like that?! // * Why use recursion instead of `$()*`? // // None of these are here by accident. For some reason unknown to me, if you accept an argument to // macro with any fragment specifier other than `tt` it will **not** match any of the rules // requiring a specific token. Yep, I tried it, I literally got error that `hash_newtype` doesn't // match `hash_newtype`. So all input attributes must be `tt`. // // Originally I wanted to define a bunch of macros that would filter-out hash_type attributes. Then // I remembered (by seeing compiler error) that calling macros is not allowed inside attributes. // And no, you can't bypass it by calling a helper macro and passing "output of another macro" into // it. The whole macro gets passed, not the resulting value. So we have to generate the entire // attributes. And you can't just place an attribute-producing macro above struct - they are // considered separate items. This is not C. // // Thus struct is generated in a separate macro together with attributes. And since the macro needs // attributes as the input and I didn't want to create confusion by using `#[]` syntax *after* // struct, I opted to use `{}` as a separator. Yes, a separator is required because an attribute // may be composed of multiple token trees - that's the point of "double repetition". #[macro_export] macro_rules! hash_newtype { ($($(#[$($type_attrs:tt)*])* $type_vis:vis struct $newtype:ident($(#[$field_attrs:tt])* $field_vis:vis $hash:path);)+) => { $( $($crate::hash_newtype_known_attrs!(#[ $($type_attrs)* ]);)* $crate::hash_newtype_struct! { $type_vis struct $newtype($(#[$field_attrs])* $field_vis $hash); $({ $($type_attrs)* })* } $crate::impl_bytelike_traits!($newtype, { <$newtype as $crate::Hash>::LEN }); #[allow(unused)] // Private wrapper types may not need all functions. impl $newtype { /// Constructs a new hash from the underlying byte array. pub const fn from_byte_array(bytes: <$hash as $crate::Hash>::Bytes) -> Self { $newtype(<$hash>::from_byte_array(bytes)) } /// Copies a byte slice into a hash object. #[deprecated(since = "0.15.0", note = "use `from_byte_array` instead")] #[allow(deprecated_in_future)] // Because of `FromSliceError`. #[allow(deprecated)] // Because of `from_slice`. pub fn from_slice(sl: &[u8]) -> $crate::_export::_core::result::Result<$newtype, $crate::FromSliceError> { Ok($newtype(<$hash as $crate::Hash>::from_slice(sl)?)) } /// Returns the underlying byte array. pub const fn to_byte_array(self) -> <$hash as $crate::Hash>::Bytes { self.0.to_byte_array() } /// Returns a reference to the underlying byte array. pub const fn as_byte_array(&self) -> &<$hash as $crate::Hash>::Bytes { self.0.as_byte_array() } } impl $crate::Hash for $newtype { type Bytes = <$hash as $crate::Hash>::Bytes; const DISPLAY_BACKWARD: bool = $crate::hash_newtype_get_direction!($hash, $(#[$($type_attrs)*])*); fn from_byte_array(bytes: Self::Bytes) -> Self { Self::from_byte_array(bytes) } #[inline] #[allow(deprecated_in_future)] // Because of `FromSliceError`. #[allow(deprecated)] // Because of `from_slice`. fn from_slice(sl: &[u8]) -> $crate::_export::_core::result::Result<$newtype, $crate::FromSliceError> { Self::from_slice(sl) } fn to_byte_array(self) -> Self::Bytes { self.to_byte_array() } fn as_byte_array(&self) -> &Self::Bytes { self.as_byte_array() } } )+ }; } /// Implements string functions using hex for a new type created with [`crate::hash_newtype`] macro. /// /// Implements: /// /// * `str::FromStr` /// * `fmt::{LowerHex, UpperHex}` using `hex-conservative` /// * `fmt::{Display, Debug}` by calling `LowerHex` #[macro_export] #[cfg(feature = "hex")] macro_rules! impl_hex_for_newtype { ($($newtype:ident),*) => { $( $crate::impl_hex_string_traits!($newtype, { <$newtype as $crate::Hash>::LEN }, { <$newtype as $crate::Hash>::DISPLAY_BACKWARD }); )* } } /// Implements `fmt::Debug` using hex for a new type created with [`crate::hash_newtype`] macro. /// /// This is provided in case you do not want to use the `hex` feature. #[macro_export] macro_rules! impl_debug_only_for_newtype { ($($newtype:ident),*) => { $( $crate::impl_debug_only!($newtype, { <$newtype as $crate::Hash>::LEN }, { <$newtype as $crate::Hash>::DISPLAY_BACKWARD }); )* } } /// Adds trait impls to a bytelike type. /// /// Implements: /// /// * `AsRef[u8; $len]` /// * `AsRef[u8]` /// * `Borrow<[u8; $len]>` /// * `Borrow<[u8]>` /// /// # Parameters /// /// * `ty` - The bytelike type to implement the traits on. /// * `$len` - The number of bytes this type has. /// * `$gen: $gent` - generic type(s) and trait bound(s). #[doc(hidden)] #[macro_export] macro_rules! impl_bytelike_traits { ($ty:ident, $len:expr $(, $gen:ident: $gent:ident)*) => { impl<$($gen: $gent),*> $crate::_export::_core::convert::AsRef<[u8; { $len }]> for $ty<$($gen),*> { #[inline] fn as_ref(&self) -> &[u8; { $len }] { self.as_byte_array() } } impl<$($gen: $gent),*> $crate::_export::_core::convert::AsRef<[u8]> for $ty<$($gen),*> { #[inline] fn as_ref(&self) -> &[u8] { self.as_byte_array() } } impl<$($gen: $gent),*> $crate::_export::_core::borrow::Borrow<[u8; { $len }]> for $ty<$($gen),*> { fn borrow(&self) -> &[u8; { $len }] { self.as_byte_array() } } impl<$($gen: $gent),*> $crate::_export::_core::borrow::Borrow<[u8]> for $ty<$($gen),*> { fn borrow(&self) -> &[u8] { self.as_byte_array() } } } } /// Adds hex string trait impls to a bytelike type using hex. /// /// Implements: /// /// * `str::FromStr` /// * `fmt::{LowerHex, UpperHex}` using `hex-conservative`. /// * `fmt::{Display, Debug}` by calling `LowerHex` /// /// Requires: /// /// * [`hex-conservative`] to publicly available as `$crate::hex`. /// * `$ty` must implement `IntoIterator>`. /// /// (See also [`hex-conservative::fmt_hex_exact`].) /// /// # Parameters /// /// * `ty` - The bytelike type to implement the traits on. /// * `$len` - The number of bytes this type has. /// * `$reverse` - `true` if the type should be displayed backwards, `false` otherwise. /// * `$gen: $gent` - generic type(s) and trait bound(s). /// /// [`hex-conservative`]: #[doc(hidden)] #[macro_export] #[cfg(feature = "hex")] macro_rules! impl_hex_string_traits { ($ty:ident, $len:expr, $reverse:expr $(, $gen:ident: $gent:ident)*) => { impl<$($gen: $gent),*> $crate::_export::_core::str::FromStr for $ty<$($gen),*> { type Err = $crate::hex::HexToArrayError; fn from_str(s: &str) -> $crate::_export::_core::result::Result { use $crate::hex::FromHex; let mut bytes = <[u8; { $len }]>::from_hex(s)?; if $reverse { bytes.reverse(); } Ok(Self::from_byte_array(bytes)) } } $crate::hex::impl_fmt_traits! { #[display_backward($reverse)] impl<$($gen: $gent),*> fmt_traits for $ty<$($gen),*> { const LENGTH: usize = ($len); // parens required due to rustc parser weirdness } } } } /// Implements `fmt::Debug` using hex. #[doc(hidden)] #[macro_export] macro_rules! impl_debug_only { ($ty:ident, $len:expr, $reverse:expr $(, $gen:ident: $gent:ident)*) => { impl<$($gen: $gent),*> $crate::_export::_core::fmt::Debug for $ty<$($gen),*> { #[inline] fn fmt(&self, f: &mut $crate::_export::_core::fmt::Formatter) -> $crate::_export::_core::fmt::Result { $crate::debug_hex(self.as_byte_array(), f) } } } } // Generates the struct only (no impls) // // This is a separate macro to make it more readable and have a separate interface that allows for // two groups of type attributes: processed and not-yet-processed ones (think about it like // computation via recursion). The macro recursively matches unprocessed attributes, popping them // one at a time and either ignoring them (`hash_newtype`) or appending them to the list of // processed attributes to be added to the struct. // // Once the list of not-yet-processed attributes is empty the struct is generated with processed // attributes added. #[doc(hidden)] #[macro_export] macro_rules! hash_newtype_struct { ($(#[$other_attrs:meta])* $type_vis:vis struct $newtype:ident($(#[$field_attrs:meta])* $field_vis:vis $hash:path);) => { $(#[$other_attrs])* #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] $type_vis struct $newtype($(#[$field_attrs])* $field_vis $hash); }; ($(#[$other_attrs:meta])* $type_vis:vis struct $newtype:ident($(#[$field_attrs:meta])* $field_vis:vis $hash:path); { hash_newtype($($ignore:tt)*) } $($type_attrs:tt)*) => { $crate::hash_newtype_struct! { $(#[$other_attrs])* $type_vis struct $newtype($(#[$field_attrs])* $field_vis $hash); $($type_attrs)* } }; ($(#[$other_attrs:meta])* $type_vis:vis struct $newtype:ident($(#[$field_attrs:meta])* $field_vis:vis $hash:path); { $other_attr:meta } $($type_attrs:tt)*) => { $crate::hash_newtype_struct! { $(#[$other_attrs])* #[$other_attr] $type_vis struct $newtype($(#[$field_attrs])* $field_vis $hash); $($type_attrs)* } }; } // Extracts `hash_newtype(forward)` and `hash_newtype(backward)` attributes if any and turns them // into bool, defaulting to `DISPLAY_BACKWARD` of the wrapped type if the attribute is omitted. // // Once an appropriate attribute is found we pass the remaining ones into another macro to detect // duplicates/conflicts and report an error. // // FYI, no, we can't use a helper macro to first filter all `hash_newtype` attributes. We would be // attempting to match on macros instead. So we must write `hashe_newtype` in each branch. #[doc(hidden)] #[macro_export] macro_rules! hash_newtype_get_direction { ($hash:ty, ) => { <$hash as $crate::Hash>::DISPLAY_BACKWARD }; ($hash:ty, #[hash_newtype(forward)] $($others:tt)*) => { { $crate::hash_newtype_forbid_direction!(forward, $($others)*); false } }; ($hash:ty, #[hash_newtype(backward)] $($others:tt)*) => { { $crate::hash_newtype_forbid_direction!(backward, $($others)*); true } }; ($hash:ty, #[$($ignore:tt)*] $($others:tt)*) => { $crate::hash_newtype_get_direction!($hash, $($others)*) }; } // Reports an error if any of the attributes is `hash_newtype($direction)`. // // This is used for detection of duplicates/conflicts, see the macro above. #[doc(hidden)] #[macro_export] macro_rules! hash_newtype_forbid_direction { ($direction:ident, ) => {}; ($direction:ident, #[hash_newtype(forward)] $(others:tt)*) => { compile_error!(concat!("cannot set display direction to forward: ", stringify!($direction), " was already specified")); }; ($direction:ident, #[hash_newtype(backward)] $(others:tt)*) => { compile_error!(concat!("cannot set display direction to backward: ", stringify!($direction), " was already specified")); }; ($direction:ident, #[$($ignore:tt)*] $(#[$others:tt])*) => { $crate::hash_newtype_forbid_direction!($direction, $(#[$others])*) }; } // Checks (at compile time) that all `hash_newtype` attributes are known. // // An unknown attribute could be a typo that could cause problems - e.g. wrong display direction if // it's missing. To prevent this, we call this macro above. The macro produces nothing unless an // unknown attribute is found in which case it produces `compile_error!`. #[doc(hidden)] #[macro_export] macro_rules! hash_newtype_known_attrs { (#[hash_newtype(forward)]) => {}; (#[hash_newtype(backward)]) => {}; (#[hash_newtype($($unknown:tt)*)]) => { compile_error!(concat!("unrecognized attribute ", stringify!($($unknown)*))); }; ($($ignore:tt)*) => {}; } /// Functions used by serde impls of all hashes. #[cfg(feature = "serde")] pub mod serde_details { use core::marker::PhantomData; use core::str::FromStr; use core::{fmt, str}; use serde::de; /// Type used to implement serde traits for hashes as hex strings. pub struct HexVisitor(PhantomData); impl Default for HexVisitor { fn default() -> Self { Self(PhantomData) } } impl de::Visitor<'_> for HexVisitor where ValueT: FromStr, ::Err: fmt::Display, { type Value = ValueT; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("an ASCII hex string") } fn visit_bytes(self, v: &[u8]) -> core::result::Result where E: de::Error, { if let Ok(hex) = str::from_utf8(v) { hex.parse::().map_err(E::custom) } else { Err(E::invalid_value(de::Unexpected::Bytes(v), &self)) } } fn visit_str(self, v: &str) -> core::result::Result where E: de::Error, { v.parse::().map_err(E::custom) } } /// Type used to implement serde traits for hashes as bytes. pub struct BytesVisitor(PhantomData); impl Default for BytesVisitor { fn default() -> Self { Self(PhantomData) } } impl de::Visitor<'_> for BytesVisitor where ValueT: crate::Hash, ValueT: crate::Hash, { type Value = ValueT; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a bytestring") } fn visit_bytes(self, v: &[u8]) -> core::result::Result where E: de::Error, { let bytes = <[u8; N]>::try_from(v).map_err(|_| { // from_slice only errors on incorrect length E::invalid_length(v.len(), &stringify!(N)) })?; Ok(::from_byte_array(bytes)) } } } /// Implements `Serialize` and `Deserialize` for a new type created with [`crate::hash_newtype`] macro. #[macro_export] #[cfg(feature = "serde")] macro_rules! impl_serde_for_newtype { ($($newtype:ident),*) => { $( $crate::serde_impl!($newtype, { <$newtype as $crate::Hash>::LEN }); )* } } /// Implements `Serialize` and `Deserialize` for a type `$t` which /// represents a newtype over a byte-slice over length `$len`. #[doc(hidden)] #[macro_export] #[cfg(feature = "serde")] macro_rules! serde_impl( ($t:ident, $len:expr $(, $gen:ident: $gent:ident)*) => ( impl<$($gen: $gent),*> $crate::serde::Serialize for $t<$($gen),*> { fn serialize(&self, s: S) -> core::result::Result { if s.is_human_readable() { s.collect_str(self) } else { s.serialize_bytes(::as_byte_array(self)) } } } impl<'de $(, $gen: $gent)*> $crate::serde::Deserialize<'de> for $t<$($gen),*> { fn deserialize>(d: D) -> core::result::Result<$t<$($gen),*>, D::Error> { use $crate::serde_macros::serde_details::{BytesVisitor, HexVisitor}; if d.is_human_readable() { d.deserialize_str(HexVisitor::::default()) } else { d.deserialize_bytes(BytesVisitor::::default()) } } } )); /// Does an "empty" serde implementation for the configuration without serde feature. #[doc(hidden)] #[macro_export] #[cfg(not(feature = "serde"))] macro_rules! serde_impl( ($t:ident, $len:expr $(, $gen:ident: $gent:ident)*) => () ); #[cfg(test)] mod test { use crate::sha256; #[test] fn hash_as_ref_array() { let hash = sha256::Hash::hash(&[3, 50]); let r = AsRef::<[u8; 32]>::as_ref(&hash); assert_eq!(r, hash.as_byte_array()); } #[test] fn hash_as_ref_slice() { let hash = sha256::Hash::hash(&[3, 50]); let r = AsRef::<[u8]>::as_ref(&hash); assert_eq!(r, hash.as_byte_array()); } #[test] fn hash_borrow() { use core::borrow::Borrow; let hash = sha256::Hash::hash(&[3, 50]); let borrowed: &[u8] = hash.borrow(); assert_eq!(borrowed, hash.as_byte_array()); } hash_newtype! { /// Test hash. struct TestHash(crate::sha256d::Hash); } #[cfg(feature = "hex")] crate::impl_hex_for_newtype!(TestHash); #[cfg(not(feature = "hex"))] crate::impl_debug_only_for_newtype!(TestHash); impl TestHash { fn all_zeros() -> Self { Self::from_byte_array([0; 32]) } } #[test] fn macros_work_in_function_scope() { use crate::sha256t; sha256t_tag! { #[repr(align(2))] // This tests that we can add additional attributes. pub struct FunctionScopeTag = hash_str("It works"); } hash_newtype! { /// Some docs. #[repr(align(4))] // This tests that we can add additional attributes. pub struct FunctionScopeHash(pub(crate) sha256t::Hash); } assert_eq!(2, core::mem::align_of::()); assert_eq!(4, core::mem::align_of::()); } // NB: This runs with and without `hex` feature enabled, testing different code paths for each. #[test] #[cfg(feature = "alloc")] fn debug() { use alloc::format; let want = "0000000000000000000000000000000000000000000000000000000000000000"; let got = format!("{:?}", TestHash::all_zeros()); assert_eq!(got, want) } #[test] #[cfg(feature = "alloc")] #[cfg(feature = "hex")] fn display() { use alloc::format; let want = "0000000000000000000000000000000000000000000000000000000000000000"; let got = format!("{}", TestHash::all_zeros()); assert_eq!(got, want) } #[test] #[cfg(feature = "alloc")] #[cfg(feature = "hex")] fn display_alternate() { use alloc::format; let want = "0x0000000000000000000000000000000000000000000000000000000000000000"; let got = format!("{:#}", TestHash::all_zeros()); assert_eq!(got, want) } #[test] #[cfg(feature = "alloc")] #[cfg(feature = "hex")] fn lower_hex() { use alloc::format; let want = "0000000000000000000000000000000000000000000000000000000000000000"; let got = format!("{:x}", TestHash::all_zeros()); assert_eq!(got, want) } #[test] #[cfg(feature = "alloc")] #[cfg(feature = "hex")] fn lower_hex_alternate() { use alloc::format; let want = "0x0000000000000000000000000000000000000000000000000000000000000000"; let got = format!("{:#x}", TestHash::all_zeros()); assert_eq!(got, want) } #[test] fn inner_hash_as_ref_array() { let hash = TestHash::all_zeros(); let r = AsRef::<[u8; 32]>::as_ref(&hash); assert_eq!(r, hash.as_byte_array()); } #[test] fn inner_hash_as_ref_slice() { let hash = TestHash::all_zeros(); let r = AsRef::<[u8]>::as_ref(&hash); assert_eq!(r, hash.as_byte_array()); } }