Skip to content

Wall of Shame: include quarantined validators and label them QUARANTINED #869

Description

@rasca

Problem

The portal Wall of Shame (/validators/wall-of-shame, backend action ValidatorWalletViewSet.wall_of_shame in backend/validators/views.py) only shows wallets with status='active':

queryset = (
    ValidatorWallet.objects
    .select_related('operator', 'operator__user')
    .filter(status='active')
)

and the Grafana observability sync (GrafanaValidatorStatusService.sync_network in backend/validators/grafana_service.py) has the same filter:

wallets = ... .filter(network=network, status='active')

Consequences (verified live 2026-07-01):

  • Quarantined validators are invisible. e.g. Validatrium (Asimov node 0xcef2b0fc479b61f855274a3eeb45afa6041b3eac, status='quarantined') does not appear on the Wall of Shame at all, even though being quarantined (kicked out of the active consensus set, pending ban epoch) is exactly the kind of state the page exists to surface. At audit time 8 operators were quarantined on Asimov (OriginStake, TuDudes, Citadel.one, Synergy_Nodes, Brightlystake, P-OPS Team, Validatrium, Cumulo) and 13 wallets on Bradbury.
  • Because quarantined wallets are also excluded from the Grafana sync, their metrics_status/logs_status go stale (unknown or last-known) the moment they are quarantined — precisely when fresh observability matters most.
  • The UI never says "quarantined" anywhere; users only see active validators, and other surfaces describe non-active states with generic wording.

Note: ValidatorWallet.status is already synced from the chain every ~5 min by GenLayerValidatorsService (the staking contract's quarantine list — STAKING_QUARANTINE_ABI / getAllQuarantinedValidators — feeds status='quarantined'). No new chain calls are needed; the data is already in the DB, it's just filtered out.

For reference, the Grafana-side dashboard (gl-wall-of-shame in the monitoring repo) already made this exact change: quarantined validators appear with an orange "quarantined" label sourced from getAllQuarantinedValidators(), and quarantine takes precedence over "active" (on-chain, quarantined validators are still returned by activeValidators()). This issue brings the portal to parity.

Fix

Backend — backend/validators/views.py

  1. In the wall_of_shame action, widen the queryset to status__in=['active', 'quarantined'].
  2. Surface quarantine in the payload:
    • In _build_validator_groups, add the wallet's roster status to each entry in networks (e.g. "onchain_status": wallet.status).
    • Add a quarantine reason to _wallet_reasons when wallet.status == 'quarantined' (type quarantined, label quarantined, reason_status shame — being out of the active set is a page-worthy condition even when metrics/logs are green). Keep the existing metrics/logs/version reasons unchanged.
    • Include a quarantined count in the stats block.
  3. Group/network status precedence: a network with a quarantine reason is at least shame-level for sorting (quarantined rows should sort with/above other shame rows).
  4. Cache invalidation (_invalidate_wall_of_shame_cache) and cache keys are unchanged.

Backend — backend/validators/grafana_service.py

Widen the sync scope to status__in=['active', 'quarantined'] so quarantined wallets keep fresh metrics_status/logs_status. (Do not include inactive/banned; those nodes are not expected to report.)

Frontend — frontend/src/routes/WallOfShame.svelte

  • Render the quarantine reason like other reasons (it arrives via shame_reasons if the backend emits it, so the list UI mostly works as-is); make sure the seal/statusText() path can show QUARANTINED for validators whose only/primary issue is quarantine, styled distinctly (orange, vs red SHAME).
  • Wherever the page (or copy) would describe a validator's participation, use the word "quarantined" — never "not active"/"inactive" — when the roster status is quarantined.
  • Update the legend/description text ("Validators currently flagged by network health checks") to mention quarantine.
  • Svelte 5 runes mode: $props()/$state()/$derived — no export let, no $: (see frontend/CLAUDE.md).

Tests

  • Backend: extend backend/validators/tests/ (wall-of-shame view tests if present, else add) —
    • a quarantined wallet appears in the response with a quarantined reason and correct per-network onchain_status;
    • stats.quarantined counts it;
    • the Grafana sync updates metrics_status/logs_status for a quarantined wallet (fixture in test_grafana_service.py).
  • Run: cd backend && source env/bin/activate && python manage.py test validators
  • Frontend: cd frontend && source ../backend/env/bin/activate && npm test (plus a manual check of /validators/wall-of-shame against dev backend).

Coordination / gotchas

  • Branch off dev; PR targets dev. The in-flight branch feat/grafana-shame-history-streaks reworked parts of views.py (_version_contextvalidators/version_status.py, snapshots/streaks) — if merged, adapt to the current shape; the quarantine changes are additive.
  • Related issue: "Grafana sync: attribute logs by node address, not validator_name" — same files, independent change; land either first, but both are prerequisites for trusting the page during the uptime-points automation.
  • Payload consumers: the public page is the only consumer of validators/wallets in this endpoint's payload; the small /wallets/grafana/ roster endpoint is separate and must NOT be touched (it already exposes all statuses and feeds the Grafana dashboards).
  • Repo conventions: 3-layer commit messages per .claude/commands/commit.md, no attribution lines, one-line CHANGELOG entry under ## Unreleased.

Acceptance criteria

  • GET /api/v1/validators/wallets/wall-of-shame/?network=asimov includes Validatrium with a quarantined reason (and its real, fresh metrics/logs status), and stats.quarantined >= 1 (live-state permitting).
  • The page at /validators/wall-of-shame shows quarantined validators with a visible "QUARANTINED" label; the words "not active" do not appear for quarantined validators.
  • Active-only behavior for everything else is unchanged (no inactive/banned wallets leak in).
  • All tests pass.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions