cash/closest: return 404 when no oracle price within 2h of timestamp

Prevents returning stale v1 data as if it were the price for a gap-period
timestamp (Apr 23–May 4 2026). If the closest indexed entry is more than
MAX_ORACLE_STALENESS_SECS (7200s) away, respond with PriceNotFound 404
instead of silently returning wrong data.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Jakob Notland 2026-06-09 13:20:22 +02:00
parent 62c5b2ff4a
commit 7c196faf87

View file

@ -5,7 +5,7 @@
use crate::db::oracle::{get_closest, get_range, get_range_with_step};
use crate::db::DB;
use crate::rpc::err::{bad_request, db_error, ApiErrorCode, CachedApiResult};
use crate::rpc::err::{bad_request, db_error, not_found, ApiErrorCode, CachedApiResult};
use crate::rpc::response::{cached_ok, CACHE_AGGREGATE};
use crate::timeutil::time_now;
use bitcoin_hashes::hex::FromHex;
@ -193,9 +193,13 @@ pub async fn oracle_get_history(
))
}
/// Delphi oracle updates roughly every 515 minutes. An entry more than 2 hours
/// older than the requested timestamp means there is genuinely no price for that
/// period (e.g. the Apr 23May 4 2026 gap between v1 and v2).
const MAX_ORACLE_STALENESS_SECS: i64 = 7200;
/// Get the closest BCH/USD price for a given timestamp.
/// Backed by Delphi v2 (and v1 for historical timestamps).
/// Returns `null` if no data is available.
/// Returns 404 if no oracle price is available within 2 hours of the timestamp.
#[get("/cash/closest?<timestamp>")]
pub async fn oracle_cash_closest(
timestamp: Option<i64>,
@ -205,12 +209,16 @@ pub async fn oracle_cash_closest(
let entry = get_closest(&db.oracle_r, &None, ts)
.await
.map_err(db_error)?;
Ok(cached_ok(
entry.map_or(serde_json::Value::Null, |e| {
serde_json::to_value(e).unwrap()
}),
CACHE_AGGREGATE,
))
match entry {
Some(e) if ts - e.oracle_timestamp <= MAX_ORACLE_STALENESS_SECS => {
Ok(cached_ok(serde_json::to_value(e).unwrap(), CACHE_AGGREGATE))
}
_ => Err(not_found(
ApiErrorCode::PriceNotFound,
&format!("No oracle price available for timestamp {ts}"),
)),
}
}
/// Get historical BCH/USD prices for a time range.