Files
plm-lottery/app/rounds/pause.py
T

45 lines
2.1 KiB
Python
Raw Normal View History

"""Pausing the lottery from code (B-78, DAT-69, AUD-11).
A pause only stops the *next* round from opening (rounds/service.py:rounds_can_open); a
round in progress still closes, draws and pays. Besides the operator's own switch there
are pauses the system raises itself when something needs a human look — a transaction
given up for dead that confirmed after all (`resurrected_tx`, B-78) and, in later
findings, a deep reorg (`reorg`, B-85) and a payout whose winning bet left the chain
(`payout_blocked`, B-82).
"""
from sqlalchemy.ext.asyncio import AsyncSession
from app.audit.log import write_audit_log
from app.rounds.config import get_round_config
OPERATOR_REASON = "operator"
AUTOMATIC_REASONS = ("reorg", "payout_blocked", "resurrected_tx")
async def pause_automatically(session: AsyncSession, reason: str) -> None:
"""Pause the lottery for an automatic `reason` in the caller's transaction and write
the `lottery_paused` row. An automatic reason is written even over a pause that is
already active (DAT-69): the cause that needs checking must not be hidden behind an
earlier one, and an operator pause is never what overrides it."""
if reason not in AUTOMATIC_REASONS:
raise ValueError(f"not an automatic pause reason: {reason!r}")
config = await get_round_config(session)
config.paused = True
config.paused_reason = reason
await write_audit_log(session, "lottery_paused", {"reason": reason})
async def pause_by_operator(session: AsyncSession, note: str | None = None):
"""The operator's pause (`POST /admin/pause`, B-90's `START_PAUSED`): never overwrites
an automatic reason already in force — that is the one that needs a look — and the
audit row always says `operator`. Returns the config row; the caller commits."""
config = await get_round_config(session)
if not (config.paused and config.paused_reason in AUTOMATIC_REASONS):
config.paused_reason = OPERATOR_REASON
config.paused = True
payload = {"reason": OPERATOR_REASON}
if note:
payload["note"] = note
await write_audit_log(session, "lottery_paused", payload)
return config