Skip to content

Commit 5f61a12

Browse files
committed
f - Use saturating_add
1 parent f805e2b commit 5f61a12

File tree

2 files changed

+30
-100
lines changed

2 files changed

+30
-100
lines changed

lightning-invoice/src/de.rs

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use secp256k1::key::PublicKey;
2424

2525
use super::{Invoice, Sha256, TaggedField, ExpiryTime, MinFinalCltvExpiry, Fallback, PayeePubKey, InvoiceSignature, PositiveTimestamp,
2626
SemanticError, PrivateRoute, Description, RawTaggedField, Currency, RawHrp, SiPrefix, RawInvoice, constants, SignedRawInvoice,
27-
RawDataPart, CreationError, InvoiceFeatures};
27+
RawDataPart, InvoiceFeatures};
2828

2929
use self::hrp_sm::parse_hrp;
3030

@@ -357,11 +357,7 @@ impl FromBase32 for PositiveTimestamp {
357357
}
358358
let timestamp: u64 = parse_int_be(b32, 32)
359359
.expect("7*5bit < 64bit, no overflow possible");
360-
match PositiveTimestamp::from_unix_timestamp(timestamp) {
361-
Ok(t) => Ok(t),
362-
Err(CreationError::TimestampOutOfBounds) => Err(ParseError::TimestampOverflow),
363-
Err(_) => unreachable!(),
364-
}
360+
Ok(PositiveTimestamp::from_unix_timestamp(timestamp))
365361
}
366362
}
367363

@@ -516,7 +512,7 @@ impl FromBase32 for ExpiryTime {
516512

517513
fn from_base32(field_data: &[u5]) -> Result<ExpiryTime, ParseError> {
518514
match parse_int_be::<u64, u5>(field_data, 32)
519-
.and_then(|t| ExpiryTime::from_seconds(t).ok()) // ok, since the only error is out of bounds
515+
.map(|t| ExpiryTime::from_seconds(t))
520516
{
521517
Some(t) => Ok(t),
522518
None => Err(ParseError::IntegerOverflowError),
@@ -646,7 +642,6 @@ pub enum ParseError {
646642
/// Not an error, but used internally to signal that a part of the invoice should be ignored
647643
/// according to BOLT11
648644
Skip,
649-
TimestampOverflow,
650645
}
651646

652647
/// Indicates that something went wrong while parsing or validating the invoice. Parsing errors
@@ -709,9 +704,6 @@ impl Display for ParseError {
709704
ParseError::Skip => {
710705
f.write_str("the tagged field has to be skipped because of an unexpected, but allowed property")
711706
},
712-
ParseError::TimestampOverflow => {
713-
f.write_str("the invoice's timestamp could not be represented as SystemTime")
714-
},
715707
}
716708
}
717709
}
@@ -877,7 +869,7 @@ mod test {
877869
use bech32::FromBase32;
878870

879871
let input = from_bech32("pu".as_bytes());
880-
let expected = Ok(ExpiryTime::from_seconds(60).unwrap());
872+
let expected = Ok(ExpiryTime::from_seconds(60));
881873
assert_eq!(ExpiryTime::from_base32(&input), expected);
882874

883875
let input_too_large = from_bech32("sqqqqqqqqqqqq".as_bytes());
@@ -1028,7 +1020,7 @@ mod test {
10281020
si_prefix: Some(SiPrefix::Milli)
10291021
},
10301022
data: RawDataPart {
1031-
timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
1023+
timestamp: PositiveTimestamp::from_unix_timestamp(1496314658),
10321024
tagged_fields: vec ! [
10331025
PaymentHash(Sha256(sha256::Hash::from_hex(
10341026
"0001020304050607080900010203040506070809000102030405060708090102"
@@ -1075,7 +1067,7 @@ mod test {
10751067
si_prefix: None,
10761068
},
10771069
data: RawDataPart {
1078-
timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
1070+
timestamp: PositiveTimestamp::from_unix_timestamp(1496314658),
10791071
tagged_fields: vec ! [
10801072
PaymentHash(Sha256(sha256::Hash::from_hex(
10811073
"0001020304050607080900010203040506070809000102030405060708090102"

lightning-invoice/src/lib.rs

Lines changed: 24 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -85,17 +85,6 @@ mod sync;
8585

8686
pub use de::{ParseError, ParseOrSemanticError};
8787

88-
/// The maximum timestamp that can be represented as a [`Duration`] since the UNIX epoch while
89-
/// allowing for adding an expiry without overflowing.
90-
const MAX_TIMESTAMP: u64 = core::i64::MAX as u64;
91-
92-
/// The maximum expiry allowed, represented as a [`Duration`] since the invoice timestamp.
93-
const MAX_EXPIRY_TIME: u64 = (core::i64::MAX) as u64 + 1;
94-
95-
/// Assert that the maximum expiry represented as a [`Duration`] since the UNIX epoch does not
96-
/// exceed [`u64::MAX`].
97-
const _MAX_EXPIRY_TIMESTAMP: u64 = MAX_TIMESTAMP + MAX_EXPIRY_TIME;
98-
9988
/// Default expiry time as defined by [BOLT 11].
10089
///
10190
/// [BOLT 11]: https://github.com/lightningnetwork/lightning-rfc/blob/master/11-payment-encoding.md
@@ -385,10 +374,6 @@ pub struct PayeePubKey(pub PublicKey);
385374

386375
/// Positive duration that defines when (relatively to the timestamp) in the future the invoice
387376
/// expires
388-
///
389-
/// # Invariants
390-
/// The number of seconds this expiry time represents has to be in the range
391-
/// `0...MAX_EXPIRY_TIME` to avoid overflows when adding it to a timestamp.
392377
#[derive(Clone, Debug, Hash, Eq, PartialEq)]
393378
pub struct ExpiryTime(Duration);
394379

@@ -496,10 +481,7 @@ impl<D: tb::Bool, H: tb::Bool, T: tb::Bool, C: tb::Bool, S: tb::Bool> InvoiceBui
496481

497482
/// Sets the expiry time
498483
pub fn expiry_time(mut self, expiry_time: Duration) -> Self {
499-
match ExpiryTime::from_duration(expiry_time) {
500-
Ok(t) => self.tagged_fields.push(TaggedField::ExpiryTime(t)),
501-
Err(e) => self.error = Some(e),
502-
};
484+
self.tagged_fields.push(TaggedField::ExpiryTime(ExpiryTime::from_duration(expiry_time)));
503485
self
504486
}
505487

@@ -591,19 +573,15 @@ impl<D: tb::Bool, H: tb::Bool, C: tb::Bool, S: tb::Bool> InvoiceBuilder<D, H, tb
591573

592574
/// Sets the timestamp to a duration since the UNIX epoch.
593575
pub fn duration_since_epoch(mut self, time: Duration) -> InvoiceBuilder<D, H, tb::True, C, S> {
594-
match PositiveTimestamp::from_duration_since_epoch(time) {
595-
Ok(t) => self.timestamp = Some(t),
596-
Err(e) => self.error = Some(e),
597-
}
598-
576+
self.timestamp = Some(PositiveTimestamp::from_duration_since_epoch(time));
599577
self.set_flags()
600578
}
601579

602580
/// Sets the timestamp to the current system time.
603581
#[cfg(feature = "std")]
604582
pub fn current_timestamp(mut self) -> InvoiceBuilder<D, H, tb::True, C, S> {
605583
let now = PositiveTimestamp::from_system_time(SystemTime::now());
606-
self.timestamp = Some(now.expect("for the foreseeable future this shouldn't happen"));
584+
self.timestamp = Some(now.expect("this shouldn't happen after the UNIX epoch"));
607585
self.set_flags()
608586
}
609587
}
@@ -946,35 +924,24 @@ impl PositiveTimestamp {
946924
/// Creates a `PositiveTimestamp` from a unix timestamp in the range `0...MAX_TIMESTAMP`.
947925
///
948926
/// Otherwise, returns a [`CreationError::TimestampOutOfBounds`].
949-
pub fn from_unix_timestamp(unix_seconds: u64) -> Result<Self, CreationError> {
950-
if unix_seconds > MAX_TIMESTAMP {
951-
Err(CreationError::TimestampOutOfBounds)
952-
} else {
953-
Ok(PositiveTimestamp(Duration::from_secs(unix_seconds)))
954-
}
927+
pub fn from_unix_timestamp(unix_seconds: u64) -> Self {
928+
PositiveTimestamp(Duration::from_secs(unix_seconds))
955929
}
956930

957-
/// Creates a `PositiveTimestamp` from a [`SystemTime`] with a corresponding unix timestamp in
958-
/// the range `0...MAX_TIMESTAMP`.
931+
/// Creates a `PositiveTimestamp` from a [`SystemTime`] with a corresponding unix timestamp.
959932
///
960-
/// Otherwise, returns a [`CreationError::TimestampOutOfBounds`].
933+
/// Returns a [`CreationError::TimestampOutOfBounds`] if `time` is before
934+
/// [`SystemTime::UNIX_EPOCH`].
961935
#[cfg(feature = "std")]
962936
pub fn from_system_time(time: SystemTime) -> Result<Self, CreationError> {
963937
time.duration_since(SystemTime::UNIX_EPOCH)
964938
.map(Self::from_duration_since_epoch)
965-
.unwrap_or(Err(CreationError::TimestampOutOfBounds))
939+
.map_err(|_| CreationError::TimestampOutOfBounds)
966940
}
967941

968-
/// Creates a `PositiveTimestamp` from a [`Duration`] since the UNIX epoch in the range
969-
/// `0...MAX_TIMESTAMP`.
970-
///
971-
/// Otherwise, returns a [`CreationError::TimestampOutOfBounds`].
972-
pub fn from_duration_since_epoch(duration: Duration) -> Result<Self, CreationError> {
973-
if duration.as_secs() <= MAX_TIMESTAMP {
974-
Ok(PositiveTimestamp(duration))
975-
} else {
976-
Err(CreationError::TimestampOutOfBounds)
977-
}
942+
/// Creates a `PositiveTimestamp` from a [`Duration`] since the UNIX epoch.
943+
pub fn from_duration_since_epoch(duration: Duration) -> Self {
944+
PositiveTimestamp(duration)
978945
}
979946

980947
/// Returns the UNIX timestamp representing the stored time
@@ -1219,7 +1186,7 @@ impl Invoice {
12191186
/// Returns whether the expiry time would pass at the given point in time.
12201187
/// `at_time` is the timestamp as a duration since the UNIX epoch.
12211188
pub fn would_expire(&self, at_time: Duration) -> bool {
1222-
self.duration_since_epoch() + self.expiry_time() < at_time
1189+
self.duration_since_epoch().saturating_add(self.expiry_time()) < at_time
12231190
}
12241191

12251192
/// Returns the invoice's `min_final_cltv_expiry` time, if present, otherwise
@@ -1340,26 +1307,14 @@ impl Deref for PayeePubKey {
13401307
}
13411308

13421309
impl ExpiryTime {
1343-
/// Construct an `ExpiryTime` from seconds. If there exists a `PositiveTimestamp` which would
1344-
/// overflow on adding the `ExpiryTime` to it then this function will return a
1345-
/// `CreationError::ExpiryTimeOutOfBounds`.
1346-
pub fn from_seconds(seconds: u64) -> Result<ExpiryTime, CreationError> {
1347-
if seconds <= MAX_EXPIRY_TIME {
1348-
Ok(ExpiryTime(Duration::from_secs(seconds)))
1349-
} else {
1350-
Err(CreationError::ExpiryTimeOutOfBounds)
1351-
}
1310+
/// Construct an `ExpiryTime` from seconds.
1311+
pub fn from_seconds(seconds: u64) -> ExpiryTime {
1312+
ExpiryTime(Duration::from_secs(seconds))
13521313
}
13531314

1354-
/// Construct an `ExpiryTime` from a `Duration`. If there exists a `PositiveTimestamp` which
1355-
/// would overflow on adding the `ExpiryTime` to it then this function will return a
1356-
/// `CreationError::ExpiryTimeOutOfBounds`.
1357-
pub fn from_duration(duration: Duration) -> Result<ExpiryTime, CreationError> {
1358-
if duration.as_secs() <= MAX_EXPIRY_TIME {
1359-
Ok(ExpiryTime(duration))
1360-
} else {
1361-
Err(CreationError::ExpiryTimeOutOfBounds)
1362-
}
1315+
/// Construct an `ExpiryTime` from a `Duration`.
1316+
pub fn from_duration(duration: Duration) -> ExpiryTime {
1317+
ExpiryTime(duration)
13631318
}
13641319

13651320
/// Returns the expiry time in seconds
@@ -1428,12 +1383,9 @@ pub enum CreationError {
14281383
/// The specified route has too many hops and can't be encoded
14291384
RouteTooLong,
14301385

1431-
/// The unix timestamp of the supplied date is <0 or can't be represented as `SystemTime`
1386+
/// The unix timestamp of the supplied date is less than zero
14321387
TimestampOutOfBounds,
14331388

1434-
/// The supplied expiry time could cause an overflow if added to a `PositiveTimestamp`
1435-
ExpiryTimeOutOfBounds,
1436-
14371389
/// The supplied millisatoshi amount was greater than the total bitcoin supply.
14381390
InvalidAmount,
14391391
}
@@ -1443,8 +1395,7 @@ impl Display for CreationError {
14431395
match self {
14441396
CreationError::DescriptionTooLong => f.write_str("The supplied description string was longer than 639 bytes"),
14451397
CreationError::RouteTooLong => f.write_str("The specified route has too many hops and can't be encoded"),
1446-
CreationError::TimestampOutOfBounds => f.write_str("The unix timestamp of the supplied date is <0 or can't be represented as `SystemTime`"),
1447-
CreationError::ExpiryTimeOutOfBounds => f.write_str("The supplied expiry time could cause an overflow if added to a `PositiveTimestamp`"),
1398+
CreationError::TimestampOutOfBounds => f.write_str("The unix timestamp of the supplied date is less than zero"),
14481399
CreationError::InvalidAmount => f.write_str("The supplied millisatoshi amount was greater than the total bitcoin supply"),
14491400
}
14501401
}
@@ -1534,19 +1485,6 @@ mod test {
15341485
use bitcoin_hashes::hex::FromHex;
15351486
use bitcoin_hashes::sha256;
15361487

1537-
#[test]
1538-
fn test_system_time_bounds_assumptions() {
1539-
assert_eq!(
1540-
::PositiveTimestamp::from_unix_timestamp(::MAX_TIMESTAMP + 1),
1541-
Err(::CreationError::TimestampOutOfBounds)
1542-
);
1543-
1544-
assert_eq!(
1545-
::ExpiryTime::from_seconds(::MAX_EXPIRY_TIME + 1),
1546-
Err(::CreationError::ExpiryTimeOutOfBounds)
1547-
);
1548-
}
1549-
15501488
#[test]
15511489
fn test_calc_invoice_hash() {
15521490
use ::{RawInvoice, RawHrp, RawDataPart, Currency, PositiveTimestamp};
@@ -1559,7 +1497,7 @@ mod test {
15591497
si_prefix: None,
15601498
},
15611499
data: RawDataPart {
1562-
timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
1500+
timestamp: PositiveTimestamp::from_unix_timestamp(1496314658),
15631501
tagged_fields: vec![
15641502
PaymentHash(::Sha256(sha256::Hash::from_hex(
15651503
"0001020304050607080900010203040506070809000102030405060708090102"
@@ -1597,7 +1535,7 @@ mod test {
15971535
si_prefix: None,
15981536
},
15991537
data: RawDataPart {
1600-
timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
1538+
timestamp: PositiveTimestamp::from_unix_timestamp(1496314658),
16011539
tagged_fields: vec ! [
16021540
PaymentHash(Sha256(sha256::Hash::from_hex(
16031541
"0001020304050607080900010203040506070809000102030405060708090102"
@@ -1667,7 +1605,7 @@ mod test {
16671605
si_prefix: None,
16681606
},
16691607
data: RawDataPart {
1670-
timestamp: PositiveTimestamp::from_unix_timestamp(1496314658).unwrap(),
1608+
timestamp: PositiveTimestamp::from_unix_timestamp(1496314658),
16711609
tagged_fields: vec ! [
16721610
PaymentHash(Sha256(sha256::Hash::from_hex(
16731611
"0001020304050607080900010203040506070809000102030405060708090102"

0 commit comments

Comments
 (0)