Safe restore from a backup: START_PAUSED and the derivation-gap check (B-90, REG-15, OPS-27)

START_PAUSED=true pauses the lottery as the operator before the scheduler exists.
On the first Electrum session the 20 derivation indexes past the highest
assigned one are looked up on every server; if any has history, registration
stays closed (503 registration_unavailable) and restore_gap_detected is
written, so a restored database cannot hand a lost user's address to a new one.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
davideandClaude Sonnet 5.5 committed 2026-10-18 00:43:45 +02:00
1 parent 55d852fe08
commit 40ac490826
21 files changed
+281 -70

No files matched your search

+2 -9
View File
@@ -14,7 +14,7 @@ from app.config import settings
from app.db.models import AuditLog, BugReport, PendingTransaction, Round, User
from app.db.session import get_session
from app.rounds.config import get_round_config
from app.rounds.pause import AUTOMATIC_REASONS, OPERATOR_REASON
from app.rounds.pause import pause_by_operator
from app.rounds.service import get_active_round, payout_config_problem
from app.wallet.address import is_valid_plm_address
from app.wallet.hd import derive_user_wif
@@ -196,14 +196,7 @@ async def pause_lottery(
B-78 (DAT-69, API-32): the reason is "operator", except that an automatic reason
already in force stays — it is the one that needs a look. The audit row always
says `operator` for this call."""
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 body is not None and body.note:
payload["note"] = body.note
await write_audit_log(session, "lottery_paused", payload)
config = await pause_by_operator(session, body.note if body is not None else None)
await session.commit()
return _config_response(config)