use std::time::{Duration, Instant};
use crate::authority::UserCapability;
use crate::ingress::OracleSet;
pub const BASE_AUTHORIZATION_OVERHEAD: Duration = Duration::from_millis(5);
pub const SAFETY_MARGIN: Duration = Duration::from_millis(2);
pub async fn equalize_timing(start: Instant, target: Duration) {
let elapsed = start.elapsed();
if elapsed < target {
tokio::time::sleep(target - elapsed).await;
}
}
#[must_use]
pub fn equalize_timing_target_for<C: UserCapability>(oracles: &OracleSet<'_>) -> Duration {
let mut target = BASE_AUTHORIZATION_OVERHEAD;
for query in C::ORACLE_CONSULTATIONS.block {
target += oracles.block.worst_case_latency_for(*query);
}
for query in C::ORACLE_CONSULTATIONS.audience {
target += oracles.audience.worst_case_latency_for(*query);
}
for query in C::ORACLE_CONSULTATIONS.mute {
target += oracles.mute.worst_case_latency_for(*query);
}
target + SAFETY_MARGIN
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn timing_constants_within_sane_bounds() {
assert_eq!(BASE_AUTHORIZATION_OVERHEAD, Duration::from_millis(5));
assert_eq!(SAFETY_MARGIN, Duration::from_millis(2));
assert!(BASE_AUTHORIZATION_OVERHEAD + SAFETY_MARGIN < Duration::from_secs(1));
}
#[tokio::test]
async fn equalize_timing_waits_until_target_when_op_was_fast() {
let start = Instant::now();
let target = Duration::from_millis(50);
equalize_timing(start, target).await;
let observed = start.elapsed();
assert!(
observed >= target,
"equalize_timing returned at {observed:?}, before target {target:?}"
);
assert!(
observed < target + Duration::from_millis(50),
"equalize_timing slept way past target: {observed:?} vs {target:?}"
);
}
#[tokio::test]
async fn equalize_timing_returns_immediately_when_op_was_slow() {
let start = Instant::now() - Duration::from_millis(100);
let target = Duration::from_millis(50);
let call_start = Instant::now();
equalize_timing(start, target).await;
let call_elapsed = call_start.elapsed();
assert!(
call_elapsed < Duration::from_millis(10),
"equalize_timing returned in {call_elapsed:?}; expected near-immediate return"
);
}
#[tokio::test]
async fn equalize_timing_handles_zero_target() {
let start = Instant::now();
let call_start = Instant::now();
equalize_timing(start, Duration::ZERO).await;
let call_elapsed = call_start.elapsed();
assert!(
call_elapsed < Duration::from_millis(10),
"equalize_timing with ZERO target took {call_elapsed:?}; expected near-immediate"
);
}
}