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
774
        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
36
        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
34
        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
            #[allow(clippy::useless_concat)] // False positive
165
            {
166
                write!(f, concat!("string representing ", stringify!($tname)))
167
            }
168
        }
169
    }
170
}
171

            
172
//---------- data types ----------
173

            
174
/// Representation of an absolute time, in the future, suitable for storing to disk
175
///
176
/// Only meaningful in combination with a [`Reference`].
177
///
178
/// Obtain one of these from an `Instant` using [`Storing::store_future()`],
179
/// and convert it back to an `Instant` with [`Loading::load_future()`],
180
///
181
/// (Serialises as a representation of how many seconds this was into the future,
182
/// when it was stored - ie, with respect to the corresponding [`Reference`];
183
/// in binary as a `u64`, in human readable formats as
184
/// `T+` plus [`humantime`]'s formatting of the `Duration` in seconds.)
185
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Deftly)]
186
#[derive_deftly(RawConversions, SerdeStringOrTransparent)]
187
pub struct FutureTimestamp {
188
    /// How far this timestamp was in the future, when we stored it
189
    offset: u64,
190
}
191

            
192
/// On-disk representation of a reference time, used as context for stored timestamps
193
///
194
/// During store, obtained by [`Storing::store_ref`], and should then be serialised
195
/// along with the [`FutureTimestamp`]s.
196
///
197
/// During load, should be passed to [`Loading::start`], to build a [`Loading`]
198
/// which is then used to convert the [`FutureTimestamp`]s back to `Instant`s.
199
///
200
/// (Serialises as an absolute time:
201
/// in binary, as an `i64` representing the `time_t` (Unix Time);
202
/// in human-readable formats, an RFC3339 string with seconds precision and timezone `Z`.)
203
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, derive_more::Display)]
204
#[display(
205
    "{}",
206
    humantime::format_rfc3339_seconds(time_t_to_system_time(*time_t))
207
)]
208
#[derive(Deftly)]
209
#[derive_deftly(RawConversions, SerdeStringOrTransparent)]
210
pub struct Reference {
211
    /// Unix time (at which the other timestamps were stored)
212
    time_t: i64,
213
}
214

            
215
/// Context for storing `Instant`s to disk
216
///
217
/// Obtained from [`Storing::start`].
218
///
219
/// Contains a reference time, which must also be serialised,
220
/// as `Reference` obtained from [`Storing::store_ref`],
221
/// and stored alongside the converted timestamps.
222
pub struct Storing(Now);
223

            
224
/// The two notions of the current time, for internal use
225
//
226
// This is a separate type from Storing so that Loading::start can call Now::new,
227
// without having to temporarily create a semantically inappropriate Storing.
228
struct Now {
229
    /// Represents the current time as an opaque `Instant`
230
    inst: Instant,
231
    /// Represents the current time as a `time_t`
232
    time_t: i64,
233
}
234

            
235
/// Context for loading `Instant`s from disk
236
///
237
/// Obtained by [`Loading::start`],
238
/// from a [`Reference`]
239
/// (loaded from disk alongside the converted timestamps).
240
pub struct Loading {
241
    /// The current time when this `Loading` was created
242
    inst: Instant,
243
    /// How long has elapsed, at `inst`, since the timestamps were stored
244
    elapsed: u64,
245
}
246

            
247
//---------- implementation ----------
248

            
249
/// Convert a `SystemTime` to an `i64` `time_t`
250
222
fn system_time_to_time_t(st: SystemTime) -> i64 {
251
222
    if let Ok(d) = st.duration_since(SystemTime::UNIX_EPOCH) {
252
222
        d.as_secs().try_into().unwrap_or(i64::MAX)
253
    } else if let Ok(d) = SystemTime::UNIX_EPOCH.duration_since(st) {
254
        d.as_secs().try_into().map(|v: i64| -v).unwrap_or(i64::MIN)
255
    } else {
256
        panic!("two SystemTimes are neither <= nor >=")
257
    }
258
222
}
259

            
260
/// Minimum value of SystemTime
261
///
262
/// Not a tight bound but tests guarantee no runtime panics.
263
2
fn system_time_min() -> SystemTime {
264
2
    for attempt in [
265
2
        //
266
2
        0x7fff_ffff_ffff_ffff,
267
2
        0x7fff_ffff,
268
2
        0,
269
2
    ] {
270
2
        if let Some(r) = SystemTime::UNIX_EPOCH.checked_sub(Duration::from_secs(attempt)) {
271
2
            return r;
272
        }
273
    }
274
    panic!("cannot calculate a minimum value for the SystemTime!");
275
2
}
276

            
277
/// Maximum value of SystemTime
278
///
279
/// Not a tight bound but tests guarantee no runtime panics.
280
2
fn system_time_max() -> SystemTime {
281
2
    for attempt in [
282
2
        //
283
2
        0x7fff_ffff_ffff_ffff,
284
2
        0x7fff_ffff,
285
2
    ] {
286
2
        if let Some(r) = SystemTime::UNIX_EPOCH.checked_add(Duration::from_secs(attempt)) {
287
2
            return r;
288
        }
289
    }
290
    panic!("cannot calculate a maximum value for the SystemTime!");
291
2
}
292

            
293
/// Convert a `SystemTime` to an `i64` `time_t`
294
//
295
// We need this to make the RFC3339 string using humantime.
296
// Otherwise we'd have to implement the whole calendar and leap days stuff ourselves.
297
// Probably there doesn't end up being any actual code here on Unix at least...
298
208
fn time_t_to_system_time(time_t: i64) -> SystemTime {
299
208
    if let Ok(time_t) = u64::try_from(time_t) {
300
208
        let d = Duration::from_secs(time_t);
301
208
        SystemTime::UNIX_EPOCH
302
208
            .checked_add(d)
303
208
            .unwrap_or_else(system_time_max)
304
    } else {
305
        let rev: u64 = time_t
306
            .saturating_neg()
307
            .try_into()
308
            .expect("-(-ve i64) not u64");
309
        let d = Duration::from_secs(rev);
310
        SystemTime::UNIX_EPOCH
311
            .checked_sub(d)
312
            .unwrap_or_else(system_time_min)
313
    }
314
208
}
315

            
316
impl Now {
317
    /// Obtain `Now` from a runtime
318
208
    fn new(runtime: &impl SleepProvider) -> Self {
319
208
        let inst = runtime.now();
320
208
        let st = runtime.wallclock();
321
208
        let time_t = system_time_to_time_t(st);
322
208
        Self { inst, time_t }
323
208
    }
324
}
325

            
326
impl Storing {
327
    /// Prepare to store timestamps, returning a context for serialising `Instant`s
328
    ///
329
    /// Incorporates reference times obtained from the runtime's clock.
330
    //
331
    // We don't provide `Storing::{to,from}_raw_parts` nor `Loading::from_raw_parts`
332
    // because you can (sort of) do all of constructors with a suitable `SleepProvider`,
333
    // and we don't want to give too much detail about the implementation and innards.
334
196
    pub fn start(runtime: &impl SleepProvider) -> Self {
335
196
        Storing(Now::new(runtime))
336
196
    }
337

            
338
    /// Prepare a reference time for storage
339
196
    pub fn store_ref(&self) -> Reference {
340
196
        Reference {
341
196
            time_t: self.0.time_t,
342
196
        }
343
196
    }
344

            
345
    /// Convert an `Instant` in the future into a form suitable for saving on disk
346
    ///
347
    /// If `val` *isn't* in the future, the current time will be stored instead.
348
286
    pub fn store_future(&self, val: Instant) -> FutureTimestamp {
349
286
        let offset = val
350
286
            .checked_duration_since(self.0.inst)
351
286
            .unwrap_or_default()
352
286
            .as_secs();
353
286
        FutureTimestamp { offset }
354
286
    }
355
}
356

            
357
impl Loading {
358
    /// Obtain a [`Loading`] from a stored [`Reference`], for deserialising `Instant`s
359
    ///
360
    /// Uses the runtime's clock as a basis for understanding the supplied `Reference`
361
    /// and relating it to the current monotonic time (`Instant`) on this host.
362
12
    pub fn start(runtime: &impl SleepProvider, stored: Reference) -> Loading {
363
12
        let now = Now::new(runtime);
364
12
        let elapsed = now.time_t.saturating_sub(stored.time_t);
365
12
        // If time went backwards, pretend it stood still
366
12
        let elapsed = elapsed.try_into().unwrap_or(0);
367
12
        Loading {
368
12
            inst: now.inst,
369
12
            elapsed,
370
12
        }
371
12
    }
372

            
373
    /// Convert a future `Instant` from a value saved on disk
374
    ///
375
    /// If the `Instant` that was saved has since passed,
376
    /// the returned value is the current time.
377
    ///
378
    /// If the system clock is inaccurate (or was inaccurate when the timestamp was saved),
379
    /// the value may be wrong:
380
    /// but, regardless, the returned value will be no further in the future,
381
    /// than how far it was in the future when it was saved.
382
    ///
383
    /// In other words, even in the presence of clock skew, the effect is, at worst,
384
    /// as if the local system's clock has stood still, or has run very fast.
385
24
    pub fn load_future(&self, stored: FutureTimestamp) -> Instant {
386
24
        let offset = stored.offset.saturating_sub(self.elapsed);
387
24
        self.inst
388
24
            .checked_add(Duration::from_secs(offset))
389
24
            .unwrap_or_else(|| {
390
                warn!("time overflow loading time_t now+{offset}!");
391
                // `Instant` is overflowing, which can only happen if something is
392
                // very wrong with the system, or `stored.offset` was stupidly large.
393
                // Using "now" is clearly wrong but there is no Instant::MAX,
394
                // and this seems better than making this method fallible and bailing.
395
                self.inst
396
24
            })
397
24
    }
398

            
399
    //----- accessors for Loading -----
400

            
401
    /// Returns how long has elapsed since the timestamps were stored
402
    ///
403
    /// This depends on the system wall clock being right both when we stored, and now.
404
    /// In the presence of clock skew, may return a value which is far too large,
405
    /// or too small.
406
    ///
407
    /// But, if the system wall clock seems to have gone backwards, returns zero.
408
    ///
409
    /// The time is measured from when [`start`](Loading::start) was called.
410
    /// If you need to know that time as an `Instant`,
411
    /// use [`as_raw_parts`](Loading::as_raw_parts).
412
    //
413
    // We provide this (and `as_raw_parts`) because some callers may
414
    // actually have a good use for it.
415
    pub fn elapsed(&self) -> Duration {
416
        Duration::from_secs(self.elapsed)
417
    }
418

            
419
    /// Returns how long has elapsed, in seconds, and the `Instant` at which that was true
420
    ///
421
    /// Returns number of seconds elapsed,
422
    /// between when the timestamps were stored,
423
    /// and the returned `Instant`.
424
    ///
425
    /// See [`elapsed`](Loading::elapsed) for details of clock skew handling.
426
    pub fn as_raw_parts(&self) -> (u64, Instant) {
427
        (self.elapsed, self.inst)
428
    }
429
}
430

            
431
//---------- formatting ----------
432

            
433
/// Displays as `T+Duration`, where [`Duration`] is in seconds, formatted using [`humantime`]
434
//
435
// This format is a balance between human-readability and the desire to avoid
436
// allowing the possibility of invalid (corrupted) files whose `FutureTimestamp`
437
// is actually before the stored `Reference`.
438
impl Display for FutureTimestamp {
439
288
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
440
288
        let d = humantime::format_duration(Duration::from_secs(self.offset));
441
288
        write!(f, "T+{}", d)
442
288
    }
443
}
444

            
445
/// Error parsing a timestamp or reference
446
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
447
#[non_exhaustive]
448
#[derive(Error)]
449
#[error("invalid timestamp or reference time format")]
450
pub struct ParseError {
451
    // We probably don't need to say precisely what's wrong
452
}
453

            
454
impl FromStr for FutureTimestamp {
455
    type Err = ParseError;
456
    // Bespoke parser so we have control over our error/overflow cases
457
    // (and also since ideally we don't want to deal with a complex HMS time API).
458
30
    fn from_str(s: &str) -> Result<Self, Self::Err> {
459
30
        let s = s.strip_prefix("T+").ok_or(ParseError {})?;
460
28
        let offset = humantime::parse_duration(s)
461
32
            .map_err(|_: humantime::DurationError| ParseError {})?
462
20
            .as_secs();
463
20
        Ok(FutureTimestamp { offset })
464
30
    }
465
}
466

            
467
impl FromStr for Reference {
468
    type Err = ParseError;
469
22
    fn from_str(s: &str) -> Result<Self, Self::Err> {
470
26
        let st = humantime::parse_rfc3339(s).map_err(|_| ParseError {})?;
471
14
        let time_t = system_time_to_time_t(st);
472
14
        Ok(Reference { time_t })
473
22
    }
474
}
475

            
476
#[cfg(test)]
477
mod test {
478
    // @@ begin test lint list maintained by maint/add_warning @@
479
    #![allow(clippy::bool_assert_comparison)]
480
    #![allow(clippy::clone_on_copy)]
481
    #![allow(clippy::dbg_macro)]
482
    #![allow(clippy::mixed_attributes_style)]
483
    #![allow(clippy::print_stderr)]
484
    #![allow(clippy::print_stdout)]
485
    #![allow(clippy::single_char_pattern)]
486
    #![allow(clippy::unwrap_used)]
487
    #![allow(clippy::unchecked_duration_subtraction)]
488
    #![allow(clippy::useless_vec)]
489
    #![allow(clippy::needless_pass_by_value)]
490
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
491
    use super::*;
492
    use humantime::parse_rfc3339;
493
    use itertools::{chain, Itertools};
494
    use tor_rtmock::{simple_time::SimpleMockTimeProvider, MockRuntime};
495

            
496
    fn secs(s: u64) -> Duration {
497
        Duration::from_secs(s)
498
    }
499

            
500
    #[test]
501
    fn hms_fmt() {
502
        let ft = FutureTimestamp {
503
            offset: 2 * 86400 + 22 * 3600 + 4 * 60 + 5,
504
        };
505

            
506
        assert_eq!(ft.to_string(), "T+2days 22h 4m 5s");
507

            
508
        assert_eq!(Ok(ft), FutureTimestamp::from_str("T+2days 22h 4m 5s"));
509
        assert_eq!(Ok(ft), FutureTimestamp::from_str("T+70h 4m 5s"));
510
        // we inherit rounding behaviour from humantime; we don't mind tolerating that
511
        assert_eq!(Ok(ft), FutureTimestamp::from_str("T+2days 22h 4m 5s 100ms"));
512
        assert_eq!(Ok(ft), FutureTimestamp::from_str("T+2days 22h 4m 5s 900ms"));
513
        let e = Err(ParseError {});
514
        assert_eq!(e, FutureTimestamp::from_str("2days"));
515
        assert_eq!(e, FutureTimestamp::from_str("T+"));
516
        assert_eq!(e, FutureTimestamp::from_str("T+ "));
517
        assert_eq!(e, FutureTimestamp::from_str("T+X"));
518
        assert_eq!(e, FutureTimestamp::from_str("T+23kg"));
519
    }
520

            
521
    #[test]
522
    #[allow(clippy::unusual_byte_groupings)] // we want them to line up, dammit!
523
    fn system_time_conversions() {
524
        assert!(system_time_min() <= SystemTime::UNIX_EPOCH);
525
        assert!(system_time_max() > SystemTime::UNIX_EPOCH);
526

            
527
        let p = |s| parse_rfc3339(s).expect(s);
528
        let time_t_2st = time_t_to_system_time;
529
        assert_eq!(p("1970-01-01T00:00:00Z"), time_t_2st(0));
530
        assert_eq!(p("2038-01-19T03:14:07Z"), time_t_2st(0x___7fff_ffff));
531
        assert_eq!(p("2038-01-19T03:14:08Z"), time_t_2st(0x___8000_0000));
532
        assert_eq!(p("2106-02-07T06:28:16Z"), time_t_2st(0x_1_0000_0000));
533
        assert_eq!(p("4147-08-20T07:32:16Z"), time_t_2st(0x10_0000_0000));
534
    }
535

            
536
    #[test]
537
    fn ref_fmt() {
538
        let time_t = 1217635200;
539
        let rf = Reference { time_t };
540
        let s = "2008-08-02T00:00:00Z";
541
        assert_eq!(rf.to_string(), s);
542

            
543
        let p = Reference::from_str;
544
        assert_eq!(Ok(rf), p(s));
545
        // we inherit rounding behaviour from humantime; we don't mind tolerating that
546
        assert_eq!(Ok(rf), p("2008-08-02T00:00:00.00Z"));
547
        assert_eq!(Ok(rf), p("2008-08-02T00:00:00.999Z"));
548
        let e = Err(ParseError {});
549
        assert_eq!(e, p("2008-08-02T00:00Z"));
550
        assert_eq!(e, p("2008-08-02T00:00:00"));
551
        assert_eq!(e, p("2008-08-02T00:00:00B"));
552
        assert_eq!(e, p("2008-08-02T00:00:00+00:00"));
553
    }
554

            
555
    #[test]
556
    fn basic() {
557
        #[derive(Serialize, Deserialize, Debug)]
558
        struct Stored {
559
            stored: Reference,
560
            s0: FutureTimestamp,
561
            s1: FutureTimestamp,
562
            s2: FutureTimestamp,
563
        }
564

            
565
        let real_instant = Instant::now();
566
        let test_systime = parse_rfc3339("2008-08-02T00:00:00Z").unwrap();
567

            
568
        let mk_runtime = |instant, systime| {
569
            let times = SimpleMockTimeProvider::new(instant, systime);
570
            MockRuntime::builder().sleep_provider(times).build()
571
        };
572

            
573
        let stored = {
574
            let runtime = mk_runtime(real_instant + secs(100_000), test_systime);
575
            let now = Storing::start(&runtime);
576

            
577
            let t0 = runtime.now() - secs(1000);
578
            let t1 = runtime.now() + secs(10);
579
            let t2 = runtime.now() + secs(3000);
580

            
581
            Stored {
582
                stored: now.store_ref(),
583
                s0: now.store_future(t0),
584
                s1: now.store_future(t1),
585
                s2: now.store_future(t2),
586
            }
587
        };
588

            
589
        let json = serde_json::to_string(&stored).unwrap();
590
        println!("{json}");
591
        assert_eq!(
592
            json,
593
            format!(concat!(
594
                r#"{{"stored":"2008-08-02T00:00:00Z","#,
595
                r#""s0":"T+0s","s1":"T+10s","s2":"T+50m"}}"#
596
            ))
597
        );
598

            
599
        let mpack = rmp_serde::to_vec_named(&stored).unwrap();
600
        println!("{}", hex::encode(&mpack));
601
        assert_eq!(
602
            mpack,
603
            chain!(
604
                &[132, 166],
605
                b"stored",
606
                &[206],
607
                &[0x48, 0x93, 0xa3, 0x80], // 0x4893a380 1217635200 = 2008-08-02T00:00:00Z
608
                &[162],
609
                b"s0",
610
                &[0],
611
                &[162],
612
                b"s1",
613
                &[10],
614
                &[162],
615
                b"s2",
616
                &[205],
617
                &[0x0b, 0xb8], // 0xbb8 == 3000
618
            )
619
            .cloned()
620
            .collect_vec(),
621
        );
622

            
623
        // Simulate a restart with an Instant which is *smaller* (maybe the host rebooted),
624
        // but with a wall clock time 200s later.
625
        {
626
            let runtime = mk_runtime(real_instant, test_systime + secs(200));
627
            let now = Loading::start(&runtime, stored.stored);
628

            
629
            let t0 = now.load_future(stored.s0);
630
            let t1 = now.load_future(stored.s1);
631
            let t2 = now.load_future(stored.s2);
632

            
633
            assert_eq!(t0, runtime.now()); // was already in the past when stored
634
            assert_eq!(t1, runtime.now()); // is now in the past
635
            assert_eq!(t2, runtime.now() + secs(2800));
636
        }
637

            
638
        // Simulate a restart with a later Instant
639
        // and with a wall clock time 1200s *earlier* due to clock skew.
640
        {
641
            let runtime = mk_runtime(real_instant + secs(200_000), test_systime - secs(1200));
642
            let now = Loading::start(&runtime, stored.stored);
643

            
644
            let t0 = now.load_future(stored.s0);
645
            let t1 = now.load_future(stored.s1);
646
            let t2 = now.load_future(stored.s2);
647

            
648
            assert_eq!(t0, runtime.now()); // was already in the past when stored
649
            assert_eq!(t1, runtime.now() + secs(10)); // well, it was only 10 even then
650
            assert_eq!(t2, runtime.now() + secs(3000)); // can't be increased
651
        }
652
    }
653
}