diff --git a/bitcoin/src/crypto/sighash.rs b/bitcoin/src/crypto/sighash.rs index 4b85e1b1..cbdb6c85 100644 --- a/bitcoin/src/crypto/sighash.rs +++ b/bitcoin/src/crypto/sighash.rs @@ -47,22 +47,25 @@ macro_rules! impl_thirty_two_byte_hash { } } -#[rustfmt::skip] -hash_newtype!(LegacySighash, sha256d::Hash, 32, - doc="Hash of a transaction according to the legacy signature algorithm", false); -impl_thirty_two_byte_hash!(LegacySighash); +hash_newtype! { + /// Hash of a transaction according to the legacy signature algorithm. + #[hash_newtype(forward)] + pub struct LegacySighash(sha256d::Hash); -#[rustfmt::skip] -hash_newtype!(SegwitV0Sighash, sha256d::Hash, 32, - doc="Hash of a transaction according to the segwit version 0 signature algorithm", false); + /// Hash of a transaction according to the segwit version 0 signature algorithm. + #[hash_newtype(forward)] + pub struct SegwitV0Sighash(sha256d::Hash); +} + +impl_thirty_two_byte_hash!(LegacySighash); impl_thirty_two_byte_hash!(SegwitV0Sighash); -#[rustfmt::skip] sha256t_hash_newtype!(TapSighash, TapSighashTag, MIDSTATE_TAPSIGHASH, 64, doc="Taproot-tagged hash with tag \"TapSighash\". -This hash type is used for computing taproot signature hash.", false +This hash type is used for computing taproot signature hash.", forward ); + impl_thirty_two_byte_hash!(TapSighash); /// Efficiently calculates signature hash message for legacy, segwit and taproot inputs. diff --git a/bitcoin/src/hash_types.rs b/bitcoin/src/hash_types.rs index 31b7cb84..be978a69 100644 --- a/bitcoin/src/hash_types.rs +++ b/bitcoin/src/hash_types.rs @@ -55,30 +55,44 @@ pub use newtypes::*; mod newtypes { use crate::hashes::{sha256, sha256d, hash160, hash_newtype}; - hash_newtype!( - Txid, sha256d::Hash, 32, doc="A bitcoin transaction hash/transaction ID. + hash_newtype! { + /// A bitcoin transaction hash/transaction ID. + /// + /// For compatibility with the existing Bitcoin infrastructure and historical + /// and current versions of the Bitcoin Core software itself, this and + /// other [`sha256d::Hash`] types, are serialized in reverse + /// byte order when converted to a hex string via [`std::fmt::Display`] trait operations. + /// See [`hashes::Hash::DISPLAY_BACKWARD`] for more details. + pub struct Txid(sha256d::Hash); -For compatibility with the existing Bitcoin infrastructure and historical -and current versions of the Bitcoin Core software itself, this and -other [`sha256d::Hash`] types, are serialized in reverse -byte order when converted to a hex string via [`std::fmt::Display`] trait operations. -See [`hashes::Hash::DISPLAY_BACKWARD`] for more details. -"); - hash_newtype!(Wtxid, sha256d::Hash, 32, doc="A bitcoin witness transaction ID."); - hash_newtype!(BlockHash, sha256d::Hash, 32, doc="A bitcoin block hash."); + /// A bitcoin witness transaction ID. + pub struct Wtxid(sha256d::Hash); + /// A bitcoin block hash. + pub struct BlockHash(sha256d::Hash); - hash_newtype!(PubkeyHash, hash160::Hash, 20, doc="A hash of a public key."); - hash_newtype!(ScriptHash, hash160::Hash, 20, doc="A hash of Bitcoin Script bytecode."); - hash_newtype!(WPubkeyHash, hash160::Hash, 20, doc="SegWit version of a public key hash."); - hash_newtype!(WScriptHash, sha256::Hash, 32, doc="SegWit version of a Bitcoin Script bytecode hash."); + /// A hash of a public key. + pub struct PubkeyHash(hash160::Hash); + /// A hash of Bitcoin Script bytecode. + pub struct ScriptHash(hash160::Hash); + /// SegWit version of a public key hash. + pub struct WPubkeyHash(hash160::Hash); + /// SegWit version of a Bitcoin Script bytecode hash. + pub struct WScriptHash(sha256::Hash); - hash_newtype!(TxMerkleNode, sha256d::Hash, 32, doc="A hash of the Merkle tree branch or root for transactions"); - hash_newtype!(WitnessMerkleNode, sha256d::Hash, 32, doc="A hash corresponding to the Merkle tree root for witness data"); - hash_newtype!(WitnessCommitment, sha256d::Hash, 32, doc="A hash corresponding to the witness structure commitment in the coinbase transaction"); - hash_newtype!(XpubIdentifier, hash160::Hash, 20, doc="XpubIdentifier as defined in BIP-32."); + /// A hash of the Merkle tree branch or root for transactions + pub struct TxMerkleNode(sha256d::Hash); + /// A hash corresponding to the Merkle tree root for witness data + pub struct WitnessMerkleNode(sha256d::Hash); + /// A hash corresponding to the witness structure commitment in the coinbase transaction + pub struct WitnessCommitment(sha256d::Hash); + /// XpubIdentifier as defined in BIP-32. + pub struct XpubIdentifier(hash160::Hash); - hash_newtype!(FilterHash, sha256d::Hash, 32, doc="Filter hash, as defined in BIP-157"); - hash_newtype!(FilterHeader, sha256d::Hash, 32, doc="Filter header, as defined in BIP-157"); + /// Filter hash, as defined in BIP-157 + pub struct FilterHash(sha256d::Hash); + /// Filter header, as defined in BIP-157 + pub struct FilterHeader(sha256d::Hash); + } impl_hashencode!(Txid); impl_hashencode!(Wtxid); diff --git a/bitcoin/src/taproot.rs b/bitcoin/src/taproot.rs index 06bfe9e0..5ca97c79 100644 --- a/bitcoin/src/taproot.rs +++ b/bitcoin/src/taproot.rs @@ -46,16 +46,16 @@ const MIDSTATE_TAPTWEAK: [u8; 32] = [ sha256t_hash_newtype!(TapLeafHash, TapLeafTag, MIDSTATE_TAPLEAF, 64, doc="Taproot-tagged hash with tag \"TapLeaf\". -This is used for computing tapscript script spend hash.", false +This is used for computing tapscript script spend hash.", forward ); #[rustfmt::skip] sha256t_hash_newtype!(TapNodeHash, TapBranchTag, MIDSTATE_TAPBRANCH, 64, - doc="Tagged hash used in taproot trees; see BIP-340 for tagging rules", false + doc="Tagged hash used in taproot trees; see BIP-340 for tagging rules", forward ); #[rustfmt::skip] sha256t_hash_newtype!(TapTweakHash, TapTweakTag, MIDSTATE_TAPTWEAK, 64, doc="Taproot-tagged hash with tag \"TapTweak\". - This hash type is used while computing the tweaked public key", false + This hash type is used while computing the tweaked public key", forward ); impl TapTweakHash { diff --git a/hashes/embedded/src/main.rs b/hashes/embedded/src/main.rs index 22773ad2..98bb4bb3 100644 --- a/hashes/embedded/src/main.rs +++ b/hashes/embedded/src/main.rs @@ -18,7 +18,9 @@ use cortex_m_rt::entry; use cortex_m_semihosting::{debug, hprintln}; use panic_halt as _; -hash_newtype!(TestType, sha256::Hash, 32, doc = "test"); +hash_newtype! { + struct TestType(sha256::Hash); +} // this is the allocator the application will use #[cfg(feature = "alloc")] diff --git a/hashes/extended_tests/schemars/src/main.rs b/hashes/extended_tests/schemars/src/main.rs index 3bfadcec..23c36a9c 100644 --- a/hashes/extended_tests/schemars/src/main.rs +++ b/hashes/extended_tests/schemars/src/main.rs @@ -139,7 +139,7 @@ mod tests { TEST_MIDSTATE, 64, doc = "test hash", - true + backward ); static HASH_BYTES: [u8; 32] = [ 0xef, 0x53, 0x7f, 0x25, 0xc8, 0x95, 0xbf, 0xa7, 0x82, 0x52, 0x65, 0x29, 0xa9, 0xb6, diff --git a/hashes/src/lib.rs b/hashes/src/lib.rs index 66b3a75d..3b6d062a 100644 --- a/hashes/src/lib.rs +++ b/hashes/src/lib.rs @@ -220,8 +220,13 @@ pub trait Hash: Copy + Clone + PartialEq + Eq + PartialOrd + Ord + mod tests { use crate::{Hash, sha256d}; - hash_newtype!(TestNewtype, sha256d::Hash, 32, doc="A test newtype"); - hash_newtype!(TestNewtype2, sha256d::Hash, 32, doc="A test newtype"); + hash_newtype! { + /// A test newtype + struct TestNewtype(sha256d::Hash); + + /// A test newtype + struct TestNewtype2(sha256d::Hash); + } #[test] fn convert_newtypes() { diff --git a/hashes/src/sha256t.rs b/hashes/src/sha256t.rs index e52e0ae0..6f47448c 100644 --- a/hashes/src/sha256t.rs +++ b/hashes/src/sha256t.rs @@ -97,11 +97,11 @@ fn from_engine(e: sha256::HashEngine) -> Hash { /// - a sha256t::Hash type alias. #[macro_export] macro_rules! sha256t_hash_newtype { - ($newtype:ident, $tag:ident, $midstate:ident, $midstate_len:expr, $docs:meta, $reverse: expr) => { - sha256t_hash_newtype!($newtype, $tag, $midstate, $midstate_len, $docs, $reverse, stringify!($newtype)); + ($newtype:ident, $tag:ident, $midstate:ident, $midstate_len:expr, $docs:meta, $direction:tt) => { + sha256t_hash_newtype!($newtype, $tag, $midstate, $midstate_len, $docs, $direction, stringify!($newtype)); }; - ($newtype:ident, $tag:ident, $midstate:ident, $midstate_len:expr, $docs:meta, $reverse: expr, $sname:expr) => { + ($newtype:ident, $tag:ident, $midstate:ident, $midstate_len:expr, $docs:meta, $direction:tt, $sname:expr) => { #[doc = "The tag used for ["] #[doc = $sname] #[doc = "]"] @@ -115,7 +115,11 @@ macro_rules! sha256t_hash_newtype { } } - $crate::hash_newtype!($newtype, $crate::sha256t::Hash<$tag>, 32, $docs, $reverse); + $crate::hash_newtype! { + #[$docs] + #[hash_newtype($direction)] + pub struct $newtype($crate::sha256t::Hash<$tag>); + } }; } @@ -146,7 +150,7 @@ mod tests { #[cfg(feature = "alloc")] pub type TestHash = sha256t::Hash; - sha256t_hash_newtype!(NewTypeHash, NewTypeTag, TEST_MIDSTATE, 64, doc="test hash", true); + sha256t_hash_newtype!(NewTypeHash, NewTypeTag, TEST_MIDSTATE, 64, doc="test hash", backward); #[test] #[cfg(feature = "alloc")] diff --git a/hashes/src/util.rs b/hashes/src/util.rs index ca007300..743e94c7 100644 --- a/hashes/src/util.rs +++ b/hashes/src/util.rs @@ -110,28 +110,77 @@ macro_rules! engine_input_impl( /// 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 leave it out if you +/// don't want the type or its field to be private. +/// +/// Whether the hash is reversed or not 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`]. With the `serde` feature on, this also adds +/// [`Serialize`](serde::Serialize) and [`Deserialize](serde::Deserialize) implementations. +/// +/// 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); +/// } +/// ``` #[macro_export] macro_rules! hash_newtype { - ($newtype:ident, $hash:ty, $len:expr, $docs:meta) => { - $crate::hash_newtype!($newtype, $hash, $len, $docs, <$hash as $crate::Hash>::DISPLAY_BACKWARD); - }; - ($newtype:ident, $hash:ty, $len:expr, $docs:meta, $reverse:expr) => { - #[$docs] - #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] - #[repr(transparent)] - pub struct $newtype($hash); + ($($(#[$($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::hex_fmt_impl!($reverse, $newtype); - $crate::serde_impl!($newtype, $len); + $crate::hash_newtype_struct! { + $type_vis struct $newtype($(#[$field_attrs])* $field_vis $hash); + + $({ $($type_attrs)* })* + } + + $crate::hex_fmt_impl!(<$newtype as $crate::Hash>::DISPLAY_BACKWARD, $newtype); + $crate::serde_impl!($newtype, <$newtype as $crate::Hash>::LEN); $crate::borrow_slice_impl!($newtype); impl $newtype { /// Creates this type from the inner hash type. + #[allow(unused)] // the user of macro may not need this pub fn from_hash(inner: $hash) -> $newtype { $newtype(inner) } /// Converts this type into the inner hash type. + #[allow(unused)] // the user of macro may not need this pub fn as_hash(&self) -> $hash { // Hashes implement Copy so don't need into_hash. self.0 @@ -156,7 +205,7 @@ macro_rules! hash_newtype { type Inner = <$hash as $crate::Hash>::Inner; const LEN: usize = <$hash as $crate::Hash>::LEN; - const DISPLAY_BACKWARD: bool = $reverse; + const DISPLAY_BACKWARD: bool = $crate::hash_newtype_get_direction!($hash, $(#[$($type_attrs)*])*); fn engine() -> Self::Engine { <$hash as $crate::Hash>::engine() @@ -199,7 +248,7 @@ macro_rules! hash_newtype { use $crate::hex::{HexIterator, FromHex}; use $crate::Hash; - let inner: <$hash as Hash>::Inner = if $reverse { + let inner: <$hash as Hash>::Inner = if ::DISPLAY_BACKWARD { FromHex::from_byte_iter(HexIterator::new(s)?.rev())? } else { FromHex::from_byte_iter(HexIterator::new(s)?)? @@ -208,9 +257,9 @@ macro_rules! hash_newtype { } } - impl $crate::_export::_core::convert::AsRef<[u8; $len]> for $newtype { - fn as_ref(&self) -> &[u8; $len] { - AsRef::<[u8; $len]>::as_ref(&self.0) + impl $crate::_export::_core::convert::AsRef<[u8; <$hash as $crate::Hash>::LEN]> for $newtype { + fn as_ref(&self) -> &[u8; <$hash as $crate::Hash>::LEN] { + AsRef::<[u8; <$hash as $crate::Hash>::LEN]>::as_ref(&self.0) } } @@ -222,9 +271,77 @@ macro_rules! hash_newtype { &self.0[index] } } + )+ }; } +#[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)* + } + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! hash_newtype_our_attrs { + (hash_newtype($($attr:tt)*)) => { $($attr)* }; + ($($ignore:tt)*) => {}; +} + +#[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)*) }; +} + +#[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])*) + }; +} + +#[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)*) => {}; +} + #[cfg(feature = "schemars")] #[cfg_attr(docsrs, doc(cfg(feature = "schemars")))] pub mod json_hex_string { @@ -276,7 +393,10 @@ mod test { assert_eq!(borrowed, hash.as_inner()); } - hash_newtype!(TestHash, crate::sha256d::Hash, 32, doc="Test hash."); + hash_newtype! { + /// Test hash. + struct TestHash(crate::sha256d::Hash); + } #[test] fn display() {