use std::sync::Arc;
use chrono::{DateTime, Duration, Utc};
use thiserror::Error;
use crate::{
fmt::HexDebug,
parameters::Network,
serialization::{TrustedPreallocate, MAX_PROTOCOL_MESSAGE_LEN},
work::{difficulty::CompactDifficulty, equihash::Solution},
};
use super::{merkle, Commitment, CommitmentError, Hash, Height};
#[cfg(any(test, feature = "proptest-impl"))]
use proptest_derive::Arbitrary;
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct Header {
pub version: u32,
pub previous_block_hash: Hash,
pub merkle_root: merkle::Root,
pub commitment_bytes: HexDebug<[u8; 32]>,
pub time: DateTime<Utc>,
pub difficulty_threshold: CompactDifficulty,
pub nonce: HexDebug<[u8; 32]>,
pub solution: Solution,
}
#[allow(missing_docs)]
#[derive(Error, Debug)]
pub enum BlockTimeError {
#[error("invalid time {0:?} in block header {1:?} {2:?}: block time is more than 2 hours in the future ({3:?}). Hint: check your machine's date, time, and time zone.")]
InvalidBlockTime(
DateTime<Utc>,
crate::block::Height,
crate::block::Hash,
DateTime<Utc>,
),
}
impl Header {
#[allow(clippy::unwrap_in_result)]
pub fn time_is_valid_at(
&self,
now: DateTime<Utc>,
height: &Height,
hash: &Hash,
) -> Result<(), BlockTimeError> {
let two_hours_in_the_future = now
.checked_add_signed(Duration::hours(2))
.expect("calculating 2 hours in the future does not overflow");
if self.time <= two_hours_in_the_future {
Ok(())
} else {
Err(BlockTimeError::InvalidBlockTime(
self.time,
*height,
*hash,
two_hours_in_the_future,
))?
}
}
pub fn commitment(
&self,
network: &Network,
height: Height,
) -> Result<Commitment, CommitmentError> {
Commitment::from_bytes(*self.commitment_bytes, network, height)
}
pub fn hash(&self) -> Hash {
Hash::from(self)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
pub struct CountedHeader {
pub header: Arc<Header>,
}
const BLOCK_HEADER_LENGTH: usize =
crate::work::equihash::Solution::INPUT_LENGTH + 32 + 3 + crate::work::equihash::SOLUTION_SIZE;
pub(crate) const MIN_COUNTED_HEADER_LEN: usize = BLOCK_HEADER_LENGTH + 1;
pub const ZCASH_BLOCK_VERSION: u32 = 4;
impl TrustedPreallocate for CountedHeader {
fn max_allocation() -> u64 {
((MAX_PROTOCOL_MESSAGE_LEN - 1) / MIN_COUNTED_HEADER_LEN) as u64
}
}