use core::fmt;
use hashes::{Hash, HashEngine};
use super::Weight;
use crate::blockdata::script;
use crate::blockdata::transaction::Transaction;
use crate::consensus::{encode, Decodable, Encodable};
pub use crate::hash_types::BlockHash;
use crate::hash_types::{TxMerkleNode, WitnessCommitment, WitnessMerkleNode, Wtxid};
use crate::internal_macros::impl_consensus_encoding;
use crate::pow::{CompactTarget, Target, Work};
use crate::prelude::*;
use crate::{io, merkle_tree, VarInt};
#[derive(Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
pub struct HeaderWithoutAuxPow {
pub version: Version,
pub prev_blockhash: BlockHash,
pub merkle_root: TxMerkleNode,
pub time: u32,
pub bits: CompactTarget,
pub nonce: u32,
}
impl From<&Header> for HeaderWithoutAuxPow {
fn from(header: &Header) -> Self {
Self {
version: header.version,
prev_blockhash: header.prev_blockhash,
merkle_root: header.merkle_root,
time: header.time,
bits: header.bits,
nonce: header.nonce,
}
}
}
impl_consensus_encoding!(
HeaderWithoutAuxPow,
version,
prev_blockhash,
merkle_root,
time,
bits,
nonce
);
#[derive(PartialEq, Eq, Clone, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
pub struct AuxPow {
pub coinbase_tx: Transaction,
pub block_hash: BlockHash,
pub coinbase_branch: Vec<TxMerkleNode>,
pub n_index: i32,
pub blockchain_branch: Vec<TxMerkleNode>,
pub chain_index: i32,
pub parent_block_hash: HeaderWithoutAuxPow,
}
impl_consensus_encoding!(
AuxPow,
coinbase_tx,
block_hash,
coinbase_branch,
n_index,
blockchain_branch,
chain_index,
parent_block_hash
);
#[derive(PartialEq, Eq, Clone, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
pub struct Header {
pub version: Version,
pub prev_blockhash: BlockHash,
pub merkle_root: TxMerkleNode,
pub time: u32,
pub bits: CompactTarget,
pub nonce: u32,
pub auxpow: Option<AuxPow>,
}
impl Decodable for Header {
#[inline]
fn consensus_decode_from_finite_reader<R: io::Read + ?Sized>(
d: &mut R,
) -> Result<Self, encode::Error> {
let version: Version = Decodable::consensus_decode_from_finite_reader(d)?;
let prev_blockhash = Decodable::consensus_decode_from_finite_reader(d)?;
let merkle_root = Decodable::consensus_decode_from_finite_reader(d)?;
let time = Decodable::consensus_decode_from_finite_reader(d)?;
let bits = Decodable::consensus_decode_from_finite_reader(d)?;
let nonce = Decodable::consensus_decode_from_finite_reader(d)?;
let auxpow = if version.to_consensus() & (1 << 8) != 0 {
Some(Decodable::consensus_decode_from_finite_reader(d)?)
} else {
None
};
Ok(Self { bits, merkle_root, nonce, time, prev_blockhash, version, auxpow })
}
}
impl Encodable for Header {
#[inline]
fn consensus_encode<R: io::Write + ?Sized>(&self, s: &mut R) -> Result<usize, std::io::Error> {
let mut len = 0;
len += self.version.consensus_encode(s)?;
len += self.prev_blockhash.consensus_encode(s)?;
len += self.merkle_root.consensus_encode(s)?;
len += self.time.consensus_encode(s)?;
len += self.bits.consensus_encode(s)?;
len += self.nonce.consensus_encode(s)?;
if self.version.to_consensus() & (1 << 8) != 0 {
len += self.auxpow.as_ref().unwrap().consensus_encode(s)?;
}
Ok(len)
}
}
impl Header {
pub const SIZE: usize = 4 + 32 + 32 + 4 + 4 + 4;
pub fn block_hash(&self) -> BlockHash {
let mut engine = BlockHash::engine();
self.into_simple_header().consensus_encode(&mut engine).expect("engines don't error");
BlockHash::from_engine(engine)
}
pub fn into_simple_header(&self) -> HeaderWithoutAuxPow {
self.into()
}
pub fn target(&self) -> Target {
self.bits.into()
}
pub fn difficulty(&self) -> u128 {
self.target().difficulty()
}
pub fn difficulty_float(&self) -> f64 {
self.target().difficulty_float()
}
pub fn validate_pow(&self, required_target: Target) -> Result<BlockHash, ValidationError> {
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)
}
}
pub fn work(&self) -> Work {
self.target().to_work()
}
}
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()
}
}
#[derive(Copy, PartialEq, Eq, Clone, Debug, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
pub struct Version(i32);
impl Version {
pub const ONE: Self = Self(1);
pub const TWO: Self = Self(2);
pub const NO_SOFT_FORK_SIGNALLING: Self = Self(Self::USE_VERSION_BITS as i32);
const VERSION_BITS_MASK: u32 = 0x1FFF_FFFF;
const USE_VERSION_BITS: u32 = 0x2000_0000;
pub fn from_consensus(v: i32) -> Self {
Version(v)
}
pub fn to_consensus(self) -> i32 {
self.0
}
pub fn is_signalling_soft_fork(&self, bit: u8) -> bool {
if bit > 28 {
return false;
}
if (self.0 as u32) & !Self::VERSION_BITS_MASK != Self::USE_VERSION_BITS {
return false;
}
(self.0 as u32 & Self::VERSION_BITS_MASK) & (1 << bit) > 0
}
}
impl Default for Version {
fn default() -> Version {
Self::NO_SOFT_FORK_SIGNALLING
}
}
impl Encodable for Version {
fn consensus_encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
self.0.consensus_encode(w)
}
}
impl Decodable for Version {
fn consensus_decode<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
Decodable::consensus_decode(r).map(Version)
}
}
#[derive(PartialEq, Eq, Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
pub struct Block {
pub header: Header,
pub txdata: Vec<Transaction>,
}
impl_consensus_encoding!(Block, header, txdata);
impl Block {
pub fn block_hash(&self) -> BlockHash {
self.header.block_hash()
}
pub fn check_merkle_root(&self) -> bool {
match self.compute_merkle_root() {
Some(merkle_root) => self.header.merkle_root == merkle_root,
None => false,
}
}
pub fn check_witness_commitment(&self) -> bool {
const MAGIC: [u8; 6] = [0x6a, 0x24, 0xaa, 0x21, 0xa9, 0xed];
if self.txdata.iter().all(|t| t.input.iter().all(|i| i.witness.is_empty())) {
return true;
}
if self.txdata.is_empty() {
return false;
}
let coinbase = &self.txdata[0];
if !coinbase.is_coinbase() {
return false;
}
if let Some(pos) = coinbase
.output
.iter()
.rposition(|o| o.script_pubkey.len() >= 38 && o.script_pubkey.as_bytes()[0..6] == MAGIC)
{
let commitment = WitnessCommitment::from_slice(
&coinbase.output[pos].script_pubkey.as_bytes()[6..38],
)
.unwrap();
let witness_vec: Vec<_> = coinbase.input[0].witness.iter().collect();
if witness_vec.len() == 1 && witness_vec[0].len() == 32 {
if let Some(witness_root) = self.witness_root() {
return commitment
== Self::compute_witness_commitment(&witness_root, witness_vec[0]);
}
}
}
false
}
pub fn compute_merkle_root(&self) -> Option<TxMerkleNode> {
let hashes = self.txdata.iter().map(|obj| obj.txid().to_raw_hash());
merkle_tree::calculate_root(hashes).map(|h| h.into())
}
pub fn compute_witness_commitment(
witness_root: &WitnessMerkleNode,
witness_reserved_value: &[u8],
) -> WitnessCommitment {
let mut encoder = WitnessCommitment::engine();
witness_root.consensus_encode(&mut encoder).expect("engines don't error");
encoder.input(witness_reserved_value);
WitnessCommitment::from_engine(encoder)
}
pub fn witness_root(&self) -> Option<WitnessMerkleNode> {
let hashes = self.txdata.iter().enumerate().map(|(i, t)| {
if i == 0 {
Wtxid::all_zeros().to_raw_hash()
} else {
t.wtxid().to_raw_hash()
}
});
merkle_tree::calculate_root(hashes).map(|h| h.into())
}
pub fn weight(&self) -> Weight {
let wu = self.base_size() * 3 + self.total_size();
Weight::from_wu_usize(wu)
}
fn base_size(&self) -> usize {
let mut size = Header::SIZE;
size += VarInt::from(self.txdata.len()).size();
size += self.txdata.iter().map(|tx| tx.base_size()).sum::<usize>();
size
}
pub fn total_size(&self) -> usize {
let mut size = Header::SIZE;
size += VarInt::from(self.txdata.len()).size();
size += self.txdata.iter().map(|tx| tx.total_size()).sum::<usize>();
size
}
#[deprecated(since = "0.31.0", note = "use Block::base_size() instead")]
pub fn strippedsize(&self) -> usize {
self.base_size()
}
pub fn coinbase(&self) -> Option<&Transaction> {
self.txdata.first()
}
pub fn bip34_block_height(&self) -> Result<u64, Bip34Error> {
if self.header.version < Version::TWO {
return Err(Bip34Error::Unsupported);
}
let cb = self.coinbase().ok_or(Bip34Error::NotPresent)?;
let input = cb.input.first().ok_or(Bip34Error::NotPresent)?;
let push = input.script_sig.instructions_minimal().next().ok_or(Bip34Error::NotPresent)?;
match push.map_err(|_| Bip34Error::NotPresent)? {
script::Instruction::PushBytes(b) => {
let h = script::read_scriptint(b.as_bytes())
.map_err(|_e| Bip34Error::UnexpectedPush(b.as_bytes().to_vec()))?;
if h < 0 {
Err(Bip34Error::NegativeHeight)
} else {
Ok(h as u64)
}
}
_ => Err(Bip34Error::NotPresent),
}
}
}
impl From<Header> 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<Block> for BlockHash {
fn from(block: Block) -> BlockHash {
block.block_hash()
}
}
impl From<&Block> for BlockHash {
fn from(block: &Block) -> BlockHash {
block.block_hash()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum Bip34Error {
Unsupported,
NotPresent,
UnexpectedPush(Vec<u8>),
NegativeHeight,
}
impl fmt::Display for Bip34Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use Bip34Error::*;
match *self {
Unsupported => write!(f, "block doesn't support BIP34"),
NotPresent => write!(f, "BIP34 push not present in block's coinbase"),
UnexpectedPush(ref p) => {
write!(f, "unexpected byte push of > 8 bytes: {:?}", p)
}
NegativeHeight => write!(f, "negative BIP34 height"),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for Bip34Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
use Bip34Error::*;
match *self {
Unsupported | NotPresent | UnexpectedPush(_) | NegativeHeight => None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum ValidationError {
BadProofOfWork,
BadTarget,
}
impl fmt::Display for ValidationError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use ValidationError::*;
match *self {
BadProofOfWork => f.write_str("block target correct but not attained"),
BadTarget => f.write_str("block target incorrect"),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for ValidationError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
use self::ValidationError::*;
match *self {
BadProofOfWork | BadTarget => None,
}
}
}