@@ -58,8 +58,9 @@ pub enum ValidationError {
5858 DuplicateSigners ,
5959 InvalidBytesLength ,
6060 InvalidCrossChainData ,
61- SelfInteractionNotAllowed ,
62- WhitespaceOnlyString ,
61+ InvalidTimestamp ,
62+ TimestampNotMonotonic ,
63+ TimestampSkewExceeded ,
6364}
6465
6566/// Result type for validation operations
@@ -335,6 +336,74 @@ impl BytesValidator {
335336 }
336337}
337338
339+ /// Time validation utilities
340+ pub struct TimeValidator ;
341+
342+ impl TimeValidator {
343+ /// Validates if a timestamp is within the global sanity bound (10 years)
344+ pub fn validate_global_bounds ( env : & Env , timestamp : u64 ) -> ValidationResult < ( ) > {
345+ let current_time = env. ledger ( ) . timestamp ( ) ;
346+
347+ // Prevent far-future timestamps
348+ if timestamp > current_time + config:: MAX_TIMEOUT_SECONDS {
349+ return Err ( ValidationError :: InvalidTimestamp ) ;
350+ }
351+
352+ // Prevent far-past timestamps (saturating sub for safety)
353+ if timestamp < current_time. saturating_sub ( config:: MAX_TIMEOUT_SECONDS ) {
354+ return Err ( ValidationError :: InvalidTimestamp ) ;
355+ }
356+
357+ Ok ( ( ) )
358+ }
359+
360+ /// Validates if a timestamp is within operational bounds (90 days)
361+ pub fn validate_operational_bounds ( env : & Env , timestamp : u64 ) -> ValidationResult < ( ) > {
362+ let current_time = env. ledger ( ) . timestamp ( ) ;
363+
364+ if timestamp > current_time + config:: MAX_OPERATIONAL_TIMEOUT {
365+ return Err ( ValidationError :: InvalidTimestamp ) ;
366+ }
367+
368+ if timestamp < current_time. saturating_sub ( config:: MAX_OPERATIONAL_TIMEOUT ) {
369+ return Err ( ValidationError :: InvalidTimestamp ) ;
370+ }
371+
372+ Ok ( ( ) )
373+ }
374+
375+ /// Ensures that time has progressed monotonically
376+ pub fn check_monotonic ( last_timestamp : u64 , current_timestamp : u64 ) -> ValidationResult < ( ) > {
377+ if current_timestamp < last_timestamp {
378+ return Err ( ValidationError :: TimestampNotMonotonic ) ;
379+ }
380+ Ok ( ( ) )
381+ }
382+
383+ /// Validates a timestamp with network skew tolerance (15 minutes)
384+ pub fn validate_skew ( env : & Env , external_timestamp : u64 ) -> ValidationResult < ( ) > {
385+ let current_time = env. ledger ( ) . timestamp ( ) ;
386+ let diff = if external_timestamp > current_time {
387+ external_timestamp - current_time
388+ } else {
389+ current_time - external_timestamp
390+ } ;
391+
392+ if diff > config:: MAX_TIME_SKEW {
393+ return Err ( ValidationError :: TimestampSkewExceeded ) ;
394+ }
395+ Ok ( ( ) )
396+ }
397+
398+ /// Validates that a deadline is actually in the future
399+ pub fn validate_is_future ( env : & Env , deadline : u64 ) -> ValidationResult < ( ) > {
400+ if deadline <= env. ledger ( ) . timestamp ( ) {
401+ return Err ( ValidationError :: InvalidTimestamp ) ;
402+ }
403+ Ok ( ( ) )
404+ }
405+ }
406+
338407/// Cross-chain data validation utilities
339408pub struct CrossChainValidator ;
340409
@@ -415,10 +484,23 @@ impl EscrowValidator {
415484 }
416485
417486 // Validate time constraints
487+ if let Some ( release) = release_time {
488+ TimeValidator :: validate_global_bounds ( env, release)
489+ . map_err ( |_| EscrowError :: InvalidTimestamp ) ?;
490+ TimeValidator :: validate_is_future ( env, release)
491+ . map_err ( |_| EscrowError :: InvalidTimestamp ) ?;
492+ }
493+
494+ if let Some ( refund) = refund_time {
495+ TimeValidator :: validate_global_bounds ( env, refund)
496+ . map_err ( |_| EscrowError :: InvalidTimestamp ) ?;
497+ TimeValidator :: validate_is_future ( env, refund)
498+ . map_err ( |_| EscrowError :: InvalidTimestamp ) ?;
499+ }
500+
418501 if let ( Some ( release) , Some ( refund) ) = ( release_time, refund_time) {
419- if refund <= release {
420- return Err ( EscrowError :: RefundTimeMustBeAfterReleaseTime ) ;
421- }
502+ TimeValidator :: check_monotonic ( release, refund)
503+ . map_err ( |_| EscrowError :: RefundTimeMustBeAfterReleaseTime ) ?;
422504 }
423505
424506 Ok ( ( ) )
@@ -606,6 +688,10 @@ impl BridgeValidator {
606688 InputSanitizer :: sanitize_destination_address ( destination_address)
607689 . map_err ( |_| crate :: errors:: BridgeError :: InvalidInput ) ?;
608690
691+ // Validate current timestamp sanity
692+ TimeValidator :: validate_global_bounds ( env, env. ledger ( ) . timestamp ( ) )
693+ . map_err ( |_| crate :: errors:: BridgeError :: InvalidTimestamp ) ?;
694+
609695 Ok ( ( ) )
610696 }
611697
@@ -631,6 +717,10 @@ impl BridgeValidator {
631717 )
632718 . map_err ( |_| crate :: errors:: BridgeError :: InvalidInput ) ?;
633719
720+ // Validate cross-chain message timestamp sanity (use current ledger time)
721+ TimeValidator :: validate_global_bounds ( env, env. ledger ( ) . timestamp ( ) )
722+ . map_err ( |_| crate :: errors:: BridgeError :: InvalidTimestamp ) ?;
723+
634724 Ok ( ( ) )
635725 }
636726}
0 commit comments