Skip to content

[PR #35] oracle.rs: unix_now() returns 0 on clock failure — stale prices pass freshness #112

@obchain

Description

@obchain

PR: #35 (feat/10-chainlink-pricecache)
Commit: 5c36873
File: crates/charon-scanner/src/oracle.rs, unix_now() and is_fresh()

Problem:

fn unix_now() -> u64 {
    SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .map(|d| d.as_secs())
        .unwrap_or(0)
}

If SystemTime::now() predates UNIX_EPOCH (clock rollback, container clock skew), duration_since() returns Err and function returns 0.

is_fresh() evaluates cached.updated_at + max_age.as_secs() >= nowsome_recent_ts + 600 >= 0 → always true.

Every cached price — expired, stale, uninitialized — reported as fresh. Staleness gate completely bypassed with no log or error.

Same 0-return affects refresh(): staleness bail! becomes updated_at + max_age < 0 (impossible for u64, always false) → every feed including stale ones inserted and served.

Separately, updatedAt = 0 from uninitialized aggregator not explicitly detected. Error message says "stale" (age = now - 0) rather than "uninitialized feed", harder to debug.

Risk: Containerized environments (Hetzner CX22 Docker, PR #55) experience clock drift or resets during upgrades. Any such event silently serves stale prices to liquidation path.

Fix:

fn unix_now() -> Result<u64> {
    Ok(SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .context("system clock is before UNIX_EPOCH")?
        .as_secs())
}

is_fresh(): return false on clock Err. refresh(): bail! on clock Err. Separate guard:

if updated_at == 0 {
    anyhow::bail!("feed '{symbol}': updatedAt=0, feed is uninitialized");
}

Keeps clock-failure and uninitialized-feed failure modes distinct in logs.

Metadata

Metadata

Assignees

No one assigned

    Labels

    layer:rustRust crates (core / scanner / protocols / executor / cli)pr-reviewFindings from PR review processpriority:p1-coreCore MVP scopetype:featureNew capability or deliverable

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions