use beserial::{Deserialize, ReadBytesExt, Serialize, SerializingError};
use hash::{Argon2dHash, Blake2bHash, Hash};
use crate::{BlockBody, BlockError, BlockHeader, BlockInterlink, Target};
use primitives::networks::NetworkId;
#[derive(Default, Clone, PartialEq, PartialOrd, Eq, Ord, Debug, Serialize)]
pub struct Block {
pub header: BlockHeader,
pub interlink: BlockInterlink,
pub body: Option<BlockBody>,
}
impl Deserialize for Block {
fn deserialize<R: ReadBytesExt>(reader: &mut R) -> Result<Self, SerializingError> {
let header: BlockHeader = Deserialize::deserialize(reader)?;
let interlink = BlockInterlink::deserialize(reader, &header.prev_hash)?;
Ok(Block {
header,
interlink,
body: Deserialize::deserialize(reader)?,
})
}
}
impl Block {
pub const VERSION: u16 = 1;
pub const MAX_SIZE: usize = 100_000; const TIMESTAMP_DRIFT_MAX: u64 = 600 * 1000;
pub fn verify(&self, timestamp_now: u64, network_id: NetworkId, genesis_hash: Blake2bHash) -> Result<(), BlockError> {
if self.header.version != Block::VERSION {
return Err(BlockError::UnsupportedVersion);
}
if self.header.timestamp_in_millis() > timestamp_now + Block::TIMESTAMP_DRIFT_MAX {
return Err(BlockError::FromTheFuture);
}
if !self.header.verify_proof_of_work() {
return Err(BlockError::InvalidPoW);
}
if self.serialized_size() > Block::MAX_SIZE {
return Err(BlockError::SizeExceeded);
}
self.verify_interlink(genesis_hash)?;
if let Option::Some(ref body) = self.body {
self.verify_body(body, network_id)?;
}
Ok(())
}
fn verify_interlink(&self, genesis_hash: Blake2bHash) -> Result<(), BlockError> {
if self.header.height == 1 && self.header.interlink_hash == Blake2bHash::from([0u8; Blake2bHash::SIZE]) {
return Ok(());
}
if self.header.interlink_hash != self.interlink.hash(genesis_hash) {
return Err(BlockError::InterlinkHashMismatch);
}
Ok(())
}
fn verify_body(&self, body: &BlockBody, network_id: NetworkId) -> Result<(), BlockError> {
body.verify(self.header.height, network_id)?;
if self.header.body_hash != body.hash() {
return Err(BlockError::BodyHashMismatch);
}
Ok(())
}
pub fn is_immediate_successor_of(&self, predecessor: &Block) -> bool {
if !self.header.is_immediate_successor_of(&predecessor.header) {
return false;
}
let interlink = predecessor.get_next_interlink(&self.header.n_bits.into());
if self.interlink != interlink {
return false;
}
true
}
pub fn get_next_interlink(&self, next_target: &Target) -> BlockInterlink {
let mut hashes: Vec<Blake2bHash> = vec![];
let hash: Blake2bHash = self.header.hash();
let pow: Argon2dHash = self.header.hash();
let this_pow_depth = i16::from(Target::from(&pow).get_depth());
let next_target_depth = i16::from(next_target.get_depth());
let num_occurrences = (this_pow_depth - next_target_depth + 1).max(0);
for _ in 0..num_occurrences {
hashes.push(hash.clone());
}
let this_target_depth = i16::from(Target::from(self.header.n_bits).get_depth());
let target_offset = next_target_depth - this_target_depth;
let interlink_offset = (num_occurrences + target_offset) as usize;
for i in interlink_offset..self.interlink.len() {
hashes.push(self.interlink.hashes[i].clone());
}
BlockInterlink::new(hashes, &hash)
}
pub fn into_light(mut self) -> Block {
self.body = None;
self
}
pub fn is_light(&self) -> bool {
self.body.is_none()
}
}