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
29pub type Validator = validator::Info;
31pub 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#[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 #[cfg_attr(
75 all(feature = "wasm-bindgen", target_arch = "wasm32"),
76 wasm_bindgen(skip)
77 )]
78 pub header: Header,
79 #[cfg_attr(
81 all(feature = "wasm-bindgen", target_arch = "wasm32"),
82 wasm_bindgen(skip)
83 )]
84 pub commit: Commit,
85 #[cfg_attr(
87 all(feature = "wasm-bindgen", target_arch = "wasm32"),
88 wasm_bindgen(skip)
89 )]
90 pub validator_set: ValidatorSet,
91 #[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 pub fn decode_and_validate(bytes: &[u8]) -> Result<Self> {
108 let header = ExtendedHeader::decode(bytes)?;
109 header.validate()?;
110 Ok(header)
111 }
112
113 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 pub fn chain_id(&self) -> &Id {
126 &self.header.chain_id
127 }
128
129 pub fn height(&self) -> Height {
131 self.header.height
132 }
133
134 pub fn time(&self) -> Time {
136 self.header.time
137 }
138
139 pub fn hash(&self) -> Hash {
141 self.commit.block_id.hash
142 }
143
144 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 pub fn validate(&self) -> Result<()> {
177 self.header.validate_basic()?;
178 self.commit.validate_basic()?;
179 self.validator_set.validate_basic()?;
180
181 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 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 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 pub fn verify(&self, untrusted: &ExtendedHeader) -> Result<()> {
246 if untrusted.height() <= self.height() {
247 bail_verification!(
248 "untrusted header height({}) <= current trusted header({})",
249 untrusted.height(),
250 self.height()
251 );
252 }
253
254 if untrusted.chain_id() != self.chain_id() {
255 bail_verification!(
256 "untrusted header has different chain {}, not {}",
257 untrusted.chain_id(),
258 self.chain_id()
259 );
260 }
261
262 if !untrusted.time().after(self.time()) {
263 bail_verification!(
264 "untrusted header time ({}) must be after current trusted header ({})",
265 untrusted.time(),
266 self.time()
267 );
268 }
269
270 #[cfg(any(
271 not(any(target_arch = "wasm32", target_arch = "riscv32")),
272 feature = "wasm-bindgen"
273 ))]
274 {
275 let now = Time::now();
276 let valid_until = now.checked_add(VERIFY_CLOCK_DRIFT).unwrap();
277
278 if !untrusted.time().before(valid_until) {
279 bail_verification!(
280 "new untrusted header has a time from the future {} (now: {}, clock_drift: {:?})",
281 untrusted.time(),
282 now,
283 VERIFY_CLOCK_DRIFT
284 );
285 }
286 }
287
288 if self.height().increment() == untrusted.height() {
292 if untrusted.header.validators_hash != self.header.next_validators_hash {
293 bail_verification!(
294 "expected old header next validators ({}) to match those from new header ({})",
295 self.header.next_validators_hash,
296 untrusted.header.validators_hash,
297 );
298 }
299
300 if untrusted.last_header_hash() != self.hash() {
301 bail_verification!(
302 "expected new header to point to last header hash ({}), but got {}",
303 self.hash(),
304 untrusted.last_header_hash()
305 );
306 }
307
308 return Ok(());
309 }
310
311 self.validator_set.verify_commit_light_trusting(
312 self.chain_id(),
313 &untrusted.commit,
314 DEFAULT_TRUST_LEVEL,
315 )?;
316
317 Ok(())
318 }
319
320 pub fn verify_adjacent(&self, untrusted: &ExtendedHeader) -> Result<()> {
335 if self.height().increment() != untrusted.height() {
337 bail_verification!(
338 "untrusted header height ({}) not adjacent to the current trusted ({})",
339 untrusted.height(),
340 self.height(),
341 );
342 }
343
344 self.verify(untrusted)
345 }
346
347 pub fn verify_range(&self, untrusted: &[ExtendedHeader]) -> Result<()> {
377 let mut trusted = self;
378
379 for (i, untrusted) in untrusted.iter().enumerate() {
380 if i != 0 && trusted.height().increment() != untrusted.height() {
384 bail_verification!(
385 "untrusted header height ({}) not adjacent to the current trusted ({})",
386 untrusted.height(),
387 trusted.height(),
388 );
389 }
390
391 trusted.verify(untrusted)?;
392 trusted = untrusted;
393 }
394
395 Ok(())
396 }
397
398 pub fn verify_adjacent_range(&self, untrusted: &[ExtendedHeader]) -> Result<()> {
434 if untrusted.is_empty() {
435 return Ok(());
436 }
437
438 if self.height().increment() != untrusted[0].height() {
440 bail_verification!(
441 "untrusted header height ({}) not adjacent to the current trusted ({})",
442 untrusted[0].height(),
443 self.height(),
444 );
445 }
446
447 self.verify_range(untrusted)
448 }
449}
450
451#[cfg(all(feature = "wasm-bindgen", target_arch = "wasm32"))]
452#[wasm_bindgen]
453impl ExtendedHeader {
454 #[wasm_bindgen(js_name = clone)]
456 pub fn js_clone(&self) -> Self {
457 self.clone()
458 }
459
460 #[wasm_bindgen(js_name = height)]
462 pub fn js_height(&self) -> u64 {
463 self.height().value()
464 }
465
466 #[wasm_bindgen(js_name = time)]
468 pub fn js_time(&self) -> Result<f64, JsValue> {
469 Ok(self
470 .time()
471 .duration_since(Time::unix_epoch())
472 .map_err(|e| JsError::new(&e.to_string()))?
473 .as_secs_f64()
474 * 1000.0)
475 }
476
477 #[wasm_bindgen(js_name = hash)]
479 pub fn js_hash(&self) -> String {
480 self.hash().to_string()
481 }
482
483 #[wasm_bindgen(js_name = previousHeaderHash)]
485 pub fn js_previous_header_hash(&self) -> String {
486 self.last_header_hash().to_string()
487 }
488
489 #[wasm_bindgen(getter, js_name = header)]
491 pub fn js_header(&self) -> Result<JsValue, serde_wasm_bindgen::Error> {
492 to_value(&self.header)
493 }
494
495 #[wasm_bindgen(getter, js_name = commit)]
497 pub fn js_commit(&self) -> Result<JsValue, serde_wasm_bindgen::Error> {
498 to_value(&self.commit)
499 }
500
501 #[wasm_bindgen(getter, js_name = validatorSet)]
503 pub fn js_validator_set(&self) -> Result<JsValue, serde_wasm_bindgen::Error> {
504 to_value(&self.validator_set)
505 }
506
507 #[wasm_bindgen(js_name = validate)]
509 pub fn js_validate(&self) -> Result<(), JsValue> {
510 Ok(self.validate()?)
511 }
512
513 #[wasm_bindgen(js_name = verify)]
521 pub fn js_verify(&self, untrusted: &ExtendedHeader) -> Result<(), JsValue> {
522 Ok(self.verify(untrusted)?)
523 }
524
525 #[wasm_bindgen(js_name = verifyRange)]
545 pub fn js_verify_range(&self, untrusted: Vec<ExtendedHeader>) -> Result<(), JsValue> {
546 Ok(self.verify_range(&untrusted)?)
547 }
548
549 #[wasm_bindgen(js_name = verifyAdjacentRange)]
569 pub fn js_verify_adjacent_range(&self, untrusted: Vec<ExtendedHeader>) -> Result<(), JsValue> {
570 Ok(self.verify_adjacent_range(&untrusted)?)
571 }
572}
573
574impl Protobuf<RawExtendedHeader> for ExtendedHeader {}
575
576impl TryFrom<RawExtendedHeader> for ExtendedHeader {
577 type Error = Error;
578
579 fn try_from(value: RawExtendedHeader) -> Result<Self, Self::Error> {
580 let header = value.header.ok_or(Error::MissingHeader)?.try_into()?;
581 let commit = value.commit.ok_or(Error::MissingCommit)?.try_into()?;
582 let validator_set = value
583 .validator_set
584 .ok_or(Error::MissingValidatorSet)?
585 .try_into()?;
586 let dah = value
587 .dah
588 .ok_or(Error::MissingDataAvailabilityHeader)?
589 .try_into()?;
590
591 Ok(ExtendedHeader {
592 header,
593 commit,
594 validator_set,
595 dah,
596 })
597 }
598}
599
600impl From<ExtendedHeader> for RawExtendedHeader {
601 fn from(value: ExtendedHeader) -> RawExtendedHeader {
602 RawExtendedHeader {
603 header: Some(value.header.into()),
604 commit: Some(value.commit.into()),
605 validator_set: Some(value.validator_set.into()),
606 dah: Some(value.dah.into()),
607 }
608 }
609}
610
611mod custom_serde {
614 use celestia_proto::celestia::core::v1::da::DataAvailabilityHeader;
615 use celestia_proto::header::pb::ExtendedHeader as RawExtendedHeader;
616 use serde::{Deserialize, Serialize};
617 use tendermint_proto::v0_38::types::Commit as RawCommit;
618 use tendermint_proto::v0_38::types::{BlockId, CommitSig, Header, ValidatorSet};
619
620 #[derive(Deserialize, Serialize)]
621 pub(super) struct SerdeExtendedHeader {
622 header: Option<Header>,
623 commit: Option<SerdeCommit>,
624 validator_set: Option<ValidatorSet>,
625 dah: Option<DataAvailabilityHeader>,
626 }
627
628 #[derive(Deserialize, Serialize)]
629 pub(super) struct SerdeCommit {
630 #[serde(with = "celestia_proto::serializers::maybe_quoted")]
631 height: i64,
632 round: i32,
633 block_id: Option<BlockId>,
634 #[serde(with = "tendermint_proto::serializers::nullable")]
635 signatures: Vec<CommitSig>,
636 }
637
638 impl From<RawExtendedHeader> for SerdeExtendedHeader {
639 fn from(value: RawExtendedHeader) -> Self {
640 SerdeExtendedHeader {
641 header: value.header,
642 commit: value.commit.map(|commit| commit.into()),
643 validator_set: value.validator_set,
644 dah: value.dah,
645 }
646 }
647 }
648
649 impl From<SerdeExtendedHeader> for RawExtendedHeader {
650 fn from(value: SerdeExtendedHeader) -> Self {
651 RawExtendedHeader {
652 header: value.header,
653 commit: value.commit.map(|commit| commit.into()),
654 validator_set: value.validator_set,
655 dah: value.dah,
656 }
657 }
658 }
659
660 impl From<RawCommit> for SerdeCommit {
661 fn from(value: RawCommit) -> Self {
662 SerdeCommit {
663 height: value.height,
664 round: value.round,
665 block_id: value.block_id,
666 signatures: value.signatures,
667 }
668 }
669 }
670
671 impl From<SerdeCommit> for RawCommit {
672 fn from(value: SerdeCommit) -> Self {
673 RawCommit {
674 height: value.height,
675 round: value.round,
676 block_id: value.block_id,
677 signatures: value.signatures,
678 }
679 }
680 }
681
682 #[cfg(test)]
683 mod tests {
684 use super::SerdeCommit;
685
686 #[test]
687 fn deserialize_quoted_and_unquoted_commit_height() {
688 let unquoted = r#"{
689 "height": 27,
690 "round": 0,
691 "block_id": {
692 "hash": "6F754536418C0574629379BA6F145C62C86DAEAA8F5772FA1AD5D5AEB4FE5B97",
693 "parts": {
694 "total": 1,
695 "hash": "791BF8972B46DA4582779629D7E3D925510178D3930A4F6CA82FB88636FDA2C6"
696 }
697 },
698 "signatures": [
699 {
700 "block_id_flag": 2,
701 "validator_address": "F1F83230835AA69A1AD6EA68C6D894A4106B8E53",
702 "timestamp": "2023-06-23T10:47:18.421006821Z",
703 "signature": "/2U/PzplnCuSi2jjlOxCdwfVh2+wPQZQoWYOH/AMzwR1iQ/G68yxmamZbaen2c4Z06KUVJMcP7WtbBKtciy5AA=="
704 }
705 ]
706 }"#;
707 serde_json::from_str::<SerdeCommit>(unquoted).unwrap();
708
709 let quoted = r#"{
710 "height": "27",
711 "round": 0,
712 "block_id": {
713 "hash": "6F754536418C0574629379BA6F145C62C86DAEAA8F5772FA1AD5D5AEB4FE5B97",
714 "parts": {
715 "total": 1,
716 "hash": "791BF8972B46DA4582779629D7E3D925510178D3930A4F6CA82FB88636FDA2C6"
717 }
718 },
719 "signatures": [
720 {
721 "block_id_flag": 2,
722 "validator_address": "F1F83230835AA69A1AD6EA68C6D894A4106B8E53",
723 "timestamp": "2023-06-23T10:47:18.421006821Z",
724 "signature": "/2U/PzplnCuSi2jjlOxCdwfVh2+wPQZQoWYOH/AMzwR1iQ/G68yxmamZbaen2c4Z06KUVJMcP7WtbBKtciy5AA=="
725 }
726 ]
727 }"#;
728 serde_json::from_str::<SerdeCommit>(quoted).unwrap();
729 }
730 }
731}
732
733impl Serialize for ExtendedHeader {
734 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
735 where
736 S: Serializer,
737 {
738 let pb: RawExtendedHeader = self.clone().into();
739 let custom_ser: custom_serde::SerdeExtendedHeader = pb.into();
740 custom_ser.serialize(serializer)
741 }
742}
743
744impl<'de> Deserialize<'de> for ExtendedHeader {
745 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
746 where
747 D: Deserializer<'de>,
748 {
749 let custom_de = custom_serde::SerdeExtendedHeader::deserialize(deserializer)?;
750 let pb: RawExtendedHeader = custom_de.into();
751 ExtendedHeader::try_from(pb).map_err(serde::de::Error::custom)
752 }
753}
754
755#[cfg(test)]
756mod tests {
757 use super::*;
758 use crate::test_utils::{invalidate, unverify};
759
760 #[cfg(target_arch = "wasm32")]
761 use wasm_bindgen_test::wasm_bindgen_test as test;
762
763 fn sample_eh_chain_1_block_1() -> ExtendedHeader {
764 let s = include_str!("../test_data/chain1/extended_header_block_1.json");
765 serde_json::from_str(s).unwrap()
766 }
767
768 fn sample_eh_chain_1_block_27() -> ExtendedHeader {
769 let s = include_str!("../test_data/chain1/extended_header_block_27.json");
770 serde_json::from_str(s).unwrap()
771 }
772
773 fn sample_eh_chain_2_block_1() -> ExtendedHeader {
774 let s = include_str!("../test_data/chain2/extended_header_block_1.json");
775 serde_json::from_str(s).unwrap()
776 }
777
778 fn sample_eh_chain_2_block_27() -> ExtendedHeader {
779 let s = include_str!("../test_data/chain2/extended_header_block_27.json");
780 serde_json::from_str(s).unwrap()
781 }
782
783 fn sample_eh_chain_2_block_28() -> ExtendedHeader {
784 let s = include_str!("../test_data/chain2/extended_header_block_28.json");
785 serde_json::from_str(s).unwrap()
786 }
787
788 fn sample_eh_chain_2_block_35() -> ExtendedHeader {
789 let s = include_str!("../test_data/chain2/extended_header_block_35.json");
790 serde_json::from_str(s).unwrap()
791 }
792
793 fn sample_eh_chain_3_block_1_to_256() -> Vec<ExtendedHeader> {
794 let s = include_str!("../test_data/chain3/extended_header_block_1_to_256.json");
795 serde_json::from_str(s).unwrap()
796 }
797
798 #[test]
799 fn validate_correct() {
800 sample_eh_chain_1_block_1().validate().unwrap();
801 sample_eh_chain_1_block_27().validate().unwrap();
802
803 sample_eh_chain_2_block_1().validate().unwrap();
804 sample_eh_chain_2_block_27().validate().unwrap();
805 sample_eh_chain_2_block_28().validate().unwrap();
806 sample_eh_chain_2_block_35().validate().unwrap();
807 }
808
809 #[test]
810 fn validate_validator_hash_mismatch() {
811 let mut eh = sample_eh_chain_1_block_27();
812 eh.header.validators_hash = Hash::None;
813
814 eh.validate().unwrap_err();
815 }
816
817 #[test]
818 fn validate_dah_hash_mismatch() {
819 let mut eh = sample_eh_chain_1_block_27();
820 eh.header.data_hash = Some(Hash::Sha256([0; 32]));
821
822 eh.validate().unwrap_err();
823 }
824
825 #[test]
826 fn validate_commit_height_mismatch() {
827 let mut eh = sample_eh_chain_1_block_27();
828 eh.commit.height = 0xdeadbeefu32.into();
829
830 eh.validate().unwrap_err();
831 }
832
833 #[test]
834 fn validate_commit_block_hash_mismatch() {
835 let mut eh = sample_eh_chain_1_block_27();
836 eh.commit.block_id.hash = Hash::None;
837
838 eh.validate().unwrap_err();
839 }
840
841 #[test]
842 fn verify() {
843 let eh_block_1 = sample_eh_chain_1_block_1();
844 let eh_block_27 = sample_eh_chain_1_block_27();
845
846 eh_block_1.verify(&eh_block_27).unwrap();
847
848 let eh_block_1 = sample_eh_chain_2_block_1();
849 let eh_block_27 = sample_eh_chain_2_block_27();
850
851 eh_block_1.verify(&eh_block_27).unwrap();
852 }
853
854 #[test]
855 fn verify_adjacent() {
856 let eh_block_27 = sample_eh_chain_2_block_27();
857 let eh_block_28 = sample_eh_chain_2_block_28();
858
859 eh_block_27.verify(&eh_block_28).unwrap();
860 }
861
862 #[test]
863 fn verify_invalid_validator() {
864 let eh_block_27 = sample_eh_chain_2_block_27();
865 let mut eh_block_28 = sample_eh_chain_2_block_28();
866
867 eh_block_28.header.validators_hash = Hash::None;
868
869 eh_block_27.verify(&eh_block_28).unwrap_err();
870 }
871
872 #[test]
873 fn verify_invalid_last_block_hash() {
874 let eh_block_27 = sample_eh_chain_2_block_27();
875 let mut eh_block_28 = sample_eh_chain_2_block_28();
876
877 eh_block_28.header.last_block_id.as_mut().unwrap().hash = Hash::None;
878
879 eh_block_27.verify(&eh_block_28).unwrap_err();
880 }
881
882 #[test]
883 fn verify_invalid_adjacent() {
884 let eh_block_27 = sample_eh_chain_1_block_27();
885 let eh_block_28 = sample_eh_chain_2_block_28();
886
887 eh_block_27.verify(&eh_block_28).unwrap_err();
888 }
889
890 #[test]
891 fn verify_same_chain_id_but_different_chain() {
892 let eh_block_1 = sample_eh_chain_1_block_1();
893 let eh_block_27 = sample_eh_chain_2_block_27();
894
895 eh_block_1.verify(&eh_block_27).unwrap_err();
896 }
897
898 #[test]
899 fn verify_invalid_height() {
900 let eh_block_27 = sample_eh_chain_1_block_27();
901 eh_block_27.verify(&eh_block_27).unwrap_err();
902 }
903
904 #[test]
905 fn verify_invalid_chain_id() {
906 let eh_block_1 = sample_eh_chain_1_block_1();
907 let mut eh_block_27 = sample_eh_chain_1_block_27();
908
909 eh_block_27.header.chain_id = "1112222".parse().unwrap();
910 eh_block_1.verify(&eh_block_27).unwrap_err();
911 }
912
913 #[test]
914 fn verify_invalid_time() {
915 let eh_block_1 = sample_eh_chain_1_block_1();
916 let mut eh_block_27 = sample_eh_chain_1_block_27();
917
918 eh_block_27.header.time = eh_block_1.header.time;
919 eh_block_1.verify(&eh_block_27).unwrap_err();
920 }
921
922 #[test]
923 fn verify_time_from_the_future() {
924 let eh_block_1 = sample_eh_chain_1_block_1();
925 let mut eh_block_27 = sample_eh_chain_1_block_27();
926
927 eh_block_27.header.time = Time::now().checked_add(Duration::from_secs(60)).unwrap();
928 eh_block_1.verify(&eh_block_27).unwrap_err();
929 }
930
931 #[test]
932 fn verify_range() {
933 let eh_chain = sample_eh_chain_3_block_1_to_256();
934
935 eh_chain[0].verify_range(&eh_chain[1..]).unwrap();
936 eh_chain[0].verify_range(&eh_chain[..]).unwrap_err();
937 eh_chain[0].verify_range(&eh_chain[10..]).unwrap();
938
939 eh_chain[10].verify_range(&eh_chain[11..]).unwrap();
940 eh_chain[10].verify_range(&eh_chain[100..]).unwrap();
941 eh_chain[10].verify_range(&eh_chain[..9]).unwrap_err();
942 eh_chain[10].verify_range(&eh_chain[10..]).unwrap_err();
943 }
944
945 #[test]
946 fn verify_range_missing_height() {
947 let eh_chain = sample_eh_chain_3_block_1_to_256();
948
949 let mut headers = eh_chain[10..15].to_vec();
950 headers.remove(2);
951 eh_chain[0].verify_range(&headers).unwrap_err();
952 }
953
954 #[test]
955 fn verify_range_duplicate_height() {
956 let eh_chain = sample_eh_chain_3_block_1_to_256();
957
958 let mut headers = eh_chain[10..15].to_vec();
959 headers.insert(2, eh_chain[12].clone());
960 eh_chain[0].verify_range(&headers).unwrap_err();
961 }
962
963 #[test]
964 fn verify_range_bad_header_in_middle() {
965 let eh_chain = sample_eh_chain_3_block_1_to_256();
966
967 let mut headers = eh_chain[10..15].to_vec();
968
969 unverify(&mut headers[2]);
970
971 eh_chain[0].verify_range(&headers).unwrap_err();
972 }
973
974 #[test]
975 fn verify_range_allow_invalid_header_in_middle() {
976 let eh_chain = sample_eh_chain_3_block_1_to_256();
977
978 let mut headers = eh_chain[10..15].to_vec();
979
980 invalidate(&mut headers[2]);
981
982 eh_chain[0].verify_range(&headers).unwrap();
983 }
984
985 #[test]
986 fn verify_adjacent_range() {
987 let eh_chain = sample_eh_chain_3_block_1_to_256();
988
989 eh_chain[0].verify_adjacent_range(&eh_chain[1..]).unwrap();
990 eh_chain[0]
991 .verify_adjacent_range(&eh_chain[..])
992 .unwrap_err();
993 eh_chain[0]
994 .verify_adjacent_range(&eh_chain[10..])
995 .unwrap_err();
996
997 eh_chain[10].verify_adjacent_range(&eh_chain[11..]).unwrap();
998 eh_chain[10]
999 .verify_adjacent_range(&eh_chain[100..])
1000 .unwrap_err();
1001 eh_chain[10]
1002 .verify_adjacent_range(&eh_chain[..9])
1003 .unwrap_err();
1004 eh_chain[10]
1005 .verify_adjacent_range(&eh_chain[10..])
1006 .unwrap_err();
1007 }
1008}