Zum Inhalt

11 - Sicherheit & Access Control

Defense Layers, Threat Model, Emergency Procedures


Defense Layers

Das System implementiert 6 Verteidigungsebenen:

Layer 1: Access Control (Rollentrennung)

Rolle Verantwortung Kann NICHT
SAFE (Owner) Konfiguration, Emergency, Withdraw Operatoren-Aktionen, spezifische Claims
Operator VRF Request, Settlement, Wallet-Reassignment Funds zugreifen, Fee-Splits ändern
Settlement Payout-Autorisierung Funds halten, Konfiguration ändern
Relayer Batch Claims ausführen Funds abzweigen (nur operatorFeeReceiver)
Spieler Claim via Merkle Proof Merkle-Verifizierung umgehen

Layer 2: One-Time Setters

Kritische Contract-Referenzen können nur einmal gesetzt werden:

function setSettlement(address _settlement) external onlyOwner {
    require(settlement == address(0), "Already set");
    settlement = _settlement;
}
Verhindert nachträgliche Manipulation der Contract-Verdrahtung.

Layer 3: Fund Segregation (Option B)

┌──────────────────────────────┐
│ ReserveVault (hält USDT)     │  ← Einziger Fund Holder
├──────────────────────────────┤
│ CharityVault (hält USDT)     │  ← Sekundärer Fund Holder
├──────────────────────────────┤
│ Alle anderen Contracts       │  ← State only, KEIN USDT
└──────────────────────────────┘

Layer 4: Merkle Verification

  • Gewinner und Affiliates müssen kryptografische Proofs vorlegen
  • Roots sind immutable (einmal gesetzt, nie änderbar)
  • Double-Claim Prevention via claimed Mapping

Layer 5: Reentrancy Guards

Alle payout-relevanten Funktionen nutzen OpenZeppelin ReentrancyGuard:

function payPrize(...) external onlyAuthorizedSettlement nonReentrant
function claimMerkle(...) external nonReentrant
function claimAllFor(...) external onlyRelayer nonReentrant

Layer 6: Emergency Controls

  • emergencyMode auf ReserveVault blockiert alle Payouts
  • forceMarkDaySettled löst Settlement-Deadlocks
  • Operator-Key-Rotation über 4 Contracts
  • VRF Receiver-Rotation möglich

Vollständige Access Control Matrix

ReserveVaultV2

Funktion SAFE Settlement Treasury Anyone
depositFrom - - -
payPrize - - -
payFee - - -
payCharity - - -
payOperatorFee - - -
setEmergencyMode - - -
ownerWithdraw - - -

GameManagerV3

Funktion SAFE Operator VRFReceiver
buyTicketFor - -
setVrfSeed -
setDrandSeed - -
setOperator - -
setPoolUsesVRF - -

SettlementV5

Funktion SAFE Operator PrizeVault AffiliateVault
commitMerkleSettlement - - -
payoutFromPrizeVault - - -
payoutFromAffiliateVault - - -
forceMarkDaySettled - - -

PrizeVaultV3

Funktion SAFE Operator Settlement TrustedClaimer Spieler
claimMerkle - - - ✅ (eigene)
adminReassignWallet - - - -
setMerkleRootForDay - - - -

ClaimRouterV2

Funktion SAFE Relayer
claimAllFor -
setRelayer -
setMaxGasCap -

Threat Model

Bedrohung Wahrscheinlichkeit Impact Mitigation
Operator Key Kompromittiert Mittel Hoch Key-Rotation, SAFE bleibt sicher, Settlement-Validierung
SAFE Key Kompromittiert Niedrig Kritisch Multisig (mehrere Signaturen nötig)
Merkle Root Manipulation Niedrig Kritisch Root nur vom Settlement setzbar, immutable
VRF DoS Mittel Mittel try/catch, prevrandao Fallback
Reentrancy Angriff Niedrig Hoch ReentrancyGuard auf allen Payouts
Flash Loan Angriff N/A N/A Kein DeFi-Composability (USDT nur)
Signature Replay Niedrig Hoch Nonce + Deadline + Chain ID
Fund Drain via Logic Bug Niedrig Kritisch Fund Segregation, Emergency Mode

Emergency Procedures

1. Emergency Mode aktivieren

Situation: Verdacht auf Exploit oder kritischer Bug
Aktion:    SAFE aktiviert Emergency Mode auf ReserveVault

SAFE → ReserveVaultV2.setEmergencyMode(true)

Effekt:
    ✅ Alle Payouts blockiert (payPrize, payFee, payCharity, payOperatorFee)
    ✅ Deposits weiterhin möglich
    ✅ ownerWithdraw() funktioniert (Fund Recovery)
    ❌ Settlement kann nicht committen (Payouts reverieren)
    ❌ Claims können nicht ausgeführt werden

Dauer: ~5 Minuten (SAFE Multisig Signatur-Sammlung)

2. Settlement Deadlock auflösen

Situation: Tag X hat Tickets aber Backend kann nicht settlen
Aktion:    SAFE markiert Tag als settled (ohne Merkle Root)

SAFE → SettlementV5.forceMarkDaySettled(dayId)

Effekt:
    ✅ daySettled[dayId] = true
    ✅ Nächster Tag kann Tickets verkaufen
    ❌ Keine Merkle Roots gesetzt (Spieler für diesen Tag verlieren Gewinne)
    ❌ Keine Fee Splits ausgeführt

Verwendung: Nur als absoluter Notfall!

3. Operator Key Rotation

Situation: Operator-Key kompromittiert oder Mitarbeiter-Wechsel
Aktion:    Neuen Operator auf allen relevanten Contracts setzen

SAFE Batch-Transaktion:
    1. GameManagerV3.setOperator(newOperator)
    2. SettlementV5.setOperator(newOperator)
    3. PrizeVaultV3.setOperator(newOperator)
    4. VRFReceiverV3.setOperator(newOperator)

Effekt: Alter Operator verliert sofort alle Berechtigungen

4. VRF Receiver Rotation

Situation: VRF Receiver muss aktualisiert werden
Aktion:    Neuen VRFReceiver deployen und verdrahten

    1. Deploy: new VRFReceiverV3(coordinator, keyHash, subId, gameManager, operator, lastSeed)
    2. SAFE: GameManagerV3.setVrfReceiver(newReceiver) (falls One-Time-Setter erlaubt)
    3. Chainlink: Neuen Consumer zur Subscription hinzufügen
    4. Alten Consumer entfernen

Invarianten für Audit-Prüfung

Ökonomische Invarianten

  1. Balance Conservation:

    ReserveVault.balance >= Σ(potAcc + rolloverAcc + jackpotReserveAcc + feeAcc + unclaimed)
    

  2. Payout Bounds:

    ∀ poolId: payoutAmount <= potAcc[poolId] + rolloverAcc[poolId]
    

  3. Fee Split:

    affiliateTotal + charityTotal + netFee == dailyFee
    

  4. Merkle Claims:

    Σ(claimed amounts per day) <= totalPayoutByDay[dayId]
    

State-Invarianten

  1. Settlement Sequentialität:

    ∀ settled day d: d > 0 → daySettled[d-1] == true (oder d == firstDay)
    

  2. Merkle Root Immutability:

    once merkleRootByDay[d] != 0 → merkleRootByDay[d] never changes
    

  3. Nonce Uniqueness:

    usedNonce[addr][nonce] can only transition false → true (never back)
    

  4. Double-Claim Prevention:

    claimed[dayId][wallet] can only transition false → true
    


Audit-Hinweise

Kritischste Angriffsfläche

Die größte Gefahr ist ein Bug im Settlement-Commit, der falsche Merkle Roots setzt. Da Roots immutable sind, wäre das nicht rückgängig zu machen. Die Korrektheit wird off-chain sichergestellt.

SAFE ist Single Point of Trust

Der SAFE-Owner hat ultimative Kontrolle (Emergency, Withdraw, Konfiguration). Die Sicherheit des gesamten Systems hängt von der SAFE-Sicherheit ab.

Kein Proxy/Upgrade Pattern

Die Contracts nutzen keine Proxy-Pattern (kein UUPS, kein TransparentProxy). Upgrades erfolgen durch Deploy + Re-Wiring. Das ist sicherer aber aufwändiger.