fix: pre-launch hardening — HYPE price feed, KOL wallet cleanup, Telegram Trump alert, rate limiting, brittle test
Batch of the pre-launch audit campaign (BUG-01…14 plus three new features): Pricing / TP-SL protection - Add app/services/hl_price_feed.py: supplemental HL allMids poller for HL-native assets (HYPE, PURR) not listed on Binance. Pumps price_store + tp_sl_monitor.on_price_tick so bot trades on these assets keep full stop-loss / take-profit / trailing protection instead of max-hold only. - Wire feed into main.py lifespan (startup task + graceful shutdown cancel). Telegram - Add format_trump_mention + PATH B in _dispatch: crypto-relevant Trump posts with no directional signal (relevant=True, signal=hold) now alert the public channel only (no per-subscriber noise). - Rate limiter (slowapi) on the API; assorted bot/digest fixes. KOL on-chain - seed_kol_wallets.py: KOL_FEEDS coverage cross-check; reversibly deactivate orphaned wallets (handle not in KOL_FEEDS → can never produce divergence) so the scanner stops burning cycles on them. Tests / misc - Fix brittle test_macro_ahr999_uses_same_formula_as_scanner: mock now uses realistic ms timestamps so the in-progress-day drop fires, matching the fetcher's bar count (was 0.3179 vs 0.3178 off-by-one). - Refresh stale notify_signal comment in truth_social.py. Frontend reduce-action type fix lives in the sibling repo. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -52,15 +52,22 @@ def build_message(action: str, wallet: str, timestamp_ms: int, body_hash: str) -
|
||||
# In-memory; fine for a single-process deploy. For multi-worker, move to Redis.
|
||||
_seen: dict[str, float] = {} # sha256(signature) → expiry epoch seconds
|
||||
|
||||
# Purge when the dict exceeds this size. At 5 minutes TTL and ~10 req/s peak
|
||||
# that's ~3000 entries at saturation; 1000 gives comfortable headroom while
|
||||
# keeping purge cost tiny (<1 ms for 1000-entry linear scan).
|
||||
_SEEN_PURGE_THRESHOLD = 1000
|
||||
|
||||
|
||||
def _cache_put(sig: str, ttl: float) -> bool:
|
||||
"""Return True if sig was new, False if replay."""
|
||||
now = time.time()
|
||||
# lazy purge
|
||||
if len(_seen) > 5000:
|
||||
for k, v in list(_seen.items()):
|
||||
if v < now:
|
||||
_seen.pop(k, None)
|
||||
# Lazy purge: only scan when we've crossed the threshold, not every call.
|
||||
# Scanning 1000 vs 5000 entries keeps individual request latency negligible
|
||||
# even under burst traffic.
|
||||
if len(_seen) >= _SEEN_PURGE_THRESHOLD:
|
||||
expired = [k for k, v in _seen.items() if v < now]
|
||||
for k in expired:
|
||||
del _seen[k]
|
||||
key = hashlib.sha256(sig.encode("utf-8")).hexdigest()
|
||||
if key in _seen and _seen[key] > now:
|
||||
return False
|
||||
@@ -105,3 +112,35 @@ def verify_signed_request(
|
||||
if not allow_replay:
|
||||
if not _cache_put(signature, ttl=MAX_SKEW_SECONDS):
|
||||
raise HTTPException(401, "Signature already used (replay blocked)")
|
||||
|
||||
|
||||
def verify_signed_request_any(
|
||||
*,
|
||||
actions: list[str],
|
||||
wallet: str,
|
||||
timestamp_ms: int,
|
||||
signature: str,
|
||||
body: Optional[Any],
|
||||
allow_replay: bool = False,
|
||||
) -> None:
|
||||
"""Accept any one of several signed read-actions.
|
||||
|
||||
Useful for wallet-owned read endpoints where the frontend may already hold
|
||||
a fresh cached envelope for a sibling read action such as `view_user`.
|
||||
"""
|
||||
last_exc: Optional[HTTPException] = None
|
||||
for action in actions:
|
||||
try:
|
||||
verify_signed_request(
|
||||
action=action,
|
||||
wallet=wallet,
|
||||
timestamp_ms=timestamp_ms,
|
||||
signature=signature,
|
||||
body=body,
|
||||
allow_replay=allow_replay,
|
||||
)
|
||||
return
|
||||
except HTTPException as exc:
|
||||
last_exc = exc
|
||||
if last_exc is not None:
|
||||
raise last_exc
|
||||
|
||||
Reference in New Issue
Block a user