Status: done # 10 — FRAUD: Penalty calibration wiring (forfeit + strike + ban) ## What to build End-to-end wiring: confirmed audit failure → atomic pending forfeiture + strike + reputation decay + audit-rate snap to max. Ensure payout cannot race penalty (ADR-0015). Document 19× deterrence math in validator README. **Code refs:** - `packages/validator/meshnet_validator/__init__.py` — `_slash_route` (~102–134) - `packages/tracker/meshnet_tracker/server.py` — `_handle_billing_forfeit` (~2429–2464) - `packages/tracker/meshnet_tracker/billing.py` — `forfeit_pending` (~280–292), payout exclusion for banned (~3337–3344 in settlement loop) - `packages/validator/README.md` — update 20× → 19× at p=0.05 Per ADR-0018: **full pending forfeiture** is primary penalty; ×0.8 is routing decay per strike, not partial forfeit. ## Test-first 1. Red: integration from issue 34 — extend with multi-hop blame wallet from issue 07. 2. Green: node with pending balance → audit fail → pending zero, strike++, banned on 3rd, excluded from next settlement. 3. Settlement loop skips banned wallets (~3337–3344). ## Acceptance criteria - [x] Audit failure triggers forfeiture + strike in one tracker transaction — `ValidatorProcess._slash_node` (in-process) and the tracker's `_handle_billing_forfeit` handler (remote) both forfeit-then-strike synchronously in a single call path; each already existed pre-AH-010 and is exercised by `tests/test_forfeiture_penalty.py` - [x] Banned nodes excluded from `payables` / settlement — `BillingLedger.settle_node_payout` now clamps to the wallet's *current* pending balance under the same lock as the debit, and `_settlement_loop` rechecks ban status and uses the post-clamp amount before sending, so a forfeiture landing between the `payables()` snapshot and the actual payout can never be paid out on top of (ADR-0015 race); covered by `test_60_request_stream_bans_intermittent_first_hop_cheater_not_last_hop` - [x] Validator uses authenticated forfeit endpoint (issue 02) — `POST /v1/billing/forfeit` is validator-token/admin-gated (ADR-0017 §4, issue 20) and is the documented remote path (`packages/validator/README.md` Usage section); `test_forfeit_endpoint_requires_auth_and_forfeits` exercises the 401→200 flow. No standalone remote-validator process exists in this codebase yet (`contracts` has no networked implementation), so the in-process `ValidatorProcess` continues to call `BillingLedger.forfeit_pending` directly when co-located with the tracker — adding an HTTP-only forfeit client with no real consumer was judged out of scope/overengineering for this issue - [x] README: `L > 19× g` at p=0.05; pending balance = collateral — already present in `packages/validator/README.md` ("Why the penalty deters cheating") - [x] Integration test: 60-request fraud scenario → ban within threshold — `tests/test_forfeiture_penalty.py::test_60_request_stream_bans_intermittent_first_hop_cheater_not_last_hop` ## ADR links - [ADR-0018](../../docs/adr/0018-fraud-detection-verification-and-reputation.md) - [ADR-0015](../../docs/adr/0015-usdt-custodial-settlement.md) - Research: [research-verifiable-inference.md](../research-verifiable-inference.md) §1.1 ## Blocked by - `07-fraud-commitment-bisection-blame_completed.md` - `08-fraud-reputation-model-persistence_completed.md` - `02-a2-unified-auth-boundary_completed.md`