diff --git a/bitcoin/src/blockdata/block.rs b/bitcoin/src/blockdata/block.rs index 7d6a9897e..247e98419 100644 --- a/bitcoin/src/blockdata/block.rs +++ b/bitcoin/src/blockdata/block.rs @@ -20,103 +20,54 @@ use crate::consensus::{encode, Decodable, Encodable}; use crate::internal_macros::{impl_consensus_encoding, impl_hashencode}; use crate::merkle_tree::{MerkleNode as _, TxMerkleNode, WitnessMerkleNode}; use crate::network::Params; -use crate::pow::{CompactTarget, Target, Work}; +use crate::pow::{Target, Work}; use crate::prelude::Vec; use crate::script::{self, ScriptExt as _}; use crate::transaction::{Transaction, Wtxid}; #[rustfmt::skip] // Keep public re-exports separate. #[doc(inline)] -pub use primitives::block::*; +pub use primitives::block::{Version, BlockHash, Header, WitnessCommitment}; impl_hashencode!(BlockHash); -/// Bitcoin block header. -/// -/// Contains all the block's information except the actual transactions, but -/// including a root of a [Merkle tree] committing to all transactions in the block. -/// -/// [Merkle tree]: https://en.wikipedia.org/wiki/Merkle_tree -/// -/// ### Bitcoin Core References -/// -/// * [CBlockHeader definition](https://github.com/bitcoin/bitcoin/blob/345457b542b6a980ccfbc868af0970a6f91d1b82/src/primitives/block.h#L20) -#[derive(Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct Header { - /// Block version, now repurposed for soft fork signalling. - pub version: Version, - /// Reference to the previous block in the chain. - pub prev_blockhash: BlockHash, - /// The root hash of the Merkle tree of transactions in the block. - pub merkle_root: TxMerkleNode, - /// The timestamp of the block, as claimed by the miner. - pub time: u32, - /// The target value below which the blockhash must lie. - pub bits: CompactTarget, - /// The nonce, selected to obtain a low enough blockhash. - pub nonce: u32, -} - impl_consensus_encoding!(Header, version, prev_blockhash, merkle_root, time, bits, nonce); -impl Header { - /// The number of bytes that the block header contributes to the size of a block. - // Serialized length of fields (version, prev_blockhash, merkle_root, time, bits, nonce) - pub const SIZE: usize = 4 + 32 + 32 + 4 + 4 + 4; // 80 +crate::internal_macros::define_extension_trait! { + /// Extension functionality for the [`Header`] type. + pub trait HeaderExt impl for Header { + /// Computes the target (range [0, T] inclusive) that a blockhash must land in to be valid. + fn target(&self) -> Target { self.bits.into() } - /// Returns the block hash. - pub fn block_hash(&self) -> BlockHash { - let mut engine = sha256d::Hash::engine(); - self.consensus_encode(&mut engine).expect("engines don't error"); - BlockHash::from_byte_array(sha256d::Hash::from_engine(engine).to_byte_array()) - } - - /// Computes the target (range [0, T] inclusive) that a blockhash must land in to be valid. - pub fn target(&self) -> Target { self.bits.into() } - - /// Computes the popular "difficulty" measure for mining. - /// - /// Difficulty represents how difficult the current target makes it to find a block, relative to - /// how difficult it would be at the highest possible target (highest target == lowest difficulty). - pub fn difficulty(&self, params: impl AsRef) -> u128 { - self.target().difficulty(params) - } - - /// Computes the popular "difficulty" measure for mining and returns a float value of f64. - pub fn difficulty_float(&self, params: impl AsRef) -> f64 { - self.target().difficulty_float(params) - } - - /// Checks that the proof-of-work for the block is valid, returning the block hash. - pub fn validate_pow(&self, required_target: Target) -> Result { - let target = self.target(); - if target != required_target { - return Err(ValidationError::BadTarget); + /// Computes the popular "difficulty" measure for mining. + /// + /// Difficulty represents how difficult the current target makes it to find a block, relative to + /// how difficult it would be at the highest possible target (highest target == lowest difficulty). + fn difficulty(&self, params: impl AsRef) -> u128 { + self.target().difficulty(params) } - let block_hash = self.block_hash(); - if target.is_met_by(block_hash) { - Ok(block_hash) - } else { - Err(ValidationError::BadProofOfWork) + + /// Computes the popular "difficulty" measure for mining and returns a float value of f64. + fn difficulty_float(&self, params: impl AsRef) -> f64 { + self.target().difficulty_float(params) } - } - /// Returns the total work of the block. - pub fn work(&self) -> Work { self.target().to_work() } -} + /// Checks that the proof-of-work for the block is valid, returning the block hash. + fn validate_pow(&self, required_target: Target) -> Result { + let target = self.target(); + if target != required_target { + return Err(ValidationError::BadTarget); + } + let block_hash = self.block_hash(); + if target.is_met_by(block_hash) { + Ok(block_hash) + } else { + Err(ValidationError::BadProofOfWork) + } + } -impl fmt::Debug for Header { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("Header") - .field("block_hash", &self.block_hash()) - .field("version", &self.version) - .field("prev_blockhash", &self.prev_blockhash) - .field("merkle_root", &self.merkle_root) - .field("time", &self.time) - .field("bits", &self.bits) - .field("nonce", &self.nonce) - .finish() + /// Returns the total work of the block. + fn work(&self) -> Work { self.target().to_work() } } } @@ -308,14 +259,6 @@ impl Block { } } -impl From
for BlockHash { - fn from(header: Header) -> BlockHash { header.block_hash() } -} - -impl From<&Header> for BlockHash { - fn from(header: &Header) -> BlockHash { header.block_hash() } -} - impl From for BlockHash { fn from(block: Block) -> BlockHash { block.block_hash() } } @@ -400,20 +343,6 @@ impl std::error::Error for ValidationError { } } -#[cfg(feature = "arbitrary")] -impl<'a> Arbitrary<'a> for Header { - fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { - Ok(Header { - version: Version::arbitrary(u)?, - prev_blockhash: BlockHash::from_byte_array(u.arbitrary()?), - merkle_root: TxMerkleNode::from_byte_array(u.arbitrary()?), - time: u.arbitrary()?, - bits: CompactTarget::from_consensus(u.arbitrary()?), - nonce: u.arbitrary()?, - }) - } -} - #[cfg(feature = "arbitrary")] impl<'a> Arbitrary<'a> for Block { fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { @@ -428,7 +357,7 @@ mod tests { use super::*; use crate::consensus::encode::{deserialize, serialize}; - use crate::{Network, TestnetVersion}; + use crate::{CompactTarget, Network, TestnetVersion}; #[test] fn test_coinbase_and_bip34() { @@ -572,16 +501,27 @@ mod tests { } } + fn header() -> Header { + let header = hex!("010000004ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000bf4473e53794beae34e64fccc471dace6ae544180816f89591894e0f417a914cd74d6e49ffff001d323b3a7b"); + deserialize(&header).expect("can't deserialize correct block header") + } + #[test] fn compact_roundrtip_test() { - let some_header = hex!("010000004ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000bf4473e53794beae34e64fccc471dace6ae544180816f89591894e0f417a914cd74d6e49ffff001d323b3a7b"); - - let header: Header = - deserialize(&some_header).expect("can't deserialize correct block header"); - + let header = header(); assert_eq!(header.bits, header.target().to_compact_lossy()); } + #[test] + fn header_block_hash_regression() { + let header = header(); + let block_hash = "00000000b0c5a240b2a61d2e75692224efd4cbecdf6eaf4cc2cf477ca7c270e7"; + + let want = block_hash.parse::().unwrap(); + let got = header.block_hash(); + assert_eq!(got, want) + } + #[test] fn soft_fork_signalling() { for i in 0..31 { diff --git a/bitcoin/src/pow.rs b/bitcoin/src/pow.rs index a22fcb64c..e62c6a779 100644 --- a/bitcoin/src/pow.rs +++ b/bitcoin/src/pow.rs @@ -254,7 +254,7 @@ impl Target { /// Panics if `self` is zero (divide by zero). /// /// [max]: Target::max - /// [target]: crate::block::Header::target + /// [target]: crate::block::HeaderExt::target #[cfg_attr(all(test, mutate), mutate)] pub fn difficulty(&self, params: impl AsRef) -> u128 { // Panic here may be eaiser to debug than during the actual division. diff --git a/primitives/src/block.rs b/primitives/src/block.rs index 6eeed6f24..5a87f2039 100644 --- a/primitives/src/block.rs +++ b/primitives/src/block.rs @@ -7,9 +7,83 @@ //! module describes structures and functions needed to describe //! these blocks and the blockchain. +use core::fmt; + #[cfg(feature = "arbitrary")] use arbitrary::{Arbitrary, Unstructured}; -use hashes::sha256d; +use hashes::{sha256d, HashEngine as _}; + +use crate::merkle_tree::TxMerkleNode; +use crate::pow::CompactTarget; + +/// Bitcoin block header. +/// +/// Contains all the block's information except the actual transactions, but +/// including a root of a [Merkle tree] committing to all transactions in the block. +/// +/// [Merkle tree]: https://en.wikipedia.org/wiki/Merkle_tree +/// +/// ### Bitcoin Core References +/// +/// * [CBlockHeader definition](https://github.com/bitcoin/bitcoin/blob/345457b542b6a980ccfbc868af0970a6f91d1b82/src/primitives/block.h#L20) +#[derive(Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct Header { + /// Block version, now repurposed for soft fork signalling. + pub version: Version, + /// Reference to the previous block in the chain. + pub prev_blockhash: BlockHash, + /// The root hash of the Merkle tree of transactions in the block. + pub merkle_root: TxMerkleNode, + /// The timestamp of the block, as claimed by the miner. + pub time: u32, + /// The target value below which the blockhash must lie. + pub bits: CompactTarget, + /// The nonce, selected to obtain a low enough blockhash. + pub nonce: u32, +} + +impl Header { + /// The number of bytes that the block header contributes to the size of a block. + // Serialized length of fields (version, prev_blockhash, merkle_root, time, bits, nonce) + pub const SIZE: usize = 4 + 32 + 32 + 4 + 4 + 4; // 80 + + /// Returns the block hash. + // This is the same as `Encodable` but done manually because `Encodable` isn't in `primitives`. + pub fn block_hash(&self) -> BlockHash { + let mut engine = sha256d::Hash::engine(); + engine.input(&self.version.to_consensus().to_le_bytes()); + engine.input(self.prev_blockhash.as_byte_array()); + engine.input(self.merkle_root.as_byte_array()); + engine.input(&self.time.to_le_bytes()); + engine.input(&self.bits.to_consensus().to_le_bytes()); + engine.input(&self.nonce.to_le_bytes()); + + BlockHash::from_byte_array(sha256d::Hash::from_engine(engine).to_byte_array()) + } +} + +impl fmt::Debug for Header { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Header") + .field("block_hash", &self.block_hash()) + .field("version", &self.version) + .field("prev_blockhash", &self.prev_blockhash) + .field("merkle_root", &self.merkle_root) + .field("time", &self.time) + .field("bits", &self.bits) + .field("nonce", &self.nonce) + .finish() + } +} + +impl From
for BlockHash { + fn from(header: Header) -> BlockHash { header.block_hash() } +} + +impl From<&Header> for BlockHash { + fn from(header: &Header) -> BlockHash { header.block_hash() } +} /// Bitcoin block version number. /// @@ -93,6 +167,20 @@ impl BlockHash { pub const GENESIS_PREVIOUS_BLOCK_HASH: Self = Self::from_byte_array([0; 32]); } +#[cfg(feature = "arbitrary")] +impl<'a> Arbitrary<'a> for Header { + fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { + Ok(Header { + version: Version::arbitrary(u)?, + prev_blockhash: BlockHash::from_byte_array(u.arbitrary()?), + merkle_root: TxMerkleNode::from_byte_array(u.arbitrary()?), + time: u.arbitrary()?, + bits: CompactTarget::from_consensus(u.arbitrary()?), + nonce: u.arbitrary()?, + }) + } +} + #[cfg(feature = "arbitrary")] impl<'a> Arbitrary<'a> for Version { fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result {