121 lines
4.8 KiB
Rust
121 lines
4.8 KiB
Rust
use chrono::{DateTime, Duration, Utc};
|
||
|
||
use crate::equihash_solution::EquihashSolution;
|
||
use crate::merkle_tree::MerkleTreeRootHash;
|
||
use crate::serialization::ZcashSerialize;
|
||
use crate::treestate::note_commitment_tree::SaplingNoteTreeRootHash;
|
||
|
||
use super::{difficulty::CompactDifficulty, BlockHeaderHash, Error};
|
||
|
||
/// 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, Serialize, Deserialize)]
|
||
pub struct BlockHeader {
|
||
/// The block's version field. This is supposed to be `4`:
|
||
///
|
||
/// > The current and only defined block version number for Zcash is 4.
|
||
///
|
||
/// but this was not enforced by the consensus rules, and defective mining
|
||
/// software created blocks with other versions, so instead it's effectively
|
||
/// a free field. The only constraint is that it must be at least `4` when
|
||
/// interpreted as an `i32`.
|
||
pub version: u32,
|
||
|
||
/// A SHA-256d hash in internal byte order of the previous block’s
|
||
/// header. This ensures no previous block can be changed without
|
||
/// also changing this block’s header.
|
||
pub 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.
|
||
pub merkle_root_hash: MerkleTreeRootHash,
|
||
|
||
/// The light client root hash.
|
||
///
|
||
/// Unfortunately, the interpretation of this field was changed without
|
||
/// incrementing the version, so it cannot be parsed without the block height
|
||
/// and network. Use
|
||
/// [`Block::light_client_root_hash`](super::Block::light_client_root_hash)
|
||
/// to get the parsed [`LightClientRootHash`](super::LightClientRootHash).
|
||
pub light_client_root_bytes: [u8; 32],
|
||
|
||
/// The block timestamp is a Unix epoch time (UTC) when the miner
|
||
/// started hashing the header (according to the miner).
|
||
pub time: DateTime<Utc>,
|
||
|
||
/// An encoded version of the target threshold this block’s 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)
|
||
pub difficulty_threshold: CompactDifficulty,
|
||
|
||
/// 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.
|
||
pub nonce: [u8; 32],
|
||
|
||
/// The Equihash solution.
|
||
pub solution: EquihashSolution,
|
||
}
|
||
|
||
impl BlockHeader {
|
||
/// Returns true if the header is valid based on its `EquihashSolution`
|
||
pub fn is_equihash_solution_valid(&self) -> Result<(), EquihashError> {
|
||
let n = 200;
|
||
let k = 9;
|
||
let nonce = &self.nonce;
|
||
let solution = &self.solution.0;
|
||
let mut input = Vec::new();
|
||
|
||
self.zcash_serialize(&mut input)
|
||
.expect("serialization into a vec can't fail");
|
||
|
||
let input = &input[0..EquihashSolution::INPUT_LENGTH];
|
||
|
||
equihash::is_valid_solution(n, k, input, nonce, solution)?;
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// Check if `self.time` is less than or equal to
|
||
/// 2 hours in the future, according to the node's local clock (`now`).
|
||
///
|
||
/// This is a non-deterministic rule, as clocks vary over time, and
|
||
/// between different nodes.
|
||
///
|
||
/// "In addition, a full validator MUST NOT accept blocks with nTime
|
||
/// more than two hours in the future according to its clock. This
|
||
/// is not strictly a consensus rule because it is nondeterministic,
|
||
/// and clock time varies between nodes. Also note that a block that
|
||
/// is rejected by this rule at a given point in time may later be
|
||
/// accepted." [§7.5][7.5]
|
||
///
|
||
/// [7.5]: https://zips.z.cash/protocol/protocol.pdf#blockheader
|
||
pub fn is_time_valid_at(&self, now: DateTime<Utc>) -> Result<(), Error> {
|
||
let two_hours_in_the_future = now
|
||
.checked_add_signed(Duration::hours(2))
|
||
.ok_or("overflow when calculating 2 hours in the future")?;
|
||
if self.time <= two_hours_in_the_future {
|
||
Ok(())
|
||
} else {
|
||
Err("block header time is more than 2 hours in the future")?
|
||
}
|
||
}
|
||
}
|
||
|
||
#[non_exhaustive]
|
||
#[derive(Debug, thiserror::Error)]
|
||
#[error("invalid equihash solution for BlockHeader")]
|
||
pub struct EquihashError(#[from] equihash::Error);
|