/ crates / tor-hsservice / src / time_store.rs
time_store.rs
  1  //! Saving/loading timestamps to disk
  2  //!
  3  //! Storing timestamps on disk is not so straightforward.
  4  //! We need to use wall clock time in order to survive restarts.
  5  //! But wall clocks can be wrong,
  6  //! so we need at least to apply some sanity checks.
  7  //!
  8  //! This module encapsulates those checks, and some error handling choices.
  9  //! It allows `Instant`s to be used while the system is running,
 10  //! with bespoke types for loading/saving.
 11  //! See [`Loading::load_future`] for the load/save guarantees provided.
 12  //!
 13  //! The initial entrypoints are [`Storing::start`] and [`Loading::start`].
 14  //!
 15  //! Granularity is 1 second and the precise rounding behaviour is not specified.
 16  //!
 17  //! ### Data model
 18  //!
 19  //! To mitigate clock skew, we store the wall clock time at which
 20  //! each timestamp was saved to disk ([`Reference`])
 21  //! and the offset from now to that timestamp ([`FutureTimestamp`]).
 22  //!
 23  //! The same storage time can be used for multiple timestamps that are stored together.
 24  //!
 25  //! ### Example
 26  //!
 27  //! ```
 28  //! use serde::{Serialize, Deserialize};
 29  //! use std::time::{Duration, Instant};
 30  //! use tor_rtcompat::{PreferredRuntime, SleepProvider as _};
 31  //!
 32  //! # use tor_hsservice::time_store_for_doctests_unstable_no_semver_guarantees as time_store;
 33  //! # #[cfg(all)] // works like #[cfg(FALSE)].  Instead, we have this workaround ^.
 34  //! use crate::time_store;
 35  //!
 36  //! let runtime = PreferredRuntime::create().unwrap();
 37  //!
 38  //! #[derive(Serialize, Deserialize, Debug)]
 39  //! struct Stored {
 40  //!     time_ref: time_store::Reference,
 41  //!     t0: time_store::FutureTimestamp,
 42  //! }
 43  //!
 44  //! let t0: Instant = runtime.now() + Duration::from_secs(60);
 45  //!
 46  //! let storing = time_store::Storing::start(&runtime);
 47  //! let data = Stored {
 48  //!     time_ref: storing.store_ref(),
 49  //!     t0: storing.store_future(t0),
 50  //! };
 51  //!
 52  //! let json = serde_json::to_string(&data).unwrap();
 53  //!
 54  //! // later:
 55  //!
 56  //! let data: Stored = serde_json::from_str(&json).unwrap();
 57  //! let loading = time_store::Loading::start(&runtime, data.time_ref);
 58  //! let t0: Instant = loading.load_future(data.t0);
 59  //!
 60  //! assert!(t0 - runtime.now() <= Duration::from_secs(60));
 61  //! ```
 62  //!
 63  //! ### Time arithmetic overflows and stupid system time settings
 64  //!
 65  //! Arithmetic is done with signed 64-bit numbers of seconds.
 66  //! So overflow cannot occur unless the clock is completely ludicrous.
 67  //! If the clock is ludicrous, time calculations are going to be a mess.
 68  //! We treat this as clock skew, using saturating arithmetic, rather than returning errors.
 69  //! Reasonable operation will resume when the clock becomes sane.
 70  //
 71  // We generally use u64 for values that can't, for our algorithms, be negative,
 72  // but i64 for time_t's (even though negative time_t's can't happen on Unix).
 73  
 74  // TODO - eventually we hope this will become pub, in another crate
 75  
 76  // Rustdoc can complains if we link to these private docs from these docs which are
 77  // themselves only formatted with --document-private-items.
 78  // TODO - Remove when this is actually public
 79  #![allow(rustdoc::private_intra_doc_links)]
 80  
 81  use std::fmt::{self, Display};
 82  use std::str::FromStr;
 83  use std::time::{Duration, Instant, SystemTime};
 84  
 85  use derive_deftly::{define_derive_deftly, Deftly};
 86  use serde::{Deserialize, Serialize};
 87  use serde::{Deserializer, Serializer};
 88  use thiserror::Error;
 89  use tracing::warn;
 90  
 91  use tor_rtcompat::SleepProvider;
 92  
 93  //---------- derive-deftly macro for raw accessors, must come first ----------
 94  
 95  define_derive_deftly! {
 96      /// Define `as_raw` and `from_raw` methods (for a struct with a single field)
 97      //
 98      // We provide these for the types which are serde, since we are already exposing
 99      // and documenting their innards (and we don't want to force people to use serde
100      // trickery if they want to do something unusual).
101      RawConversions expect items:
102  
103      impl $ttype {
104        ${for fields { // we have only one field; but d-a wants a context for "a specific field"
105          /// Returns the raw value, as would be serialised
106          pub fn as_raw(self) -> $ftype {
107              self.$fname
108          }
109          #[doc = concat!("/// Constructs a ",stringify!($tname)," from a raw value")]
110          pub fn from_raw(seconds: $ftype) -> $ttype {
111              Self { $fname: seconds }
112          }
113        }}
114      }
115  }
116  
117  define_derive_deftly! {
118      /// Define [`Serialize`] and [`Deserialize`] via string rep or transparently, depending
119      ///
120      /// In human-readable formats, uses the [`Display`] and [`FromStr`].
121      /// In non-human-readable formats, serialises as the single field.
122      ///
123      /// Uses serde's `is_human_readable` to decide.
124      /// structs which don't have exactly one field will cause a compile error.
125      //
126      // This has to be a macro rather than simply a helper newtype
127      // to implement the "transparent" binary version,
128      // since that involves looking into the struct's field.
129      SerdeStringOrTransparent for struct, expect items:
130  
131      impl Serialize for $ttype {
132          fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
133              if s.is_human_readable() {
134                  s.collect_str(self)
135              } else {
136                  let Self { $( $fname: raw, ) } = self;
137                  raw.serialize(s)
138              }
139          }
140      }
141  
142      ${define STRING_VISITOR { $<Deserialize $ttype StringVisitor> }}
143  
144      impl<'de> Deserialize<'de> for $ttype {
145          fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
146              if d.is_human_readable() {
147                  d.deserialize_str($STRING_VISITOR)
148              } else {
149                  let raw = Deserialize::deserialize(d)?;
150                  Ok(Self { $( $fname: raw, ) })
151              }
152          }
153      }
154  
155      /// Visitor for deserializing from a string
156      struct $STRING_VISITOR;
157  
158      impl<'de> serde::de::Visitor<'de> for $STRING_VISITOR {
159          type Value = $ttype;
160          fn visit_str<E: serde::de::Error>(self, s: &str) -> Result<$ttype, E> {
161              s.parse().map_err(|e| E::custom(e))
162          }
163          fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
164              write!(f, concat!("string representing ", stringify!($tname)))
165          }
166      }
167  }
168  
169  //---------- data types ----------
170  
171  /// Representation of an absolute time, in the future, suitable for storing to disk
172  ///
173  /// Only meaningful in combination with a [`Reference`].
174  ///
175  /// Obtain one of these from an `Instant` using [`Storing::store_future()`],
176  /// and convert it back to an `Instant` with [`Loading::load_future()`],
177  ///
178  /// (Serialises as a representation of how many seconds this was into the future,
179  /// when it was stored - ie, with respect to the corresponding [`Reference`];
180  /// in binary as a `u64`, in human readable formats as
181  /// `T+` plus [`humantime`]'s formatting of the `Duration` in seconds.)
182  #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Deftly)]
183  #[derive_deftly(RawConversions, SerdeStringOrTransparent)]
184  pub struct FutureTimestamp {
185      /// How far this timestamp was in the future, when we stored it
186      offset: u64,
187  }
188  
189  /// On-disk representation of a reference time, used as context for stored timestamps
190  ///
191  /// During store, obtained by [`Storing::store_ref`], and should then be serialised
192  /// along with the [`FutureTimestamp`]s.
193  ///
194  /// During load, should be passed to [`Loading::start`], to build a [`Loading`]
195  /// which is then used to convert the [`FutureTimestamp`]s back to `Instant`s.
196  ///
197  /// (Serialises as an absolute time:
198  /// in binary, as an `i64` representing the `time_t` (Unix Time);
199  /// in human-readable formats, an RFC3339 string with seconds precision and timezone `Z`.)
200  #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, derive_more::Display)]
201  #[display(
202      "{}",
203      humantime::format_rfc3339_seconds(time_t_to_system_time(*time_t))
204  )]
205  #[derive(Deftly)]
206  #[derive_deftly(RawConversions, SerdeStringOrTransparent)]
207  pub struct Reference {
208      /// Unix time (at which the other timestamps were stored)
209      time_t: i64,
210  }
211  
212  /// Context for storing `Instant`s to disk
213  ///
214  /// Obtained from [`Storing::start`].
215  ///
216  /// Contains a reference time, which must also be serialised,
217  /// as `Reference` obtained from [`Storing::store_ref`],
218  /// and stored alongside the converted timestamps.
219  pub struct Storing(Now);
220  
221  /// The two notions of the current time, for internal use
222  //
223  // This is a separate type from Storing so that Loading::start can call Now::new,
224  // without having to temporarily create a semantically inappropriate Storing.
225  struct Now {
226      /// Represents the current time as an opaque `Instant`
227      inst: Instant,
228      /// Represents the current time as a `time_t`
229      time_t: i64,
230  }
231  
232  /// Context for loading `Instant`s from disk
233  ///
234  /// Obtained by [`Loading::start`],
235  /// from a [`Reference`]
236  /// (loaded from disk alongside the converted timestamps).
237  pub struct Loading {
238      /// The current time when this `Loading` was created
239      inst: Instant,
240      /// How long has elapsed, at `inst`, since the timestamps were stored
241      elapsed: u64,
242  }
243  
244  //---------- implementation ----------
245  
246  /// Convert a `SystemTime` to an `i64` `time_t`
247  fn system_time_to_time_t(st: SystemTime) -> i64 {
248      if let Ok(d) = st.duration_since(SystemTime::UNIX_EPOCH) {
249          d.as_secs().try_into().unwrap_or(i64::MAX)
250      } else if let Ok(d) = SystemTime::UNIX_EPOCH.duration_since(st) {
251          d.as_secs().try_into().map(|v: i64| -v).unwrap_or(i64::MIN)
252      } else {
253          panic!("two SystemTimes are neither <= nor >=")
254      }
255  }
256  
257  /// Minimum value of SystemTime
258  ///
259  /// Not a tight bound but tests guarantee no runtime panics.
260  fn system_time_min() -> SystemTime {
261      for attempt in [
262          //
263          0x7fff_ffff_ffff_ffff,
264          0x7fff_ffff,
265          0,
266      ] {
267          if let Some(r) = SystemTime::UNIX_EPOCH.checked_sub(Duration::from_secs(attempt)) {
268              return r;
269          }
270      }
271      panic!("cannot calculate a minimum value for the SystemTime!");
272  }
273  
274  /// Maximum value of SystemTime
275  ///
276  /// Not a tight bound but tests guarantee no runtime panics.
277  fn system_time_max() -> SystemTime {
278      for attempt in [
279          //
280          0x7fff_ffff_ffff_ffff,
281          0x7fff_ffff,
282      ] {
283          if let Some(r) = SystemTime::UNIX_EPOCH.checked_add(Duration::from_secs(attempt)) {
284              return r;
285          }
286      }
287      panic!("cannot calculate a maximum value for the SystemTime!");
288  }
289  
290  /// Convert a `SystemTime` to an `i64` `time_t`
291  //
292  // We need this to make the RFC3339 string using humantime.
293  // Otherwise we'd have to implement the whole calendar and leap days stuff ourselves.
294  // Probably there doesn't end up being any actual code here on Unix at least...
295  fn time_t_to_system_time(time_t: i64) -> SystemTime {
296      if let Ok(time_t) = u64::try_from(time_t) {
297          let d = Duration::from_secs(time_t);
298          SystemTime::UNIX_EPOCH
299              .checked_add(d)
300              .unwrap_or_else(system_time_max)
301      } else {
302          let rev: u64 = time_t
303              .saturating_neg()
304              .try_into()
305              .expect("-(-ve i64) not u64");
306          let d = Duration::from_secs(rev);
307          SystemTime::UNIX_EPOCH
308              .checked_sub(d)
309              .unwrap_or_else(system_time_min)
310      }
311  }
312  
313  impl Now {
314      /// Obtain `Now` from a runtime
315      fn new(runtime: &impl SleepProvider) -> Self {
316          let inst = runtime.now();
317          let st = runtime.wallclock();
318          let time_t = system_time_to_time_t(st);
319          Self { inst, time_t }
320      }
321  }
322  
323  impl Storing {
324      /// Prepare to store timestamps, returning a context for serialising `Instant`s
325      ///
326      /// Incorporates reference times obtained from the runtime's clock.
327      //
328      // We don't provide `Storing::{to,from}_raw_parts` nor `Loading::from_raw_parts`
329      // because you can (sort of) do all of constructors with a suitable `SleepProvider`,
330      // and we don't want to give too much detail about the implementation and innards.
331      pub fn start(runtime: &impl SleepProvider) -> Self {
332          Storing(Now::new(runtime))
333      }
334  
335      /// Prepare a reference time for storage
336      pub fn store_ref(&self) -> Reference {
337          Reference {
338              time_t: self.0.time_t,
339          }
340      }
341  
342      /// Convert an `Instant` in the future into a form suitable for saving on disk
343      ///
344      /// If `val` *isn't* in the future, the current time will be stored instead.
345      pub fn store_future(&self, val: Instant) -> FutureTimestamp {
346          let offset = val
347              .checked_duration_since(self.0.inst)
348              .unwrap_or_default()
349              .as_secs();
350          FutureTimestamp { offset }
351      }
352  }
353  
354  impl Loading {
355      /// Obtain a [`Loading`] from a stored [`Reference`], for deserialising `Instant`s
356      ///
357      /// Uses the runtime's clock as a basis for understanding the supplied `Reference`
358      /// and relating it to the current monotonic time (`Instant`) on this host.
359      pub fn start(runtime: &impl SleepProvider, stored: Reference) -> Loading {
360          let now = Now::new(runtime);
361          let elapsed = now.time_t.saturating_sub(stored.time_t);
362          // If time went backwards, pretend it stood still
363          let elapsed = elapsed.try_into().unwrap_or(0);
364          Loading {
365              inst: now.inst,
366              elapsed,
367          }
368      }
369  
370      /// Convert a future `Instant` from a value saved on disk
371      ///
372      /// If the `Instant` that was saved has since passed,
373      /// the returned value is the current time.
374      ///
375      /// If the system clock is inaccurate (or was inaccurate when the timestamp was saved),
376      /// the value may be wrong:
377      /// but, regardless, the returned value will be no further in the future,
378      /// than how far it was in the future when it was saved.
379      ///
380      /// In other words, even in the presence of clock skew, the effect is, at worst,
381      /// as if the local system's clock has stood still, or has run very fast.
382      pub fn load_future(&self, stored: FutureTimestamp) -> Instant {
383          let offset = stored.offset.saturating_sub(self.elapsed);
384          self.inst
385              .checked_add(Duration::from_secs(offset))
386              .unwrap_or_else(|| {
387                  warn!("time overflow loading time_t now+{offset}!");
388                  // `Instant` is overflowing, which can only happen if something is
389                  // very wrong with the system, or `stored.offset` was stupidly large.
390                  // Using "now" is clearly wrong but there is no Instant::MAX,
391                  // and this seems better than making this method fallible and bailing.
392                  self.inst
393              })
394      }
395  
396      //----- accessors for Loading -----
397  
398      /// Returns how long has elapsed since the timestamps were stored
399      ///
400      /// This depends on the system wall clock being right both when we stored, and now.
401      /// In the presence of clock skew, may return a value which is far too large,
402      /// or too small.
403      ///
404      /// But, if the system wall clock seems to have gone backwards, returns zero.
405      ///
406      /// The time is measured from when [`start`](Loading::start) was called.
407      /// If you need to know that time as an `Instant`,
408      /// use [`as_raw_parts`](Loading::as_raw_parts).
409      //
410      // We provide this (and `as_raw_parts`) because some callers may
411      // actually have a good use for it.
412      pub fn elapsed(&self) -> Duration {
413          Duration::from_secs(self.elapsed)
414      }
415  
416      /// Returns how long has elapsed, in seconds, and the `Instant` at which that was true
417      ///
418      /// Returns number of seconds elapsed,
419      /// between when the timestamps were stored,
420      /// and the returned `Instant`.
421      ///
422      /// See [`elapsed`](Loading::elapsed) for details of clock skew handling.
423      pub fn as_raw_parts(&self) -> (u64, Instant) {
424          (self.elapsed, self.inst)
425      }
426  }
427  
428  //---------- formatting ----------
429  
430  /// Displays as `T+Duration`, where [`Duration`] is in seconds, formatted using [`humantime`]
431  //
432  // This format is a balance between human-readability and the desire to avoid
433  // allowing the possibility of invalid (corrupted) files whose `FutureTimestamp`
434  // is actually before the stored `Reference`.
435  impl Display for FutureTimestamp {
436      fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
437          let d = humantime::format_duration(Duration::from_secs(self.offset));
438          write!(f, "T+{}", d)
439      }
440  }
441  
442  /// Error parsing a timestamp or reference
443  #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
444  #[non_exhaustive]
445  #[derive(Error)]
446  #[error("invalid timestamp or reference time format")]
447  pub struct ParseError {
448      // We probably don't need to say precisely what's wrong
449  }
450  
451  impl FromStr for FutureTimestamp {
452      type Err = ParseError;
453      // Bespoke parser so we have control over our error/overflow cases
454      // (and also since ideally we don't want to deal with a complex HMS time API).
455      fn from_str(s: &str) -> Result<Self, Self::Err> {
456          let s = s.strip_prefix("T+").ok_or(ParseError {})?;
457          let offset = humantime::parse_duration(s)
458              .map_err(|_: humantime::DurationError| ParseError {})?
459              .as_secs();
460          Ok(FutureTimestamp { offset })
461      }
462  }
463  
464  impl FromStr for Reference {
465      type Err = ParseError;
466      fn from_str(s: &str) -> Result<Self, Self::Err> {
467          let st = humantime::parse_rfc3339(s).map_err(|_| ParseError {})?;
468          let time_t = system_time_to_time_t(st);
469          Ok(Reference { time_t })
470      }
471  }
472  
473  #[cfg(test)]
474  mod test {
475      // @@ begin test lint list maintained by maint/add_warning @@
476      #![allow(clippy::bool_assert_comparison)]
477      #![allow(clippy::clone_on_copy)]
478      #![allow(clippy::dbg_macro)]
479      #![allow(clippy::mixed_attributes_style)]
480      #![allow(clippy::print_stderr)]
481      #![allow(clippy::print_stdout)]
482      #![allow(clippy::single_char_pattern)]
483      #![allow(clippy::unwrap_used)]
484      #![allow(clippy::unchecked_duration_subtraction)]
485      #![allow(clippy::useless_vec)]
486      #![allow(clippy::needless_pass_by_value)]
487      //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
488      use super::*;
489      use humantime::parse_rfc3339;
490      use itertools::{chain, Itertools};
491      use tor_rtmock::{simple_time::SimpleMockTimeProvider, MockRuntime};
492  
493      fn secs(s: u64) -> Duration {
494          Duration::from_secs(s)
495      }
496  
497      #[test]
498      fn hms_fmt() {
499          let ft = FutureTimestamp {
500              offset: 2 * 86400 + 22 * 3600 + 4 * 60 + 5,
501          };
502  
503          assert_eq!(ft.to_string(), "T+2days 22h 4m 5s");
504  
505          assert_eq!(Ok(ft), FutureTimestamp::from_str("T+2days 22h 4m 5s"));
506          assert_eq!(Ok(ft), FutureTimestamp::from_str("T+70h 4m 5s"));
507          // we inherit rounding behaviour from humantime; we don't mind tolerating that
508          assert_eq!(Ok(ft), FutureTimestamp::from_str("T+2days 22h 4m 5s 100ms"));
509          assert_eq!(Ok(ft), FutureTimestamp::from_str("T+2days 22h 4m 5s 900ms"));
510          let e = Err(ParseError {});
511          assert_eq!(e, FutureTimestamp::from_str("2days"));
512          assert_eq!(e, FutureTimestamp::from_str("T+"));
513          assert_eq!(e, FutureTimestamp::from_str("T+ "));
514          assert_eq!(e, FutureTimestamp::from_str("T+X"));
515          assert_eq!(e, FutureTimestamp::from_str("T+23kg"));
516      }
517  
518      #[test]
519      #[allow(clippy::unusual_byte_groupings)] // we want them to line up, dammit!
520      fn system_time_conversions() {
521          assert!(system_time_min() <= SystemTime::UNIX_EPOCH);
522          assert!(system_time_max() > SystemTime::UNIX_EPOCH);
523  
524          let p = |s| parse_rfc3339(s).expect(s);
525          let time_t_2st = time_t_to_system_time;
526          assert_eq!(p("1970-01-01T00:00:00Z"), time_t_2st(0));
527          assert_eq!(p("2038-01-19T03:14:07Z"), time_t_2st(0x___7fff_ffff));
528          assert_eq!(p("2038-01-19T03:14:08Z"), time_t_2st(0x___8000_0000));
529          assert_eq!(p("2106-02-07T06:28:16Z"), time_t_2st(0x_1_0000_0000));
530          assert_eq!(p("4147-08-20T07:32:16Z"), time_t_2st(0x10_0000_0000));
531      }
532  
533      #[test]
534      fn ref_fmt() {
535          let time_t = 1217635200;
536          let rf = Reference { time_t };
537          let s = "2008-08-02T00:00:00Z";
538          assert_eq!(rf.to_string(), s);
539  
540          let p = Reference::from_str;
541          assert_eq!(Ok(rf), p(s));
542          // we inherit rounding behaviour from humantime; we don't mind tolerating that
543          assert_eq!(Ok(rf), p("2008-08-02T00:00:00.00Z"));
544          assert_eq!(Ok(rf), p("2008-08-02T00:00:00.999Z"));
545          let e = Err(ParseError {});
546          assert_eq!(e, p("2008-08-02T00:00Z"));
547          assert_eq!(e, p("2008-08-02T00:00:00"));
548          assert_eq!(e, p("2008-08-02T00:00:00B"));
549          assert_eq!(e, p("2008-08-02T00:00:00+00:00"));
550      }
551  
552      #[test]
553      fn basic() {
554          #[derive(Serialize, Deserialize, Debug)]
555          struct Stored {
556              stored: Reference,
557              s0: FutureTimestamp,
558              s1: FutureTimestamp,
559              s2: FutureTimestamp,
560          }
561  
562          let real_instant = Instant::now();
563          let test_systime = parse_rfc3339("2008-08-02T00:00:00Z").unwrap();
564  
565          let mk_runtime = |instant, systime| {
566              let times = SimpleMockTimeProvider::new(instant, systime);
567              MockRuntime::builder().sleep_provider(times).build()
568          };
569  
570          let stored = {
571              let runtime = mk_runtime(real_instant + secs(100_000), test_systime);
572              let now = Storing::start(&runtime);
573  
574              let t0 = runtime.now() - secs(1000);
575              let t1 = runtime.now() + secs(10);
576              let t2 = runtime.now() + secs(3000);
577  
578              Stored {
579                  stored: now.store_ref(),
580                  s0: now.store_future(t0),
581                  s1: now.store_future(t1),
582                  s2: now.store_future(t2),
583              }
584          };
585  
586          let json = serde_json::to_string(&stored).unwrap();
587          println!("{json}");
588          assert_eq!(
589              json,
590              format!(concat!(
591                  r#"{{"stored":"2008-08-02T00:00:00Z","#,
592                  r#""s0":"T+0s","s1":"T+10s","s2":"T+50m"}}"#
593              ))
594          );
595  
596          let mpack = rmp_serde::to_vec_named(&stored).unwrap();
597          println!("{}", hex::encode(&mpack));
598          assert_eq!(
599              mpack,
600              chain!(
601                  &[132, 166],
602                  b"stored",
603                  &[206],
604                  &[0x48, 0x93, 0xa3, 0x80], // 0x4893a380 1217635200 = 2008-08-02T00:00:00Z
605                  &[162],
606                  b"s0",
607                  &[0],
608                  &[162],
609                  b"s1",
610                  &[10],
611                  &[162],
612                  b"s2",
613                  &[205],
614                  &[0x0b, 0xb8], // 0xbb8 == 3000
615              )
616              .cloned()
617              .collect_vec(),
618          );
619  
620          // Simulate a restart with an Instant which is *smaller* (maybe the host rebooted),
621          // but with a wall clock time 200s later.
622          {
623              let runtime = mk_runtime(real_instant, test_systime + secs(200));
624              let now = Loading::start(&runtime, stored.stored);
625  
626              let t0 = now.load_future(stored.s0);
627              let t1 = now.load_future(stored.s1);
628              let t2 = now.load_future(stored.s2);
629  
630              assert_eq!(t0, runtime.now()); // was already in the past when stored
631              assert_eq!(t1, runtime.now()); // is now in the past
632              assert_eq!(t2, runtime.now() + secs(2800));
633          }
634  
635          // Simulate a restart with a later Instant
636          // and with a wall clock time 1200s *earlier* due to clock skew.
637          {
638              let runtime = mk_runtime(real_instant + secs(200_000), test_systime - secs(1200));
639              let now = Loading::start(&runtime, stored.stored);
640  
641              let t0 = now.load_future(stored.s0);
642              let t1 = now.load_future(stored.s1);
643              let t2 = now.load_future(stored.s2);
644  
645              assert_eq!(t0, runtime.now()); // was already in the past when stored
646              assert_eq!(t1, runtime.now() + secs(10)); // well, it was only 10 even then
647              assert_eq!(t2, runtime.now() + secs(3000)); // can't be increased
648          }
649      }
650  }