use celestia_core_proto::v0_34::{
types::{BlockId as RawBlockId, Header as RawHeader},
version::Consensus as RawConsensusVersion,
};
use celestia_core_proto::Protobuf;
use serde::{Deserialize, Serialize};
use crate::{
account, block, chain,
crypto::Sha256,
merkle::{self, MerkleHash},
prelude::*,
AppHash, Hash, Time,
};
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(try_from = "RawHeader", into = "RawHeader")]
pub struct Header {
pub version: Version,
pub chain_id: chain::Id,
pub height: block::Height,
pub time: Time,
pub last_block_id: Option<block::Id>,
pub last_commit_hash: Hash,
pub data_hash: Hash,
pub validators_hash: Hash,
pub next_validators_hash: Hash,
pub consensus_hash: Hash,
pub app_hash: AppHash,
pub last_results_hash: Hash,
pub evidence_hash: Hash,
pub proposer_address: account::Id,
}
impl Header {
#[cfg(feature = "rust-crypto")]
pub fn hash(&self) -> Hash {
self.hash_with::<crate::crypto::default::Sha256>()
}
pub fn hash_with<H>(&self) -> Hash
where
H: MerkleHash + Sha256 + Default,
{
let fields_bytes = vec![
Protobuf::<RawConsensusVersion>::encode_vec(&self.version).unwrap(),
self.chain_id.encode_vec().unwrap(),
self.height.encode_vec().unwrap(),
self.time.encode_vec().unwrap(),
Protobuf::<RawBlockId>::encode_vec(&self.last_block_id.unwrap_or_default()).unwrap(),
self.last_commit_hash.encode_vec().unwrap(),
self.data_hash.encode_vec().unwrap(),
self.validators_hash.encode_vec().unwrap(),
self.next_validators_hash.encode_vec().unwrap(),
self.consensus_hash.encode_vec().unwrap(),
self.app_hash.encode_vec().unwrap(),
self.last_results_hash.encode_vec().unwrap(),
self.evidence_hash.encode_vec().unwrap(),
self.proposer_address.encode_vec().unwrap(),
];
Hash::Sha256(merkle::simple_hash_from_byte_vectors::<H>(&fields_bytes))
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct Version {
pub block: u64,
pub app: u64,
}
tendermint_pb_modules! {
use super::{Header, Version};
use crate::{block, Error};
use pb::{
types::Header as RawHeader,
version::Consensus as RawConsensusVersion,
};
impl Protobuf<RawHeader> for Header {}
impl TryFrom<RawHeader> for Header {
type Error = Error;
fn try_from(value: RawHeader) -> Result<Self, Self::Error> {
let last_block_id = value
.last_block_id
.map(TryInto::try_into)
.transpose()?
.filter(|l| l != &block::Id::default());
let height: block::Height = value.height.try_into()?;
if last_block_id.is_some() && height.value() == 1 {
return Err(Error::invalid_first_header());
}
Ok(Header {
version: value.version.ok_or_else(Error::missing_version)?.into(),
chain_id: value.chain_id.try_into()?,
height,
time: value
.time
.ok_or_else(Error::missing_timestamp)?
.try_into()?,
last_block_id,
last_commit_hash: value.last_commit_hash.try_into()?,
data_hash: value.data_hash.try_into()?,
validators_hash: value.validators_hash.try_into()?,
next_validators_hash: value.next_validators_hash.try_into()?,
consensus_hash: value.consensus_hash.try_into()?,
app_hash: value.app_hash.try_into()?,
last_results_hash: value.last_results_hash.try_into()?,
evidence_hash: value.evidence_hash.try_into()?, proposer_address: value.proposer_address.try_into()?,
})
}
}
impl From<Header> for RawHeader {
fn from(value: Header) -> Self {
RawHeader {
version: Some(value.version.into()),
chain_id: value.chain_id.into(),
height: value.height.into(),
time: Some(value.time.into()),
last_block_id: value.last_block_id.map(Into::into),
last_commit_hash: value.last_commit_hash.into(),
data_hash: value.data_hash.into(),
validators_hash: value.validators_hash.into(),
next_validators_hash: value.next_validators_hash.into(),
consensus_hash: value.consensus_hash.into(),
app_hash: value.app_hash.into(),
last_results_hash: value.last_results_hash.into(),
evidence_hash: value.evidence_hash.into(),
proposer_address: value.proposer_address.into(),
}
}
}
impl Protobuf<RawConsensusVersion> for Version {}
impl From<RawConsensusVersion> for Version {
fn from(value: RawConsensusVersion) -> Self {
Version {
block: value.block,
app: value.app,
}
}
}
impl From<Version> for RawConsensusVersion {
fn from(value: Version) -> Self {
RawConsensusVersion {
block: value.block,
app: value.app,
}
}
}
}