@@ -8,7 +8,7 @@ pub const VALIDATION_CONTEXT_TYPE_EMPTY_EMPTY: u16 = 0;
88pub const VALIDATION_CONTEXT_TYPE_EMPTY_WITHIN_TRUSTING_PERIOD : u16 = 1 ;
99pub const VALIDATION_CONTEXT_HEADER_SIZE : usize = 32 ;
1010
11- #[ derive( Debug , Clone , PartialEq , Serialize , Deserialize ) ]
11+ #[ derive( Debug , Clone , PartialEq , Eq , Serialize , Deserialize ) ]
1212pub enum ValidationContext {
1313 Empty ,
1414 TrustingPeriod ( TrustingPeriodContext ) ,
@@ -41,6 +41,42 @@ impl ValidationContext {
4141 header
4242 }
4343
44+ pub fn aggregate ( self , other : Self ) -> Result < Self , Error > {
45+ match ( self , other) {
46+ ( Self :: Empty , Self :: Empty ) => Ok ( Self :: Empty ) ,
47+ ( Self :: Empty , Self :: TrustingPeriod ( ctx) ) => Ok ( Self :: TrustingPeriod ( ctx) ) ,
48+ ( Self :: TrustingPeriod ( ctx) , Self :: Empty ) => Ok ( Self :: TrustingPeriod ( ctx) ) ,
49+ ( Self :: TrustingPeriod ( ctx1) , Self :: TrustingPeriod ( ctx2) ) => {
50+ if ctx1. trusting_period != ctx2. trusting_period {
51+ return Err ( Error :: context_aggregation_failed ( format ! (
52+ "trusting_period mismatch: ctx1={:?} ctx2={:?}" ,
53+ ctx1. trusting_period, ctx2. trusting_period,
54+ ) ) ) ;
55+ }
56+ if ctx1. clock_drift != ctx2. clock_drift {
57+ return Err ( Error :: context_aggregation_failed ( format ! (
58+ "clock_drift mismatch: ctx1={:?} ctx2={:?}" ,
59+ ctx1. clock_drift, ctx2. clock_drift
60+ ) ) ) ;
61+ }
62+ Ok ( Self :: TrustingPeriod ( TrustingPeriodContext :: new (
63+ ctx1. trusting_period ,
64+ ctx1. clock_drift ,
65+ if ctx1. untrusted_header_timestamp > ctx2. untrusted_header_timestamp {
66+ ctx1. untrusted_header_timestamp
67+ } else {
68+ ctx2. untrusted_header_timestamp
69+ } ,
70+ if ctx1. trusted_state_timestamp < ctx2. trusted_state_timestamp {
71+ ctx1. trusted_state_timestamp
72+ } else {
73+ ctx2. trusted_state_timestamp
74+ } ,
75+ ) ) )
76+ }
77+ }
78+ }
79+
4480 fn parse_context_type_from_header ( header_bytes : & [ u8 ] ) -> Result < u16 , Error > {
4581 if header_bytes. len ( ) != VALIDATION_CONTEXT_HEADER_SIZE {
4682 return Err ( Error :: invalid_validation_context_header ( format ! (
@@ -150,7 +186,7 @@ impl Display for ValidationContext {
150186}
151187
152188/// NOTE: time precision is in seconds (i.e. nanoseconds are truncated)
153- #[ derive( Debug , Clone , PartialEq , Serialize , Deserialize ) ]
189+ #[ derive( Debug , Clone , PartialEq , Eq , Serialize , Deserialize ) ]
154190pub struct TrustingPeriodContext {
155191 /// How long a validator set is trusted for (must be shorter than the chain's
156192 /// unbonding period)
@@ -236,7 +272,7 @@ impl Display for TrustingPeriodContext {
236272 write ! (
237273 f,
238274 "trusting_period={} clock_drift={} untrusted_header_timestamp={} trusted_state_timestamp={}" ,
239- self . trusting_period. as_secs ( ) , self . clock_drift. as_secs ( ) , self . untrusted_header_timestamp, self . trusted_state_timestamp
275+ self . trusting_period. as_nanos ( ) , self . clock_drift. as_nanos ( ) , self . untrusted_header_timestamp. as_unix_timestamp_nanos ( ) , self . trusted_state_timestamp. as_unix_timestamp_nanos ( )
240276 )
241277 }
242278}
@@ -537,4 +573,154 @@ mod tests {
537573 validate_and_assert_no_error ( ctx, current_timestamp) ;
538574 }
539575 }
576+
577+ #[ test]
578+ fn test_validation_context_aggregation ( ) {
579+ {
580+ let ctx0 = ValidationContext :: from ( build_trusting_period_context (
581+ 1 ,
582+ 1 ,
583+ datetime ! ( 2023 -08 -19 0 : 00 UTC ) ,
584+ datetime ! ( 2023 -08 -19 0 : 00 UTC ) ,
585+ ) ) ;
586+ let ctx1 = ValidationContext :: from ( build_trusting_period_context (
587+ 1 ,
588+ 1 ,
589+ datetime ! ( 2023 -08 -20 0 : 00 UTC ) ,
590+ datetime ! ( 2023 -08 -20 0 : 00 UTC ) ,
591+ ) ) ;
592+ let expected = ValidationContext :: from ( build_trusting_period_context (
593+ 1 ,
594+ 1 ,
595+ datetime ! ( 2023 -08 -20 0 : 00 UTC ) ,
596+ datetime ! ( 2023 -08 -19 0 : 00 UTC ) ,
597+ ) ) ;
598+ let res = ctx0. aggregate ( ctx1) ;
599+ if let Ok ( ctx) = res {
600+ assert_eq ! ( ctx, expected) ;
601+ } else {
602+ panic ! ( "{:?}" , res) ;
603+ }
604+ }
605+
606+ {
607+ let ctx0 = ValidationContext :: from ( build_trusting_period_context (
608+ 1 ,
609+ 1 ,
610+ datetime ! ( 2023 -08 -20 0 : 00 UTC ) ,
611+ datetime ! ( 2023 -08 -20 0 : 00 UTC ) ,
612+ ) ) ;
613+ let ctx1 = ValidationContext :: from ( build_trusting_period_context (
614+ 1 ,
615+ 1 ,
616+ datetime ! ( 2023 -08 -19 0 : 00 UTC ) ,
617+ datetime ! ( 2023 -08 -19 0 : 00 UTC ) ,
618+ ) ) ;
619+ let expected = ValidationContext :: from ( build_trusting_period_context (
620+ 1 ,
621+ 1 ,
622+ datetime ! ( 2023 -08 -20 0 : 00 UTC ) ,
623+ datetime ! ( 2023 -08 -19 0 : 00 UTC ) ,
624+ ) ) ;
625+ let res = ctx0. aggregate ( ctx1) ;
626+ if let Ok ( ctx) = res {
627+ assert_eq ! ( ctx, expected) ;
628+ } else {
629+ panic ! ( "{:?}" , res) ;
630+ }
631+ }
632+
633+ {
634+ let ctx0 = ValidationContext :: from ( build_trusting_period_context (
635+ 2 ,
636+ 1 ,
637+ datetime ! ( 2023 -08 -20 0 : 00 UTC ) ,
638+ datetime ! ( 2023 -08 -20 0 : 00 UTC ) ,
639+ ) ) ;
640+ let ctx1 = ValidationContext :: from ( build_trusting_period_context (
641+ 1 ,
642+ 1 ,
643+ datetime ! ( 2023 -08 -20 0 : 00 UTC ) ,
644+ datetime ! ( 2023 -08 -20 0 : 00 UTC ) ,
645+ ) ) ;
646+ let res = ctx0. aggregate ( ctx1) ;
647+ assert ! ( res. is_err( ) ) ;
648+ }
649+
650+ {
651+ let ctx0 = ValidationContext :: from ( build_trusting_period_context (
652+ 1 ,
653+ 2 ,
654+ datetime ! ( 2023 -08 -20 0 : 00 UTC ) ,
655+ datetime ! ( 2023 -08 -20 0 : 00 UTC ) ,
656+ ) ) ;
657+ let ctx1 = ValidationContext :: from ( build_trusting_period_context (
658+ 1 ,
659+ 1 ,
660+ datetime ! ( 2023 -08 -20 0 : 00 UTC ) ,
661+ datetime ! ( 2023 -08 -20 0 : 00 UTC ) ,
662+ ) ) ;
663+ let res = ctx0. aggregate ( ctx1) ;
664+ assert ! ( res. is_err( ) ) ;
665+ }
666+ }
667+
668+ #[ test]
669+ fn test_validation_context_and_empty_aggregation ( ) {
670+ {
671+ let ctx0 = ValidationContext :: from ( build_trusting_period_context (
672+ 1 ,
673+ 1 ,
674+ datetime ! ( 2023 -08 -20 0 : 00 UTC ) ,
675+ datetime ! ( 2023 -08 -20 0 : 00 UTC ) ,
676+ ) ) ;
677+ let ctx1 = ValidationContext :: Empty ;
678+ let expected = ValidationContext :: from ( build_trusting_period_context (
679+ 1 ,
680+ 1 ,
681+ datetime ! ( 2023 -08 -20 0 : 00 UTC ) ,
682+ datetime ! ( 2023 -08 -20 0 : 00 UTC ) ,
683+ ) ) ;
684+ let res = ctx0. aggregate ( ctx1) ;
685+ if let Ok ( ctx) = res {
686+ assert_eq ! ( ctx, expected) ;
687+ } else {
688+ panic ! ( "{:?}" , res) ;
689+ }
690+ }
691+
692+ {
693+ let ctx0 = ValidationContext :: Empty ;
694+ let ctx1 = ValidationContext :: from ( build_trusting_period_context (
695+ 1 ,
696+ 1 ,
697+ datetime ! ( 2023 -08 -20 0 : 00 UTC ) ,
698+ datetime ! ( 2023 -08 -20 0 : 00 UTC ) ,
699+ ) ) ;
700+ let expected = ValidationContext :: from ( build_trusting_period_context (
701+ 1 ,
702+ 1 ,
703+ datetime ! ( 2023 -08 -20 0 : 00 UTC ) ,
704+ datetime ! ( 2023 -08 -20 0 : 00 UTC ) ,
705+ ) ) ;
706+ let res = ctx0. aggregate ( ctx1) ;
707+ if let Ok ( ctx) = res {
708+ assert_eq ! ( ctx, expected) ;
709+ } else {
710+ panic ! ( "{:?}" , res) ;
711+ }
712+ }
713+ }
714+
715+ #[ test]
716+ fn test_empty_context_aggregation ( ) {
717+ let ctx0 = ValidationContext :: Empty ;
718+ let ctx1 = ValidationContext :: Empty ;
719+ let res = ctx0. aggregate ( ctx1) ;
720+ if let Ok ( ctx) = res {
721+ assert_eq ! ( ctx, ValidationContext :: Empty ) ;
722+ } else {
723+ panic ! ( "{:?}" , res) ;
724+ }
725+ }
540726}
0 commit comments