@@ -86,90 +86,28 @@ mod sync;
8686
8787pub use de:: { ParseError , ParseOrSemanticError } ;
8888
89- // TODO: fix before 2037 (see rust PR #55527)
90- /// Defines the maximum UNIX timestamp that can be represented as `SystemTime`. This is checked by
91- /// one of the unit tests, please run them.
92- const SYSTEM_TIME_MAX_UNIX_TIMESTAMP : u64 = core:: i32:: MAX as u64 ;
89+ /// The number of bits used to represent timestamps as defined in BOLT 11.
90+ const TIMESTAMP_BITS : usize = 35 ;
9391
94- /// Allow the expiry time to be up to one year. Since this reduces the range of possible timestamps
95- /// it should be rather low as long as we still have to support 32bit time representations
96- const MAX_EXPIRY_TIME : u64 = 60 * 60 * 24 * 356 ;
92+ /// The maximum timestamp as [`Duration::as_secs`] since the Unix epoch allowed by [`BOLT 11`].
93+ ///
94+ /// [BOLT 11]: https://github.com/lightning/bolts/blob/master/11-payment-encoding.md
95+ pub const MAX_TIMESTAMP : u64 = ( 1 << TIMESTAMP_BITS ) - 1 ;
9796
9897/// Default expiry time as defined by [BOLT 11].
9998///
100- /// [BOLT 11]: https://github.com/lightningnetwork/ lightning-rfc /blob/master/11-payment-encoding.md
99+ /// [BOLT 11]: https://github.com/lightning/bolts /blob/master/11-payment-encoding.md
101100pub const DEFAULT_EXPIRY_TIME : u64 = 3600 ;
102101
103102/// Default minimum final CLTV expiry as defined by [BOLT 11].
104103///
105104/// Note that this is *not* the same value as rust-lightning's minimum CLTV expiry, which is
106105/// provided in [`MIN_FINAL_CLTV_EXPIRY`].
107106///
108- /// [BOLT 11]: https://github.com/lightningnetwork/ lightning-rfc /blob/master/11-payment-encoding.md
107+ /// [BOLT 11]: https://github.com/lightning/bolts /blob/master/11-payment-encoding.md
109108/// [`MIN_FINAL_CLTV_EXPIRY`]: lightning::ln::channelmanager::MIN_FINAL_CLTV_EXPIRY
110109pub const DEFAULT_MIN_FINAL_CLTV_EXPIRY : u64 = 18 ;
111110
112- /// This function is used as a static assert for the size of `SystemTime`. If the crate fails to
113- /// compile due to it this indicates that your system uses unexpected bounds for `SystemTime`. You
114- /// can remove this functions and run the test `test_system_time_bounds_assumptions`. In any case,
115- /// please open an issue. If all tests pass you should be able to use this library safely by just
116- /// removing this function till we patch it accordingly.
117- #[ cfg( feature = "std" ) ]
118- fn __system_time_size_check ( ) {
119- // Use 2 * sizeof(u64) as expected size since the expected underlying implementation is storing
120- // a `Duration` since `SystemTime::UNIX_EPOCH`.
121- unsafe { let _ = core:: mem:: transmute_copy :: < SystemTime , [ u8 ; 16 ] > ( & SystemTime :: UNIX_EPOCH ) ; }
122- }
123-
124-
125- /// **Call this function on startup to ensure that all assumptions about the platform are valid.**
126- ///
127- /// Unfortunately we have to make assumptions about the upper bounds of the `SystemTime` type on
128- /// your platform which we can't fully verify at compile time and which isn't part of it's contract.
129- /// To our best knowledge our assumptions hold for all platforms officially supported by rust, but
130- /// since this check is fast we recommend to do it anyway.
131- ///
132- /// If this function fails this is considered a bug. Please open an issue describing your
133- /// platform and stating your current system time.
134- ///
135- /// Note that this currently does nothing in `no_std` environments, because they don't have
136- /// a `SystemTime` implementation.
137- ///
138- /// # Panics
139- /// If the check fails this function panics. By calling this function on startup you ensure that
140- /// this wont happen at an arbitrary later point in time.
141- pub fn check_platform ( ) {
142- #[ cfg( feature = "std" ) ]
143- check_system_time_bounds ( ) ;
144- }
145-
146- #[ cfg( feature = "std" ) ]
147- fn check_system_time_bounds ( ) {
148- // The upper and lower bounds of `SystemTime` are not part of its public contract and are
149- // platform specific. That's why we have to test if our assumptions regarding these bounds
150- // hold on the target platform.
151- //
152- // If this test fails on your platform, please don't use the library and open an issue
153- // instead so we can resolve the situation. Currently this library is tested on:
154- // * Linux (64bit)
155- let fail_date = SystemTime :: UNIX_EPOCH + Duration :: from_secs ( SYSTEM_TIME_MAX_UNIX_TIMESTAMP ) ;
156- let year = Duration :: from_secs ( 60 * 60 * 24 * 365 ) ;
157-
158- // Make sure that the library will keep working for another year
159- assert ! ( fail_date. duration_since( SystemTime :: now( ) ) . unwrap( ) > year) ;
160-
161- let max_ts = PositiveTimestamp :: from_unix_timestamp (
162- SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME
163- ) . unwrap ( ) ;
164- let max_exp = :: ExpiryTime :: from_seconds ( MAX_EXPIRY_TIME ) . unwrap ( ) ;
165-
166- assert_eq ! (
167- ( max_ts. as_time( ) + * max_exp. as_duration( ) ) . duration_since( SystemTime :: UNIX_EPOCH ) . unwrap( ) . as_secs( ) ,
168- SYSTEM_TIME_MAX_UNIX_TIMESTAMP
169- ) ;
170- }
171-
172-
173111/// Builder for `Invoice`s. It's the most convenient and advised way to use this library. It ensures
174112/// that only a semantically and syntactically correct Invoice can be built using it.
175113///
@@ -330,12 +268,12 @@ pub struct RawDataPart {
330268 pub tagged_fields : Vec < RawTaggedField > ,
331269}
332270
333- /// A timestamp that refers to a date after 1 January 1970 which means its representation as UNIX
334- /// timestamp is positive.
271+ /// A timestamp that refers to a date after 1 January 1970.
335272///
336273/// # Invariants
337- /// The UNIX timestamp representing the stored time has to be positive and small enough so that
338- /// a `ExpiryTime` can be added to it without an overflow.
274+ ///
275+ /// The Unix timestamp representing the stored time has to be positive and no greater than
276+ /// [`MAX_TIMESTAMP`].
339277#[ derive( Eq , PartialEq , Debug , Clone ) ]
340278pub struct PositiveTimestamp ( Duration ) ;
341279
@@ -445,11 +383,6 @@ pub struct PayeePubKey(pub PublicKey);
445383
446384/// Positive duration that defines when (relatively to the timestamp) in the future the invoice
447385/// expires
448- ///
449- /// # Invariants
450- /// The number of seconds this expiry time represents has to be in the range
451- /// `0...(SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME)` to avoid overflows when adding it to a
452- /// timestamp
453386#[ derive( Clone , Debug , Hash , Eq , PartialEq ) ]
454387pub struct ExpiryTime ( Duration ) ;
455388
@@ -557,10 +490,7 @@ impl<D: tb::Bool, H: tb::Bool, T: tb::Bool, C: tb::Bool, S: tb::Bool> InvoiceBui
557490
558491 /// Sets the expiry time
559492 pub fn expiry_time ( mut self , expiry_time : Duration ) -> Self {
560- match ExpiryTime :: from_duration ( expiry_time) {
561- Ok ( t) => self . tagged_fields . push ( TaggedField :: ExpiryTime ( t) ) ,
562- Err ( e) => self . error = Some ( e) ,
563- } ;
493+ self . tagged_fields . push ( TaggedField :: ExpiryTime ( ExpiryTime :: from_duration ( expiry_time) ) ) ;
564494 self
565495 }
566496
@@ -650,7 +580,7 @@ impl<D: tb::Bool, H: tb::Bool, C: tb::Bool, S: tb::Bool> InvoiceBuilder<D, H, tb
650580 self . set_flags ( )
651581 }
652582
653- /// Sets the timestamp to a duration since the UNIX epoch.
583+ /// Sets the timestamp to a duration since the Unix epoch.
654584 pub fn duration_since_epoch ( mut self , time : Duration ) -> InvoiceBuilder < D , H , tb:: True , C , S > {
655585 match PositiveTimestamp :: from_duration_since_epoch ( time) {
656586 Ok ( t) => self . timestamp = Some ( t) ,
@@ -981,49 +911,47 @@ impl RawInvoice {
981911}
982912
983913impl PositiveTimestamp {
984- /// Create a new `PositiveTimestamp` from a unix timestamp in the Range
985- /// `0...SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME`, otherwise return a
986- /// `CreationError::TimestampOutOfBounds`.
914+ /// Creates a `PositiveTimestamp` from a Unix timestamp in the range `0..=MAX_TIMESTAMP`.
915+ ///
916+ /// Otherwise, returns a [ `CreationError::TimestampOutOfBounds`] .
987917 pub fn from_unix_timestamp ( unix_seconds : u64 ) -> Result < Self , CreationError > {
988- if unix_seconds > SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME {
989- Err ( CreationError :: TimestampOutOfBounds )
990- } else {
991- Ok ( PositiveTimestamp ( Duration :: from_secs ( unix_seconds) ) )
992- }
918+ Self :: from_duration_since_epoch ( Duration :: from_secs ( unix_seconds) )
993919 }
994920
995- /// Create a new `PositiveTimestamp` from a `SystemTime` with a corresponding unix timestamp in
996- /// the range `0...SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME`, otherwise return a
997- /// `CreationError::TimestampOutOfBounds`.
921+ /// Creates a `PositiveTimestamp` from a [`SystemTime`] with a corresponding Unix timestamp in
922+ /// the range `0..=MAX_TIMESTAMP`.
923+ ///
924+ /// Otherwise, returns a [`CreationError::TimestampOutOfBounds`].
998925 #[ cfg( feature = "std" ) ]
999926 pub fn from_system_time ( time : SystemTime ) -> Result < Self , CreationError > {
1000927 time. duration_since ( SystemTime :: UNIX_EPOCH )
1001928 . map ( Self :: from_duration_since_epoch)
1002929 . unwrap_or ( Err ( CreationError :: TimestampOutOfBounds ) )
1003930 }
1004931
1005- /// Create a new `PositiveTimestamp` from a `Duration` since the UNIX epoch in
1006- /// the range `0...SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME`, otherwise return a
1007- /// `CreationError::TimestampOutOfBounds`.
932+ /// Creates a `PositiveTimestamp` from a [`Duration`] since the Unix epoch in the range
933+ /// `0..=MAX_TIMESTAMP`.
934+ ///
935+ /// Otherwise, returns a [`CreationError::TimestampOutOfBounds`].
1008936 pub fn from_duration_since_epoch ( duration : Duration ) -> Result < Self , CreationError > {
1009- if duration. as_secs ( ) <= SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME {
937+ if duration. as_secs ( ) <= MAX_TIMESTAMP {
1010938 Ok ( PositiveTimestamp ( duration) )
1011939 } else {
1012940 Err ( CreationError :: TimestampOutOfBounds )
1013941 }
1014942 }
1015943
1016- /// Returns the UNIX timestamp representing the stored time
944+ /// Returns the Unix timestamp representing the stored time
1017945 pub fn as_unix_timestamp ( & self ) -> u64 {
1018946 self . 0 . as_secs ( )
1019947 }
1020948
1021- /// Returns the duration of the stored time since the UNIX epoch
949+ /// Returns the duration of the stored time since the Unix epoch
1022950 pub fn as_duration_since_epoch ( & self ) -> Duration {
1023951 self . 0
1024952 }
1025953
1026- /// Returns the `SystemTime` representing the stored time
954+ /// Returns the [ `SystemTime`] representing the stored time
1027955 #[ cfg( feature = "std" ) ]
1028956 pub fn as_time ( & self ) -> SystemTime {
1029957 SystemTime :: UNIX_EPOCH + self . 0
@@ -1180,7 +1108,7 @@ impl Invoice {
11801108 self . signed_invoice . raw_invoice ( ) . data . timestamp . as_time ( )
11811109 }
11821110
1183- /// Returns the `Invoice`'s timestamp as a duration since the UNIX epoch
1111+ /// Returns the `Invoice`'s timestamp as a duration since the Unix epoch
11841112 pub fn duration_since_epoch ( & self ) -> Duration {
11851113 self . signed_invoice . raw_invoice ( ) . data . timestamp . 0
11861114 }
@@ -1253,9 +1181,11 @@ impl Invoice {
12531181 }
12541182
12551183 /// Returns whether the expiry time would pass at the given point in time.
1256- /// `at_time` is the timestamp as a duration since the UNIX epoch.
1184+ /// `at_time` is the timestamp as a duration since the Unix epoch.
12571185 pub fn would_expire ( & self , at_time : Duration ) -> bool {
1258- self . duration_since_epoch ( ) + self . expiry_time ( ) < at_time
1186+ self . duration_since_epoch ( )
1187+ . checked_add ( self . expiry_time ( ) )
1188+ . unwrap_or_else ( || Duration :: new ( u64:: max_value ( ) , 1_000_000_000 - 1 ) ) < at_time
12591189 }
12601190
12611191 /// Returns the invoice's `min_final_cltv_expiry` time, if present, otherwise
@@ -1376,26 +1306,14 @@ impl Deref for PayeePubKey {
13761306}
13771307
13781308impl ExpiryTime {
1379- /// Construct an `ExpiryTime` from seconds. If there exists a `PositiveTimestamp` which would
1380- /// overflow on adding the `EpiryTime` to it then this function will return a
1381- /// `CreationError::ExpiryTimeOutOfBounds`.
1382- pub fn from_seconds ( seconds : u64 ) -> Result < ExpiryTime , CreationError > {
1383- if seconds <= MAX_EXPIRY_TIME {
1384- Ok ( ExpiryTime ( Duration :: from_secs ( seconds) ) )
1385- } else {
1386- Err ( CreationError :: ExpiryTimeOutOfBounds )
1387- }
1309+ /// Construct an `ExpiryTime` from seconds.
1310+ pub fn from_seconds ( seconds : u64 ) -> ExpiryTime {
1311+ ExpiryTime ( Duration :: from_secs ( seconds) )
13881312 }
13891313
1390- /// Construct an `ExpiryTime` from a `Duration`. If there exists a `PositiveTimestamp` which
1391- /// would overflow on adding the `EpiryTime` to it then this function will return a
1392- /// `CreationError::ExpiryTimeOutOfBounds`.
1393- pub fn from_duration ( duration : Duration ) -> Result < ExpiryTime , CreationError > {
1394- if duration. as_secs ( ) <= MAX_EXPIRY_TIME {
1395- Ok ( ExpiryTime ( duration) )
1396- } else {
1397- Err ( CreationError :: ExpiryTimeOutOfBounds )
1398- }
1314+ /// Construct an `ExpiryTime` from a `Duration`.
1315+ pub fn from_duration ( duration : Duration ) -> ExpiryTime {
1316+ ExpiryTime ( duration)
13991317 }
14001318
14011319 /// Returns the expiry time in seconds
@@ -1464,12 +1382,9 @@ pub enum CreationError {
14641382 /// The specified route has too many hops and can't be encoded
14651383 RouteTooLong ,
14661384
1467- /// The unix timestamp of the supplied date is <0 or can't be represented as `SystemTime`
1385+ /// The Unix timestamp of the supplied date is less than zero or greater than 35-bits
14681386 TimestampOutOfBounds ,
14691387
1470- /// The supplied expiry time could cause an overflow if added to a `PositiveTimestamp`
1471- ExpiryTimeOutOfBounds ,
1472-
14731388 /// The supplied millisatoshi amount was greater than the total bitcoin supply.
14741389 InvalidAmount ,
14751390}
@@ -1479,8 +1394,7 @@ impl Display for CreationError {
14791394 match self {
14801395 CreationError :: DescriptionTooLong => f. write_str ( "The supplied description string was longer than 639 bytes" ) ,
14811396 CreationError :: RouteTooLong => f. write_str ( "The specified route has too many hops and can't be encoded" ) ,
1482- CreationError :: TimestampOutOfBounds => f. write_str ( "The unix timestamp of the supplied date is <0 or can't be represented as `SystemTime`" ) ,
1483- CreationError :: ExpiryTimeOutOfBounds => f. write_str ( "The supplied expiry time could cause an overflow if added to a `PositiveTimestamp`" ) ,
1397+ CreationError :: TimestampOutOfBounds => f. write_str ( "The Unix timestamp of the supplied date is less than zero or greater than 35-bits" ) ,
14841398 CreationError :: InvalidAmount => f. write_str ( "The supplied millisatoshi amount was greater than the total bitcoin supply" ) ,
14851399 }
14861400 }
@@ -1572,17 +1486,10 @@ mod test {
15721486
15731487 #[ test]
15741488 fn test_system_time_bounds_assumptions ( ) {
1575- :: check_platform ( ) ;
1576-
15771489 assert_eq ! (
1578- :: PositiveTimestamp :: from_unix_timestamp( :: SYSTEM_TIME_MAX_UNIX_TIMESTAMP + 1 ) ,
1490+ :: PositiveTimestamp :: from_unix_timestamp( :: MAX_TIMESTAMP + 1 ) ,
15791491 Err ( :: CreationError :: TimestampOutOfBounds )
15801492 ) ;
1581-
1582- assert_eq ! (
1583- :: ExpiryTime :: from_seconds( :: MAX_EXPIRY_TIME + 1 ) ,
1584- Err ( :: CreationError :: ExpiryTimeOutOfBounds )
1585- ) ;
15861493 }
15871494
15881495 #[ test]
0 commit comments