hashes: drop the `all_zeros` method on arbitrary hashes
Manually implement it for Wtxid, Txid and BlockHash, where the all-zero "hash" has a consensus meaning. But in general we should not be implementing this method unless we have a good reason to do so. It can be emulated or implemeted in terms of from_byte_array. The use of Wtxid::all_zeros is obscure and specific enough that I am tempted to drop it. But for txid and blockhash, the 0 hash appears in actual blockdata and we should keep it. All other uses of all_zeros were either in test code or in places where the specific hash was not important and [u8; 32] was a more appropriate type.
This commit is contained in:
parent
9f8797f486
commit
8869f35a69
|
@ -28,6 +28,13 @@ hashes::hash_newtype! {
|
||||||
pub struct WitnessCommitment(sha256d::Hash);
|
pub struct WitnessCommitment(sha256d::Hash);
|
||||||
}
|
}
|
||||||
impl_hashencode!(BlockHash);
|
impl_hashencode!(BlockHash);
|
||||||
|
impl BlockHash {
|
||||||
|
/// The "all zeros" blockhash.
|
||||||
|
///
|
||||||
|
/// This is not the hash of a real block. It is used as the previous blockhash
|
||||||
|
/// of the genesis block and in other placeholder contexts.
|
||||||
|
pub fn all_zeros() -> Self { Self::from_byte_array([0; 32]) }
|
||||||
|
}
|
||||||
|
|
||||||
/// Bitcoin block header.
|
/// Bitcoin block header.
|
||||||
///
|
///
|
||||||
|
|
|
@ -49,6 +49,24 @@ hashes::hash_newtype! {
|
||||||
impl_hashencode!(Txid);
|
impl_hashencode!(Txid);
|
||||||
impl_hashencode!(Wtxid);
|
impl_hashencode!(Wtxid);
|
||||||
|
|
||||||
|
impl Txid {
|
||||||
|
/// The "all zeros" TXID.
|
||||||
|
///
|
||||||
|
/// This is used as the "txid" of the dummy input of a coinbase transaction. It is
|
||||||
|
/// not a real TXID and should not be used in other contexts.
|
||||||
|
pub fn all_zeros() -> Self { Self::from_byte_array([0; 32]) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Wtxid {
|
||||||
|
/// The "all zeros" wTXID.
|
||||||
|
///
|
||||||
|
/// This is used as the wTXID for the coinbase transaction when constructing blocks,
|
||||||
|
/// since the coinbase transaction contains a commitment to all transactions' wTXIDs
|
||||||
|
/// but naturally cannot commit to its own. It is not a real wTXID and should not be
|
||||||
|
/// used in other contexts.
|
||||||
|
pub fn all_zeros() -> Self { Self::from_byte_array([0; 32]) }
|
||||||
|
}
|
||||||
|
|
||||||
/// The marker MUST be a 1-byte zero value: 0x00. (BIP-141)
|
/// The marker MUST be a 1-byte zero value: 0x00. (BIP-141)
|
||||||
const SEGWIT_MARKER: u8 = 0x00;
|
const SEGWIT_MARKER: u8 = 0x00;
|
||||||
/// The flag MUST be a 1-byte non-zero value. Currently, 0x01 MUST be used. (BIP-141)
|
/// The flag MUST be a 1-byte non-zero value. Currently, 0x01 MUST be used. (BIP-141)
|
||||||
|
|
|
@ -781,7 +781,7 @@ impl<R: Borrow<Transaction>> SighashCache<R> {
|
||||||
value: Amount,
|
value: Amount,
|
||||||
sighash_type: EcdsaSighashType,
|
sighash_type: EcdsaSighashType,
|
||||||
) -> Result<(), SigningDataError<transaction::InputsIndexError>> {
|
) -> Result<(), SigningDataError<transaction::InputsIndexError>> {
|
||||||
let zero_hash = sha256d::Hash::all_zeros();
|
let zero_hash = [0; 32];
|
||||||
|
|
||||||
let (sighash, anyone_can_pay) = sighash_type.split_anyonecanpay_flag();
|
let (sighash, anyone_can_pay) = sighash_type.split_anyonecanpay_flag();
|
||||||
|
|
||||||
|
@ -819,7 +819,7 @@ impl<R: Borrow<Transaction>> SighashCache<R> {
|
||||||
let hash = LegacySighash::from_engine(single_enc);
|
let hash = LegacySighash::from_engine(single_enc);
|
||||||
writer.write_all(hash.as_byte_array())?;
|
writer.write_all(hash.as_byte_array())?;
|
||||||
} else {
|
} else {
|
||||||
writer.write_all(zero_hash.as_byte_array())?;
|
writer.write_all(&zero_hash)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.tx.borrow().lock_time.consensus_encode(writer)?;
|
self.tx.borrow().lock_time.consensus_encode(writer)?;
|
||||||
|
|
|
@ -603,7 +603,7 @@ mod tests {
|
||||||
|
|
||||||
// Check that it has the same merkle root as the original, and a valid one
|
// Check that it has the same merkle root as the original, and a valid one
|
||||||
assert_eq!(merkle_root_1, merkle_root_2);
|
assert_eq!(merkle_root_1, merkle_root_2);
|
||||||
assert_ne!(merkle_root_2, TxMerkleNode::all_zeros());
|
assert_ne!(merkle_root_2, TxMerkleNode::from_byte_array([0; 32]));
|
||||||
|
|
||||||
// check that it contains the matched transactions (in the same order!)
|
// check that it contains the matched transactions (in the same order!)
|
||||||
assert_eq!(match_txid1, match_txid2);
|
assert_eq!(match_txid1, match_txid2);
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
//! This module describes network messages which are used for passing
|
//! This module describes network messages which are used for passing
|
||||||
//! Bitcoin data (blocks and transactions) around.
|
//! Bitcoin data (blocks and transactions) around.
|
||||||
|
|
||||||
use hashes::sha256d;
|
|
||||||
use io::{BufRead, Write};
|
use io::{BufRead, Write};
|
||||||
|
|
||||||
use crate::block::BlockHash;
|
use crate::block::BlockHash;
|
||||||
|
@ -67,7 +66,7 @@ impl Encodable for Inventory {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Ok(match *self {
|
Ok(match *self {
|
||||||
Inventory::Error => encode_inv!(0, sha256d::Hash::all_zeros()),
|
Inventory::Error => encode_inv!(0, [0; 32]),
|
||||||
Inventory::Transaction(ref t) => encode_inv!(1, t),
|
Inventory::Transaction(ref t) => encode_inv!(1, t),
|
||||||
Inventory::Block(ref b) => encode_inv!(2, b),
|
Inventory::Block(ref b) => encode_inv!(2, b),
|
||||||
Inventory::CompactBlock(ref b) => encode_inv!(4, b),
|
Inventory::CompactBlock(ref b) => encode_inv!(4, b),
|
||||||
|
|
|
@ -1795,7 +1795,7 @@ mod tests {
|
||||||
let current = Header {
|
let current = Header {
|
||||||
version: Version::ONE,
|
version: Version::ONE,
|
||||||
prev_blockhash: BlockHash::all_zeros(),
|
prev_blockhash: BlockHash::all_zeros(),
|
||||||
merkle_root: TxMerkleNode::all_zeros(),
|
merkle_root: TxMerkleNode::from_byte_array([0; 32]),
|
||||||
time: 1599332177,
|
time: 1599332177,
|
||||||
bits: epoch_start.bits,
|
bits: epoch_start.bits,
|
||||||
nonce: epoch_start.nonce,
|
nonce: epoch_start.nonce,
|
||||||
|
@ -1817,7 +1817,7 @@ mod tests {
|
||||||
let epoch_start = Header {
|
let epoch_start = Header {
|
||||||
version: Version::ONE,
|
version: Version::ONE,
|
||||||
prev_blockhash: BlockHash::all_zeros(),
|
prev_blockhash: BlockHash::all_zeros(),
|
||||||
merkle_root: TxMerkleNode::all_zeros(),
|
merkle_root: TxMerkleNode::from_byte_array([0; 32]),
|
||||||
time: 1599332844,
|
time: 1599332844,
|
||||||
bits: starting_bits,
|
bits: starting_bits,
|
||||||
nonce: 0,
|
nonce: 0,
|
||||||
|
@ -1827,7 +1827,7 @@ mod tests {
|
||||||
let current = Header {
|
let current = Header {
|
||||||
version: Version::ONE,
|
version: Version::ONE,
|
||||||
prev_blockhash: BlockHash::all_zeros(),
|
prev_blockhash: BlockHash::all_zeros(),
|
||||||
merkle_root: TxMerkleNode::all_zeros(),
|
merkle_root: TxMerkleNode::from_byte_array([0; 32]),
|
||||||
time: 1600591200,
|
time: 1600591200,
|
||||||
bits: starting_bits,
|
bits: starting_bits,
|
||||||
nonce: 0,
|
nonce: 0,
|
||||||
|
|
|
@ -404,6 +404,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn taptree_hidden() {
|
fn taptree_hidden() {
|
||||||
|
let dummy_hash = TapNodeHash::from_byte_array([0x12; 32]);
|
||||||
let mut builder = compose_taproot_builder(0x51, &[2, 2, 2]);
|
let mut builder = compose_taproot_builder(0x51, &[2, 2, 2]);
|
||||||
builder = builder
|
builder = builder
|
||||||
.add_leaf_with_ver(
|
.add_leaf_with_ver(
|
||||||
|
@ -412,7 +413,7 @@ mod tests {
|
||||||
LeafVersion::from_consensus(0xC2).unwrap(),
|
LeafVersion::from_consensus(0xC2).unwrap(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
builder = builder.add_hidden_node(3, TapNodeHash::all_zeros()).unwrap();
|
builder = builder.add_hidden_node(3, dummy_hash).unwrap();
|
||||||
assert!(TapTree::try_from(builder).is_err());
|
assert!(TapTree::try_from(builder).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -147,11 +147,6 @@ impl<T: Hash> Hash for Hmac<T> {
|
||||||
fn as_byte_array(&self) -> &Self::Bytes { self.0.as_byte_array() }
|
fn as_byte_array(&self) -> &Self::Bytes { self.0.as_byte_array() }
|
||||||
|
|
||||||
fn from_byte_array(bytes: T::Bytes) -> Self { Hmac(T::from_byte_array(bytes)) }
|
fn from_byte_array(bytes: T::Bytes) -> Self { Hmac(T::from_byte_array(bytes)) }
|
||||||
|
|
||||||
fn all_zeros() -> Self {
|
|
||||||
let zeros = T::all_zeros();
|
|
||||||
Hmac(zeros)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
|
|
|
@ -119,8 +119,6 @@ macro_rules! hash_trait_impls {
|
||||||
fn as_byte_array(&self) -> &Self::Bytes { self.as_byte_array() }
|
fn as_byte_array(&self) -> &Self::Bytes { self.as_byte_array() }
|
||||||
|
|
||||||
fn from_byte_array(bytes: Self::Bytes) -> Self { Self::from_byte_array(bytes) }
|
fn from_byte_array(bytes: Self::Bytes) -> Self { Self::from_byte_array(bytes) }
|
||||||
|
|
||||||
fn all_zeros() -> Self { Self::all_zeros() }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -218,13 +216,6 @@ macro_rules! hash_type {
|
||||||
pub const fn from_byte_array(bytes: [u8; $bits / 8]) -> Self {
|
pub const fn from_byte_array(bytes: [u8; $bits / 8]) -> Self {
|
||||||
Self::internal_new(bytes)
|
Self::internal_new(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an all zero hash.
|
|
||||||
///
|
|
||||||
/// An all zeros hash is a made up construct because there is not a known input that can create
|
|
||||||
/// it, however it is used in various places in Bitcoin e.g., the Bitcoin genesis block's
|
|
||||||
/// previous blockhash and the coinbase transaction's outpoint txid.
|
|
||||||
pub const fn all_zeros() -> Self { Hash::internal_new([0x00; $bits / 8]) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "schemars")]
|
#[cfg(feature = "schemars")]
|
||||||
|
|
|
@ -214,13 +214,6 @@ pub trait Hash:
|
||||||
|
|
||||||
/// Constructs a hash from the underlying byte array.
|
/// Constructs a hash from the underlying byte array.
|
||||||
fn from_byte_array(bytes: Self::Bytes) -> Self;
|
fn from_byte_array(bytes: Self::Bytes) -> Self;
|
||||||
|
|
||||||
/// Returns an all zero hash.
|
|
||||||
///
|
|
||||||
/// An all zeros hash is a made up construct because there is not a known input that can create
|
|
||||||
/// it, however it is used in various places in Bitcoin e.g., the Bitcoin genesis block's
|
|
||||||
/// previous blockhash and the coinbase transaction's outpoint txid.
|
|
||||||
fn all_zeros() -> Self;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempted to create a hash from an invalid length slice.
|
/// Attempted to create a hash from an invalid length slice.
|
||||||
|
|
|
@ -117,13 +117,6 @@ where
|
||||||
|
|
||||||
/// Constructs a hash from the underlying byte array.
|
/// Constructs a hash from the underlying byte array.
|
||||||
pub const fn from_byte_array(bytes: [u8; 32]) -> Self { Self::internal_new(bytes) }
|
pub const fn from_byte_array(bytes: [u8; 32]) -> Self { Self::internal_new(bytes) }
|
||||||
|
|
||||||
/// Returns an all zero hash.
|
|
||||||
///
|
|
||||||
/// An all zeros hash is a made up construct because there is not a known input that can create
|
|
||||||
/// it, however it is used in various places in Bitcoin e.g., the Bitcoin genesis block's
|
|
||||||
/// previous blockhash and the coinbase transaction's outpoint txid.
|
|
||||||
pub fn all_zeros() -> Self { Hash::internal_new([0x00; 32]) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Tag> Copy for Hash<T> {}
|
impl<T: Tag> Copy for Hash<T> {}
|
||||||
|
|
|
@ -263,17 +263,6 @@ macro_rules! hash_newtype {
|
||||||
pub const fn from_byte_array(bytes: <$hash as $crate::Hash>::Bytes) -> Self {
|
pub const fn from_byte_array(bytes: <$hash as $crate::Hash>::Bytes) -> Self {
|
||||||
$newtype(<$hash>::from_byte_array(bytes))
|
$newtype(<$hash>::from_byte_array(bytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an all zero hash.
|
|
||||||
///
|
|
||||||
/// An all zeros hash is a made up construct because there is not a known input that can create
|
|
||||||
/// it, however it is used in various places in Bitcoin e.g., the Bitcoin genesis block's
|
|
||||||
/// previous blockhash and the coinbase transaction's outpoint txid.
|
|
||||||
pub fn all_zeros() -> Self {
|
|
||||||
let zeros = <$hash>::all_zeros();
|
|
||||||
$newtype(zeros)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl $crate::_export::_core::convert::From<$hash> for $newtype {
|
impl $crate::_export::_core::convert::From<$hash> for $newtype {
|
||||||
|
@ -309,8 +298,6 @@ macro_rules! hash_newtype {
|
||||||
fn as_byte_array(&self) -> &Self::Bytes { self.as_byte_array() }
|
fn as_byte_array(&self) -> &Self::Bytes { self.as_byte_array() }
|
||||||
|
|
||||||
fn from_byte_array(bytes: Self::Bytes) -> Self { Self::from_byte_array(bytes) }
|
fn from_byte_array(bytes: Self::Bytes) -> Self { Self::from_byte_array(bytes) }
|
||||||
|
|
||||||
fn all_zeros() -> Self { Self::all_zeros() }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl $crate::_export::_core::str::FromStr for $newtype {
|
impl $crate::_export::_core::str::FromStr for $newtype {
|
||||||
|
@ -462,6 +449,10 @@ mod test {
|
||||||
struct TestHash(crate::sha256d::Hash);
|
struct TestHash(crate::sha256d::Hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TestHash {
|
||||||
|
fn all_zeros() -> Self { Self::from_byte_array([0; 32]) }
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn display() {
|
fn display() {
|
||||||
let want = "0000000000000000000000000000000000000000000000000000000000000000";
|
let want = "0000000000000000000000000000000000000000000000000000000000000000";
|
||||||
|
|
Loading…
Reference in New Issue