Zum Inhalt

16 - Testing & Akzeptanzkriterien

Teststruktur, Szenarien, Failure Modes, Akzeptanzkriterien


Teststruktur

contracts/test/
├── commit_20456.js                        # Settlement Commit Tests
├── claim_20456.js                         # Claim Verification Tests
├── check_charity_state.js                 # Charity Vault Audit
├── test_G_wallet_swap.js                  # Lost Wallet Recovery
├── WORK/
│   ├── test_B_bonus_tickets_resilient.js  # Bonus-Tip Berechnung
│   ├── test_C_mit_Affiliate.js            # Affiliate Flow
│   ├── test_E_check_claim_test.js         # Claim Flow
│   ├── test_E3_affiliate_claim.js         # Affiliate Claims
│   ├── test_F_check_emergency_claim_block.js  # Emergency Mode
│   └── ...

Test-Framework

Tool Version Zweck
Hardhat Latest Test-Runner + Network
ethers.js v6 Contract Interaction
Chai Latest Assertions

Test-Szenarien

Test A: Basis Ticket-Kauf

Setup: - 3 Spieler (Alice, Bob, Charlie) - Pool 1 (0.50 USDT pro Tip) - Je 5 Tips pro Spieler

Ablauf: 1. USDT Mint + Approve an ReserveVault 2. BuyPermit signieren (EIP-712) 3. Operator submitted buyTicketFor() 4. Ticket-Verifizierung

Erwarteter State:

tickets[0].player == Alice
tickets[0].tipCount == 5
tickets[0].bonusTipCount == 3  (5 Tips → 3 Bonus)
tickets[0].rngNumbers.length == 6

potAcc[1] > 0
feeAcc[dayId] > 0
rolloverAcc[1] > 0
jackpotReserveAcc[1] > 0

ReserveVault.balance == 3 × (5 × 0.50 + gasAmount)


Test B: Merkle Settlement & Claim

Setup: - Tickets von Test A - Off-chain Gewinn-Berechnung simuliert - Merkle Tree mit 2 Gewinnern (Alice: 1.50 USDT, Bob: 0.75 USDT)

Ablauf: 1. Merkle Tree bauen 2. commitMerkleSettlement() aufrufen 3. Alice claimed direkt via PrizeVault.claimMerkle() 4. Bob claimed via ClaimRouter.claimAllFor()

Erwarteter State:

daySettled[dayId] == true
merkleRootByDay[dayId] != 0

claimed[dayId][Alice] == true
claimed[dayId][Bob] == true

Alice.usdtBalance += 1.50 USDT
Bob.usdtBalance += 0.75 USDT - gasCost

potAcc[1] < vorher (Payout abgezogen)

Negativtests: - Double-Claim → revert "Already claimed" - Falscher Proof → revert "Invalid proof" - Falscher Amount → revert "Invalid proof" - Claim für anderen Tag → revert "Invalid proof"


Test C: No-Winner-Day

Setup: - Tickets von Tag X, aber keine Gewinner

Ablauf: 1. Dummy Merkle Leaf bauen (address(0), amount 0) 2. commitMerkleSettlement() mit Zero-Payouts 3. Prüfe dass nächster Tag unblocked ist

Erwarteter State:

daySettled[dayId] == true
merkleRootByDay[dayId] != 0  (Dummy Root)

potAcc[pool] == unchanged (kein Payout)
rolloverAcc[pool] == recalculated

// Nächster Tag funktioniert:
settlement.daySettled(dayId) == true
→ buyTicketFor() für dayId+1 funktioniert


Test D: Emergency Mode

Setup: - Aktive Claims ausstehend - Funds im ReserveVault

Ablauf: 1. SAFE aktiviert Emergency Mode 2. Versuche payPrize → REVERT 3. Versuche payFee → REVERT 4. Versuche commitMerkleSettlement → REVERT (weil payCharity reveriert) 5. SAFE führt ownerWithdraw aus → ERFOLG 6. SAFE deaktiviert Emergency Mode 7. Claims funktionieren wieder

Erwarteter State:

// Emergency an:
reserveVault.emergencyMode() == true
payPrize() → revert
payFee() → revert
ownerWithdraw() → success

// Emergency aus:
reserveVault.emergencyMode() == false
payPrize() → success


Test E: Lost Wallet Recovery

Setup: - Alice hat Gewinn auf Day X - Alice verliert Zugang zu ihrer Wallet - Neue Wallet: Alice_new

Ablauf: 1. Operator ruft adminReassignWallet(Alice, Alice_new, caseId) auf 2. Alice_new claimed mit Original-Proof (wallet = Alice) 3. Payout geht an Alice_new

Erwarteter State:

reassignedTo[Alice] == Alice_new
claimed[dayId][Alice] == true
Alice_new.usdtBalance += amount

Negativtests: - Zweites Reassignment → revert "Already reassigned" - Claim mit paidTo = Alice → revert "Invalid paidTo" (muss Alice_new sein)


Test F: Affiliate Flow

Setup: - Alice wird von Referrer eingeladen - Alice kauft Ticket mit affiliate = Referrer - Settlement mit Affiliate-Provisionen

Ablauf: 1. Alice kauft Ticket → affiliateOf[Alice] = Referrer 2. Settlement mit affiliateRoot + affiliateTotal 3. Referrer claimed Provision via AffiliateVault

Erwarteter State:

affiliateOf[Alice] == Referrer
affiliateMerkleRootByDay[dayId] != 0
claimed[dayId][Referrer] == true (in AffiliateVault)
Referrer.usdtBalance += commission


Failure Mode Matrix

ID Failure Impact Detection Recovery Prevention
F1 Operator Key verloren Hoch Manuelle Erkennung SAFE setzt neuen Operator Key-Management, HSM
F2 VRF Timeout Mittel Monitoring prevrandao Fallback, manueller Seed-Push Chainlink SLA, LINK Balance
F3 Settlement Backend down Hoch Monitoring forceMarkDaySettled, Backend Restart Redundanz, Auto-Restart
F4 USDT Transfer fehlschlägt Hoch TX Revert Debug Transfer, Approval prüfen Pre-Check im Backend
F5 Merkle Root falsch Kritisch Spieler können nicht claimen Nicht rückgängig machbar! Backend-Validierung, Tests
F6 ReserveVault underfunded Kritisch Balance-Monitoring SAFE deposited Funds Invariant-Checks
F7 Nonce Collision Niedrig TX Revert Neues Nonce generieren 16 Bytes Random
F8 Chainlink Sub leer Mittel Chainlink Dashboard LINK nachfüllen Automatisches Monitoring
F9 Gas Spike Mittel Hohe Kosten Warten, Gas Limit anpassen L2 (niedrige Gas-Kosten)
F10 No-Winner Deadlock Mittel Settlement Guard blockiert Dummy Merkle / forceSettle Automatischer Dummy-Merkle

Akzeptanzkriterien

Funktionale Kriterien

# Kriterium Test
AK-1 Ticket-Kauf via EIP-712 funktioniert Test A
AK-2 Settlement committiert atomare Ergebnisse Test B
AK-3 Merkle Claims funktionieren (direkt + batch) Test B
AK-4 Double-Claims werden verhindert Test B (Negativtest)
AK-5 No-Winner-Days blocken nicht den nächsten Tag Test C
AK-6 Emergency Mode blockiert alle Payouts Test D
AK-7 Lost Wallet Recovery funktioniert einmalig Test E
AK-8 Affiliate-Provisionen werden korrekt verteilt Test F

Sicherheitskriterien

# Kriterium Verifikation
SK-1 Nur ReserveVault hält USDT Balance-Check aller Contracts
SK-2 Fee Splits sind immutable Versuch, Constants zu ändern
SK-3 Merkle Roots sind immutable Versuch, Root zu überschreiben
SK-4 Nonces können nicht wiederverwendet werden Replay-Versuch
SK-5 Reentrancy ist nicht möglich Reentrancy-Test
SK-6 Operator kann keine Funds abziehen Access Control Prüfung
SK-7 Settlement ist sequentiell Versuch, Tags zu überspringen

Ökonomische Kriterien

# Kriterium Verifikation
EK-1 Fee Split: 67.5% + 27.5% + 5% = 100% Mathematischer Beweis
EK-2 Rollover-Formel erhält Gesamtmasse Conservation-of-Mass Beweis
EK-3 Jackpot-Reserve wird korrekt akkumuliert/ausgezahlt Multi-Day Test
EK-4 ReserveVault Balance >= alle Verpflichtungen Invariant-Check nach jedem Settlement

Test-Ausführung

# Alle Tests
cd contracts
npx hardhat test

# Einzelner Test
npx hardhat test test/WORK/test_B_bonus_tickets_resilient.js

# Mit Gas-Report
npx hardhat test --gas-reporter

# Auf Arbitrum Sepolia (Fork)
npx hardhat test --network hardhat --fork https://sepolia-rollup.arbitrum.io/rpc

Audit-Hinweise

Kritischster Test

Test B (Merkle Settlement & Claim) ist der wichtigste Test. Er deckt den gesamten Flow von Ticket-Kauf bis Gewinn-Auszahlung ab. Jeder Bug hier ist ein potenzieller Exploit.

Failure Mode F5

Ein falscher Merkle Root (F5) ist der einzige Fehler, der nicht rückgängig gemacht werden kann. Alle anderen Failure Modes haben Recovery-Pfade. Die Backend-Validierung vor dem Settlement-Commit ist daher audit-kritisch.