1use celestia_core_proto::v0_34::{
4 types::{BlockId as RawBlockId, Header as RawHeader},
5 version::Consensus as RawConsensusVersion,
6};
7use celestia_core_proto::Protobuf;
8use serde::{Deserialize, Serialize};
9
10use crate::{
11 account, block, chain,
12 crypto::Sha256,
13 merkle::{self, MerkleHash},
14 prelude::*,
15 AppHash, Hash, Time,
16};
17
18#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
24#[serde(try_from = "RawHeader", into = "RawHeader")]
25pub struct Header {
26 pub version: Version,
28
29 pub chain_id: chain::Id,
31
32 pub height: block::Height,
34
35 pub time: Time,
37
38 pub last_block_id: Option<block::Id>,
40
41 pub last_commit_hash: Hash,
43
44 pub data_hash: Hash,
46
47 pub validators_hash: Hash,
49
50 pub next_validators_hash: Hash,
52
53 pub consensus_hash: Hash,
55
56 pub app_hash: AppHash,
58
59 pub last_results_hash: Hash,
61
62 pub evidence_hash: Hash,
64
65 pub proposer_address: account::Id,
67}
68
69impl Header {
70 #[cfg(feature = "rust-crypto")]
72 pub fn hash(&self) -> Hash {
73 self.hash_with::<crate::crypto::default::Sha256>()
74 }
75
76 pub fn hash_with<H>(&self) -> Hash
78 where
79 H: MerkleHash + Sha256 + Default,
80 {
81 let fields_bytes = vec![
87 Protobuf::<RawConsensusVersion>::encode_vec(&self.version).unwrap(),
88 self.chain_id.encode_vec().unwrap(),
89 self.height.encode_vec().unwrap(),
90 self.time.encode_vec().unwrap(),
91 Protobuf::<RawBlockId>::encode_vec(&self.last_block_id.unwrap_or_default()).unwrap(),
92 self.last_commit_hash.encode_vec().unwrap(),
93 self.data_hash.encode_vec().unwrap(),
94 self.validators_hash.encode_vec().unwrap(),
95 self.next_validators_hash.encode_vec().unwrap(),
96 self.consensus_hash.encode_vec().unwrap(),
97 self.app_hash.encode_vec().unwrap(),
98 self.last_results_hash.encode_vec().unwrap(),
99 self.evidence_hash.encode_vec().unwrap(),
100 self.proposer_address.encode_vec().unwrap(),
101 ];
102
103 Hash::Sha256(merkle::simple_hash_from_byte_vectors::<H>(&fields_bytes))
104 }
105}
106
107#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
112pub struct Version {
113 pub block: u64,
115
116 pub app: u64,
118}
119
120tendermint_pb_modules! {
125 use super::{Header, Version};
126 use crate::{block, Error};
127 use pb::{
128 types::Header as RawHeader,
129 version::Consensus as RawConsensusVersion,
130 };
131
132 impl Protobuf<RawHeader> for Header {}
133
134 impl TryFrom<RawHeader> for Header {
135 type Error = Error;
136
137 fn try_from(value: RawHeader) -> Result<Self, Self::Error> {
138 let last_block_id = value
140 .last_block_id
141 .map(TryInto::try_into)
142 .transpose()?
143 .filter(|l| l != &block::Id::default());
144 let height: block::Height = value.height.try_into()?;
145
146 if last_block_id.is_some() && height.value() == 1 {
152 return Err(Error::invalid_first_header());
153 }
154 Ok(Header {
172 version: value.version.ok_or_else(Error::missing_version)?.into(),
173 chain_id: value.chain_id.try_into()?,
174 height,
175 time: value
176 .time
177 .ok_or_else(Error::missing_timestamp)?
178 .try_into()?,
179 last_block_id,
180 last_commit_hash: value.last_commit_hash.try_into()?,
181 data_hash: value.data_hash.try_into()?,
182 validators_hash: value.validators_hash.try_into()?,
183 next_validators_hash: value.next_validators_hash.try_into()?,
184 consensus_hash: value.consensus_hash.try_into()?,
185 app_hash: value.app_hash.try_into()?,
186 last_results_hash: value.last_results_hash.try_into()?,
187 evidence_hash: value.evidence_hash.try_into()?, proposer_address: value.proposer_address.try_into()?,
189 })
190 }
191 }
192
193 impl From<Header> for RawHeader {
194 fn from(value: Header) -> Self {
195 RawHeader {
196 version: Some(value.version.into()),
197 chain_id: value.chain_id.into(),
198 height: value.height.into(),
199 time: Some(value.time.into()),
200 last_block_id: value.last_block_id.map(Into::into),
201 last_commit_hash: value.last_commit_hash.into(),
202 data_hash: value.data_hash.into(),
203 validators_hash: value.validators_hash.into(),
204 next_validators_hash: value.next_validators_hash.into(),
205 consensus_hash: value.consensus_hash.into(),
206 app_hash: value.app_hash.into(),
207 last_results_hash: value.last_results_hash.into(),
208 evidence_hash: value.evidence_hash.into(),
209 proposer_address: value.proposer_address.into(),
210 }
211 }
212 }
213
214 impl Protobuf<RawConsensusVersion> for Version {}
215
216 impl From<RawConsensusVersion> for Version {
217 fn from(value: RawConsensusVersion) -> Self {
218 Version {
219 block: value.block,
220 app: value.app,
221 }
222 }
223 }
224
225 impl From<Version> for RawConsensusVersion {
226 fn from(value: Version) -> Self {
227 RawConsensusVersion {
228 block: value.block,
229 app: value.app,
230 }
231 }
232 }
233}