422 lines
14 KiB
Rust
422 lines
14 KiB
Rust
// 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::Timestamp;
|
|
|
|
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<V = Unchecked>
|
|
where
|
|
V: Validation,
|
|
{
|
|
/// The block header
|
|
header: Header,
|
|
/// List of transactions contained in the block
|
|
transactions: Vec<Transaction>,
|
|
/// Cached witness root if its been computed.
|
|
#[cfg_attr(feature = "serde", serde(skip_serializing))]
|
|
witness_root: Option<WitnessMerkleNode>,
|
|
/// Validation marker.
|
|
marker: PhantomData<V>,
|
|
}
|
|
|
|
#[cfg(feature = "alloc")]
|
|
impl Block<Unchecked> {
|
|
/// Constructs a new `Block` without doing any validation.
|
|
#[inline]
|
|
pub fn new_unchecked(header: Header, transactions: Vec<Transaction>) -> Block<Unchecked> {
|
|
Block { header, transactions, witness_root: None, marker: PhantomData::<Unchecked> }
|
|
}
|
|
|
|
/// 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<WitnessMerkleNode>) -> Block<Checked> {
|
|
Block {
|
|
header: self.header,
|
|
transactions: self.transactions,
|
|
witness_root,
|
|
marker: PhantomData::<Checked>,
|
|
}
|
|
}
|
|
|
|
/// Decomposes block into its constituent parts.
|
|
#[inline]
|
|
pub fn into_parts(self) -> (Header, Vec<Transaction>) { (self.header, self.transactions) }
|
|
}
|
|
|
|
#[cfg(feature = "alloc")]
|
|
impl Block<Checked> {
|
|
/// 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<WitnessMerkleNode> { self.witness_root }
|
|
}
|
|
|
|
#[cfg(feature = "alloc")]
|
|
impl<V: Validation> Block<V> {
|
|
/// Returns the block hash.
|
|
#[inline]
|
|
pub fn block_hash(&self) -> BlockHash { self.header.block_hash() }
|
|
}
|
|
|
|
#[cfg(feature = "alloc")]
|
|
impl From<Block> 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: Timestamp,
|
|
/// 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())
|
|
}
|
|
}
|
|
|
|
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<Header> 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);
|
|
}
|
|
|
|
hashes::impl_hex_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<Self> {
|
|
let header = Header::arbitrary(u)?;
|
|
let transactions = Vec::<Transaction>::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<Self> {
|
|
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<Self> {
|
|
// 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::*;
|
|
|
|
#[test]
|
|
fn version_is_not_signalling_with_invalid_bit() {
|
|
let arbitrary_version = Version::from_consensus(1234567890);
|
|
// 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(0b01000000000000000000000000000000);
|
|
// 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(0b00100000000000000000000000000010);
|
|
assert!(Version::is_signalling_soft_fork(&version, 1));
|
|
let version = Version::from_consensus(0b00110000000000000000000000000000);
|
|
assert!(Version::is_signalling_soft_fork(&version, 28));
|
|
}
|
|
|
|
#[test]
|
|
fn version_is_not_signalling() {
|
|
let version = Version::from_consensus(0b00100000000000000000000000000010);
|
|
assert!(!Version::is_signalling_soft_fork(&version, 0));
|
|
}
|
|
|
|
#[test]
|
|
fn version_to_consensus() {
|
|
let version = Version::from_consensus(1234567890);
|
|
assert_eq!(version.to_consensus(), 1234567890);
|
|
}
|
|
|
|
// Check that the size of the header consensus serialization matches the const SIZE value
|
|
#[test]
|
|
fn header_size() {
|
|
let header = Header {
|
|
version: Version::ONE,
|
|
prev_blockhash: BlockHash::from_byte_array([0x99; 32]),
|
|
merkle_root: TxMerkleNode::from_byte_array([0x77; 32]),
|
|
time: Timestamp::from(2),
|
|
bits: CompactTarget::from_consensus(3),
|
|
nonce: 4,
|
|
};
|
|
|
|
// 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);
|
|
}
|
|
}
|