Make `hash_newtype` evocative of the output

The API guidelines say macro input should be evocative of the output.
`hash_newtype` didn't have this property.

This change makes it look exactly like the resulting struct, `$len`
parameter was removed since it's not needed, reversing is controlled
using an attribute. The macro is also better documented and ready to be
extended in the future.

The tagged SHA256 newtype is not yet modified because it has a more
complicated input parameters.

Closes #1648
This commit is contained in:
Martin Habovstiak 2023-02-22 09:20:28 +01:00
parent b018f3e90b
commit 06f1f027ab
8 changed files with 201 additions and 53 deletions

View File

@ -47,22 +47,25 @@ macro_rules! impl_thirty_two_byte_hash {
}
}
#[rustfmt::skip]
hash_newtype!(LegacySighash, sha256d::Hash,
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,
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.

View File

@ -55,30 +55,44 @@ pub use newtypes::*;
mod newtypes {
use crate::hashes::{sha256, sha256d, hash160, hash_newtype};
hash_newtype!(
Txid, sha256d::Hash, 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, doc="A bitcoin witness transaction ID.");
hash_newtype!(BlockHash, sha256d::Hash, 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, doc="A hash of a public key.");
hash_newtype!(ScriptHash, hash160::Hash, doc="A hash of Bitcoin Script bytecode.");
hash_newtype!(WPubkeyHash, hash160::Hash, doc="SegWit version of a public key hash.");
hash_newtype!(WScriptHash, sha256::Hash, 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, doc="A hash of the Merkle tree branch or root for transactions");
hash_newtype!(WitnessMerkleNode, sha256d::Hash, doc="A hash corresponding to the Merkle tree root for witness data");
hash_newtype!(WitnessCommitment, sha256d::Hash, doc="A hash corresponding to the witness structure commitment in the coinbase transaction");
hash_newtype!(XpubIdentifier, hash160::Hash, 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, doc="Filter hash, as defined in BIP-157");
hash_newtype!(FilterHeader, sha256d::Hash, 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);

View File

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

View File

@ -18,7 +18,9 @@ use cortex_m_rt::entry;
use cortex_m_semihosting::{debug, hprintln};
use panic_halt as _;
hash_newtype!(TestType, sha256::Hash, doc = "test");
hash_newtype! {
struct TestType(sha256::Hash);
}
// this is the allocator the application will use
#[cfg(feature = "alloc")]

View File

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

View File

@ -220,8 +220,13 @@ pub trait Hash: Copy + Clone + PartialEq + Eq + PartialOrd + Ord +
mod tests {
use crate::{Hash, sha256d};
hash_newtype!(TestNewtype, sha256d::Hash, doc="A test newtype");
hash_newtype!(TestNewtype2, sha256d::Hash, doc="A test newtype");
hash_newtype! {
/// A test newtype
struct TestNewtype(sha256d::Hash);
/// A test newtype
struct TestNewtype2(sha256d::Hash);
}
#[test]
fn convert_newtypes() {

View File

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

View File

@ -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, $docs:meta) => {
$crate::hash_newtype!($newtype, $hash, $docs, <$hash as $crate::Hash>::DISPLAY_BACKWARD);
};
($newtype:ident, $hash:ty, $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::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 <Self as $crate::Hash>::DISPLAY_BACKWARD {
FromHex::from_byte_iter(HexIterator::new(s)?.rev())?
} else {
FromHex::from_byte_iter(HexIterator::new(s)?)?
@ -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, doc="Test hash.");
hash_newtype! {
/// Test hash.
struct TestHash(crate::sha256d::Hash);
}
#[test]
fn display() {