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>
127 lines
4.3 KiB
Markdown
127 lines
4.3 KiB
Markdown
# 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.
|