From 095b7958dda0a896ef6d8d2d5d72250f6090c86f Mon Sep 17 00:00:00 2001 From: Martin Habovstiak Date: Sun, 2 Apr 2023 15:09:48 +0200 Subject: [PATCH] Make `sha256t_hash_newtype!` evocative of the output. The Rust API guidelines state that macros should be evocative of the output, which is a sensible recommendation. We already had this for `hash_newtype!` macro but didn't for sha256t version. This changes the macro to have this syntax: ```rust sha256t_hash_newtype! { // Order of these structs is fixed. /// Optional documentation details here. Summary is auto-generated. /*pub*/ struct Tag = raw(MIDSTATE_BYTES, LEN); /// Documentation here #[hash_newtype(forward)] // optional, default is backward /*pub*/ struct HashType(/* attributes allowed here */ _); } ``` Closes #1427 --- bitcoin/src/crypto/sighash.rs | 17 ++-- bitcoin/src/taproot.rs | 38 ++++--- hashes/extended_tests/schemars/src/main.rs | 14 ++- hashes/src/sha256.rs | 2 +- hashes/src/sha256t.rs | 110 ++++++++++++++++----- 5 files changed, 122 insertions(+), 59 deletions(-) diff --git a/bitcoin/src/crypto/sighash.rs b/bitcoin/src/crypto/sighash.rs index 437ea337..72e06d62 100644 --- a/bitcoin/src/crypto/sighash.rs +++ b/bitcoin/src/crypto/sighash.rs @@ -61,16 +61,15 @@ hash_newtype! { impl_thirty_two_byte_hash!(LegacySighash); impl_thirty_two_byte_hash!(SegwitV0Sighash); -sha256t_hash_newtype!( - TapSighash, - TapSighashTag, - MIDSTATE_TAPSIGHASH, - 64, - doc = "Taproot-tagged hash with tag \"TapSighash\". +sha256t_hash_newtype! { + pub struct TapSighashTag = raw(MIDSTATE_TAPSIGHASH, 64); -This hash type is used for computing taproot signature hash.", - forward -); + /// Taproot-tagged hash with tag \"TapSighash\". + /// + /// This hash type is used for computing taproot signature hash." + #[hash_newtype(forward)] + pub struct TapSighash(_); +} impl_thirty_two_byte_hash!(TapSighash); diff --git a/bitcoin/src/taproot.rs b/bitcoin/src/taproot.rs index ee21c9a4..491b0afb 100644 --- a/bitcoin/src/taproot.rs +++ b/bitcoin/src/taproot.rs @@ -43,21 +43,31 @@ const MIDSTATE_TAPTWEAK: [u8; 32] = [ // d129a2f3701c655d6583b6c3b941972795f4e23294fd54f4a2ae8d8547ca590b // Taproot test vectors from BIP-341 state the hashes without any reversing -#[rustfmt::skip] -sha256t_hash_newtype!(TapLeafHash, TapLeafTag, MIDSTATE_TAPLEAF, 64, - doc="Taproot-tagged hash with tag \"TapLeaf\". +sha256t_hash_newtype! { + pub struct TapLeafTag = raw(MIDSTATE_TAPLEAF, 64); -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", 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", forward -); + /// Taproot-tagged hash with tag \"TapLeaf\". + /// + /// This is used for computing tapscript script spend hash. + #[hash_newtype(forward)] + pub struct TapLeafHash(_); + + pub struct TapBranchTag = raw(MIDSTATE_TAPBRANCH, 64); + + /// Tagged hash used in taproot trees. + /// + /// See BIP-340 for tagging rules. + #[hash_newtype(forward)] + pub struct TapNodeHash(_); + + pub struct TapTweakTag = raw(MIDSTATE_TAPTWEAK, 64); + + /// Taproot-tagged hash with tag \"TapTweak\". + /// + /// This hash type is used while computing the tweaked public key. + #[hash_newtype(forward)] + pub struct TapTweakHash(_); +} impl TapTweakHash { /// Creates a new BIP341 [`TapTweakHash`] from key and tweak. Produces `H_taptweak(P||R)` where diff --git a/hashes/extended_tests/schemars/src/main.rs b/hashes/extended_tests/schemars/src/main.rs index 7ceee050..2af4690e 100644 --- a/hashes/extended_tests/schemars/src/main.rs +++ b/hashes/extended_tests/schemars/src/main.rs @@ -133,14 +133,12 @@ mod tests { /// A hash tagged with `$name`. pub type TestHash = sha256t::Hash; - sha256t_hash_newtype!( - NewTypeHash, - NewTypeTag, - TEST_MIDSTATE, - 64, - doc = "test hash", - backward - ); + sha256t_hash_newtype! { + struct NewTypeTag = raw(TEST_MIDSTATE, 64); + + #[hash_newtype(backward)] + struct NewTypeHash(_); + } static HASH_BYTES: [u8; 32] = [ 0xef, 0x53, 0x7f, 0x25, 0xc8, 0x95, 0xbf, 0xa7, 0x82, 0x52, 0x65, 0x29, 0xa9, 0xb6, 0x3d, 0x97, 0xaa, 0x63, 0x15, 0x64, 0xd5, 0xd7, 0x89, 0xc2, 0xb7, 0x65, 0x44, 0x8c, diff --git a/hashes/src/sha256.rs b/hashes/src/sha256.rs index 0bbdf6c6..5a020b4e 100644 --- a/hashes/src/sha256.rs +++ b/hashes/src/sha256.rs @@ -151,7 +151,7 @@ impl Midstate { const DISPLAY_BACKWARD: bool = true; /// Construct a new [`Midstate`] from the inner value. - pub fn from_byte_array(inner: [u8; 32]) -> Self { Midstate(inner) } + pub const fn from_byte_array(inner: [u8; 32]) -> Self { Midstate(inner) } /// Copies a byte slice into the [`Midstate`] object. pub fn from_slice(sl: &[u8]) -> Result { diff --git a/hashes/src/sha256t.rs b/hashes/src/sha256t.rs index 95c23e19..469a3816 100644 --- a/hashes/src/sha256t.rs +++ b/hashes/src/sha256t.rs @@ -80,42 +80,91 @@ fn from_engine(e: sha256::HashEngine) -> Hash { } /// Macro used to define a newtype tagged hash. -/// It creates two public types: -/// - a sha256t::Tag struct, -/// - a sha256t::Hash type alias. +/// +/// This macro creates two types: +/// +/// * a tag struct +/// * a hash wrapper +/// +/// The syntax is: +/// +/// ``` +/// # use bitcoin_hashes::sha256t_hash_newtype; +/// sha256t_hash_newtype! { +/// /// Optional documentation details here. +/// /// Summary is always generated. +/// pub struct FooTag = hash_str("foo"); +/// +/// /// A foo hash. +/// // Direction works just like in case of hash_newtype! macro. +/// #[hash_newtype(forward)] +/// pub struct FooHash(_); +/// } +/// ``` +/// +/// The structs must be defined in this order - tag first, then hash type. `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_LENGHT)` instead. +/// +/// Both visibility modifiers and attributes are optional and passed to inner structs (excluding +/// `#[hash_newtype(...)]`). The attributes suffer same compiler performance limitations as in +/// [`hash_newtype`] macro. +/// +/// The macro accepts multiple inputs so you can define multiple hash newtypes in one macro call. +/// Just make sure to enter the structs in order `Tag0`, `Hash0`, `Tag1`, `Hash1`... +/// +/// [`hash_newtype`]: crate::hash_newtype #[macro_export] macro_rules! sha256t_hash_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, $direction:tt, $sname:expr) => { - #[doc = "The tag used for ["] - #[doc = $sname] - #[doc = "]"] - #[derive(Copy, Clone, PartialEq, Eq, Default, PartialOrd, Ord, Hash)] - pub struct $tag; + ($($(#[$($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)*])* _);)+) => { + $( + $crate::sha256t_hash_newtype_tag!($tag_vis, $tag, stringify!($hash_name), $(#[$($tag_attr)*])*); impl $crate::sha256t::Tag for $tag { + #[inline] fn engine() -> $crate::sha256::HashEngine { - let midstate = $crate::sha256::Midstate::from_byte_array($midstate); - $crate::sha256::HashEngine::from_midstate(midstate, $midstate_len) + const MIDSTATE: ($crate::sha256::Midstate, usize) = $crate::sha256t_hash_newtype_tag_constructor!($constructor, $($tag_value)+); + #[allow(unused)] + const _LENGTH_CHECK: () = [(); 1][MIDSTATE.1 % 64]; + $crate::sha256::HashEngine::from_midstate(MIDSTATE.0, MIDSTATE.1) } } $crate::hash_newtype! { - #[$docs] - #[hash_newtype($direction)] - pub struct $newtype($crate::sha256t::Hash<$tag>); + $(#[$($hash_attr)*])* + $hash_vis struct $hash_name($(#[$($field_attr)*])* $crate::sha256t::Hash<$tag>); } + )+ + } +} + +// Workaround macros being unavailable in attributes. +#[doc(hidden)] +#[macro_export] +macro_rules! sha256t_hash_newtype_tag { + ($vis:vis, $tag:ident, $name:expr, $(#[$($attr:meta)*])*) => { + #[doc = "The tag used for [`"] + #[doc = $name] + #[doc = "`]\n\n"] + $(#[$($attr)*])* + #[derive(Copy, Clone, PartialEq, Eq, Default, PartialOrd, Ord, Hash)] + $vis struct $tag; + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! sha256t_hash_newtype_tag_constructor { + (hash_str, $value:expr) => { + ($crate::sha256::Midstate::hash_tag($value.as_bytes()), 64) + }; + (hash_bytes, $value:expr) => { + ($crate::sha256::Midstate::hash_tag($value), 64) + }; + (raw, $bytes:expr, $len:expr) => { + ($crate::sha256::Midstate::from_byte_array($bytes), $len) }; } @@ -146,7 +195,14 @@ mod tests { #[cfg(feature = "alloc")] pub type TestHash = sha256t::Hash; - sha256t_hash_newtype!(NewTypeHash, NewTypeTag, TEST_MIDSTATE, 64, doc = "test hash", backward); + sha256t_hash_newtype! { + /// Test detailed explanation. + struct NewTypeTag = raw(TEST_MIDSTATE, 64); + + /// A test hash. + #[hash_newtype(backward)] + struct NewTypeHash(_); + } #[test] #[cfg(feature = "alloc")]