Everything about how Lucky Spin outcomes are provably fair, and how to verify them yourself.
Welcome. This wiki explains, in full, how the Lucky Spin wheel on maczo.co proves every result is fair โ and how you can verify it yourself, with no need to trust us.
Lucky Spin uses a commit-first design: we lock in a secret server seed and publish its hash before you spin, so we can't change the outcome after seeing your bet. Every result is a pure function of three values you can inspect.
| Value | Who controls it | When you see it |
|---|---|---|
| Server seed | maczo (secret) | its SHA-256 hash is shown before you spin; the raw seed is revealed when you Rotate |
| Client seed | you (editable on the Lucky Spin page) | always |
| Nonce | server counter, +1 per spin | shown for each spin |
Every Verify โ link in your spin history on maczo.co opens the verifier pre-filled with that spin's data and computes the result instantly.
You can verify any spin in three ways. All of them run the same math the server used โ nothing is trusted, everything is recomputed.
From the Lucky Spin page (๐งพ Spin history & verify), each spin shows: - Server Seed Hash (commitment) โ shown before the spin - Client Seed โ yours - Nonce โ the spin number - Server Seed โ appears only after you press Rotate (which reveals it)
To fully reproduce the segment, you also need the weights (segment odds). These are published in
weights.maczo.json and are
embedded automatically in the site's Verify โ links.
On maczo.co open Lucky Spin โ Spin history & verify, press Rotate seed to reveal the server seed, then click Verify โ next to any spin. It opens https://verify.maczo.co/ with everything filled in and computes instantly.
index.html from the repo โ works fully offline).SHA-256(serverSeed) matches the commitmentgit clone https://github.com/maczo-co/lucky-spin
cd lucky-spin
# verify one spin
node verify.js \
--commit <serverSeedHash> \
--server <revealed serverSeed> \
--client <your clientSeed> \
--nonce <n> \
--weights @weights.maczo.json
# confirm the code matches the spec with bundled known-answer vectors
node verify.js --selftest
If either check fails, the result was tampered with. (It won't โ that's the point.)
Lucky Spin's outcome is fully deterministic: the same
(serverSeed, clientSeed, nonce, weights) always produces the same segment. Here is exactly what the
server computes โ the verifier in this repo reproduces it byte-for-byte.
Before your first spin, the site shows:
commitment = SHA-256(serverSeed) // 64 hex chars, lowercase
The raw serverSeed stays secret while it is active. The commitment proves the seed already existed
and cannot be swapped later โ any change would produce a different SHA-256.
For each spin:
digest = HMAC-SHA256(key = serverSeed, message = "clientSeed:nonce:cursor") // cursor starts at 0
serverSeed is the HMAC key (UTF-8 bytes).message is the UTF-8 string clientSeed + : + nonce + : + cursor.The 32-byte digest is read as four 8-byte big-endian unsigned integers (uint64). To keep the odds
exactly as configured we reject the small biased tail:
total = sum(weights)
limit = 2^64 - (2^64 mod total) // largest exact multiple of total
For each 8-byte word in order: if word >= limit, skip it (biased โ would distort odds); otherwise it
is accepted. If all four words are skipped, increment cursor and re-HMAC (astronomically rare).
The accepted word is reduced and walked across the cumulative weights:
pick = acceptedWord mod total
acc = 0
for i in 0..weights.length-1:
acc += weights[i]
if pick < acc: return i // this is the winning segment index
segmentIndex indexes into the wheel's segment list (weight > 0 only, in prize-table order). The prize
shown is segments[segmentIndex].
When you Rotate the seed, the site permanently retires it and reveals the original serverSeed.
You can then check SHA-256(serverSeed) == commitment and re-run every past spin under that seed.
verify.js โ Node.js (uses crypto), CLI + --selftestindex.html โ offline browser (Web Crypto subtle.digest / subtle.sign)test-vectors.json โ known-answer cases both must reproduceBoth use BigInt for exact uint64 arithmetic. See the server's ProvablyFairWheel for the canonical
source.
The wheel result is a pure cryptographic function of a server seed (which we commit to before you spin), your client seed, and a nonce. Because the commitment is published first, we cannot change the outcome afterward. You can recompute every result yourself โ see Verifying Your Spins.
If we revealed it up front, the outcome of every future spin would be predictable. Instead we publish
SHA-256(serverSeed) (the commitment). When you Rotate, the raw seed is revealed so you can verify
the whole history โ and the hash proves we never swapped it.
No. The server seed is fixed and its hash is published before you spin. Changing the seed would break
SHA-256(serverSeed) == commitment. The nonce is a server-owned counter that only ever increases, so
no result can be replayed.
No. The browser verifier (index.html) runs entirely offline โ view source and confirm it makes no
network calls. Or run the Node CLI. Or re-implement the algorithm in any language.
They are the relative frequency of each wheel segment (the paytable). They are published in full in
weights.maczo.json and embedded in the site's verify links, so the exact segment can be reproduced.
It doesn't hide them โ the prize values are printed on the wheel and the weights are published. You need the weights only to map the random number to a segment; the cryptography above guarantees that mapping is honest.
cursor in the formula is almost always 0 โ why is it there?It only advances in the extremely rare case that all four 8-byte words of one HMAC digest fall in the biased tail and are rejected. It guarantees the sampling never runs out of randomness while staying perfectly unbiased.
Double-check you used the revealed server seed (after Rotate), the exact client seed and nonce shown for that spin, and the published weights. If it still fails, contact support with the values โ but the math is deterministic, so a correct input always reproduces the published result.
Yes โ the same HMAC-SHA256 commit-reveal family, with rejection sampling to remove modulo bias. The implementation is open source so anyone can audit it.