@@ -85,90 +85,28 @@ mod sync;
85
85
86
86
pub use de:: { ParseError , ParseOrSemanticError } ;
87
87
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 ;
92
90
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 ;
96
95
97
96
/// Default expiry time as defined by [BOLT 11].
98
97
///
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
100
99
pub const DEFAULT_EXPIRY_TIME : u64 = 3600 ;
101
100
102
101
/// Default minimum final CLTV expiry as defined by [BOLT 11].
103
102
///
104
103
/// Note that this is *not* the same value as rust-lightning's minimum CLTV expiry, which is
105
104
/// provided in [`MIN_FINAL_CLTV_EXPIRY`].
106
105
///
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
108
107
/// [`MIN_FINAL_CLTV_EXPIRY`]: lightning::ln::channelmanager::MIN_FINAL_CLTV_EXPIRY
109
108
pub const DEFAULT_MIN_FINAL_CLTV_EXPIRY : u64 = 18 ;
110
109
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
-
172
110
/// Builder for `Invoice`s. It's the most convenient and advised way to use this library. It ensures
173
111
/// that only a semantically and syntactically correct Invoice can be built using it.
174
112
///
@@ -329,12 +267,12 @@ pub struct RawDataPart {
329
267
pub tagged_fields : Vec < RawTaggedField > ,
330
268
}
331
269
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.
334
271
///
335
272
/// # 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`].
338
276
#[ derive( Eq , PartialEq , Debug , Clone ) ]
339
277
pub struct PositiveTimestamp ( Duration ) ;
340
278
@@ -444,11 +382,6 @@ pub struct PayeePubKey(pub PublicKey);
444
382
445
383
/// Positive duration that defines when (relatively to the timestamp) in the future the invoice
446
384
/// 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
452
385
#[ derive( Clone , Debug , Hash , Eq , PartialEq ) ]
453
386
pub struct ExpiryTime ( Duration ) ;
454
387
@@ -556,10 +489,7 @@ impl<D: tb::Bool, H: tb::Bool, T: tb::Bool, C: tb::Bool, S: tb::Bool> InvoiceBui
556
489
557
490
/// Sets the expiry time
558
491
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) ) ) ;
563
493
self
564
494
}
565
495
@@ -649,7 +579,7 @@ impl<D: tb::Bool, H: tb::Bool, C: tb::Bool, S: tb::Bool> InvoiceBuilder<D, H, tb
649
579
self . set_flags ( )
650
580
}
651
581
652
- /// Sets the timestamp to a duration since the UNIX epoch.
582
+ /// Sets the timestamp to a duration since the Unix epoch.
653
583
pub fn duration_since_epoch ( mut self , time : Duration ) -> InvoiceBuilder < D , H , tb:: True , C , S > {
654
584
match PositiveTimestamp :: from_duration_since_epoch ( time) {
655
585
Ok ( t) => self . timestamp = Some ( t) ,
@@ -1003,49 +933,47 @@ impl RawInvoice {
1003
933
}
1004
934
1005
935
impl 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`] .
1009
939
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) )
1015
941
}
1016
942
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`].
1020
947
#[ cfg( feature = "std" ) ]
1021
948
pub fn from_system_time ( time : SystemTime ) -> Result < Self , CreationError > {
1022
949
time. duration_since ( SystemTime :: UNIX_EPOCH )
1023
950
. map ( Self :: from_duration_since_epoch)
1024
951
. unwrap_or ( Err ( CreationError :: TimestampOutOfBounds ) )
1025
952
}
1026
953
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`].
1030
958
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 {
1032
960
Ok ( PositiveTimestamp ( duration) )
1033
961
} else {
1034
962
Err ( CreationError :: TimestampOutOfBounds )
1035
963
}
1036
964
}
1037
965
1038
- /// Returns the UNIX timestamp representing the stored time
966
+ /// Returns the Unix timestamp representing the stored time
1039
967
pub fn as_unix_timestamp ( & self ) -> u64 {
1040
968
self . 0 . as_secs ( )
1041
969
}
1042
970
1043
- /// Returns the duration of the stored time since the UNIX epoch
971
+ /// Returns the duration of the stored time since the Unix epoch
1044
972
pub fn as_duration_since_epoch ( & self ) -> Duration {
1045
973
self . 0
1046
974
}
1047
975
1048
- /// Returns the `SystemTime` representing the stored time
976
+ /// Returns the [ `SystemTime`] representing the stored time
1049
977
#[ cfg( feature = "std" ) ]
1050
978
pub fn as_time ( & self ) -> SystemTime {
1051
979
SystemTime :: UNIX_EPOCH + self . 0
@@ -1202,7 +1130,7 @@ impl Invoice {
1202
1130
self . signed_invoice . raw_invoice ( ) . data . timestamp . as_time ( )
1203
1131
}
1204
1132
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
1206
1134
pub fn duration_since_epoch ( & self ) -> Duration {
1207
1135
self . signed_invoice . raw_invoice ( ) . data . timestamp . 0
1208
1136
}
@@ -1275,9 +1203,11 @@ impl Invoice {
1275
1203
}
1276
1204
1277
1205
/// 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.
1279
1207
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
1281
1211
}
1282
1212
1283
1213
/// Returns the invoice's `min_final_cltv_expiry` time, if present, otherwise
@@ -1398,26 +1328,14 @@ impl Deref for PayeePubKey {
1398
1328
}
1399
1329
1400
1330
impl 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) )
1410
1334
}
1411
1335
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)
1421
1339
}
1422
1340
1423
1341
/// Returns the expiry time in seconds
@@ -1486,12 +1404,9 @@ pub enum CreationError {
1486
1404
/// The specified route has too many hops and can't be encoded
1487
1405
RouteTooLong ,
1488
1406
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
1490
1408
TimestampOutOfBounds ,
1491
1409
1492
- /// The supplied expiry time could cause an overflow if added to a `PositiveTimestamp`
1493
- ExpiryTimeOutOfBounds ,
1494
-
1495
1410
/// The supplied millisatoshi amount was greater than the total bitcoin supply.
1496
1411
InvalidAmount ,
1497
1412
}
@@ -1501,8 +1416,7 @@ impl Display for CreationError {
1501
1416
match self {
1502
1417
CreationError :: DescriptionTooLong => f. write_str ( "The supplied description string was longer than 639 bytes" ) ,
1503
1418
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" ) ,
1506
1420
CreationError :: InvalidAmount => f. write_str ( "The supplied millisatoshi amount was greater than the total bitcoin supply" ) ,
1507
1421
}
1508
1422
}
@@ -1594,17 +1508,10 @@ mod test {
1594
1508
1595
1509
#[ test]
1596
1510
fn test_system_time_bounds_assumptions ( ) {
1597
- :: check_platform ( ) ;
1598
-
1599
1511
assert_eq ! (
1600
- :: PositiveTimestamp :: from_unix_timestamp( :: SYSTEM_TIME_MAX_UNIX_TIMESTAMP + 1 ) ,
1512
+ :: PositiveTimestamp :: from_unix_timestamp( :: MAX_TIMESTAMP + 1 ) ,
1601
1513
Err ( :: CreationError :: TimestampOutOfBounds )
1602
1514
) ;
1603
-
1604
- assert_eq ! (
1605
- :: ExpiryTime :: from_seconds( :: MAX_EXPIRY_TIME + 1 ) ,
1606
- Err ( :: CreationError :: ExpiryTimeOutOfBounds )
1607
- ) ;
1608
1515
}
1609
1516
1610
1517
#[ test]
0 commit comments