feat: playable arcade — rooms, identity, client, deployment
Round length is now bounded: the multiplier follows a hyperbolic curve diverging at 60s, replacing an exponential one where a 275x crash point produced a two-and-a-half minute round. Fixes seed reveal, which silently failed every round because pgx cannot encode a fixed-size byte array as bytea. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
126
README.md
Normal file
126
README.md
Normal file
@@ -0,0 +1,126 @@
|
||||
# Quantum Arcade
|
||||
|
||||
A private physics arcade for one Linux box and the people on your network.
|
||||
Drop-in crash rounds, instant scratch tickets, a real double-entry ledger, and
|
||||
outcomes any player can verify on their own phone.
|
||||
|
||||
## What it is
|
||||
|
||||
Three shared crash games — a rocket fighting gravity, a decaying orbit, and a
|
||||
stacking tower — running on a five-minute heartbeat, plus instant scratch
|
||||
tickets to play between rounds. Identity is a keypair your browser generates;
|
||||
there is no account, no email, and no password.
|
||||
|
||||
Everything runs on one machine: one Go binary with the client embedded,
|
||||
PostgreSQL, and Redis.
|
||||
|
||||
## Running it
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Then open `http://<your-box>:8080` from any phone on the network.
|
||||
|
||||
To play with test funds before Lightning is wired up:
|
||||
|
||||
```bash
|
||||
ARCADE_DEV_FAUCET=1 docker compose up -d
|
||||
```
|
||||
|
||||
The faucet mints through the same ledger path a real deposit uses, so the code
|
||||
under test is the production code. Leave it off otherwise.
|
||||
|
||||
## Development
|
||||
|
||||
```bash
|
||||
docker compose up -d postgres
|
||||
go test ./...
|
||||
go run ./cmd/arcade
|
||||
```
|
||||
|
||||
End-to-end tests need a live server:
|
||||
|
||||
```bash
|
||||
ARCADE_DEV_FAUCET=1 go run ./cmd/arcade &
|
||||
ARCADE_E2E=http://localhost:8080 go test ./cmd/arcade/ -v
|
||||
```
|
||||
|
||||
## How fairness works
|
||||
|
||||
Before betting opens, the server generates a random seed and publishes
|
||||
`SHA-256(seed)`. It is now committed and cannot change its mind.
|
||||
|
||||
The client seed is built from the public keys of everyone who joined the round.
|
||||
The operator does not choose who plays, so it cannot steer the outcome even
|
||||
knowing its own seed.
|
||||
|
||||
The crash point is `HMAC-SHA256(serverSeed, clientSeed || nonce)`, run through
|
||||
the simulation. After settlement the seed is published, and the Verify tab
|
||||
recomputes the whole chain in your browser — it asks the server only for the
|
||||
published values, never for a verdict.
|
||||
|
||||
Scratch tickets use the identical pipeline, and their odds tables are generated
|
||||
from the same data structure that produces outcomes, so the published odds
|
||||
cannot drift from reality. A test asserts observed frequencies and empirical
|
||||
return against the published figures across two million plays.
|
||||
|
||||
## Architecture
|
||||
|
||||
One binary, with enforced internal boundaries:
|
||||
|
||||
| Package | Responsibility |
|
||||
|---|---|
|
||||
| `pkg/fixed` | Q32.32 fixed-point arithmetic; no floats, so results are identical everywhere |
|
||||
| `pkg/sim` | Deterministic RNG and the crash curve |
|
||||
| `pkg/fair` | Commit-reveal protocol and verification proofs |
|
||||
| `pkg/ledger` | Append-only double-entry accounting |
|
||||
| `pkg/scratch` | Scratch tickets and their published odds |
|
||||
| `pkg/identity` | Keypair sign-in via signed challenge |
|
||||
| `pkg/room` | Round lifecycle and live broadcast |
|
||||
|
||||
Nine services on one machine would buy latency and 3am debugging, so this is
|
||||
one process. Modules talk through interfaces only; extracting one into its own
|
||||
service later is a transport change, not a rewrite.
|
||||
|
||||
### Ledger invariants
|
||||
|
||||
Enforced in the application and again by database constraints and triggers:
|
||||
|
||||
- every transaction's postings sum to exactly zero
|
||||
- no account may go negative, except the Lightning bridge, whose negative
|
||||
balance is by definition what is owed to players
|
||||
- rows are never updated or deleted; corrections are compensating entries
|
||||
|
||||
`GET /api/health` sums every account. It must return zero. Anything else means
|
||||
the books are corrupt.
|
||||
|
||||
### Round timing
|
||||
|
||||
The multiplier follows `m(t) = 1/(1 - t/T)²`, which diverges at exactly 60
|
||||
seconds. No round can run longer, however extreme the crash point, and the
|
||||
climb visibly accelerates as it goes — which is where the tension comes from.
|
||||
|
||||
## Status
|
||||
|
||||
Built and tested:
|
||||
|
||||
- fixed-point deterministic core, ledger, commit-reveal fairness
|
||||
- three crash games with live multiplayer rounds
|
||||
- two scratch tickets with verified-honest odds
|
||||
- keypair identity, peer-to-peer transfers, transaction history
|
||||
- in-browser verifier
|
||||
|
||||
Not yet built:
|
||||
|
||||
- **Lightning deposits and withdrawals.** The bridge account and ledger paths
|
||||
exist; the node integration does not. The dev faucet stands in for now.
|
||||
- Tournaments and scheduled events
|
||||
- Operator dashboard
|
||||
|
||||
## Scope
|
||||
|
||||
This is built to run on a private network among people who know each other.
|
||||
It is not hardened for, and should not be exposed to, the public internet.
|
||||
Doing so would make it a public real-money gambling service, which carries
|
||||
licensing, KYC, and AML obligations this codebase does not address.
|
||||
Reference in New Issue
Block a user