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:
parent
b018f3e90b
commit
06f1f027ab
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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")]
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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")]
|
||||
|
|
|
@ -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() {
|
||||
|
|
Loading…
Reference in New Issue