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
This commit is contained in:
Martin Habovstiak 2023-04-02 15:09:48 +02:00
parent 42d3ae05be
commit 095b7958dd
5 changed files with 122 additions and 59 deletions

View File

@ -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);

View File

@ -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

View File

@ -133,14 +133,12 @@ mod tests {
/// A hash tagged with `$name`.
pub type TestHash = sha256t::Hash<TestHashTag>;
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,

View File

@ -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<Midstate, Error> {

View File

@ -80,42 +80,91 @@ fn from_engine<T: Tag>(e: sha256::HashEngine) -> Hash<T> {
}
/// 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<TestHashTag>;
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")]