@@ -86,90 +86,28 @@ mod sync;
86
86
87
87
pub use de:: { ParseError , ParseOrSemanticError } ;
88
88
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 ;
93
91
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 ;
97
96
98
97
/// Default expiry time as defined by [BOLT 11].
99
98
///
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
101
100
pub const DEFAULT_EXPIRY_TIME : u64 = 3600 ;
102
101
103
102
/// Default minimum final CLTV expiry as defined by [BOLT 11].
104
103
///
105
104
/// Note that this is *not* the same value as rust-lightning's minimum CLTV expiry, which is
106
105
/// provided in [`MIN_FINAL_CLTV_EXPIRY`].
107
106
///
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
109
108
/// [`MIN_FINAL_CLTV_EXPIRY`]: lightning::ln::channelmanager::MIN_FINAL_CLTV_EXPIRY
110
109
pub const DEFAULT_MIN_FINAL_CLTV_EXPIRY : u64 = 18 ;
111
110
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
-
173
111
/// Builder for `Invoice`s. It's the most convenient and advised way to use this library. It ensures
174
112
/// that only a semantically and syntactically correct Invoice can be built using it.
175
113
///
@@ -330,12 +268,12 @@ pub struct RawDataPart {
330
268
pub tagged_fields : Vec < RawTaggedField > ,
331
269
}
332
270
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.
335
272
///
336
273
/// # 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`].
339
277
#[ derive( Eq , PartialEq , Debug , Clone ) ]
340
278
pub struct PositiveTimestamp ( Duration ) ;
341
279
@@ -445,11 +383,6 @@ pub struct PayeePubKey(pub PublicKey);
445
383
446
384
/// Positive duration that defines when (relatively to the timestamp) in the future the invoice
447
385
/// 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
453
386
#[ derive( Clone , Debug , Hash , Eq , PartialEq ) ]
454
387
pub struct ExpiryTime ( Duration ) ;
455
388
@@ -557,10 +490,7 @@ impl<D: tb::Bool, H: tb::Bool, T: tb::Bool, C: tb::Bool, S: tb::Bool> InvoiceBui
557
490
558
491
/// Sets the expiry time
559
492
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) ) ) ;
564
494
self
565
495
}
566
496
@@ -650,7 +580,7 @@ impl<D: tb::Bool, H: tb::Bool, C: tb::Bool, S: tb::Bool> InvoiceBuilder<D, H, tb
650
580
self . set_flags ( )
651
581
}
652
582
653
- /// Sets the timestamp to a duration since the UNIX epoch.
583
+ /// Sets the timestamp to a duration since the Unix epoch.
654
584
pub fn duration_since_epoch ( mut self , time : Duration ) -> InvoiceBuilder < D , H , tb:: True , C , S > {
655
585
match PositiveTimestamp :: from_duration_since_epoch ( time) {
656
586
Ok ( t) => self . timestamp = Some ( t) ,
@@ -981,49 +911,47 @@ impl RawInvoice {
981
911
}
982
912
983
913
impl 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`] .
987
917
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) )
993
919
}
994
920
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`].
998
925
#[ cfg( feature = "std" ) ]
999
926
pub fn from_system_time ( time : SystemTime ) -> Result < Self , CreationError > {
1000
927
time. duration_since ( SystemTime :: UNIX_EPOCH )
1001
928
. map ( Self :: from_duration_since_epoch)
1002
929
. unwrap_or ( Err ( CreationError :: TimestampOutOfBounds ) )
1003
930
}
1004
931
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`].
1008
936
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 {
1010
938
Ok ( PositiveTimestamp ( duration) )
1011
939
} else {
1012
940
Err ( CreationError :: TimestampOutOfBounds )
1013
941
}
1014
942
}
1015
943
1016
- /// Returns the UNIX timestamp representing the stored time
944
+ /// Returns the Unix timestamp representing the stored time
1017
945
pub fn as_unix_timestamp ( & self ) -> u64 {
1018
946
self . 0 . as_secs ( )
1019
947
}
1020
948
1021
- /// Returns the duration of the stored time since the UNIX epoch
949
+ /// Returns the duration of the stored time since the Unix epoch
1022
950
pub fn as_duration_since_epoch ( & self ) -> Duration {
1023
951
self . 0
1024
952
}
1025
953
1026
- /// Returns the `SystemTime` representing the stored time
954
+ /// Returns the [ `SystemTime`] representing the stored time
1027
955
#[ cfg( feature = "std" ) ]
1028
956
pub fn as_time ( & self ) -> SystemTime {
1029
957
SystemTime :: UNIX_EPOCH + self . 0
@@ -1180,7 +1108,7 @@ impl Invoice {
1180
1108
self . signed_invoice . raw_invoice ( ) . data . timestamp . as_time ( )
1181
1109
}
1182
1110
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
1184
1112
pub fn duration_since_epoch ( & self ) -> Duration {
1185
1113
self . signed_invoice . raw_invoice ( ) . data . timestamp . 0
1186
1114
}
@@ -1253,9 +1181,11 @@ impl Invoice {
1253
1181
}
1254
1182
1255
1183
/// 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.
1257
1185
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
1259
1189
}
1260
1190
1261
1191
/// Returns the invoice's `min_final_cltv_expiry` time, if present, otherwise
@@ -1376,26 +1306,14 @@ impl Deref for PayeePubKey {
1376
1306
}
1377
1307
1378
1308
impl 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) )
1388
1312
}
1389
1313
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)
1399
1317
}
1400
1318
1401
1319
/// Returns the expiry time in seconds
@@ -1464,12 +1382,9 @@ pub enum CreationError {
1464
1382
/// The specified route has too many hops and can't be encoded
1465
1383
RouteTooLong ,
1466
1384
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
1468
1386
TimestampOutOfBounds ,
1469
1387
1470
- /// The supplied expiry time could cause an overflow if added to a `PositiveTimestamp`
1471
- ExpiryTimeOutOfBounds ,
1472
-
1473
1388
/// The supplied millisatoshi amount was greater than the total bitcoin supply.
1474
1389
InvalidAmount ,
1475
1390
}
@@ -1479,8 +1394,7 @@ impl Display for CreationError {
1479
1394
match self {
1480
1395
CreationError :: DescriptionTooLong => f. write_str ( "The supplied description string was longer than 639 bytes" ) ,
1481
1396
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" ) ,
1484
1398
CreationError :: InvalidAmount => f. write_str ( "The supplied millisatoshi amount was greater than the total bitcoin supply" ) ,
1485
1399
}
1486
1400
}
@@ -1572,17 +1486,10 @@ mod test {
1572
1486
1573
1487
#[ test]
1574
1488
fn test_system_time_bounds_assumptions ( ) {
1575
- :: check_platform ( ) ;
1576
-
1577
1489
assert_eq ! (
1578
- :: PositiveTimestamp :: from_unix_timestamp( :: SYSTEM_TIME_MAX_UNIX_TIMESTAMP + 1 ) ,
1490
+ :: PositiveTimestamp :: from_unix_timestamp( :: MAX_TIMESTAMP + 1 ) ,
1579
1491
Err ( :: CreationError :: TimestampOutOfBounds )
1580
1492
) ;
1581
-
1582
- assert_eq ! (
1583
- :: ExpiryTime :: from_seconds( :: MAX_EXPIRY_TIME + 1 ) ,
1584
- Err ( :: CreationError :: ExpiryTimeOutOfBounds )
1585
- ) ;
1586
1493
}
1587
1494
1588
1495
#[ test]
0 commit comments