From 110aef505a51c3888418a305adeb9592618c5d7a Mon Sep 17 00:00:00 2001 From: Sylvester Kaczmarek <16242628+sylvesterkaczmarek@users.noreply.github.com> Date: Sat, 15 Aug 2026 23:32:20 +0100 Subject: [PATCH] Fail closed on malformed dispute authority --- backend/services/infonet/markets/dispute.py | 31 +++++++++++++-------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/backend/services/infonet/markets/dispute.py b/backend/services/infonet/markets/dispute.py index 5121e07..dc0de2d 100644 --- a/backend/services/infonet/markets/dispute.py +++ b/backend/services/infonet/markets/dispute.py @@ -39,13 +39,13 @@ def _payload(event: dict[str, Any]) -> dict[str, Any]: return p if isinstance(p, dict) else {} -def _finite_float(value: Any, default: float = 0.0) -> float: - """Parse a chain numeric without allowing NaN/inf to poison views.""" +def _finite_float(value: Any) -> float | None: + """Parse a chain numeric, rejecting malformed and non-finite values.""" try: - parsed = float(value or default) - except (TypeError, ValueError): - return float(default) - return parsed if isfinite(parsed) else float(default) + parsed = float(value) + except (TypeError, ValueError, OverflowError): + return None + return parsed if isfinite(parsed) else None @dataclass @@ -97,15 +97,18 @@ def collect_disputes( if not open_events: return [] - # Build by dispute_id keyed off the open event. + # Build by dispute_id keyed off the open event. A malformed + # authoritative open must not be normalized into active state. disputes: dict[str, DisputeView] = {} open_id_by_market_event: dict[str, str] = {} for ev in open_events: p = _payload(ev) - did = _dispute_id(ev) - challenger = ev.get("node_id") or "" cstake = _finite_float(p.get("challenger_stake")) opened_at = _finite_float(ev.get("timestamp")) + if cstake is None or opened_at is None: + continue + did = _dispute_id(ev) + challenger = ev.get("node_id") or "" disputes[did] = DisputeView( dispute_id=did, market_id=str(market_id), challenger_id=str(challenger), challenger_stake=cstake, @@ -128,7 +131,7 @@ def collect_disputes( if rep_type not in ("oracle", "common"): continue amount = _finite_float(p.get("amount")) - if amount <= 0: + if amount is None or amount <= 0: continue record = { "node_id": ev.get("node_id") or "", @@ -138,7 +141,8 @@ def collect_disputes( target = disputes[did].confirm_stakes if side == "confirm" else disputes[did].reverse_stakes target.append(record) - # Resolution events. + # Resolution events. Invalid resolution timestamps fail closed: + # they cannot exert consensus authority through resolved_outcome. for ev in chain_list: if ev.get("event_type") != "dispute_resolve": continue @@ -149,8 +153,11 @@ def collect_disputes( outcome = p.get("outcome") if outcome not in ("upheld", "reversed", "tie"): continue + resolved_at = _finite_float(ev.get("timestamp")) + if resolved_at is None: + continue disputes[did].resolved_outcome = outcome - disputes[did].resolved_at = _finite_float(ev.get("timestamp")) + disputes[did].resolved_at = resolved_at return sorted(disputes.values(), key=lambda d: (d.opened_at, d.dispute_id))