@@ -85,90 +85,28 @@ mod sync;
8585
8686pub use de:: { ParseError , ParseOrSemanticError } ;
8787
88- // TODO: fix before 2037 (see rust PR #55527)
89- /// Defines the maximum UNIX timestamp that can be represented as `SystemTime`. This is checked by
90- /// one of the unit tests, please run them.
91- const SYSTEM_TIME_MAX_UNIX_TIMESTAMP : u64 = core:: i32:: MAX as u64 ;
88+ /// The number of bits used to represent timestamps as defined in BOLT 11.
89+ const TIMESTAMP_BITS : usize = 35 ;
9290
93- /// Allow the expiry time to be up to one year. Since this reduces the range of possible timestamps
94- /// it should be rather low as long as we still have to support 32bit time representations
95- const MAX_EXPIRY_TIME : u64 = 60 * 60 * 24 * 356 ;
91+ /// The maximum timestamp as [`Duration::as_secs`] since the Unix epoch allowed by [`BOLT 11`].
92+ ///
93+ /// [BOLT 11]: https://github.com/lightning/bolts/blob/master/11-payment-encoding.md
94+ pub const MAX_TIMESTAMP : u64 = ( 1 << TIMESTAMP_BITS ) - 1 ;
9695
9796/// Default expiry time as defined by [BOLT 11].
9897///
99- /// [BOLT 11]: https://github.com/lightningnetwork/ lightning-rfc /blob/master/11-payment-encoding.md
98+ /// [BOLT 11]: https://github.com/lightning/bolts /blob/master/11-payment-encoding.md
10099pub const DEFAULT_EXPIRY_TIME : u64 = 3600 ;
101100
102101/// Default minimum final CLTV expiry as defined by [BOLT 11].
103102///
104103/// Note that this is *not* the same value as rust-lightning's minimum CLTV expiry, which is
105104/// provided in [`MIN_FINAL_CLTV_EXPIRY`].
106105///
107- /// [BOLT 11]: https://github.com/lightningnetwork/ lightning-rfc /blob/master/11-payment-encoding.md
106+ /// [BOLT 11]: https://github.com/lightning/bolts /blob/master/11-payment-encoding.md
108107/// [`MIN_FINAL_CLTV_EXPIRY`]: lightning::ln::channelmanager::MIN_FINAL_CLTV_EXPIRY
109108pub const DEFAULT_MIN_FINAL_CLTV_EXPIRY : u64 = 18 ;
110109
111- /// This function is used as a static assert for the size of `SystemTime`. If the crate fails to
112- /// compile due to it this indicates that your system uses unexpected bounds for `SystemTime`. You
113- /// can remove this functions and run the test `test_system_time_bounds_assumptions`. In any case,
114- /// please open an issue. If all tests pass you should be able to use this library safely by just
115- /// removing this function till we patch it accordingly.
116- #[ cfg( feature = "std" ) ]
117- fn __system_time_size_check ( ) {
118- // Use 2 * sizeof(u64) as expected size since the expected underlying implementation is storing
119- // a `Duration` since `SystemTime::UNIX_EPOCH`.
120- unsafe { let _ = core:: mem:: transmute_copy :: < SystemTime , [ u8 ; 16 ] > ( & SystemTime :: UNIX_EPOCH ) ; }
121- }
122-
123-
124- /// **Call this function on startup to ensure that all assumptions about the platform are valid.**
125- ///
126- /// Unfortunately we have to make assumptions about the upper bounds of the `SystemTime` type on
127- /// your platform which we can't fully verify at compile time and which isn't part of it's contract.
128- /// To our best knowledge our assumptions hold for all platforms officially supported by rust, but
129- /// since this check is fast we recommend to do it anyway.
130- ///
131- /// If this function fails this is considered a bug. Please open an issue describing your
132- /// platform and stating your current system time.
133- ///
134- /// Note that this currently does nothing in `no_std` environments, because they don't have
135- /// a `SystemTime` implementation.
136- ///
137- /// # Panics
138- /// If the check fails this function panics. By calling this function on startup you ensure that
139- /// this wont happen at an arbitrary later point in time.
140- pub fn check_platform ( ) {
141- #[ cfg( feature = "std" ) ]
142- check_system_time_bounds ( ) ;
143- }
144-
145- #[ cfg( feature = "std" ) ]
146- fn check_system_time_bounds ( ) {
147- // The upper and lower bounds of `SystemTime` are not part of its public contract and are
148- // platform specific. That's why we have to test if our assumptions regarding these bounds
149- // hold on the target platform.
150- //
151- // If this test fails on your platform, please don't use the library and open an issue
152- // instead so we can resolve the situation. Currently this library is tested on:
153- // * Linux (64bit)
154- let fail_date = SystemTime :: UNIX_EPOCH + Duration :: from_secs ( SYSTEM_TIME_MAX_UNIX_TIMESTAMP ) ;
155- let year = Duration :: from_secs ( 60 * 60 * 24 * 365 ) ;
156-
157- // Make sure that the library will keep working for another year
158- assert ! ( fail_date. duration_since( SystemTime :: now( ) ) . unwrap( ) > year) ;
159-
160- let max_ts = PositiveTimestamp :: from_unix_timestamp (
161- SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME
162- ) . unwrap ( ) ;
163- let max_exp = :: ExpiryTime :: from_seconds ( MAX_EXPIRY_TIME ) . unwrap ( ) ;
164-
165- assert_eq ! (
166- ( max_ts. as_time( ) + * max_exp. as_duration( ) ) . duration_since( SystemTime :: UNIX_EPOCH ) . unwrap( ) . as_secs( ) ,
167- SYSTEM_TIME_MAX_UNIX_TIMESTAMP
168- ) ;
169- }
170-
171-
172110/// Builder for `Invoice`s. It's the most convenient and advised way to use this library. It ensures
173111/// that only a semantically and syntactically correct Invoice can be built using it.
174112///
@@ -329,12 +267,12 @@ pub struct RawDataPart {
329267 pub tagged_fields : Vec < RawTaggedField > ,
330268}
331269
332- /// A timestamp that refers to a date after 1 January 1970 which means its representation as UNIX
333- /// timestamp is positive.
270+ /// A timestamp that refers to a date after 1 January 1970.
334271///
335272/// # Invariants
336- /// The UNIX timestamp representing the stored time has to be positive and small enough so that
337- /// a `ExpiryTime` can be added to it without an overflow.
273+ ///
274+ /// The Unix timestamp representing the stored time has to be positive and no greater than
275+ /// [`MAX_TIMESTAMP`].
338276#[ derive( Eq , PartialEq , Debug , Clone ) ]
339277pub struct PositiveTimestamp ( Duration ) ;
340278
@@ -444,11 +382,6 @@ pub struct PayeePubKey(pub PublicKey);
444382
445383/// Positive duration that defines when (relatively to the timestamp) in the future the invoice
446384/// expires
447- ///
448- /// # Invariants
449- /// The number of seconds this expiry time represents has to be in the range
450- /// `0...(SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME)` to avoid overflows when adding it to a
451- /// timestamp
452385#[ derive( Clone , Debug , Hash , Eq , PartialEq ) ]
453386pub struct ExpiryTime ( Duration ) ;
454387
@@ -556,10 +489,7 @@ impl<D: tb::Bool, H: tb::Bool, T: tb::Bool, C: tb::Bool, S: tb::Bool> InvoiceBui
556489
557490 /// Sets the expiry time
558491 pub fn expiry_time ( mut self , expiry_time : Duration ) -> Self {
559- match ExpiryTime :: from_duration ( expiry_time) {
560- Ok ( t) => self . tagged_fields . push ( TaggedField :: ExpiryTime ( t) ) ,
561- Err ( e) => self . error = Some ( e) ,
562- } ;
492+ self . tagged_fields . push ( TaggedField :: ExpiryTime ( ExpiryTime :: from_duration ( expiry_time) ) ) ;
563493 self
564494 }
565495
@@ -649,7 +579,7 @@ impl<D: tb::Bool, H: tb::Bool, C: tb::Bool, S: tb::Bool> InvoiceBuilder<D, H, tb
649579 self . set_flags ( )
650580 }
651581
652- /// Sets the timestamp to a duration since the UNIX epoch.
582+ /// Sets the timestamp to a duration since the Unix epoch.
653583 pub fn duration_since_epoch ( mut self , time : Duration ) -> InvoiceBuilder < D , H , tb:: True , C , S > {
654584 match PositiveTimestamp :: from_duration_since_epoch ( time) {
655585 Ok ( t) => self . timestamp = Some ( t) ,
@@ -1003,49 +933,47 @@ impl RawInvoice {
1003933}
1004934
1005935impl PositiveTimestamp {
1006- /// Create a new `PositiveTimestamp` from a unix timestamp in the Range
1007- /// `0...SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME`, otherwise return a
1008- /// `CreationError::TimestampOutOfBounds`.
936+ /// Creates a `PositiveTimestamp` from a Unix timestamp in the range `0..=MAX_TIMESTAMP`.
937+ ///
938+ /// Otherwise, returns a [ `CreationError::TimestampOutOfBounds`] .
1009939 pub fn from_unix_timestamp ( unix_seconds : u64 ) -> Result < Self , CreationError > {
1010- if unix_seconds > SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME {
1011- Err ( CreationError :: TimestampOutOfBounds )
1012- } else {
1013- Ok ( PositiveTimestamp ( Duration :: from_secs ( unix_seconds) ) )
1014- }
940+ Self :: from_duration_since_epoch ( Duration :: from_secs ( unix_seconds) )
1015941 }
1016942
1017- /// Create a new `PositiveTimestamp` from a `SystemTime` with a corresponding unix timestamp in
1018- /// the range `0...SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME`, otherwise return a
1019- /// `CreationError::TimestampOutOfBounds`.
943+ /// Creates a `PositiveTimestamp` from a [`SystemTime`] with a corresponding Unix timestamp in
944+ /// the range `0..=MAX_TIMESTAMP`.
945+ ///
946+ /// Otherwise, returns a [`CreationError::TimestampOutOfBounds`].
1020947 #[ cfg( feature = "std" ) ]
1021948 pub fn from_system_time ( time : SystemTime ) -> Result < Self , CreationError > {
1022949 time. duration_since ( SystemTime :: UNIX_EPOCH )
1023950 . map ( Self :: from_duration_since_epoch)
1024951 . unwrap_or ( Err ( CreationError :: TimestampOutOfBounds ) )
1025952 }
1026953
1027- /// Create a new `PositiveTimestamp` from a `Duration` since the UNIX epoch in
1028- /// the range `0...SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME`, otherwise return a
1029- /// `CreationError::TimestampOutOfBounds`.
954+ /// Creates a `PositiveTimestamp` from a [`Duration`] since the Unix epoch in the range
955+ /// `0..=MAX_TIMESTAMP`.
956+ ///
957+ /// Otherwise, returns a [`CreationError::TimestampOutOfBounds`].
1030958 pub fn from_duration_since_epoch ( duration : Duration ) -> Result < Self , CreationError > {
1031- if duration. as_secs ( ) <= SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME {
959+ if duration. as_secs ( ) <= MAX_TIMESTAMP {
1032960 Ok ( PositiveTimestamp ( duration) )
1033961 } else {
1034962 Err ( CreationError :: TimestampOutOfBounds )
1035963 }
1036964 }
1037965
1038- /// Returns the UNIX timestamp representing the stored time
966+ /// Returns the Unix timestamp representing the stored time
1039967 pub fn as_unix_timestamp ( & self ) -> u64 {
1040968 self . 0 . as_secs ( )
1041969 }
1042970
1043- /// Returns the duration of the stored time since the UNIX epoch
971+ /// Returns the duration of the stored time since the Unix epoch
1044972 pub fn as_duration_since_epoch ( & self ) -> Duration {
1045973 self . 0
1046974 }
1047975
1048- /// Returns the `SystemTime` representing the stored time
976+ /// Returns the [ `SystemTime`] representing the stored time
1049977 #[ cfg( feature = "std" ) ]
1050978 pub fn as_time ( & self ) -> SystemTime {
1051979 SystemTime :: UNIX_EPOCH + self . 0
@@ -1202,7 +1130,7 @@ impl Invoice {
12021130 self . signed_invoice . raw_invoice ( ) . data . timestamp . as_time ( )
12031131 }
12041132
1205- /// Returns the `Invoice`'s timestamp as a duration since the UNIX epoch
1133+ /// Returns the `Invoice`'s timestamp as a duration since the Unix epoch
12061134 pub fn duration_since_epoch ( & self ) -> Duration {
12071135 self . signed_invoice . raw_invoice ( ) . data . timestamp . 0
12081136 }
@@ -1275,9 +1203,11 @@ impl Invoice {
12751203 }
12761204
12771205 /// Returns whether the expiry time would pass at the given point in time.
1278- /// `at_time` is the timestamp as a duration since the UNIX epoch.
1206+ /// `at_time` is the timestamp as a duration since the Unix epoch.
12791207 pub fn would_expire ( & self , at_time : Duration ) -> bool {
1280- self . duration_since_epoch ( ) + self . expiry_time ( ) < at_time
1208+ self . duration_since_epoch ( )
1209+ . checked_add ( self . expiry_time ( ) )
1210+ . unwrap_or_else ( || Duration :: new ( u64:: max_value ( ) , 1_000_000_000 - 1 ) ) < at_time
12811211 }
12821212
12831213 /// Returns the invoice's `min_final_cltv_expiry` time, if present, otherwise
@@ -1398,26 +1328,14 @@ impl Deref for PayeePubKey {
13981328}
13991329
14001330impl ExpiryTime {
1401- /// Construct an `ExpiryTime` from seconds. If there exists a `PositiveTimestamp` which would
1402- /// overflow on adding the `EpiryTime` to it then this function will return a
1403- /// `CreationError::ExpiryTimeOutOfBounds`.
1404- pub fn from_seconds ( seconds : u64 ) -> Result < ExpiryTime , CreationError > {
1405- if seconds <= MAX_EXPIRY_TIME {
1406- Ok ( ExpiryTime ( Duration :: from_secs ( seconds) ) )
1407- } else {
1408- Err ( CreationError :: ExpiryTimeOutOfBounds )
1409- }
1331+ /// Construct an `ExpiryTime` from seconds.
1332+ pub fn from_seconds ( seconds : u64 ) -> ExpiryTime {
1333+ ExpiryTime ( Duration :: from_secs ( seconds) )
14101334 }
14111335
1412- /// Construct an `ExpiryTime` from a `Duration`. If there exists a `PositiveTimestamp` which
1413- /// would overflow on adding the `EpiryTime` to it then this function will return a
1414- /// `CreationError::ExpiryTimeOutOfBounds`.
1415- pub fn from_duration ( duration : Duration ) -> Result < ExpiryTime , CreationError > {
1416- if duration. as_secs ( ) <= MAX_EXPIRY_TIME {
1417- Ok ( ExpiryTime ( duration) )
1418- } else {
1419- Err ( CreationError :: ExpiryTimeOutOfBounds )
1420- }
1336+ /// Construct an `ExpiryTime` from a `Duration`.
1337+ pub fn from_duration ( duration : Duration ) -> ExpiryTime {
1338+ ExpiryTime ( duration)
14211339 }
14221340
14231341 /// Returns the expiry time in seconds
@@ -1486,12 +1404,9 @@ pub enum CreationError {
14861404 /// The specified route has too many hops and can't be encoded
14871405 RouteTooLong ,
14881406
1489- /// The unix timestamp of the supplied date is <0 or can't be represented as `SystemTime`
1407+ /// The Unix timestamp of the supplied date is less than zero or greater than 35-bits
14901408 TimestampOutOfBounds ,
14911409
1492- /// The supplied expiry time could cause an overflow if added to a `PositiveTimestamp`
1493- ExpiryTimeOutOfBounds ,
1494-
14951410 /// The supplied millisatoshi amount was greater than the total bitcoin supply.
14961411 InvalidAmount ,
14971412}
@@ -1501,8 +1416,7 @@ impl Display for CreationError {
15011416 match self {
15021417 CreationError :: DescriptionTooLong => f. write_str ( "The supplied description string was longer than 639 bytes" ) ,
15031418 CreationError :: RouteTooLong => f. write_str ( "The specified route has too many hops and can't be encoded" ) ,
1504- CreationError :: TimestampOutOfBounds => f. write_str ( "The unix timestamp of the supplied date is <0 or can't be represented as `SystemTime`" ) ,
1505- CreationError :: ExpiryTimeOutOfBounds => f. write_str ( "The supplied expiry time could cause an overflow if added to a `PositiveTimestamp`" ) ,
1419+ CreationError :: TimestampOutOfBounds => f. write_str ( "The Unix timestamp of the supplied date is less than zero or greater than 35-bits" ) ,
15061420 CreationError :: InvalidAmount => f. write_str ( "The supplied millisatoshi amount was greater than the total bitcoin supply" ) ,
15071421 }
15081422 }
@@ -1594,17 +1508,10 @@ mod test {
15941508
15951509 #[ test]
15961510 fn test_system_time_bounds_assumptions ( ) {
1597- :: check_platform ( ) ;
1598-
15991511 assert_eq ! (
1600- :: PositiveTimestamp :: from_unix_timestamp( :: SYSTEM_TIME_MAX_UNIX_TIMESTAMP + 1 ) ,
1512+ :: PositiveTimestamp :: from_unix_timestamp( :: MAX_TIMESTAMP + 1 ) ,
16011513 Err ( :: CreationError :: TimestampOutOfBounds )
16021514 ) ;
1603-
1604- assert_eq ! (
1605- :: ExpiryTime :: from_seconds( :: MAX_EXPIRY_TIME + 1 ) ,
1606- Err ( :: CreationError :: ExpiryTimeOutOfBounds )
1607- ) ;
16081515 }
16091516
16101517 #[ test]
0 commit comments