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::Commit;
13use tendermint::block::header::Header;
14use tendermint::chain::id::Id;
15use tendermint::{Time, validator};
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 DataAvailabilityHeader, Error, Result, ValidateBasic, ValidateBasicWithAppVersion,
26 bail_validation, bail_verification,
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) -> AppVersion {
121 let app_version = self.header.version.app;
122 AppVersion::from_u64(app_version).expect("header should be validated before calling")
123 }
124
125 pub fn chain_id(&self) -> &Id {
127 &self.header.chain_id
128 }
129
130 pub fn height(&self) -> u64 {
132 self.header.height.value()
133 }
134
135 pub fn time(&self) -> Time {
137 self.header.time
138 }
139
140 pub fn hash(&self) -> Hash {
142 self.commit.block_id.hash
143 }
144
145 pub fn last_header_hash(&self) -> Hash {
147 self.header
148 .last_block_id
149 .map(|block_id| block_id.hash)
150 .unwrap_or_default()
151 }
152
153 pub fn square_width(&self) -> u16 {
155 self.dah.square_width()
156 }
157
158 pub fn validate(&self) -> Result<()> {
183 self.header.validate_basic()?;
184 self.commit.validate_basic()?;
185 self.validator_set.validate_basic()?;
186
187 if self.validator_set.hash() != self.header.validators_hash {
189 bail_validation!(
190 "validator_set hash ({}) != header validators_hash ({})",
191 self.validator_set.hash(),
192 self.header.validators_hash,
193 )
194 }
195
196 if self.dah.hash() != self.header.data_hash.unwrap_or_default() {
198 bail_validation!(
199 "dah hash ({}) != header dah hash ({})",
200 self.dah.hash(),
201 self.header.data_hash.unwrap_or_default(),
202 )
203 }
204
205 if self.commit.height.value() != self.height() {
207 bail_validation!(
208 "commit height ({}) != header height ({})",
209 self.commit.height,
210 self.height(),
211 )
212 }
213
214 if self.commit.block_id.hash != self.header.hash() {
215 bail_validation!(
216 "commit block_id hash ({}) != header hash ({})",
217 self.commit.block_id.hash,
218 self.header.hash(),
219 )
220 }
221
222 self.validator_set.verify_commit_light(
223 &self.header.chain_id,
224 &self.header.height,
225 &self.commit,
226 )?;
227
228 let app_version = self.header.version.app;
229 let app_version =
230 AppVersion::from_u64(app_version).ok_or(Error::UnsupportedAppVersion(app_version))?;
231
232 self.dah.validate_basic(app_version)?;
233
234 Ok(())
235 }
236
237 pub fn verify(&self, untrusted: &ExtendedHeader) -> Result<()> {
255 if untrusted.height() <= self.height() {
256 bail_verification!(
257 "untrusted header height({}) <= current trusted header({})",
258 untrusted.height(),
259 self.height()
260 );
261 }
262
263 if untrusted.chain_id() != self.chain_id() {
264 bail_verification!(
265 "untrusted header has different chain {}, not {}",
266 untrusted.chain_id(),
267 self.chain_id()
268 );
269 }
270
271 if !untrusted.time().after(self.time()) {
272 bail_verification!(
273 "untrusted header time ({}) must be after current trusted header ({})",
274 untrusted.time(),
275 self.time()
276 );
277 }
278
279 #[cfg(any(
280 not(any(target_arch = "wasm32", target_arch = "riscv32")),
281 feature = "wasm-bindgen"
282 ))]
283 {
284 let now = Time::now();
285 let valid_until = now.checked_add(VERIFY_CLOCK_DRIFT).unwrap();
286
287 if !untrusted.time().before(valid_until) {
288 bail_verification!(
289 "new untrusted header has a time from the future {} (now: {}, clock_drift: {:?})",
290 untrusted.time(),
291 now,
292 VERIFY_CLOCK_DRIFT
293 );
294 }
295 }
296
297 if self.height() + 1 == untrusted.height() {
301 if untrusted.header.validators_hash != self.header.next_validators_hash {
302 bail_verification!(
303 "expected old header next validators ({}) to match those from new header ({})",
304 self.header.next_validators_hash,
305 untrusted.header.validators_hash,
306 );
307 }
308
309 if untrusted.last_header_hash() != self.hash() {
310 bail_verification!(
311 "expected new header to point to last header hash ({}), but got {}",
312 self.hash(),
313 untrusted.last_header_hash()
314 );
315 }
316
317 return Ok(());
318 }
319
320 self.validator_set.verify_commit_light_trusting(
321 self.chain_id(),
322 &untrusted.commit,
323 DEFAULT_TRUST_LEVEL,
324 )?;
325
326 Ok(())
327 }
328
329 pub fn verify_adjacent(&self, untrusted: &ExtendedHeader) -> Result<()> {
344 if self.height() + 1 != untrusted.height() {
346 bail_verification!(
347 "untrusted header height ({}) not adjacent to the current trusted ({})",
348 untrusted.height(),
349 self.height(),
350 );
351 }
352
353 self.verify(untrusted)
354 }
355
356 pub fn verify_range(&self, untrusted: &[ExtendedHeader]) -> Result<()> {
386 let mut trusted = self;
387
388 for (i, untrusted) in untrusted.iter().enumerate() {
389 if i != 0 && trusted.height() + 1 != untrusted.height() {
393 bail_verification!(
394 "untrusted header height ({}) not adjacent to the current trusted ({})",
395 untrusted.height(),
396 trusted.height(),
397 );
398 }
399
400 trusted.verify(untrusted)?;
401 trusted = untrusted;
402 }
403
404 Ok(())
405 }
406
407 pub fn verify_adjacent_range(&self, untrusted: &[ExtendedHeader]) -> Result<()> {
443 if untrusted.is_empty() {
444 return Ok(());
445 }
446
447 if self.height() + 1 != untrusted[0].height() {
449 bail_verification!(
450 "untrusted header height ({}) not adjacent to the current trusted ({})",
451 untrusted[0].height(),
452 self.height(),
453 );
454 }
455
456 self.verify_range(untrusted)
457 }
458}
459
460#[cfg(all(feature = "wasm-bindgen", target_arch = "wasm32"))]
461#[wasm_bindgen]
462impl ExtendedHeader {
463 #[wasm_bindgen(js_name = clone)]
465 pub fn js_clone(&self) -> Self {
466 self.clone()
467 }
468
469 #[wasm_bindgen(js_name = height)]
471 pub fn js_height(&self) -> u64 {
472 self.height()
473 }
474
475 #[wasm_bindgen(js_name = time)]
477 pub fn js_time(&self) -> Result<f64, JsValue> {
478 Ok(self
479 .time()
480 .duration_since(Time::unix_epoch())
481 .map_err(|e| JsError::new(&e.to_string()))?
482 .as_secs_f64()
483 * 1000.0)
484 }
485
486 #[wasm_bindgen(js_name = hash)]
488 pub fn js_hash(&self) -> String {
489 self.hash().to_string()
490 }
491
492 #[wasm_bindgen(js_name = previousHeaderHash)]
494 pub fn js_previous_header_hash(&self) -> String {
495 self.last_header_hash().to_string()
496 }
497
498 #[wasm_bindgen(getter, js_name = header)]
500 pub fn js_header(&self) -> Result<JsValue, serde_wasm_bindgen::Error> {
501 to_value(&self.header)
502 }
503
504 #[wasm_bindgen(getter, js_name = commit)]
506 pub fn js_commit(&self) -> Result<JsValue, serde_wasm_bindgen::Error> {
507 to_value(&self.commit)
508 }
509
510 #[wasm_bindgen(getter, js_name = validatorSet)]
512 pub fn js_validator_set(&self) -> Result<JsValue, serde_wasm_bindgen::Error> {
513 to_value(&self.validator_set)
514 }
515
516 #[wasm_bindgen(js_name = validate)]
518 pub fn js_validate(&self) -> Result<(), JsValue> {
519 Ok(self.validate()?)
520 }
521
522 #[wasm_bindgen(js_name = verify)]
530 pub fn js_verify(&self, untrusted: &ExtendedHeader) -> Result<(), JsValue> {
531 Ok(self.verify(untrusted)?)
532 }
533
534 #[wasm_bindgen(js_name = verifyRange)]
554 pub fn js_verify_range(&self, untrusted: Vec<ExtendedHeader>) -> Result<(), JsValue> {
555 Ok(self.verify_range(&untrusted)?)
556 }
557
558 #[wasm_bindgen(js_name = verifyAdjacentRange)]
578 pub fn js_verify_adjacent_range(&self, untrusted: Vec<ExtendedHeader>) -> Result<(), JsValue> {
579 Ok(self.verify_adjacent_range(&untrusted)?)
580 }
581}
582
583impl Protobuf<RawExtendedHeader> for ExtendedHeader {}
584
585impl TryFrom<RawExtendedHeader> for ExtendedHeader {
586 type Error = Error;
587
588 fn try_from(value: RawExtendedHeader) -> Result<Self, Self::Error> {
589 let header = value.header.ok_or(Error::MissingHeader)?.try_into()?;
590 let commit = value.commit.ok_or(Error::MissingCommit)?.try_into()?;
591 let validator_set = value
592 .validator_set
593 .ok_or(Error::MissingValidatorSet)?
594 .try_into()?;
595 let dah = value
596 .dah
597 .ok_or(Error::MissingDataAvailabilityHeader)?
598 .try_into()?;
599
600 let eh = ExtendedHeader {
601 header,
602 commit,
603 validator_set,
604 dah,
605 };
606
607 eh.validate()?;
608
609 Ok(eh)
610 }
611}
612
613impl From<ExtendedHeader> for RawExtendedHeader {
614 fn from(value: ExtendedHeader) -> RawExtendedHeader {
615 RawExtendedHeader {
616 header: Some(value.header.into()),
617 commit: Some(value.commit.into()),
618 validator_set: Some(value.validator_set.into()),
619 dah: Some(value.dah.into()),
620 }
621 }
622}
623
624mod custom_serde {
627 use celestia_proto::celestia::core::v1::da::DataAvailabilityHeader;
628 use celestia_proto::header::pb::ExtendedHeader as RawExtendedHeader;
629 use serde::{Deserialize, Serialize};
630 use tendermint_proto::v0_38::types::Commit as RawCommit;
631 use tendermint_proto::v0_38::types::{BlockId, CommitSig, Header, ValidatorSet};
632
633 #[derive(Deserialize, Serialize)]
634 pub(super) struct SerdeExtendedHeader {
635 header: Option<Header>,
636 commit: Option<SerdeCommit>,
637 validator_set: Option<ValidatorSet>,
638 dah: Option<DataAvailabilityHeader>,
639 }
640
641 #[derive(Deserialize, Serialize)]
642 pub(super) struct SerdeCommit {
643 #[serde(with = "celestia_proto::serializers::maybe_quoted")]
644 height: i64,
645 round: i32,
646 block_id: Option<BlockId>,
647 #[serde(with = "tendermint_proto::serializers::nullable")]
648 signatures: Vec<CommitSig>,
649 }
650
651 impl From<RawExtendedHeader> for SerdeExtendedHeader {
652 fn from(value: RawExtendedHeader) -> Self {
653 SerdeExtendedHeader {
654 header: value.header,
655 commit: value.commit.map(|commit| commit.into()),
656 validator_set: value.validator_set,
657 dah: value.dah,
658 }
659 }
660 }
661
662 impl From<SerdeExtendedHeader> for RawExtendedHeader {
663 fn from(value: SerdeExtendedHeader) -> Self {
664 RawExtendedHeader {
665 header: value.header,
666 commit: value.commit.map(|commit| commit.into()),
667 validator_set: value.validator_set,
668 dah: value.dah,
669 }
670 }
671 }
672
673 impl From<RawCommit> for SerdeCommit {
674 fn from(value: RawCommit) -> Self {
675 SerdeCommit {
676 height: value.height,
677 round: value.round,
678 block_id: value.block_id,
679 signatures: value.signatures,
680 }
681 }
682 }
683
684 impl From<SerdeCommit> for RawCommit {
685 fn from(value: SerdeCommit) -> Self {
686 RawCommit {
687 height: value.height,
688 round: value.round,
689 block_id: value.block_id,
690 signatures: value.signatures,
691 }
692 }
693 }
694
695 #[cfg(test)]
696 mod tests {
697 use super::SerdeCommit;
698
699 #[test]
700 fn deserialize_quoted_and_unquoted_commit_height() {
701 let unquoted = r#"{
702 "height": 27,
703 "round": 0,
704 "block_id": {
705 "hash": "6F754536418C0574629379BA6F145C62C86DAEAA8F5772FA1AD5D5AEB4FE5B97",
706 "parts": {
707 "total": 1,
708 "hash": "791BF8972B46DA4582779629D7E3D925510178D3930A4F6CA82FB88636FDA2C6"
709 }
710 },
711 "signatures": [
712 {
713 "block_id_flag": 2,
714 "validator_address": "F1F83230835AA69A1AD6EA68C6D894A4106B8E53",
715 "timestamp": "2023-06-23T10:47:18.421006821Z",
716 "signature": "/2U/PzplnCuSi2jjlOxCdwfVh2+wPQZQoWYOH/AMzwR1iQ/G68yxmamZbaen2c4Z06KUVJMcP7WtbBKtciy5AA=="
717 }
718 ]
719 }"#;
720 serde_json::from_str::<SerdeCommit>(unquoted).unwrap();
721
722 let quoted = r#"{
723 "height": "27",
724 "round": 0,
725 "block_id": {
726 "hash": "6F754536418C0574629379BA6F145C62C86DAEAA8F5772FA1AD5D5AEB4FE5B97",
727 "parts": {
728 "total": 1,
729 "hash": "791BF8972B46DA4582779629D7E3D925510178D3930A4F6CA82FB88636FDA2C6"
730 }
731 },
732 "signatures": [
733 {
734 "block_id_flag": 2,
735 "validator_address": "F1F83230835AA69A1AD6EA68C6D894A4106B8E53",
736 "timestamp": "2023-06-23T10:47:18.421006821Z",
737 "signature": "/2U/PzplnCuSi2jjlOxCdwfVh2+wPQZQoWYOH/AMzwR1iQ/G68yxmamZbaen2c4Z06KUVJMcP7WtbBKtciy5AA=="
738 }
739 ]
740 }"#;
741 serde_json::from_str::<SerdeCommit>(quoted).unwrap();
742 }
743 }
744}
745
746impl Serialize for ExtendedHeader {
747 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
748 where
749 S: Serializer,
750 {
751 let pb: RawExtendedHeader = self.clone().into();
752 let custom_ser: custom_serde::SerdeExtendedHeader = pb.into();
753 custom_ser.serialize(serializer)
754 }
755}
756
757impl<'de> Deserialize<'de> for ExtendedHeader {
758 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
759 where
760 D: Deserializer<'de>,
761 {
762 let custom_de = custom_serde::SerdeExtendedHeader::deserialize(deserializer)?;
763 let pb: RawExtendedHeader = custom_de.into();
764 ExtendedHeader::try_from(pb).map_err(serde::de::Error::custom)
765 }
766}
767
768#[cfg(test)]
769mod tests {
770 use super::*;
771 use crate::test_utils::{invalidate, unverify};
772
773 #[cfg(target_arch = "wasm32")]
774 use wasm_bindgen_test::wasm_bindgen_test as test;
775
776 fn sample_eh_chain_1_block_1() -> ExtendedHeader {
777 let s = include_str!("../test_data/chain1/extended_header_block_1.json");
778 serde_json::from_str(s).unwrap()
779 }
780
781 fn sample_eh_chain_1_block_27() -> ExtendedHeader {
782 let s = include_str!("../test_data/chain1/extended_header_block_27.json");
783 serde_json::from_str(s).unwrap()
784 }
785
786 fn sample_eh_chain_2_block_1() -> ExtendedHeader {
787 let s = include_str!("../test_data/chain2/extended_header_block_1.json");
788 serde_json::from_str(s).unwrap()
789 }
790
791 fn sample_eh_chain_2_block_27() -> ExtendedHeader {
792 let s = include_str!("../test_data/chain2/extended_header_block_27.json");
793 serde_json::from_str(s).unwrap()
794 }
795
796 fn sample_eh_chain_2_block_28() -> ExtendedHeader {
797 let s = include_str!("../test_data/chain2/extended_header_block_28.json");
798 serde_json::from_str(s).unwrap()
799 }
800
801 fn sample_eh_chain_2_block_35() -> ExtendedHeader {
802 let s = include_str!("../test_data/chain2/extended_header_block_35.json");
803 serde_json::from_str(s).unwrap()
804 }
805
806 fn sample_eh_chain_3_block_1_to_256() -> Vec<ExtendedHeader> {
807 let s = include_str!("../test_data/chain3/extended_header_block_1_to_256.json");
808 serde_json::from_str(s).unwrap()
809 }
810
811 #[test]
812 fn validate_correct() {
813 sample_eh_chain_1_block_1().validate().unwrap();
814 sample_eh_chain_1_block_27().validate().unwrap();
815
816 sample_eh_chain_2_block_1().validate().unwrap();
817 sample_eh_chain_2_block_27().validate().unwrap();
818 sample_eh_chain_2_block_28().validate().unwrap();
819 sample_eh_chain_2_block_35().validate().unwrap();
820 }
821
822 #[test]
823 fn validate_validator_hash_mismatch() {
824 let mut eh = sample_eh_chain_1_block_27();
825 eh.header.validators_hash = Hash::None;
826
827 eh.validate().unwrap_err();
828 }
829
830 #[test]
831 fn validate_dah_hash_mismatch() {
832 let mut eh = sample_eh_chain_1_block_27();
833 eh.header.data_hash = Some(Hash::Sha256([0; 32]));
834
835 eh.validate().unwrap_err();
836 }
837
838 #[test]
839 fn validate_commit_height_mismatch() {
840 let mut eh = sample_eh_chain_1_block_27();
841 eh.commit.height = 0xdeadbeefu32.into();
842
843 eh.validate().unwrap_err();
844 }
845
846 #[test]
847 fn validate_commit_block_hash_mismatch() {
848 let mut eh = sample_eh_chain_1_block_27();
849 eh.commit.block_id.hash = Hash::None;
850
851 eh.validate().unwrap_err();
852 }
853
854 #[test]
855 fn verify() {
856 let eh_block_1 = sample_eh_chain_1_block_1();
857 let eh_block_27 = sample_eh_chain_1_block_27();
858
859 eh_block_1.verify(&eh_block_27).unwrap();
860
861 let eh_block_1 = sample_eh_chain_2_block_1();
862 let eh_block_27 = sample_eh_chain_2_block_27();
863
864 eh_block_1.verify(&eh_block_27).unwrap();
865 }
866
867 #[test]
868 fn verify_adjacent() {
869 let eh_block_27 = sample_eh_chain_2_block_27();
870 let eh_block_28 = sample_eh_chain_2_block_28();
871
872 eh_block_27.verify(&eh_block_28).unwrap();
873 }
874
875 #[test]
876 fn verify_invalid_validator() {
877 let eh_block_27 = sample_eh_chain_2_block_27();
878 let mut eh_block_28 = sample_eh_chain_2_block_28();
879
880 eh_block_28.header.validators_hash = Hash::None;
881
882 eh_block_27.verify(&eh_block_28).unwrap_err();
883 }
884
885 #[test]
886 fn verify_invalid_last_block_hash() {
887 let eh_block_27 = sample_eh_chain_2_block_27();
888 let mut eh_block_28 = sample_eh_chain_2_block_28();
889
890 eh_block_28.header.last_block_id.as_mut().unwrap().hash = Hash::None;
891
892 eh_block_27.verify(&eh_block_28).unwrap_err();
893 }
894
895 #[test]
896 fn verify_invalid_adjacent() {
897 let eh_block_27 = sample_eh_chain_1_block_27();
898 let eh_block_28 = sample_eh_chain_2_block_28();
899
900 eh_block_27.verify(&eh_block_28).unwrap_err();
901 }
902
903 #[test]
904 fn verify_same_chain_id_but_different_chain() {
905 let eh_block_1 = sample_eh_chain_1_block_1();
906 let eh_block_27 = sample_eh_chain_2_block_27();
907
908 eh_block_1.verify(&eh_block_27).unwrap_err();
909 }
910
911 #[test]
912 fn verify_invalid_height() {
913 let eh_block_27 = sample_eh_chain_1_block_27();
914 eh_block_27.verify(&eh_block_27).unwrap_err();
915 }
916
917 #[test]
918 fn verify_invalid_chain_id() {
919 let eh_block_1 = sample_eh_chain_1_block_1();
920 let mut eh_block_27 = sample_eh_chain_1_block_27();
921
922 eh_block_27.header.chain_id = "1112222".parse().unwrap();
923 eh_block_1.verify(&eh_block_27).unwrap_err();
924 }
925
926 #[test]
927 fn verify_invalid_time() {
928 let eh_block_1 = sample_eh_chain_1_block_1();
929 let mut eh_block_27 = sample_eh_chain_1_block_27();
930
931 eh_block_27.header.time = eh_block_1.header.time;
932 eh_block_1.verify(&eh_block_27).unwrap_err();
933 }
934
935 #[test]
936 fn verify_time_from_the_future() {
937 let eh_block_1 = sample_eh_chain_1_block_1();
938 let mut eh_block_27 = sample_eh_chain_1_block_27();
939
940 eh_block_27.header.time = Time::now().checked_add(Duration::from_secs(60)).unwrap();
941 eh_block_1.verify(&eh_block_27).unwrap_err();
942 }
943
944 #[test]
945 fn verify_range() {
946 let eh_chain = sample_eh_chain_3_block_1_to_256();
947
948 eh_chain[0].verify_range(&eh_chain[1..]).unwrap();
949 eh_chain[0].verify_range(&eh_chain[..]).unwrap_err();
950 eh_chain[0].verify_range(&eh_chain[10..]).unwrap();
951
952 eh_chain[10].verify_range(&eh_chain[11..]).unwrap();
953 eh_chain[10].verify_range(&eh_chain[100..]).unwrap();
954 eh_chain[10].verify_range(&eh_chain[..9]).unwrap_err();
955 eh_chain[10].verify_range(&eh_chain[10..]).unwrap_err();
956 }
957
958 #[test]
959 fn verify_range_missing_height() {
960 let eh_chain = sample_eh_chain_3_block_1_to_256();
961
962 let mut headers = eh_chain[10..15].to_vec();
963 headers.remove(2);
964 eh_chain[0].verify_range(&headers).unwrap_err();
965 }
966
967 #[test]
968 fn verify_range_duplicate_height() {
969 let eh_chain = sample_eh_chain_3_block_1_to_256();
970
971 let mut headers = eh_chain[10..15].to_vec();
972 headers.insert(2, eh_chain[12].clone());
973 eh_chain[0].verify_range(&headers).unwrap_err();
974 }
975
976 #[test]
977 fn verify_range_bad_header_in_middle() {
978 let eh_chain = sample_eh_chain_3_block_1_to_256();
979
980 let mut headers = eh_chain[10..15].to_vec();
981
982 unverify(&mut headers[2]);
983
984 eh_chain[0].verify_range(&headers).unwrap_err();
985 }
986
987 #[test]
988 fn verify_range_allow_invalid_header_in_middle() {
989 let eh_chain = sample_eh_chain_3_block_1_to_256();
990
991 let mut headers = eh_chain[10..15].to_vec();
992
993 invalidate(&mut headers[2]);
994
995 eh_chain[0].verify_range(&headers).unwrap();
996 }
997
998 #[test]
999 fn verify_adjacent_range() {
1000 let eh_chain = sample_eh_chain_3_block_1_to_256();
1001
1002 eh_chain[0].verify_adjacent_range(&eh_chain[1..]).unwrap();
1003 eh_chain[0]
1004 .verify_adjacent_range(&eh_chain[..])
1005 .unwrap_err();
1006 eh_chain[0]
1007 .verify_adjacent_range(&eh_chain[10..])
1008 .unwrap_err();
1009
1010 eh_chain[10].verify_adjacent_range(&eh_chain[11..]).unwrap();
1011 eh_chain[10]
1012 .verify_adjacent_range(&eh_chain[100..])
1013 .unwrap_err();
1014 eh_chain[10]
1015 .verify_adjacent_range(&eh_chain[..9])
1016 .unwrap_err();
1017 eh_chain[10]
1018 .verify_adjacent_range(&eh_chain[10..])
1019 .unwrap_err();
1020 }
1021}