celestia_types/
extended_header.rs

1use std::fmt::{Display, Formatter};
2#[cfg(any(
3    not(any(target_arch = "wasm32", target_arch = "riscv32")),
4    feature = "wasm-bindgen"
5))]
6use std::time::Duration;
7
8use celestia_proto::header::pb::ExtendedHeader as RawExtendedHeader;
9use serde::{Deserialize, Deserializer, Serialize, Serializer};
10#[cfg(all(feature = "wasm-bindgen", target_arch = "wasm32"))]
11use serde_wasm_bindgen::to_value;
12use tendermint::block::header::Header;
13use tendermint::block::{Commit, Height};
14use tendermint::chain::id::Id;
15use tendermint::{validator, Time};
16use tendermint_proto::Protobuf;
17#[cfg(all(feature = "wasm-bindgen", target_arch = "wasm32"))]
18use wasm_bindgen::prelude::*;
19
20use crate::consts::appconsts::AppVersion;
21use crate::hash::Hash;
22use crate::trust_level::DEFAULT_TRUST_LEVEL;
23use crate::validator_set::ValidatorSetExt;
24use crate::{
25    bail_validation, bail_verification, DataAvailabilityHeader, Error, Result, ValidateBasic,
26    ValidateBasicWithAppVersion,
27};
28
29/// Information about a tendermint validator.
30pub type Validator = validator::Info;
31/// A collection of the tendermint validators.
32pub type ValidatorSet = validator::Set;
33
34#[cfg(any(
35    not(any(target_arch = "wasm32", target_arch = "riscv32")),
36    feature = "wasm-bindgen"
37))]
38const VERIFY_CLOCK_DRIFT: Duration = Duration::from_secs(10);
39
40/// Block header together with the relevant Data Availability metadata.
41///
42/// [`ExtendedHeader`]s are used to announce and describe the blocks
43/// in the Celestia network.
44///
45/// Before being used, each header should be validated and verified with a header you trust.
46///
47/// # Example
48///
49/// ```
50/// # use celestia_types::ExtendedHeader;
51/// # fn trusted_genesis_header() -> ExtendedHeader {
52/// #     let s = include_str!("../test_data/chain1/extended_header_block_1.json");
53/// #     serde_json::from_str(s).unwrap()
54/// # }
55/// # fn some_untrusted_header() -> ExtendedHeader {
56/// #     let s = include_str!("../test_data/chain1/extended_header_block_27.json");
57/// #     serde_json::from_str(s).unwrap()
58/// # }
59/// let genesis_header = trusted_genesis_header();
60///
61/// // fetch new header
62/// let fetched_header = some_untrusted_header();
63///
64/// fetched_header.validate().expect("Invalid block header");
65/// genesis_header.verify(&fetched_header).expect("Malicious header received");
66/// ```
67#[derive(Debug, Clone, PartialEq, Eq)]
68#[cfg_attr(
69    all(feature = "wasm-bindgen", target_arch = "wasm32"),
70    wasm_bindgen(inspectable)
71)]
72pub struct ExtendedHeader {
73    /// Tendermint block header.
74    #[cfg_attr(
75        all(feature = "wasm-bindgen", target_arch = "wasm32"),
76        wasm_bindgen(skip)
77    )]
78    pub header: Header,
79    /// Commit metadata and signatures from validators committing the block.
80    #[cfg_attr(
81        all(feature = "wasm-bindgen", target_arch = "wasm32"),
82        wasm_bindgen(skip)
83    )]
84    pub commit: Commit,
85    /// Information about the set of validators commiting the block.
86    #[cfg_attr(
87        all(feature = "wasm-bindgen", target_arch = "wasm32"),
88        wasm_bindgen(skip)
89    )]
90    pub validator_set: ValidatorSet,
91    /// Header of the block data availability.
92    #[cfg_attr(
93        all(feature = "wasm-bindgen", target_arch = "wasm32"),
94        wasm_bindgen(getter_with_clone)
95    )]
96    pub dah: DataAvailabilityHeader,
97}
98
99impl Display for ExtendedHeader {
100    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
101        write!(f, "hash: {}; height: {}", self.hash(), self.height())
102    }
103}
104
105impl ExtendedHeader {
106    /// Decode protobuf encoded header and then validate it.
107    pub fn decode_and_validate(bytes: &[u8]) -> Result<Self> {
108        let header = ExtendedHeader::decode(bytes)?;
109        header.validate()?;
110        Ok(header)
111    }
112
113    /// Get the app version.
114    ///
115    /// # Errors
116    ///
117    /// This function returns an error if the app version set in header
118    /// is not currently supported.
119    pub fn app_version(&self) -> Result<AppVersion> {
120        let app_version = self.header.version.app;
121        AppVersion::from_u64(app_version).ok_or(Error::UnsupportedAppVersion(app_version))
122    }
123
124    /// Get the block chain id.
125    pub fn chain_id(&self) -> &Id {
126        &self.header.chain_id
127    }
128
129    /// Get the block height.
130    pub fn height(&self) -> Height {
131        self.header.height
132    }
133
134    /// Get the block time.
135    pub fn time(&self) -> Time {
136        self.header.time
137    }
138
139    /// Get the block hash.
140    pub fn hash(&self) -> Hash {
141        self.commit.block_id.hash
142    }
143
144    /// Get the hash of the previous header.
145    pub fn last_header_hash(&self) -> Hash {
146        self.header
147            .last_block_id
148            .map(|block_id| block_id.hash)
149            .unwrap_or_default()
150    }
151
152    /// Validate header.
153    ///
154    /// Performs a consistency check of the data included in the header.
155    ///
156    /// # Errors
157    ///
158    /// If validation fails, this function will return an error with a reason of failure.
159    ///
160    /// ```
161    /// # use celestia_types::{ExtendedHeader, DataAvailabilityHeader};
162    /// #
163    /// # fn get_header(_: usize) -> ExtendedHeader {
164    /// #     let s = include_str!("../test_data/chain1/extended_header_block_27.json");
165    /// #     serde_json::from_str(s).unwrap()
166    /// # }
167    /// // fetch new header
168    /// let mut fetched_header = get_header(15);
169    ///
170    /// assert!(fetched_header.validate().is_ok());
171    ///
172    /// fetched_header.dah = DataAvailabilityHeader::new_unchecked(vec![], vec![]);
173    ///
174    /// assert!(fetched_header.validate().is_err());
175    /// ```
176    pub fn validate(&self) -> Result<()> {
177        self.header.validate_basic()?;
178        self.commit.validate_basic()?;
179        self.validator_set.validate_basic()?;
180
181        // make sure the validator set is consistent with the header
182        if self.validator_set.hash() != self.header.validators_hash {
183            bail_validation!(
184                "validator_set hash ({}) != header validators_hash ({})",
185                self.validator_set.hash(),
186                self.header.validators_hash,
187            )
188        }
189
190        // ensure data root from raw header matches computed root
191        if self.dah.hash() != self.header.data_hash.unwrap_or_default() {
192            bail_validation!(
193                "dah hash ({}) != header dah hash ({})",
194                self.dah.hash(),
195                self.header.data_hash.unwrap_or_default(),
196            )
197        }
198
199        // Make sure the header is consistent with the commit.
200        if self.commit.height != self.height() {
201            bail_validation!(
202                "commit height ({}) != header height ({})",
203                self.commit.height,
204                self.height(),
205            )
206        }
207
208        if self.commit.block_id.hash != self.header.hash() {
209            bail_validation!(
210                "commit block_id hash ({}) != header hash ({})",
211                self.commit.block_id.hash,
212                self.header.hash(),
213            )
214        }
215
216        self.validator_set.verify_commit_light(
217            &self.header.chain_id,
218            &self.height(),
219            &self.commit,
220        )?;
221
222        let app_version = self.app_version()?;
223        self.dah.validate_basic(app_version)?;
224
225        Ok(())
226    }
227
228    /// Verify an untrusted header.
229    ///
230    /// Ensures that the untrusted header can be trusted by verifying it against `self`.
231    ///
232    /// # Errors
233    ///
234    /// If validation fails, this function will return an error with a reason of failure.
235    ///
236    /// Please note that if verifying unadjacent headers, the verification will always
237    /// fail if the validator set commiting those blocks was changed. If that is the case,
238    /// consider verifying the untrusted header with a more recent or even previous header.
239    pub fn verify(&self, untrusted: &ExtendedHeader) -> Result<()> {
240        if untrusted.height() <= self.height() {
241            bail_verification!(
242                "untrusted header height({}) <= current trusted header({})",
243                untrusted.height(),
244                self.height()
245            );
246        }
247
248        if untrusted.chain_id() != self.chain_id() {
249            bail_verification!(
250                "untrusted header has different chain {}, not {}",
251                untrusted.chain_id(),
252                self.chain_id()
253            );
254        }
255
256        if !untrusted.time().after(self.time()) {
257            bail_verification!(
258                "untrusted header time ({}) must be after current trusted header ({})",
259                untrusted.time(),
260                self.time()
261            );
262        }
263
264        #[cfg(any(
265            not(any(target_arch = "wasm32", target_arch = "riscv32")),
266            feature = "wasm-bindgen"
267        ))]
268        {
269            let now = Time::now();
270            let valid_until = now.checked_add(VERIFY_CLOCK_DRIFT).unwrap();
271
272            if !untrusted.time().before(valid_until) {
273                bail_verification!(
274                    "new untrusted header has a time from the future {} (now: {}, clock_drift: {:?})",
275                    untrusted.time(),
276                    now,
277                    VERIFY_CLOCK_DRIFT
278                );
279            }
280        }
281
282        // Optimization: If we are verifying an adjacent header we can avoid
283        // `verify_commit_light_trusting` because we can just check the hash
284        // of next validators and last header.
285        if self.height().increment() == untrusted.height() {
286            if untrusted.header.validators_hash != self.header.next_validators_hash {
287                bail_verification!(
288                    "expected old header next validators ({}) to match those from new header ({})",
289                    self.header.next_validators_hash,
290                    untrusted.header.validators_hash,
291                );
292            }
293
294            if untrusted.last_header_hash() != self.hash() {
295                bail_verification!(
296                    "expected new header to point to last header hash ({}), but got {}",
297                    self.hash(),
298                    untrusted.last_header_hash()
299                );
300            }
301
302            return Ok(());
303        }
304
305        self.validator_set.verify_commit_light_trusting(
306            self.chain_id(),
307            &untrusted.commit,
308            DEFAULT_TRUST_LEVEL,
309        )?;
310
311        Ok(())
312    }
313
314    /// Verify a chain of adjacent untrusted headers.
315    ///
316    /// # Note
317    ///
318    /// This method does not do validation for optimization purposes.
319    /// Validation should be done from before and ideally with
320    /// [`ExtendedHeader::decode_and_validate`].
321    ///
322    /// # Errors
323    ///
324    /// If verification fails, this function will return an error with a reason of failure.
325    /// This function will also return an error if untrusted headers are not adjacent
326    /// to each other.
327    ///
328    /// # Example
329    ///
330    /// ```
331    /// # use std::ops::Range;
332    /// # use celestia_types::ExtendedHeader;
333    /// # let s = include_str!("../test_data/chain3/extended_header_block_1_to_256.json");
334    /// # let headers: Vec<ExtendedHeader> = serde_json::from_str(s).unwrap();
335    /// # let trusted_genesis = || headers[0].clone();
336    /// # // substract one as heights start from 1
337    /// # let get_headers_range = |r: Range<usize>| (&headers[r.start - 1..r.end - 1]).to_vec();
338    /// let genesis_header = trusted_genesis();
339    /// let next_headers = get_headers_range(5..50);
340    ///
341    /// assert!(genesis_header.verify_range(&next_headers).is_ok());
342    /// ```
343    pub fn verify_range(&self, untrusted: &[ExtendedHeader]) -> Result<()> {
344        let mut trusted = self;
345
346        for (i, untrusted) in untrusted.iter().enumerate() {
347            // All headers in `untrusted` must be adjacent to their previous
348            // one. However we do not check if the first untrusted is adjacent
349            // to `self`. This check is done in `verify_adjacent_range`.
350            if i != 0 && trusted.height().increment() != untrusted.height() {
351                bail_verification!(
352                    "untrusted header height ({}) not adjacent to the current trusted ({})",
353                    untrusted.height(),
354                    trusted.height(),
355                );
356            }
357
358            trusted.verify(untrusted)?;
359            trusted = untrusted;
360        }
361
362        Ok(())
363    }
364
365    /// Verify a chain of adjacent untrusted headers and make sure
366    /// they are adjacent to `self`.
367    ///
368    /// # Note
369    ///
370    /// This method does not do validation for optimization purposes.
371    /// Validation should be done from before and ideally with
372    /// [`ExtendedHeader::decode_and_validate`].
373    ///
374    /// # Errors
375    ///
376    /// If verification fails, this function will return an error with a reason of failure.
377    /// This function will also return an error if untrusted headers and `self` don't form contiguous range
378    ///
379    /// # Example
380    ///
381    /// ```
382    /// # use std::ops::Range;
383    /// # use celestia_types::ExtendedHeader;
384    /// # let s = include_str!("../test_data/chain3/extended_header_block_1_to_256.json");
385    /// # let headers: Vec<ExtendedHeader> = serde_json::from_str(s).unwrap();
386    /// # let trusted_genesis = || headers[0].clone();
387    /// # // substract one as heights start from 1
388    /// # let get_headers_range = |r: Range<usize>| (&headers[r.start - 1..r.end - 1]).to_vec();
389    /// let genesis_header = trusted_genesis();
390    /// let next_headers = get_headers_range(5..50);
391    ///
392    /// // fails, not adjacent to genesis
393    /// assert!(genesis_header.verify_adjacent_range(&next_headers).is_err());
394    ///
395    /// let next_headers = get_headers_range(2..50);
396    ///
397    /// // succeeds
398    /// genesis_header.verify_adjacent_range(&next_headers).unwrap();
399    /// ```
400    pub fn verify_adjacent_range(&self, untrusted: &[ExtendedHeader]) -> Result<()> {
401        if untrusted.is_empty() {
402            return Ok(());
403        }
404
405        // Check is first untrusted is adjacent to `self`.
406        if self.height().increment() != untrusted[0].height() {
407            bail_verification!(
408                "untrusted header height ({}) not adjacent to the current trusted ({})",
409                untrusted[0].height(),
410                self.height(),
411            );
412        }
413
414        self.verify_range(untrusted)
415    }
416}
417
418#[cfg(all(feature = "wasm-bindgen", target_arch = "wasm32"))]
419#[wasm_bindgen]
420impl ExtendedHeader {
421    /// Clone a header producing a deep copy of it.
422    #[wasm_bindgen(js_name = clone)]
423    pub fn js_clone(&self) -> Self {
424        self.clone()
425    }
426
427    /// Get the block height.
428    #[wasm_bindgen(js_name = height)]
429    pub fn js_height(&self) -> u64 {
430        self.height().value()
431    }
432
433    /// Get the block time.
434    #[wasm_bindgen(js_name = time)]
435    pub fn js_time(&self) -> Result<f64, JsValue> {
436        Ok(self
437            .time()
438            .duration_since(Time::unix_epoch())
439            .map_err(|e| JsError::new(&e.to_string()))?
440            .as_secs_f64()
441            * 1000.0)
442    }
443
444    /// Get the block hash.
445    #[wasm_bindgen(js_name = hash)]
446    pub fn js_hash(&self) -> String {
447        self.hash().to_string()
448    }
449
450    /// Get the hash of the previous header.
451    #[wasm_bindgen(js_name = previousHeaderHash)]
452    pub fn js_previous_header_hash(&self) -> String {
453        self.last_header_hash().to_string()
454    }
455
456    /// Tendermint block header.
457    #[wasm_bindgen(getter, js_name = header)]
458    pub fn js_header(&self) -> Result<JsValue, serde_wasm_bindgen::Error> {
459        to_value(&self.header)
460    }
461
462    /// Commit metadata and signatures from validators committing the block.
463    #[wasm_bindgen(getter, js_name = commit)]
464    pub fn js_commit(&self) -> Result<JsValue, serde_wasm_bindgen::Error> {
465        to_value(&self.commit)
466    }
467
468    /// Information about the set of validators commiting the block.
469    #[wasm_bindgen(getter, js_name = validatorSet)]
470    pub fn js_validator_set(&self) -> Result<JsValue, serde_wasm_bindgen::Error> {
471        to_value(&self.validator_set)
472    }
473
474    /// Decode protobuf encoded header and then validate it.
475    #[wasm_bindgen(js_name = validate)]
476    pub fn js_validate(&self) -> Result<(), JsValue> {
477        Ok(self.validate()?)
478    }
479
480    /// Verify a chain of adjacent untrusted headers and make sure
481    /// they are adjacent to `self`.
482    ///
483    /// # Errors
484    ///
485    /// If verification fails, this function will return an error with a reason of failure.
486    /// This function will also return an error if untrusted headers and `self` don't form contiguous range
487    #[wasm_bindgen(js_name = verify)]
488    pub fn js_verify(&self, untrusted: &ExtendedHeader) -> Result<(), JsValue> {
489        Ok(self.verify(untrusted)?)
490    }
491
492    /// Verify a chain of adjacent untrusted headers.
493    ///
494    /// # Note
495    ///
496    /// Provided headers will be consumed by this method, meaning
497    /// they will no longer be accessible. If this behavior is not desired,
498    /// consider using `ExtendedHeader.clone()`.
499    ///
500    /// ```js
501    /// const genesis = hdr0;
502    /// const headers = [hrd1, hdr2, hdr3];
503    /// genesis.verifyRange(headers.map(h => h.clone()));
504    /// ```
505    ///
506    /// # Errors
507    ///
508    /// If verification fails, this function will return an error with a reason of failure.
509    /// This function will also return an error if untrusted headers are not adjacent
510    /// to each other.
511    #[wasm_bindgen(js_name = verifyRange)]
512    pub fn js_verify_range(&self, untrusted: Vec<ExtendedHeader>) -> Result<(), JsValue> {
513        Ok(self.verify_range(&untrusted)?)
514    }
515
516    /// Verify a chain of adjacent untrusted headers and make sure
517    /// they are adjacent to `self`.
518    ///
519    /// # Note
520    ///
521    /// Provided headers will be consumed by this method, meaning
522    /// they will no longer be accessible. If this behavior is not desired,
523    /// consider using `ExtendedHeader.clone()`.
524    ///
525    /// ```js
526    /// const genesis = hdr0;
527    /// const headers = [hrd1, hdr2, hdr3];
528    /// genesis.verifyAdjacentRange(headers.map(h => h.clone()));
529    /// ```
530    ///
531    /// # Errors
532    ///
533    /// If verification fails, this function will return an error with a reason of failure.
534    /// This function will also return an error if untrusted headers and `self` don't form contiguous range
535    #[wasm_bindgen(js_name = verifyAdjacentRange)]
536    pub fn js_verify_adjacent_range(&self, untrusted: Vec<ExtendedHeader>) -> Result<(), JsValue> {
537        Ok(self.verify_adjacent_range(&untrusted)?)
538    }
539}
540
541impl Protobuf<RawExtendedHeader> for ExtendedHeader {}
542
543impl TryFrom<RawExtendedHeader> for ExtendedHeader {
544    type Error = Error;
545
546    fn try_from(value: RawExtendedHeader) -> Result<Self, Self::Error> {
547        let header = value.header.ok_or(Error::MissingHeader)?.try_into()?;
548        let commit = value.commit.ok_or(Error::MissingCommit)?.try_into()?;
549        let validator_set = value
550            .validator_set
551            .ok_or(Error::MissingValidatorSet)?
552            .try_into()?;
553        let dah = value
554            .dah
555            .ok_or(Error::MissingDataAvailabilityHeader)?
556            .try_into()?;
557
558        Ok(ExtendedHeader {
559            header,
560            commit,
561            validator_set,
562            dah,
563        })
564    }
565}
566
567impl From<ExtendedHeader> for RawExtendedHeader {
568    fn from(value: ExtendedHeader) -> RawExtendedHeader {
569        RawExtendedHeader {
570            header: Some(value.header.into()),
571            commit: Some(value.commit.into()),
572            validator_set: Some(value.validator_set.into()),
573            dah: Some(value.dah.into()),
574        }
575    }
576}
577
578mod custom_serde {
579    use celestia_proto::celestia::core::v1::da::DataAvailabilityHeader;
580    use celestia_proto::header::pb::ExtendedHeader as RawExtendedHeader;
581    use serde::{Deserialize, Serialize};
582    use tendermint_proto::v0_34::types::Commit as RawCommit;
583    use tendermint_proto::v0_34::types::{BlockId, CommitSig, Header, ValidatorSet};
584
585    #[derive(Deserialize, Serialize)]
586    pub(super) struct SerdeExtendedHeader {
587        header: Option<Header>,
588        commit: Option<SerdeCommit>,
589        validator_set: Option<ValidatorSet>,
590        dah: Option<DataAvailabilityHeader>,
591    }
592
593    #[derive(Deserialize, Serialize)]
594    pub(super) struct SerdeCommit {
595        height: i64,
596        round: i32,
597        block_id: Option<BlockId>,
598        #[serde(with = "tendermint_proto::serializers::nullable")]
599        signatures: Vec<CommitSig>,
600    }
601
602    impl From<RawExtendedHeader> for SerdeExtendedHeader {
603        fn from(value: RawExtendedHeader) -> Self {
604            SerdeExtendedHeader {
605                header: value.header,
606                commit: value.commit.map(|commit| commit.into()),
607                validator_set: value.validator_set,
608                dah: value.dah,
609            }
610        }
611    }
612
613    impl From<SerdeExtendedHeader> for RawExtendedHeader {
614        fn from(value: SerdeExtendedHeader) -> Self {
615            RawExtendedHeader {
616                header: value.header,
617                commit: value.commit.map(|commit| commit.into()),
618                validator_set: value.validator_set,
619                dah: value.dah,
620            }
621        }
622    }
623
624    impl From<RawCommit> for SerdeCommit {
625        fn from(value: RawCommit) -> Self {
626            SerdeCommit {
627                height: value.height,
628                round: value.round,
629                block_id: value.block_id,
630                signatures: value.signatures,
631            }
632        }
633    }
634
635    impl From<SerdeCommit> for RawCommit {
636        fn from(value: SerdeCommit) -> Self {
637            RawCommit {
638                height: value.height,
639                round: value.round,
640                block_id: value.block_id,
641                signatures: value.signatures,
642            }
643        }
644    }
645}
646
647impl Serialize for ExtendedHeader {
648    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
649    where
650        S: Serializer,
651    {
652        let pb: RawExtendedHeader = self.clone().into();
653        let custom_ser: custom_serde::SerdeExtendedHeader = pb.into();
654        custom_ser.serialize(serializer)
655    }
656}
657
658impl<'de> Deserialize<'de> for ExtendedHeader {
659    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
660    where
661        D: Deserializer<'de>,
662    {
663        let custom_de = custom_serde::SerdeExtendedHeader::deserialize(deserializer)?;
664        let pb: RawExtendedHeader = custom_de.into();
665        ExtendedHeader::try_from(pb).map_err(serde::de::Error::custom)
666    }
667}
668
669#[cfg(test)]
670mod tests {
671    use super::*;
672    use crate::test_utils::{invalidate, unverify};
673
674    #[cfg(target_arch = "wasm32")]
675    use wasm_bindgen_test::wasm_bindgen_test as test;
676
677    fn sample_eh_chain_1_block_1() -> ExtendedHeader {
678        let s = include_str!("../test_data/chain1/extended_header_block_1.json");
679        serde_json::from_str(s).unwrap()
680    }
681
682    fn sample_eh_chain_1_block_27() -> ExtendedHeader {
683        let s = include_str!("../test_data/chain1/extended_header_block_27.json");
684        serde_json::from_str(s).unwrap()
685    }
686
687    fn sample_eh_chain_2_block_1() -> ExtendedHeader {
688        let s = include_str!("../test_data/chain2/extended_header_block_1.json");
689        serde_json::from_str(s).unwrap()
690    }
691
692    fn sample_eh_chain_2_block_27() -> ExtendedHeader {
693        let s = include_str!("../test_data/chain2/extended_header_block_27.json");
694        serde_json::from_str(s).unwrap()
695    }
696
697    fn sample_eh_chain_2_block_28() -> ExtendedHeader {
698        let s = include_str!("../test_data/chain2/extended_header_block_28.json");
699        serde_json::from_str(s).unwrap()
700    }
701
702    fn sample_eh_chain_2_block_35() -> ExtendedHeader {
703        let s = include_str!("../test_data/chain2/extended_header_block_35.json");
704        serde_json::from_str(s).unwrap()
705    }
706
707    fn sample_eh_chain_3_block_1_to_256() -> Vec<ExtendedHeader> {
708        let s = include_str!("../test_data/chain3/extended_header_block_1_to_256.json");
709        serde_json::from_str(s).unwrap()
710    }
711
712    #[test]
713    fn validate_correct() {
714        sample_eh_chain_1_block_1().validate().unwrap();
715        sample_eh_chain_1_block_27().validate().unwrap();
716
717        sample_eh_chain_2_block_1().validate().unwrap();
718        sample_eh_chain_2_block_27().validate().unwrap();
719        sample_eh_chain_2_block_28().validate().unwrap();
720        sample_eh_chain_2_block_35().validate().unwrap();
721    }
722
723    #[test]
724    fn validate_validator_hash_mismatch() {
725        let mut eh = sample_eh_chain_1_block_27();
726        eh.header.validators_hash = Hash::None;
727
728        eh.validate().unwrap_err();
729    }
730
731    #[test]
732    fn validate_dah_hash_mismatch() {
733        let mut eh = sample_eh_chain_1_block_27();
734        eh.header.data_hash = Some(Hash::Sha256([0; 32]));
735
736        eh.validate().unwrap_err();
737    }
738
739    #[test]
740    fn validate_commit_height_mismatch() {
741        let mut eh = sample_eh_chain_1_block_27();
742        eh.commit.height = 0xdeadbeefu32.into();
743
744        eh.validate().unwrap_err();
745    }
746
747    #[test]
748    fn validate_commit_block_hash_mismatch() {
749        let mut eh = sample_eh_chain_1_block_27();
750        eh.commit.block_id.hash = Hash::None;
751
752        eh.validate().unwrap_err();
753    }
754
755    #[test]
756    fn verify() {
757        let eh_block_1 = sample_eh_chain_1_block_1();
758        let eh_block_27 = sample_eh_chain_1_block_27();
759
760        eh_block_1.verify(&eh_block_27).unwrap();
761
762        let eh_block_1 = sample_eh_chain_2_block_1();
763        let eh_block_27 = sample_eh_chain_2_block_27();
764
765        eh_block_1.verify(&eh_block_27).unwrap();
766    }
767
768    #[test]
769    fn verify_adjacent() {
770        let eh_block_27 = sample_eh_chain_2_block_27();
771        let eh_block_28 = sample_eh_chain_2_block_28();
772
773        eh_block_27.verify(&eh_block_28).unwrap();
774    }
775
776    #[test]
777    fn verify_invalid_validator() {
778        let eh_block_27 = sample_eh_chain_2_block_27();
779        let mut eh_block_28 = sample_eh_chain_2_block_28();
780
781        eh_block_28.header.validators_hash = Hash::None;
782
783        eh_block_27.verify(&eh_block_28).unwrap_err();
784    }
785
786    #[test]
787    fn verify_invalid_last_block_hash() {
788        let eh_block_27 = sample_eh_chain_2_block_27();
789        let mut eh_block_28 = sample_eh_chain_2_block_28();
790
791        eh_block_28.header.last_block_id.as_mut().unwrap().hash = Hash::None;
792
793        eh_block_27.verify(&eh_block_28).unwrap_err();
794    }
795
796    #[test]
797    fn verify_invalid_adjacent() {
798        let eh_block_27 = sample_eh_chain_1_block_27();
799        let eh_block_28 = sample_eh_chain_2_block_28();
800
801        eh_block_27.verify(&eh_block_28).unwrap_err();
802    }
803
804    #[test]
805    fn verify_same_chain_id_but_different_chain() {
806        let eh_block_1 = sample_eh_chain_1_block_1();
807        let eh_block_27 = sample_eh_chain_2_block_27();
808
809        eh_block_1.verify(&eh_block_27).unwrap_err();
810    }
811
812    #[test]
813    fn verify_invalid_height() {
814        let eh_block_27 = sample_eh_chain_1_block_27();
815        eh_block_27.verify(&eh_block_27).unwrap_err();
816    }
817
818    #[test]
819    fn verify_invalid_chain_id() {
820        let eh_block_1 = sample_eh_chain_1_block_1();
821        let mut eh_block_27 = sample_eh_chain_1_block_27();
822
823        eh_block_27.header.chain_id = "1112222".parse().unwrap();
824        eh_block_1.verify(&eh_block_27).unwrap_err();
825    }
826
827    #[test]
828    fn verify_invalid_time() {
829        let eh_block_1 = sample_eh_chain_1_block_1();
830        let mut eh_block_27 = sample_eh_chain_1_block_27();
831
832        eh_block_27.header.time = eh_block_1.header.time;
833        eh_block_1.verify(&eh_block_27).unwrap_err();
834    }
835
836    #[test]
837    fn verify_time_from_the_future() {
838        let eh_block_1 = sample_eh_chain_1_block_1();
839        let mut eh_block_27 = sample_eh_chain_1_block_27();
840
841        eh_block_27.header.time = Time::now().checked_add(Duration::from_secs(60)).unwrap();
842        eh_block_1.verify(&eh_block_27).unwrap_err();
843    }
844
845    #[test]
846    fn verify_range() {
847        let eh_chain = sample_eh_chain_3_block_1_to_256();
848
849        eh_chain[0].verify_range(&eh_chain[1..]).unwrap();
850        eh_chain[0].verify_range(&eh_chain[..]).unwrap_err();
851        eh_chain[0].verify_range(&eh_chain[10..]).unwrap();
852
853        eh_chain[10].verify_range(&eh_chain[11..]).unwrap();
854        eh_chain[10].verify_range(&eh_chain[100..]).unwrap();
855        eh_chain[10].verify_range(&eh_chain[..9]).unwrap_err();
856        eh_chain[10].verify_range(&eh_chain[10..]).unwrap_err();
857    }
858
859    #[test]
860    fn verify_range_missing_height() {
861        let eh_chain = sample_eh_chain_3_block_1_to_256();
862
863        let mut headers = eh_chain[10..15].to_vec();
864        headers.remove(2);
865        eh_chain[0].verify_range(&headers).unwrap_err();
866    }
867
868    #[test]
869    fn verify_range_duplicate_height() {
870        let eh_chain = sample_eh_chain_3_block_1_to_256();
871
872        let mut headers = eh_chain[10..15].to_vec();
873        headers.insert(2, eh_chain[12].clone());
874        eh_chain[0].verify_range(&headers).unwrap_err();
875    }
876
877    #[test]
878    fn verify_range_bad_header_in_middle() {
879        let eh_chain = sample_eh_chain_3_block_1_to_256();
880
881        let mut headers = eh_chain[10..15].to_vec();
882
883        unverify(&mut headers[2]);
884
885        eh_chain[0].verify_range(&headers).unwrap_err();
886    }
887
888    #[test]
889    fn verify_range_allow_invalid_header_in_middle() {
890        let eh_chain = sample_eh_chain_3_block_1_to_256();
891
892        let mut headers = eh_chain[10..15].to_vec();
893
894        invalidate(&mut headers[2]);
895
896        eh_chain[0].verify_range(&headers).unwrap();
897    }
898
899    #[test]
900    fn verify_adjacent_range() {
901        let eh_chain = sample_eh_chain_3_block_1_to_256();
902
903        eh_chain[0].verify_adjacent_range(&eh_chain[1..]).unwrap();
904        eh_chain[0]
905            .verify_adjacent_range(&eh_chain[..])
906            .unwrap_err();
907        eh_chain[0]
908            .verify_adjacent_range(&eh_chain[10..])
909            .unwrap_err();
910
911        eh_chain[10].verify_adjacent_range(&eh_chain[11..]).unwrap();
912        eh_chain[10]
913            .verify_adjacent_range(&eh_chain[100..])
914            .unwrap_err();
915        eh_chain[10]
916            .verify_adjacent_range(&eh_chain[..9])
917            .unwrap_err();
918        eh_chain[10]
919            .verify_adjacent_range(&eh_chain[10..])
920            .unwrap_err();
921    }
922}