Refs #46
PR: feat/21-mempool-monitor
File: crates/charon-scanner/src/mempool.rs
Function: unix_now() and PendingCache::drain()
Problem
The private unix_now() in mempool.rs is:
fn unix_now() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_secs())
.unwrap_or(0)
}
When the system clock fails, it returns 0. In drain(), the age check is:
if now.saturating_sub(entry.inserted_at) > max_age { drop }
With now=0 and inserted_at > 0 (any real timestamp), saturating_sub yields 0. 0 > max_age is false. Every entry passes the freshness check regardless of true age. Clock failure causes all TTL-expired pre-signs to be returned for broadcast instead of being dropped.
This is the opposite of the safe default. Compare with oracle.rs unix_now() which returns Result and propagates errors so callers can treat entries as stale.
Required fix
Align with oracle.rs: return Result, and treat clock failure as stale. In drain(), if unix_now() returns Err, drop all entries and emit warn!("system clock unavailable, dropping all pre-signs").
Refs #46
PR: feat/21-mempool-monitor
File: crates/charon-scanner/src/mempool.rs
Function: unix_now() and PendingCache::drain()
Problem
The private unix_now() in mempool.rs is:
When the system clock fails, it returns 0. In drain(), the age check is:
With now=0 and inserted_at > 0 (any real timestamp), saturating_sub yields 0. 0 > max_age is false. Every entry passes the freshness check regardless of true age. Clock failure causes all TTL-expired pre-signs to be returned for broadcast instead of being dropped.
This is the opposite of the safe default. Compare with oracle.rs unix_now() which returns Result and propagates errors so callers can treat entries as stale.
Required fix
Align with oracle.rs: return Result, and treat clock failure as stale. In drain(), if unix_now() returns Err, drop all entries and emit warn!("system clock unavailable, dropping all pre-signs").