// SPDX-License-Identifier: CC0-1.0 //! Bitcoin blocks. //! //! A block is a bundle of transactions with a proof-of-work attached, //! which commits to an earlier block to form the blockchain. This //! module describes structures and functions needed to describe //! these blocks and the blockchain. use core::fmt; #[cfg(feature = "alloc")] use core::marker::PhantomData; #[cfg(feature = "arbitrary")] use arbitrary::{Arbitrary, Unstructured}; use hashes::{sha256d, HashEngine as _}; use units::BlockTime; use crate::merkle_tree::TxMerkleNode; #[cfg(feature = "alloc")] use crate::merkle_tree::WitnessMerkleNode; use crate::pow::CompactTarget; #[cfg(feature = "alloc")] use crate::prelude::Vec; #[cfg(feature = "alloc")] use crate::transaction::Transaction; /// Marker for whether or not a block has been validated. /// /// We define valid as: /// /// * The Merkle root of the header matches Merkle root of the transaction list. /// * The witness commitment in coinbase matches the transaction list. /// /// See `bitcoin::block::BlockUncheckedExt::validate()`. #[cfg(feature = "alloc")] pub trait Validation: sealed::Validation + Sync + Send + Sized + Unpin { /// Indicates whether this `Validation` is `Checked` or not. const IS_CHECKED: bool; } /// Bitcoin block. /// /// A collection of transactions with an attached proof of work. /// /// See [Bitcoin Wiki: Block][wiki-block] for more information. /// /// [wiki-block]: https://en.bitcoin.it/wiki/Block /// /// # Bitcoin Core References /// /// * [CBlock definition](https://github.com/bitcoin/bitcoin/blob/345457b542b6a980ccfbc868af0970a6f91d1b82/src/primitives/block.h#L62) #[cfg(feature = "alloc")] #[derive(PartialEq, Eq, Clone, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Block where V: Validation, { /// The block header header: Header, /// List of transactions contained in the block transactions: Vec, /// Cached witness root if its been computed. #[cfg_attr(feature = "serde", serde(skip_serializing))] witness_root: Option, /// Validation marker. marker: PhantomData, } #[cfg(feature = "alloc")] impl Block { /// Constructs a new `Block` without doing any validation. #[inline] pub fn new_unchecked(header: Header, transactions: Vec) -> Block { Block { header, transactions, witness_root: None, marker: PhantomData:: } } /// Ignores block validation logic and just assumes you know what you are doing. /// /// You should only use this function if you trust the block i.e., it comes from a trusted node. #[must_use] #[inline] pub fn assume_checked(self, witness_root: Option) -> Block { Block { header: self.header, transactions: self.transactions, witness_root, marker: PhantomData::, } } /// Decomposes block into its constituent parts. #[inline] pub fn into_parts(self) -> (Header, Vec) { (self.header, self.transactions) } } #[cfg(feature = "alloc")] impl Block { /// Gets a reference to the block header. #[inline] pub fn header(&self) -> &Header { &self.header } /// Gets a reference to the block's list of transactions. #[inline] pub fn transactions(&self) -> &[Transaction] { &self.transactions } /// Returns the cached witness root if one is present. /// /// It is assumed that a block will have the witness root calculated and cached as part of the /// validation process. #[inline] pub fn cached_witness_root(&self) -> Option { self.witness_root } } #[cfg(feature = "alloc")] impl Block { /// Returns the block hash. #[inline] pub fn block_hash(&self) -> BlockHash { self.header.block_hash() } } #[cfg(feature = "alloc")] impl From for BlockHash { #[inline] fn from(block: Block) -> BlockHash { block.block_hash() } } #[cfg(feature = "alloc")] impl From<&Block> for BlockHash { #[inline] fn from(block: &Block) -> BlockHash { block.block_hash() } } /// Marker that the block's merkle root has been successfully validated. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[cfg(feature = "alloc")] pub enum Checked {} #[cfg(feature = "alloc")] impl Validation for Checked { const IS_CHECKED: bool = true; } /// Marker that the block's merkle root has not been validated. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[cfg(feature = "alloc")] pub enum Unchecked {} #[cfg(feature = "alloc")] impl Validation for Unchecked { const IS_CHECKED: bool = false; } #[cfg(feature = "alloc")] mod sealed { /// Seals the block validation marker traits. pub trait Validation {} impl Validation for super::Checked {} impl Validation for super::Unchecked {} } /// 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: BlockTime, /// 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_u32().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()) } } #[cfg(feature = "hex")] impl fmt::Display for Header { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use fmt::Write as _; use hex::DisplayHex as _; let mut buf = arrayvec::ArrayString::<160>::new(); write!( &mut buf, "{}{}{}{}{}{}", self.version.to_consensus().to_le_bytes().as_hex(), self.prev_blockhash.as_byte_array().as_hex(), self.merkle_root.as_byte_array().as_hex(), self.time.to_u32().to_le_bytes().as_hex(), self.bits.to_consensus().to_le_bytes().as_hex(), self.nonce.to_le_bytes().as_hex(), )?; fmt::Display::fmt(&buf, f) } } 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 { #[inline] fn from(header: Header) -> BlockHash { header.block_hash() } } impl From<&Header> for BlockHash { #[inline] fn from(header: &Header) -> BlockHash { header.block_hash() } } /// Bitcoin block version number. /// /// Originally used as a protocol version, but repurposed for soft-fork signaling. /// /// The inner value is a signed integer in Bitcoin Core for historical reasons, if version bits is /// being used the top three bits must be 001, this gives us a useful range of [0x20000000...0x3FFFFFFF]. /// /// > When a block nVersion does not have top bits 001, it is treated as if all bits are 0 for the purposes of deployments. /// /// # Relevant BIPs /// /// * [BIP9 - Version bits with timeout and delay](https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki) (current usage) /// * [BIP34 - Block v2, Height in Coinbase](https://github.com/bitcoin/bips/blob/master/bip-0034.mediawiki) #[derive(Copy, PartialEq, Eq, Clone, Debug, PartialOrd, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Version(i32); impl Version { /// The original Bitcoin Block v1. pub const ONE: Self = Self(1); /// BIP-34 Block v2. pub const TWO: Self = Self(2); /// BIP-9 compatible version number that does not signal for any softforks. pub const NO_SOFT_FORK_SIGNALLING: Self = Self(Self::USE_VERSION_BITS as i32); /// BIP-9 soft fork signal bits mask. const VERSION_BITS_MASK: u32 = 0x1FFF_FFFF; /// 32bit value starting with `001` to use version bits. /// /// The value has the top three bits `001` which enables the use of version bits to signal for soft forks. const USE_VERSION_BITS: u32 = 0x2000_0000; /// Constructs a new [`Version`] from a signed 32 bit integer value. /// /// This is the data type used in consensus code in Bitcoin Core. #[inline] pub const fn from_consensus(v: i32) -> Self { Version(v) } /// Returns the inner `i32` value. /// /// This is the data type used in consensus code in Bitcoin Core. #[inline] pub fn to_consensus(self) -> i32 { self.0 } /// Checks whether the version number is signalling a soft fork at the given bit. /// /// A block is signalling for a soft fork under BIP-9 if the first 3 bits are `001` and /// the version bit for the specific soft fork is toggled on. pub fn is_signalling_soft_fork(self, bit: u8) -> bool { // Only bits [0, 28] inclusive are used for signalling. if bit > 28 { return false; } // To signal using version bits, the first three bits must be `001`. if (self.0 as u32) & !Self::VERSION_BITS_MASK != Self::USE_VERSION_BITS { return false; } // The bit is set if signalling a soft fork. (self.0 as u32 & Self::VERSION_BITS_MASK) & (1 << bit) > 0 } } impl Default for Version { #[inline] fn default() -> Version { Self::NO_SOFT_FORK_SIGNALLING } } hashes::hash_newtype! { /// A bitcoin block hash. pub struct BlockHash(sha256d::Hash); /// A hash corresponding to the witness structure commitment in the coinbase transaction. pub struct WitnessCommitment(sha256d::Hash); } #[cfg(feature = "hex")] hashes::impl_hex_for_newtype!(BlockHash, WitnessCommitment); #[cfg(not(feature = "hex"))] hashes::impl_debug_only_for_newtype!(BlockHash, WitnessCommitment); #[cfg(feature = "serde")] hashes::impl_serde_for_newtype!(BlockHash, WitnessCommitment); impl BlockHash { /// Dummy hash used as the previous blockhash of the genesis block. pub const GENESIS_PREVIOUS_BLOCK_HASH: Self = Self::from_byte_array([0; 32]); } #[cfg(feature = "arbitrary")] #[cfg(feature = "alloc")] impl<'a> Arbitrary<'a> for Block { fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { let header = Header::arbitrary(u)?; let transactions = Vec::::arbitrary(u)?; Ok(Block::new_unchecked(header, transactions)) } } #[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 { // Equally weight known versions and arbitrary versions let choice = u.int_in_range(0..=3)?; match choice { 0 => Ok(Version::ONE), 1 => Ok(Version::TWO), 2 => Ok(Version::NO_SOFT_FORK_SIGNALLING), _ => Ok(Version::from_consensus(u.arbitrary()?)), } } } #[cfg(test)] mod tests { use super::*; fn dummy_header() -> Header { Header { version: Version::ONE, prev_blockhash: BlockHash::from_byte_array([0x99; 32]), merkle_root: TxMerkleNode::from_byte_array([0x77; 32]), time: BlockTime::from(2), bits: CompactTarget::from_consensus(3), nonce: 4, } } #[test] fn version_is_not_signalling_with_invalid_bit() { let arbitrary_version = Version::from_consensus(1_234_567_890); // The max bit number to signal is 28. assert!(!Version::is_signalling_soft_fork(arbitrary_version, 29)); } #[test] fn version_is_not_signalling_when_use_version_bit_not_set() { let version = Version::from_consensus(0b0100_0000_0000_0000_0000_0000_0000_0000); // Top three bits must be 001 to signal. assert!(!Version::is_signalling_soft_fork(version, 1)); } #[test] fn version_is_signalling() { let version = Version::from_consensus(0b0010_0000_0000_0000_0000_0000_0000_0010); assert!(Version::is_signalling_soft_fork(version, 1)); let version = Version::from_consensus(0b0011_0000_0000_0000_0000_0000_0000_0000); assert!(Version::is_signalling_soft_fork(version, 28)); } #[test] fn version_is_not_signalling() { let version = Version::from_consensus(0b0010_0000_0000_0000_0000_0000_0000_0010); assert!(!Version::is_signalling_soft_fork(version, 0)); } #[test] fn version_to_consensus() { let version = Version::from_consensus(1_234_567_890); assert_eq!(version.to_consensus(), 1_234_567_890); } #[test] fn version_default() { let version = Version::default(); assert_eq!(version.to_consensus(), Version::NO_SOFT_FORK_SIGNALLING.to_consensus()); } // Check that the size of the header consensus serialization matches the const SIZE value #[test] fn header_size() { let header = dummy_header(); // Calculate the size of the block header in bytes from the sum of the serialized lengths // it's fields: version, prev_blockhash, merkle_root, time, bits, nonce. let header_size = header.version.to_consensus().to_le_bytes().len() + header.prev_blockhash.as_byte_array().len() + header.merkle_root.as_byte_array().len() + header.time.to_u32().to_le_bytes().len() + header.bits.to_consensus().to_le_bytes().len() + header.nonce.to_le_bytes().len(); assert_eq!(header_size, Header::SIZE); } #[test] #[cfg(feature = "alloc")] fn block_new_unchecked() { let header = dummy_header(); let transactions = vec![]; let block = Block::new_unchecked(header, transactions.clone()); assert_eq!(block.header, header); assert_eq!(block.transactions, transactions); } #[test] #[cfg(feature = "alloc")] fn block_assume_checked() { let header = dummy_header(); let transactions = vec![]; let block = Block::new_unchecked(header, transactions.clone()); let witness_root = Some(WitnessMerkleNode::from_byte_array([0x88; 32])); let checked_block = block.assume_checked(witness_root); assert_eq!(checked_block.header(), &header); assert_eq!(checked_block.transactions(), &transactions); assert_eq!(checked_block.cached_witness_root(), witness_root); } #[test] #[cfg(feature = "alloc")] fn block_into_parts() { let header = dummy_header(); let transactions = vec![]; let block = Block::new_unchecked(header, transactions.clone()); let (block_header, block_transactions) = block.into_parts(); assert_eq!(block_header, header); assert_eq!(block_transactions, transactions); } #[test] #[cfg(feature = "alloc")] fn block_cached_witness_root() { let header = dummy_header(); let transactions = vec![]; let block = Block::new_unchecked(header, transactions); let witness_root = Some(WitnessMerkleNode::from_byte_array([0x88; 32])); let checked_block = block.assume_checked(witness_root); assert_eq!(checked_block.cached_witness_root(), witness_root); } #[test] #[cfg(feature = "alloc")] fn block_block_hash() { let header = dummy_header(); let transactions = vec![]; let block = Block::new_unchecked(header, transactions); assert_eq!(block.block_hash(), header.block_hash()); } #[test] fn block_hash_from_header() { let header = dummy_header(); let block_hash = header.block_hash(); assert_eq!(block_hash, BlockHash::from(header)); } #[test] fn block_hash_from_header_ref() { let header = dummy_header(); let block_hash: BlockHash = BlockHash::from(&header); assert_eq!(block_hash, header.block_hash()); } #[test] #[cfg(feature = "alloc")] fn block_hash_from_block() { let header = dummy_header(); let transactions = vec![]; let block = Block::new_unchecked(header, transactions); let block_hash: BlockHash = BlockHash::from(block); assert_eq!(block_hash, header.block_hash()); } #[test] #[cfg(feature = "alloc")] fn block_hash_from_block_ref() { let header = dummy_header(); let transactions = vec![]; let block = Block::new_unchecked(header, transactions); let block_hash: BlockHash = BlockHash::from(&block); assert_eq!(block_hash, header.block_hash()); } #[test] fn header_debug() { let header = dummy_header(); let expected = format!( "Header {{ block_hash: {:?}, version: {:?}, prev_blockhash: {:?}, merkle_root: {:?}, time: {:?}, bits: {:?}, nonce: {:?} }}", header.block_hash(), header.version, header.prev_blockhash, header.merkle_root, header.time, header.bits, header.nonce ); assert_eq!(format!("{:?}", header), expected); } #[test] #[cfg(feature = "hex")] fn header_display() { let seconds: u32 = 1_653_195_600; // Arbitrary timestamp: May 22nd, 5am UTC. let header = Header { version: Version::TWO, prev_blockhash: BlockHash::from_byte_array([0xab; 32]), merkle_root: TxMerkleNode::from_byte_array([0xcd; 32]), time: BlockTime::from(seconds), bits: CompactTarget::from_consensus(0xbeef), nonce: 0xcafe, }; let want = concat!( "02000000", // version "abababababababababababababababababababababababababababababababab", // prev_blockhash "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd", // merkle_root "50c38962", // time "efbe0000", // bits "feca0000", // nonce ); assert_eq!(want.len(), 160); assert_eq!(format!("{}", header), want); // Check how formatting options are handled. let want = format!("{:.20}", want); let got = format!("{:.20}", header); assert_eq!(got, want); } }