minip2p-platform 0.3.1

Portable clock, deadline, and entropy contracts for minip2p
Documentation

minip2p-platform

Portable clock, deadline, and entropy contracts for minip2p. no_std + alloc compatible.

Everything in minip2p is caller-driven: protocol agents, transports, and runtimes never read a system clock and never draw randomness themselves. The host samples time once per drive iteration and passes that one value to everything it drives; components that need randomness are handed an entropy source. This crate defines those contracts, so the rest of the workspace stays deterministic and testable and all platform access lives in adapters.

Features

  • Now: one time sample, carrying monotonic milliseconds and optional wall-clock seconds.
  • Clock: the trait adapters implement to produce Now.
  • Deadline: a point on a clock's monotonic timeline, used by caller-driven components to report when they next need attention.
  • EntropySource and EntropyError: cryptographically secure random bytes, with an explicit "this platform has none" error.
  • StdClock and StdEntropy behind the default std feature.

Usage

Sample once, then drive everything with that sample:

use minip2p_platform::{Clock, Deadline, Now, StdClock};

struct Heartbeat {
    interval_ms: u64,
    next: Deadline,
}

impl Heartbeat {
    fn poll(&mut self, now: Now) -> bool {
        if !self.next.is_expired_at(now) {
            return false;
        }
        self.next = now.deadline_after(self.interval_ms);
        true
    }

    fn next_deadline(&self) -> Option<Deadline> {
        Some(self.next)
    }
}

let mut clock = StdClock::new();

// The only sample of this drive iteration; everything below is derived from it.
let now = clock.now();
let mut heartbeat = Heartbeat {
    interval_ms: 15_000,
    next: now.as_deadline(),
};

assert!(heartbeat.poll(now));
assert!(!heartbeat.poll(now));

// The host can idle until the earliest deadline across all its subsystems.
let idle_ms = heartbeat
    .next_deadline()
    .map(|deadline| deadline.millis_until(now));
assert_eq!(idle_ms, Some(15_000));

Wall-clock time is optional

Now::unix_seconds is None whenever the platform has no usable wall-clock reading: an embedded board without an RTC or NTP sync has none at all, and a host whose clock is set before the Unix epoch has none that can be reported. Components that need real time — signed beacon freshness, certificate validity — must handle its absence explicitly. Substituting monotonic time is never correct: monotonic epochs are arbitrary and not comparable between peers.

no_std

Build without the std feature and supply your own implementations. A Clock must never let monotonic_ms decrease, and must saturate at Now::MAX_MONOTONIC_MSu64::MAX is reserved for Deadline::NEVER, so it is not a usable instant:

use minip2p_platform::{Clock, EntropyError, EntropySource, Now};

struct BoardClock;

impl Clock for BoardClock {
    fn now(&mut self) -> Now {
        // No RTC on this board, so no wall clock is reported.
        Now::from_millis(ticks_since_boot())
    }
}

struct BoardRng;

impl EntropySource for BoardRng {
    fn fill_bytes(&mut self, output: &mut [u8]) -> Result<(), EntropyError> {
        for byte in output.iter_mut() {
            *byte = hardware_rng_byte();
        }
        Ok(())
    }
}

License

MIT