use candid::CandidType;
use ex3_ic_stable_structures::{storable::Bound, Storable};
use ex3_serde::bincode;
use ex3_timestamp::TimeInNs;
use serde::{Deserialize, Serialize};
use serde_bytes::ByteBuf;
use crate::{BlockHash, BlockHeight, CandidBlockHeight, CandidPackageId, MerkleRoot, PackageId};
#[derive(Serialize, Deserialize, Clone, Debug, Ord, PartialOrd, PartialEq, Eq)]
pub struct BlockHead {
pub version: u8,
pub height: BlockHeight,
pub p_id: PackageId,
pub pre_block_hash: BlockHash,
pub tx_root: MerkleRoot,
pub balance_changed_root: MerkleRoot,
pub data_integrity_root: MerkleRoot,
pub rejected_tx_root: MerkleRoot,
}
#[derive(CandidType, Deserialize, Clone, Debug, Ord, PartialOrd, PartialEq, Eq)]
pub struct CandidBlockHead {
pub version: u8,
pub height: CandidBlockHeight,
pub p_id: CandidPackageId,
pub pre_block_hash: BlockHash,
pub tx_root: MerkleRoot,
pub balance_changed_root: MerkleRoot,
pub data_integrity_root: MerkleRoot,
pub rejected_tx_root: MerkleRoot,
}
impl Storable for BlockHead {
fn to_bytes(&self) -> std::borrow::Cow<[u8]> {
bincode::serialize(self).unwrap().into()
}
fn from_bytes(bytes: std::borrow::Cow<[u8]>) -> Self {
bincode::deserialize(bytes.as_ref()).unwrap()
}
const BOUND: Bound = Bound::Bounded {
max_size: 225,
is_fixed_size: false,
};
}
impl From<CandidBlockHead> for BlockHead {
fn from(candid_block_head: CandidBlockHead) -> Self {
BlockHead {
version: candid_block_head.version,
height: candid_block_head.height.into(),
p_id: candid_block_head.p_id.into(),
pre_block_hash: candid_block_head.pre_block_hash,
tx_root: candid_block_head.tx_root,
balance_changed_root: candid_block_head.balance_changed_root,
data_integrity_root: candid_block_head.data_integrity_root,
rejected_tx_root: candid_block_head.rejected_tx_root,
}
}
}
impl From<BlockHead> for CandidBlockHead {
fn from(block_head: BlockHead) -> Self {
CandidBlockHead {
version: block_head.version,
height: block_head.height.into(),
p_id: block_head.p_id.into(),
pre_block_hash: block_head.pre_block_hash,
tx_root: block_head.tx_root,
balance_changed_root: block_head.balance_changed_root,
data_integrity_root: block_head.data_integrity_root,
rejected_tx_root: block_head.rejected_tx_root,
}
}
}
impl BlockHead {
pub fn hash(&self) -> BlockHash {
let block_head_bytes = bincode::serialize(&self).unwrap();
ex3_crypto::sha256(block_head_bytes.as_slice())
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct EncodedBlockHead(pub ByteBuf);
impl From<BlockHead> for EncodedBlockHead {
fn from(head: BlockHead) -> Self {
let bytes = bincode::serialize(&head).unwrap();
EncodedBlockHead(ByteBuf::from(bytes))
}
}
impl From<EncodedBlockHead> for BlockHead {
fn from(encoded_head: EncodedBlockHead) -> Self {
bincode::deserialize(encoded_head.0.as_slice()).expect("Decode block head should success")
}
}
impl std::ops::Deref for EncodedBlockHead {
type Target = ByteBuf;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct BlockHeadExt {
pub height: BlockHeight,
pub validate_on_chain: bool,
pub sharding_reported: bool,
pub has_local_body: bool,
}
#[derive(Serialize, Deserialize, Clone, Debug, Ord, PartialOrd, PartialEq, Eq)]
pub struct ConsensusBlockHead {
pub head: BlockHead,
pub timestamp: TimeInNs,
}
#[derive(CandidType, Deserialize, Clone, Debug, Ord, PartialOrd, PartialEq, Eq)]
pub struct CandidConsensusBlockHead {
pub head: CandidBlockHead,
pub timestamp: TimeInNs,
}
impl From<CandidConsensusBlockHead> for ConsensusBlockHead {
fn from(candid_consensus_block_head: CandidConsensusBlockHead) -> Self {
ConsensusBlockHead {
head: candid_consensus_block_head.head.into(),
timestamp: candid_consensus_block_head.timestamp,
}
}
}
impl From<ConsensusBlockHead> for CandidConsensusBlockHead {
fn from(consensus_block_head: ConsensusBlockHead) -> Self {
CandidConsensusBlockHead {
head: consensus_block_head.head.into(),
timestamp: consensus_block_head.timestamp,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_storable_for_block_head() {
let block_head = BlockHead {
version: 0,
height: BlockHeight::from(u128::MAX),
p_id: PackageId::from(u128::MAX),
pre_block_hash: [1; 32],
tx_root: [2; 32],
balance_changed_root: [3; 32],
data_integrity_root: [4; 32],
rejected_tx_root: [5; 32],
};
let bytes = block_head.to_bytes();
assert!(
bytes.len() <= 225,
"block head bytes length should be less than 225, but got {}",
bytes.len()
);
let block_head2 = BlockHead::from_bytes(bytes);
assert_eq!(block_head, block_head2);
}
#[test]
fn test_bincode_for_bytes() {
let bytes = vec![1; 255];
let bytes2 = ByteBuf::from(bytes.clone());
let s1 = bincode::serialize(&bytes).unwrap();
let s2 = bincode::serialize(&bytes2).unwrap();
assert_eq!(
s1.len(),
s2.len(),
"bincode serialize for bytes and ByteBuf should have same length, but got {} and {}",
s1.len(),
s2.len()
);
}
}