@@ -22,11 +22,15 @@ mod tb;
2222pub use de:: { ParseError , ParseOrSemanticError } ;
2323
2424
25- // TODO: fix before 2038 (see rust PR #55527)
25+ // TODO: fix before 2037 (see rust PR #55527)
2626/// Defines the maximum UNIX timestamp that can be represented as `SystemTime`. This is checked by
2727/// one of the unit tests, please run them.
2828const SYSTEM_TIME_MAX_UNIX_TIMESTAMP : u64 = std:: i32:: MAX as u64 ;
2929
30+ /// Allow the expiry time to be up to one year. Since this reduces the range of possible timestamps
31+ /// it should be rather low as long as we still have to support 32bit time representations
32+ const MAX_EXPIRY_TIME : u64 = 60 * 60 * 24 * 356 ;
33+
3034/// This function is used as a static assert for the size of `SystemTime`. If the crate fails to
3135/// compile due to it this indicates that your system uses unexpected bounds for `SystemTime`. You
3236/// can remove this functions and run the test `test_system_time_bounds_assumptions`. In any case,
@@ -262,10 +266,12 @@ pub struct Description(String);
262266pub struct PayeePubKey ( pub PublicKey ) ;
263267
264268/// Positive duration that defines when (relatively to the timestamp) in the future the invoice expires
269+ ///
270+ /// # Invariants
271+ /// The number of seconds this expiry time represents has to be in the range
272+ /// `0...SYSTEM_TIME_MAX_UNIX_TIMESTAMP / 2` to avoid overflows when adding it to a timestamp
265273#[ derive( Eq , PartialEq , Debug , Clone ) ]
266- pub struct ExpiryTime {
267- pub seconds : u64
268- }
274+ pub struct ExpiryTime ( Duration ) ;
269275
270276/// `min_final_cltv_expiry` to use for the last HTLC in the route
271277#[ derive( Eq , PartialEq , Debug , Clone ) ]
@@ -382,8 +388,8 @@ impl<D: tb::Bool, H: tb::Bool, T: tb::Bool> InvoiceBuilder<D, H, T> {
382388 }
383389
384390 /// Sets the expiry time in seconds.
385- pub fn expiry_time_seconds ( mut self , expiry_seconds : u64 ) -> Self {
386- self . tagged_fields . push ( TaggedField :: ExpiryTime ( ExpiryTime { seconds : expiry_seconds } ) ) ;
391+ pub fn expiry_time_seconds ( mut self , expiry_time : Duration ) -> Self {
392+ self . tagged_fields . push ( TaggedField :: ExpiryTime ( ExpiryTime :: from_duration ( expiry_time ) . unwrap ( ) ) ) ;
387393 self
388394 }
389395
@@ -490,7 +496,7 @@ impl<D: tb::Bool, H: tb::Bool> InvoiceBuilder<D, H, tb::False> {
490496
491497 /// Sets the timestamp to the current UNIX timestamp.
492498 pub fn current_timestamp ( mut self ) -> InvoiceBuilder < D , H , tb:: True > {
493- use std:: time:: { SystemTime , UNIX_EPOCH } ;
499+ use std:: time:: SystemTime ;
494500 let now = PositiveTimestamp :: from_system_time ( SystemTime :: now ( ) ) ;
495501 self . timestamp = Some ( now. expect ( "for the foreseeable future this shouldn't happen" ) ) ;
496502 self . set_flags ( )
@@ -760,23 +766,23 @@ impl RawInvoice {
760766impl PositiveTimestamp {
761767
762768 /// Create a new `PositiveTimestamp` from a unix timestamp in the Range
763- /// `0...SYSTEM_TIME_MAX_UNIX_TIMESTAMP / 2 `, otherwise return a
769+ /// `0...SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME `, otherwise return a
764770 /// `CreationError::TimestampOutOfBounds`.
765771 pub fn from_unix_timestamp ( unix_seconds : u64 ) -> Result < Self , CreationError > {
766- if unix_seconds > SYSTEM_TIME_MAX_UNIX_TIMESTAMP / 2 {
772+ if unix_seconds > SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME {
767773 Err ( CreationError :: TimestampOutOfBounds )
768774 } else {
769775 Ok ( PositiveTimestamp ( UNIX_EPOCH + Duration :: from_secs ( unix_seconds) ) )
770776 }
771777 }
772778
773779 /// Create a new `PositiveTimestamp` from a `SystemTime` with a corresponding unix timestamp in
774- /// the Range `0...SYSTEM_TIME_MAX_UNIX_TIMESTAMP / 2 `, otherwise return a
780+ /// the Range `0...SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME `, otherwise return a
775781 /// `CreationError::TimestampOutOfBounds`.
776782 pub fn from_system_time ( time : SystemTime ) -> Result < Self , CreationError > {
777783 if time
778784 . duration_since ( UNIX_EPOCH )
779- . map ( |t| t. as_secs ( ) <= SYSTEM_TIME_MAX_UNIX_TIMESTAMP / 2 ) // check for consistency reasons
785+ . map ( |t| t. as_secs ( ) <= SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME )
780786 . unwrap_or ( true )
781787 {
782788 Ok ( PositiveTimestamp ( time) )
@@ -1010,6 +1016,32 @@ impl Deref for PayeePubKey {
10101016 }
10111017}
10121018
1019+ impl ExpiryTime {
1020+ pub fn from_seconds ( seconds : u64 ) -> Result < ExpiryTime , CreationError > {
1021+ if seconds <= SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME {
1022+ Ok ( ExpiryTime ( Duration :: from_secs ( seconds) ) )
1023+ } else {
1024+ Err ( CreationError :: ExpiryTimeOutOfBounds )
1025+ }
1026+ }
1027+
1028+ pub fn from_duration ( duration : Duration ) -> Result < ExpiryTime , CreationError > {
1029+ if duration. as_secs ( ) <= SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME {
1030+ Ok ( ExpiryTime ( duration) )
1031+ } else {
1032+ Err ( CreationError :: ExpiryTimeOutOfBounds )
1033+ }
1034+ }
1035+
1036+ pub fn as_seconds ( & self ) -> u64 {
1037+ self . 0 . as_secs ( )
1038+ }
1039+
1040+ pub fn as_duration ( & self ) -> & Duration {
1041+ & self . 0
1042+ }
1043+ }
1044+
10131045impl Route {
10141046 pub fn new ( hops : Vec < RouteHop > ) -> Result < Route , CreationError > {
10151047 if hops. len ( ) <= 12 {
@@ -1065,6 +1097,9 @@ pub enum CreationError {
10651097
10661098 /// The unix timestamp of the supplied date is <0 or can't be represented as `SystemTime`
10671099 TimestampOutOfBounds ,
1100+
1101+ /// The supplied expiry time could cause an overflow if added to a `PositiveTimestamp`
1102+ ExpiryTimeOutOfBounds ,
10681103}
10691104
10701105/// Errors that may occur when converting a `RawInvoice` to an `Invoice`. They relate to the
@@ -1369,7 +1404,7 @@ mod test {
13691404 1234567
13701405 ) ;
13711406 assert_eq ! ( invoice. payee_pub_key( ) , Some ( & PayeePubKey ( public_key) ) ) ;
1372- assert_eq ! ( invoice. expiry_time( ) , Some ( & ExpiryTime { seconds : 54321 } ) ) ;
1407+ assert_eq ! ( invoice. expiry_time( ) , Some ( & ExpiryTime :: from_seconds ( 54321 ) . unwrap ( ) ) ) ;
13731408 assert_eq ! ( invoice. min_final_cltv_expiry( ) , Some ( & MinFinalCltvExpiry ( 144 ) ) ) ;
13741409 assert_eq ! ( invoice. fallbacks( ) , vec![ & Fallback :: PubKeyHash ( [ 0 ; 20 ] ) ] ) ;
13751410 assert_eq ! ( invoice. routes( ) , vec![ & Route ( route_1) , & Route ( route_2) ] ) ;
0 commit comments