@@ -22,11 +22,15 @@ mod tb;
22
22
pub use de:: { ParseError , ParseOrSemanticError } ;
23
23
24
24
25
- // TODO: fix before 2038 (see rust PR #55527)
25
+ // TODO: fix before 2037 (see rust PR #55527)
26
26
/// Defines the maximum UNIX timestamp that can be represented as `SystemTime`. This is checked by
27
27
/// one of the unit tests, please run them.
28
28
const SYSTEM_TIME_MAX_UNIX_TIMESTAMP : u64 = std:: i32:: MAX as u64 ;
29
29
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
+
30
34
/// This function is used as a static assert for the size of `SystemTime`. If the crate fails to
31
35
/// compile due to it this indicates that your system uses unexpected bounds for `SystemTime`. You
32
36
/// can remove this functions and run the test `test_system_time_bounds_assumptions`. In any case,
@@ -262,10 +266,12 @@ pub struct Description(String);
262
266
pub struct PayeePubKey ( pub PublicKey ) ;
263
267
264
268
/// 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
265
273
#[ derive( Eq , PartialEq , Debug , Clone ) ]
266
- pub struct ExpiryTime {
267
- pub seconds : u64
268
- }
274
+ pub struct ExpiryTime ( Duration ) ;
269
275
270
276
/// `min_final_cltv_expiry` to use for the last HTLC in the route
271
277
#[ derive( Eq , PartialEq , Debug , Clone ) ]
@@ -382,8 +388,8 @@ impl<D: tb::Bool, H: tb::Bool, T: tb::Bool> InvoiceBuilder<D, H, T> {
382
388
}
383
389
384
390
/// 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 ( ) ) ) ;
387
393
self
388
394
}
389
395
@@ -490,7 +496,7 @@ impl<D: tb::Bool, H: tb::Bool> InvoiceBuilder<D, H, tb::False> {
490
496
491
497
/// Sets the timestamp to the current UNIX timestamp.
492
498
pub fn current_timestamp ( mut self ) -> InvoiceBuilder < D , H , tb:: True > {
493
- use std:: time:: { SystemTime , UNIX_EPOCH } ;
499
+ use std:: time:: SystemTime ;
494
500
let now = PositiveTimestamp :: from_system_time ( SystemTime :: now ( ) ) ;
495
501
self . timestamp = Some ( now. expect ( "for the foreseeable future this shouldn't happen" ) ) ;
496
502
self . set_flags ( )
@@ -760,23 +766,23 @@ impl RawInvoice {
760
766
impl PositiveTimestamp {
761
767
762
768
/// 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
764
770
/// `CreationError::TimestampOutOfBounds`.
765
771
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 {
767
773
Err ( CreationError :: TimestampOutOfBounds )
768
774
} else {
769
775
Ok ( PositiveTimestamp ( UNIX_EPOCH + Duration :: from_secs ( unix_seconds) ) )
770
776
}
771
777
}
772
778
773
779
/// 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
775
781
/// `CreationError::TimestampOutOfBounds`.
776
782
pub fn from_system_time ( time : SystemTime ) -> Result < Self , CreationError > {
777
783
if time
778
784
. 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 )
780
786
. unwrap_or ( true )
781
787
{
782
788
Ok ( PositiveTimestamp ( time) )
@@ -1010,6 +1016,32 @@ impl Deref for PayeePubKey {
1010
1016
}
1011
1017
}
1012
1018
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
+
1013
1045
impl Route {
1014
1046
pub fn new ( hops : Vec < RouteHop > ) -> Result < Route , CreationError > {
1015
1047
if hops. len ( ) <= 12 {
@@ -1065,6 +1097,9 @@ pub enum CreationError {
1065
1097
1066
1098
/// The unix timestamp of the supplied date is <0 or can't be represented as `SystemTime`
1067
1099
TimestampOutOfBounds ,
1100
+
1101
+ /// The supplied expiry time could cause an overflow if added to a `PositiveTimestamp`
1102
+ ExpiryTimeOutOfBounds ,
1068
1103
}
1069
1104
1070
1105
/// Errors that may occur when converting a `RawInvoice` to an `Invoice`. They relate to the
@@ -1369,7 +1404,7 @@ mod test {
1369
1404
1234567
1370
1405
) ;
1371
1406
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 ( ) ) ) ;
1373
1408
assert_eq ! ( invoice. min_final_cltv_expiry( ) , Some ( & MinFinalCltvExpiry ( 144 ) ) ) ;
1374
1409
assert_eq ! ( invoice. fallbacks( ) , vec![ & Fallback :: PubKeyHash ( [ 0 ; 20 ] ) ] ) ;
1375
1410
assert_eq ! ( invoice. routes( ) , vec![ & Route ( route_1) , & Route ( route_2) ] ) ;
0 commit comments