From 008da7726337b61677e18e497d02e3188209e3b7 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Fri, 7 Oct 2022 01:38:56 +0000 Subject: [PATCH] Drop the subsecond part of timestamps when constructing invoices We had a user who was confused why an invoice failed to round-trip (i.e. was not `PartialEq` with itself after write/read) when a subsecond creation time was used (e.g. via the `from_system_time` constructor). This commit should address this confusion by dropping subsecond parts in easily-accessible constructors when creating BOLT 11 invoices. Fixes #1759. --- lightning-invoice/src/lib.rs | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/lightning-invoice/src/lib.rs b/lightning-invoice/src/lib.rs index 20bc5218..9457d8ae 100644 --- a/lightning-invoice/src/lib.rs +++ b/lightning-invoice/src/lib.rs @@ -540,7 +540,8 @@ impl InvoiceBui self } - /// Sets the expiry time + /// Sets the expiry time, dropping the subsecond part (which is not representable in BOLT 11 + /// invoices). pub fn expiry_time(mut self, expiry_time: Duration) -> Self { self.tagged_fields.push(TaggedField::ExpiryTime(ExpiryTime::from_duration(expiry_time))); self @@ -632,7 +633,8 @@ impl InvoiceBuilder InvoiceBuilder { match PositiveTimestamp::from_duration_since_epoch(time) { Ok(t) => self.timestamp = Some(t), @@ -960,12 +962,18 @@ impl PositiveTimestamp { /// /// Otherwise, returns a [`CreationError::TimestampOutOfBounds`]. pub fn from_unix_timestamp(unix_seconds: u64) -> Result { - Self::from_duration_since_epoch(Duration::from_secs(unix_seconds)) + if unix_seconds <= MAX_TIMESTAMP { + Ok(Self(Duration::from_secs(unix_seconds))) + } else { + Err(CreationError::TimestampOutOfBounds) + } } /// Creates a `PositiveTimestamp` from a [`SystemTime`] with a corresponding Unix timestamp in /// the range `0..=MAX_TIMESTAMP`. /// + /// Note that the subsecond part is dropped as it is not representable in BOLT 11 invoices. + /// /// Otherwise, returns a [`CreationError::TimestampOutOfBounds`]. #[cfg(feature = "std")] pub fn from_system_time(time: SystemTime) -> Result { @@ -977,13 +985,11 @@ impl PositiveTimestamp { /// Creates a `PositiveTimestamp` from a [`Duration`] since the Unix epoch in the range /// `0..=MAX_TIMESTAMP`. /// + /// Note that the subsecond part is dropped as it is not representable in BOLT 11 invoices. + /// /// Otherwise, returns a [`CreationError::TimestampOutOfBounds`]. pub fn from_duration_since_epoch(duration: Duration) -> Result { - if duration.as_secs() <= MAX_TIMESTAMP { - Ok(PositiveTimestamp(duration)) - } else { - Err(CreationError::TimestampOutOfBounds) - } + Self::from_unix_timestamp(duration.as_secs()) } /// Returns the Unix timestamp representing the stored time @@ -1356,9 +1362,9 @@ impl ExpiryTime { ExpiryTime(Duration::from_secs(seconds)) } - /// Construct an `ExpiryTime` from a `Duration`. + /// Construct an `ExpiryTime` from a `Duration`, dropping the sub-second part. pub fn from_duration(duration: Duration) -> ExpiryTime { - ExpiryTime(duration) + Self::from_seconds(duration.as_secs()) } /// Returns the expiry time in seconds -- 2.30.2