No description
Find a file
2026-06-14 16:46:26 +02:00
.forgejo/workflows ci: pin each commit to IPFS and republish IPNS for random.ziemlichoptimal.de 2026-06-13 22:54:45 +02:00
vendor vendor/: externe Libs lokal (simple-peer, qrcodejs, jsQR) 2026-06-14 16:14:42 +02:00
app.js QR-Scan-Fix: höhere Auflösung, Throttle, dontInvert; QR dynamische Größe; Antwort-Rohdaten; Kopierfunktion; CDN → vendor 2026-06-14 16:15:15 +02:00
crypto.js Initial commit: P2P coin flip with QR signaling and commit-reveal protocol 2026-06-13 21:18:08 +02:00
index.html Seite scrollbar + zoombar + Footer mit © neunzig.wtf und Commitstand 2026-06-14 16:46:25 +02:00
p2p.js Fix: _pendingPeer nach Promote nullen, sonst killt _createPendingPeer die Live-Connection nach 300ms 2026-06-14 16:23:44 +02:00
protocol.js Selective Abort: 12s-Timeout in Commit/Reveal-Phase, ABORTED-State, Betrüger-Erkennung, protocol_start-Resilienz 2026-06-13 22:18:14 +02:00
README.md README: dokumentiere Full Mesh + Selective Abort 2026-06-13 22:17:53 +02:00
style.css Seite scrollbar + zoombar + Footer mit © neunzig.wtf und Commitstand 2026-06-14 16:33:52 +02:00
test-protocol.html [IMP] add crypto protocol test 2026-06-14 16:46:26 +02:00

randomp2p

Vertrauensloser Multiplayer-Münzwurf P2P im Browser, keine Server, kein Trust.

Zwei bis N Spieler verbinden sich via WebRTC (simple-peer) und führen ein kryptografisches Commit-Reveal-Protokoll aus, um einen garantiert fairen Münzwurf zu erhalten. Solange ein Teilnehmer ehrlich ist, ist das Ergebnis zufällig und manipulationssicher.

Features

  • P2P Kein Server, kein Account, keine zentrale Instanz
  • Trustless Commit-Reveal-Protokoll mit SHA-256-Bindung, jeder verifiziert
  • QR-Bootstrapping SDP-Offers/Answers werden als QR-Codes ausgetauscht
  • Multiplayer (2n) Beliebig viele Teilnehmer in einer Runde
  • Full Mesh Alle Peers verbinden sich direkt per WebRTC (Host relayt nur initial SDP)
  • Selective Abort Timeout erkennt Verweigerer, alle Teilnehmer sehen denselben Schuldigen
  • Coin-Animation 3D-Münzwurf via CSS
  • 100% Vanilla JS Keine Build-Tools, kein npm, keine Abhängigkeiten außer CDN-Libs

Quick Start

git clone <repo-url>
cd randomp2p
python3 -m http.server 8080

Dann auf zwei (oder mehr) Geräten http://localhost:8080 öffnen.

WebRTC + Kamera (getUserMedia) + Web Crypto API brauchen einen sicheren Kontext. Lokal via localhost funktioniert das. Für andere Geräte im selben Netzwerk die lokale IP verwenden.

Spielanleitung

Host

  1. "Raum erstellen" QR-Code mit SDP Offer wird angezeigt
  2. "Antwort-QR scannen" Kamera startet, scannt die Antwort-QRs der Beitreter
  3. Jeder neue Spieler erscheint in der Liste
  4. Host broadcastet roster mit allen Peer-IDs → Peers bauen direktes Mesh untereinander auf
  5. Sobald alle Peers mesh_ready gemeldet haben → "Münzwurf starten" wird aktiv
  6. "Münzwurf starten" Übergang in den Spiel-Screen
  7. "Münzwurf starten" Protokoll beginnt (Commit → Reveal → Ergebnis)

Beitreter

  1. "Raum beitreten" Kamera startet
  2. QR des Hosts scannen
  3. Antwort-QR zeigen Host scannt diesen QR
  4. Verbindung steht, empfange roster mit anderen Peer-IDs
  5. Baue direkte WebRTC-Verbindung zu jedem anderen Peer auf (kleinere PeerId initiiert → kein Glare)
  6. Sobald alle direkten Verbindungen stehen → automatisch mesh_ready an Host
  7. Host startet Protokoll → alle laufen automatisch durch (Commit → Reveal → Ergebnis)

Protokoll-Phasen

Phase Beschreibung
Commit Jeder Spieler generiert 32 Zufallsbytes und sendet den SHA-256-Hash an alle
Warten Sammle Commits aller Teilnehmer (Timeout 12s)
Reveal Jeder sendet die rohen Zufallsbytes an alle
Warten Sammle Reveals aller Teilnehmer (Timeout 12s)
Verify Jeder prüft: SHA-256(received) == stored_commit
Result XOR aller Secrets → Parity (0=Kopf, 1=Zahl)

Bei Timeout in einer Wartephase: Selective Abort ABORTED-Zustand, fehlende Peer-IDs werden angezeigt. Alle Teilnehmer sehen dieselben fehlenden Peers (Mesh → jeder sieht, wer nicht sendet).

Architektur

randomp2p/
├── index.html       5 Screens + CDN-Libs
├── style.css        Dark-Theme, Coin-Animation
├── crypto.js        SHA-256, Zufallsbytes, XOR-Combine
├── p2p.js           MeshNet: simple-peer, QR-Signaling, Datenkanal
├── protocol.js      Commit-Reveal State Machine
└── app.js           UI-Routing, Kamera/QR-Scan, Verkabelung

Abhängigkeiten (CDN)

Library Zweck
simple-peer WebRTC-Datenkanäle
qrcodejs QR-Code-Generierung
jsQR QR-Code-Scanning per Kamera

Netzwerk-Topologie

Phase 1 Stern (QR-Bootstrapping): Host tauscht via QR-Codes SDP Offers/Answers mit jedem Peer aus → jeder Peer ist direkt mit dem Host verbunden.

Phase 2 Mesh (relayed Signaling): Sobald ein Peer beitritt, broadcastet der Host ein roster mit allen Peer-IDs an das gesamte Netzwerk. Jeder Peer baut zu jedem anderen Peer eine direkte WebRTC-Verbindung auf:

  • Initiator ist immer der Peer mit der kleineren PeerId (verhindert Glare / Race-Conditions)
  • SDP Offers/Answers werden über den Host als Relay ausgetauscht (signal_relay/signal_relayed via DataChannel)
  • Nach erfolgreichem Verbindungsaufbau sendet jeder Peer mesh_ready an den Host

Ergebnis: Volles Mesh jeder Peer ist mit jedem direkt verbunden. Broadcasts erreichen alle Teilnehmer in einem Hop. expectedPeers (für Timeout/Abort) ist auf allen identisch.

Host A ──QR── Peer B      Host A ──star── Peer B
Host A ──QR── Peer C  →   Host A ──star── Peer C
                           Peer B ──mesh── Peer C  (direkt, kein Relay)

Signal-Relay-Detail

B (Initiator, peerId kleiner)        Host A              C (Responder)
  │                                    │                    │
  ├─ signal_relay(to:C, offer) ──────→│                    │
  │                                    ├─ signal_relayed(offer) ──→│
  │                                    │                    ├─ SimplePeer(non-init)
  │                                    │                    ├─ peer.signal(offer)
  │                                    │                    ├─ answer signal
  │                                    │←─ signal_relay(to:B, answer) ─┤
  │←── signal_relayed(answer) ────────┤                    │
  ├─ peer.signal(answer)              │                    │
  ├─ BC direkt verbunden!            │                    ├─ BC direkt verbunden!
  │                                    │                    │
  ├─ mesh_ready ────────────────────→│←────────────────── mesh_ready ─┤
  │                                    │ readyPeers={B,C} → Button enabled

Warum ist das fair?

Das Commit-Reveal-Protokoll garantiert:

  1. Keine späte Manipulation: Der SHA-256-Commit bindet jeden Spieler vor dem Reveal an sein Secret (Preimage-Resistenz)
  2. Keine Absprache nötig: Solange ein Teilnehmer ehrlich Zufallsbytes beisteuert, ist das XOR-Ergebnis zufällig
  3. Volle Transparenz: Jeder Teilnehmer rechnet lokal alle Prüfungen und das Endergebnis

Mathematisch: result = S_1 ⊕ S_2 ⊕ ... ⊕ S_n. Wenn S_k echt zufällig und vor S_1..S_{k-1}, S_{k+1}..S_n festgelegt wurde, ist result zufällig unabhängig von allen anderen Secrets.

IPFS-Deployment

ipfs add -r .
# → CID notieren, über IPFS-Gateway aufrufbar
# → Gateway-URL als QR-Code in die App einbauen

Ausblick / TODOs

  • Volles Mesh: Peers verbinden sich direkt via relayed signaling
  • Selective Abort: Timeout bei Verweigerern
  • Reconnect bei Verbindungsabbruch
  • Raum-Code als Alternative zum QR-Scan
  • TURN-Server-Konfiguration für NAT-Traversal
  • PWA-Manifest + ServiceWorker für IPFS-Distribution

Lizenz

MIT