zebra/zebra-chain/src/block.rs

188 lines
6.8 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! Definitions of block datastructures.
#[cfg(test)]
pub mod test_vectors;
#[cfg(test)]
mod tests;
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use chrono::{DateTime, TimeZone, Utc};
use std::{fmt, io};
#[cfg(test)]
use proptest_derive::Arbitrary;
use crate::equihash_solution::EquihashSolution;
use crate::merkle_tree::MerkleTreeRootHash;
use crate::note_commitment_tree::SaplingNoteTreeRootHash;
use crate::serialization::{ReadZcashExt, SerializationError, ZcashDeserialize, ZcashSerialize};
use crate::sha256d_writer::Sha256dWriter;
use crate::transaction::Transaction;
/// A SHA-256d hash of a BlockHeader.
///
/// This is useful when one block header is pointing to its parent
/// block header in the block chain. ⛓️
///
/// This is usually called a 'block hash', as it is frequently used
/// to identify the entire block, since the hash preimage includes
/// the merkle root of the transactions in this block. But
/// _technically_, this is just a hash of the block _header_, not
/// the direct bytes of the transactions as well as the header. So
/// for now I want to call it a `BlockHeaderHash` because that's
/// more explicit.
#[derive(Copy, Clone, Eq, PartialEq)]
#[cfg_attr(test, derive(Arbitrary))]
pub struct BlockHeaderHash(pub [u8; 32]);
impl fmt::Debug for BlockHeaderHash {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("BlockHeaderHash")
.field(&hex::encode(&self.0))
.finish()
}
}
impl From<BlockHeader> for BlockHeaderHash {
fn from(block_header: BlockHeader) -> Self {
let mut hash_writer = Sha256dWriter::default();
block_header
.zcash_serialize(&mut hash_writer)
.expect("Block headers must serialize.");
Self(hash_writer.finish())
}
}
impl ZcashSerialize for BlockHeaderHash {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), SerializationError> {
writer.write_all(&self.0)?;
Ok(())
}
}
impl ZcashDeserialize for BlockHeaderHash {
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
Ok(BlockHeaderHash(reader.read_32_bytes()?))
}
}
/// Block header.
///
/// How are blocks chained together? They are chained together via the
/// backwards reference (previous header hash) present in the block
/// header. Each block points backwards to its parent, all the way
/// back to the genesis block (the first block in the blockchain).
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct BlockHeader {
/// A SHA-256d hash in internal byte order of the previous blocks
/// header. This ensures no previous block can be changed without
/// also changing this blocks header.
previous_block_hash: BlockHeaderHash,
/// A SHA-256d hash in internal byte order. The merkle root is
/// derived from the SHA256d hashes of all transactions included
/// in this block as assembled in a binary tree, ensuring that
/// none of those transactions can be modied without modifying the
/// header.
merkle_root_hash: MerkleTreeRootHash,
/// [Sapling onward] The root LEBS2OSP256(rt) of the Sapling note
/// commitment tree corresponding to the finnal Sapling treestate of
/// this block.
final_sapling_root_hash: SaplingNoteTreeRootHash,
/// The block timestamp is a Unix epoch time (UTC) when the miner
/// started hashing the header (according to the miner).
time: DateTime<Utc>,
/// An encoded version of the target threshold this blocks header
/// hash must be less than or equal to, in the same nBits format
/// used by Bitcoin.
///
/// For a block at block height height, bits MUST be equal to
/// ThresholdBits(height).
///
/// [Bitcoin-nBits](https://bitcoin.org/en/developer-reference#target-nbits)
// pzec has their own wrapper around u32 for this field:
// https://github.com/ZcashFoundation/zebra/blob/master/zebra-primitives/src/compact.rs
bits: u32,
/// An arbitrary field that miners can change to modify the header
/// hash in order to produce a hash less than or equal to the
/// target threshold.
nonce: [u8; 32],
/// The Equihash solution.
solution: EquihashSolution,
}
impl ZcashSerialize for BlockHeader {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), SerializationError> {
// "The current and only defined block version number for Zcash is 4."
writer.write_u32::<LittleEndian>(4)?;
self.previous_block_hash.zcash_serialize(&mut writer)?;
writer.write_all(&self.merkle_root_hash.0[..])?;
writer.write_all(&self.final_sapling_root_hash.0[..])?;
writer.write_u32::<LittleEndian>(self.time.timestamp() as u32)?;
writer.write_u32::<LittleEndian>(self.bits)?;
writer.write_all(&self.nonce[..])?;
self.solution.zcash_serialize(&mut writer)?;
Ok(())
}
}
impl ZcashDeserialize for BlockHeader {
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
// "The current and only defined block version number for Zcash is 4."
let version = reader.read_u32::<LittleEndian>()?;
if version != 4 {
return Err(SerializationError::Parse("bad block header"));
}
Ok(BlockHeader {
previous_block_hash: BlockHeaderHash::zcash_deserialize(&mut reader)?,
merkle_root_hash: MerkleTreeRootHash(reader.read_32_bytes()?),
final_sapling_root_hash: SaplingNoteTreeRootHash(reader.read_32_bytes()?),
time: Utc.timestamp(reader.read_u32::<LittleEndian>()? as i64, 0),
bits: reader.read_u32::<LittleEndian>()?,
nonce: reader.read_32_bytes()?,
solution: EquihashSolution::zcash_deserialize(reader)?,
})
}
}
/// A block in your blockchain.
///
/// A block is a data structure with two fields:
///
/// Block header: a data structure containing the block's metadata
/// Transactions: an array (vector in Rust) of transactions
#[derive(Clone, Debug, Eq, PartialEq)]
#[cfg_attr(test, derive(Arbitrary))]
pub struct Block {
/// First 80 bytes of the block as defined by the encoding used by
/// "block" messages.
pub header: BlockHeader,
/// The block transactions.
pub transactions: Vec<Transaction>,
}
impl ZcashSerialize for Block {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), SerializationError> {
self.header.zcash_serialize(&mut writer)?;
self.transactions.zcash_serialize(&mut writer)?;
Ok(())
}
}
impl ZcashDeserialize for Block {
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
Ok(Block {
header: BlockHeader::zcash_deserialize(&mut reader)?,
transactions: Vec::zcash_deserialize(&mut reader)?,
})
}
}