mirror of
https://github.com/BigBodyCobain/Shadowbroker.git
synced 2026-08-15 23:20:29 +02:00
fix(infonet): reject non-finite stalemate burn inputs (#514)
* fix(infonet): reject non-finite stalemate burn inputs * test(infonet): cover non-finite stalemate burn inputs
This commit is contained in:
@@ -30,28 +30,43 @@ specific branches in ``resolution.py`` that match the spec's
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Iterable
|
||||
import math
|
||||
from typing import Any, Iterable
|
||||
|
||||
from services.infonet.config import CONFIG
|
||||
|
||||
|
||||
def _finite_float(value: Any) -> float | None:
|
||||
try:
|
||||
parsed = float(value)
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
return parsed if math.isfinite(parsed) else None
|
||||
|
||||
|
||||
def stalemate_burn_pct() -> float:
|
||||
"""Current burn percentage from CONFIG. Helper so callers don't
|
||||
need to remember the key name."""
|
||||
return float(CONFIG["resolution_stalemate_burn_pct"])
|
||||
pct = _finite_float(CONFIG["resolution_stalemate_burn_pct"])
|
||||
if pct is None:
|
||||
raise ValueError("resolution_stalemate_burn_pct must be finite")
|
||||
return pct
|
||||
|
||||
|
||||
def split_burn_and_return(amount: float, burn_pct: float | None = None) -> tuple[float, float]:
|
||||
"""Compute (burn_amount, returned_amount) for a single stake."""
|
||||
if amount <= 0:
|
||||
amt = _finite_float(amount)
|
||||
if amt is None or amt <= 0:
|
||||
return 0.0, 0.0
|
||||
pct = float(stalemate_burn_pct() if burn_pct is None else burn_pct)
|
||||
pct = _finite_float(stalemate_burn_pct() if burn_pct is None else burn_pct)
|
||||
if pct is None:
|
||||
raise ValueError("burn_pct must be finite")
|
||||
if pct <= 0:
|
||||
return 0.0, float(amount)
|
||||
return 0.0, amt
|
||||
if pct >= 1:
|
||||
return float(amount), 0.0
|
||||
burn = float(amount) * pct
|
||||
returned = float(amount) - burn
|
||||
return amt, 0.0
|
||||
burn = amt * pct
|
||||
returned = amt - burn
|
||||
return burn, returned
|
||||
|
||||
|
||||
@@ -67,18 +82,17 @@ def apply_to_stakes(
|
||||
folds these into the larger ``ResolutionResult`` rather than
|
||||
mutating any state directly.
|
||||
"""
|
||||
pct = float(stalemate_burn_pct() if burn_pct is None else burn_pct)
|
||||
pct = _finite_float(stalemate_burn_pct() if burn_pct is None else burn_pct)
|
||||
if pct is None:
|
||||
raise ValueError("burn_pct must be finite")
|
||||
returns: dict[tuple[str, str], float] = {}
|
||||
total_burned = 0.0
|
||||
for s in stakes:
|
||||
node_id = s.get("node_id") if isinstance(s, dict) else getattr(s, "node_id", None)
|
||||
rep_type = s.get("rep_type") if isinstance(s, dict) else getattr(s, "rep_type", None)
|
||||
amount = s.get("amount") if isinstance(s, dict) else getattr(s, "amount", None)
|
||||
try:
|
||||
amt = float(amount) if amount is not None else 0.0
|
||||
except (TypeError, ValueError):
|
||||
amt = 0.0
|
||||
if amt <= 0 or not isinstance(node_id, str) or rep_type not in ("oracle", "common"):
|
||||
amt = _finite_float(amount)
|
||||
if amt is None or amt <= 0 or not isinstance(node_id, str) or rep_type not in ("oracle", "common"):
|
||||
continue
|
||||
burn, ret = split_burn_and_return(amt, pct)
|
||||
if ret > 0:
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
"""Regression coverage for non-finite stalemate burn inputs."""
|
||||
|
||||
import math
|
||||
|
||||
import pytest
|
||||
|
||||
from services.infonet.markets.stalemate_burn import apply_to_stakes, split_burn_and_return
|
||||
|
||||
|
||||
def test_split_rejects_nonfinite_amounts():
|
||||
assert split_burn_and_return(float("nan"), 0.1) == (0.0, 0.0)
|
||||
assert split_burn_and_return(float("inf"), 0.1) == (0.0, 0.0)
|
||||
|
||||
|
||||
def test_apply_skips_nonfinite_stakes_without_poisoning_totals():
|
||||
returns, burned = apply_to_stakes(
|
||||
[
|
||||
{"node_id": "alice", "rep_type": "oracle", "amount": 10.0},
|
||||
{"node_id": "mallory", "rep_type": "oracle", "amount": float("nan")},
|
||||
{"node_id": "eve", "rep_type": "common", "amount": float("inf")},
|
||||
],
|
||||
burn_pct=0.1,
|
||||
)
|
||||
|
||||
assert returns == {("alice", "oracle"): 9.0}
|
||||
assert burned == 1.0
|
||||
assert math.isfinite(burned)
|
||||
|
||||
|
||||
def test_nonfinite_burn_percentage_is_rejected():
|
||||
with pytest.raises(ValueError, match="burn_pct must be finite"):
|
||||
split_burn_and_return(10.0, float("nan"))
|
||||
|
||||
with pytest.raises(ValueError, match="burn_pct must be finite"):
|
||||
apply_to_stakes([], burn_pct=float("inf"))
|
||||
Reference in New Issue
Block a user