Use `MonotonicTime` as `Instant` shifted by 10 years forward
authorAndrei <andrei.i@posteo.de>
Thu, 29 Jun 2023 00:00:00 +0000 (00:00 +0000)
committerAndrei <andrei.i@posteo.de>
Thu, 29 Jun 2023 00:00:00 +0000 (00:00 +0000)
Such implementation allows `MonotonicTime` to go backward up to 10
years on all platforms. On some platforms (e.g. iOS) `Instant` is
represented as `u64` of nanoseconds since the boot of the system.
Obviously such implementation does not allow to go backward before the
time of the boot.

Co-authored-by: Andrei <andrei.i@posteo.de>
Co-authored-by: Jeffrey Czyz <jkczyz@gmail.com>
lightning/src/ln/outbound_payment.rs
lightning/src/routing/scoring.rs
lightning/src/util/time.rs

index 2ac24baa58b3466315c2628c711f17e4f3d17d73..546dc6c5bcd93171411263ff1c77229a08ea2ae1 100644 (file)
@@ -239,7 +239,7 @@ impl Retry {
                        },
                        #[cfg(all(not(feature = "no-std"), not(test)))]
                        (Retry::Timeout(max_duration), PaymentAttempts { first_attempted_at, .. }) =>
-                               *max_duration >= std::time::Instant::now().duration_since(*first_attempted_at),
+                               *max_duration >= crate::util::time::MonotonicTime::now().duration_since(*first_attempted_at),
                        #[cfg(all(not(feature = "no-std"), test))]
                        (Retry::Timeout(max_duration), PaymentAttempts { first_attempted_at, .. }) =>
                                *max_duration >= SinceEpoch::now().duration_since(*first_attempted_at),
@@ -274,7 +274,7 @@ pub(crate) struct PaymentAttemptsUsingTime<T: Time> {
 }
 
 #[cfg(not(any(feature = "no-std", test)))]
-type ConfiguredTime = std::time::Instant;
+type ConfiguredTime = crate::util::time::MonotonicTime;
 #[cfg(feature = "no-std")]
 type ConfiguredTime = crate::util::time::Eternity;
 #[cfg(all(not(feature = "no-std"), test))]
index ee9c3d0c47356ddc53d9f7efc21f368b1ef77743..eca5ee6058b7841398b07d8137cd9842c9ec8e2d 100644 (file)
@@ -325,7 +325,7 @@ impl ReadableArgs<u64> for FixedPenaltyScorer {
 }
 
 #[cfg(not(feature = "no-std"))]
-type ConfiguredTime = std::time::Instant;
+type ConfiguredTime = crate::util::time::MonotonicTime;
 #[cfg(feature = "no-std")]
 use crate::util::time::Eternity;
 #[cfg(feature = "no-std")]
index f450dc2c3015ae4da00faa2d609d01542753b85d..0d969e7470952625d8db04965220c25df589f30f 100644 (file)
@@ -58,10 +58,20 @@ impl Sub<Duration> for Eternity {
        }
 }
 
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+#[cfg(not(feature = "no-std"))]
+pub struct MonotonicTime(std::time::Instant);
+
+/// The amount of time to shift `Instant` forward to prevent overflow when subtracting a `Duration`
+/// from `Instant::now` on some operating systems (e.g., iOS representing `Instance` as `u64`).
+#[cfg(not(feature = "no-std"))]
+const SHIFT: Duration = Duration::from_secs(10 * 365 * 24 * 60 * 60); // 10 years.
+
 #[cfg(not(feature = "no-std"))]
-impl Time for std::time::Instant {
+impl Time for MonotonicTime {
        fn now() -> Self {
-               std::time::Instant::now()
+               let instant = std::time::Instant::now().checked_add(SHIFT).expect("Overflow on MonotonicTime instantiation");
+               Self(instant)
        }
 
        fn duration_since(&self, earlier: Self) -> Duration {
@@ -70,15 +80,26 @@ impl Time for std::time::Instant {
                // clocks" that go backwards in practice (likely relatively ancient kernels/etc). Thus, we
                // manually check for time going backwards here and return a duration of zero in that case.
                let now = Self::now();
-               if now > earlier { now - earlier } else { Duration::from_secs(0) }
+               if now.0 > earlier.0 { now.0 - earlier.0 } else { Duration::from_secs(0) }
        }
 
        fn duration_since_epoch() -> Duration {
                use std::time::SystemTime;
                SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap()
        }
+
        fn elapsed(&self) -> Duration {
-               std::time::Instant::elapsed(self)
+               Self::now().0 - self.0
+       }
+}
+
+#[cfg(not(feature = "no-std"))]
+impl Sub<Duration> for MonotonicTime {
+       type Output = Self;
+
+       fn sub(self, other: Duration) -> Self {
+               let instant = self.0.checked_sub(other).expect("MonotonicTime is not supposed to go backward futher than 10 years");
+               Self(instant)
        }
 }
 
@@ -154,4 +175,15 @@ pub mod tests {
                assert_eq!(now.elapsed(), Duration::from_secs(0));
                assert_eq!(later - elapsed, now);
        }
+
+       #[test]
+       #[cfg(not(feature = "no-std"))]
+       fn monotonic_time_subtracts() {
+               let now = super::MonotonicTime::now();
+               assert!(now.elapsed() < Duration::from_secs(10));
+
+               let ten_years = Duration::from_secs(10 * 365 * 24 * 60 * 60);
+               let past = now - ten_years;
+               assert!(past.elapsed() >= ten_years);
+       }
 }