155API

How It Works

The hash chain, the domain-separated draw, and a precise account of what is and isn't proven


Provable fairness on 155.io rests on three ideas: a pre-committed hash chain of seeds, a commit-then-reveal flow per round, and a domain-separated draw that turns a seed into an outcome the same way in every language. Everything a verifier needs is published; nothing about the algorithm is secret.

The pre-committed hash chain

Before any round is played, the platform generates a chain of random seeds where each seed is the hash of the next:

seed[i] = SHA-256( seed[i+1] as its 64-char lowercase hex string, hashed as ASCII text )

Every hash output is a lowercase hex digest, and it is that exact text — not the decoded bytes — that is hashed to form the next link. The per-round commitment uses the same encoding: serverSeedHash = SHA-256(ASCII text of serverSeed).

The chain's root hash is published up front. Round j uses seed[j], and because hashing is one-way, a revealed seed[j] can be hashed forward j times to reproduce the published root, but the root reveals nothing about any future seed.

This is what the chain locks in: every seed is fixed, in its exact position, before any round is played. A revealed seed must hash back to the published root from its stated index, so we cannot reorder seeds, swap one in after seeing where the money went, or mint a new seed after the fact. Every consumed seed ends up either as a played round or a published "burn" — the chain stays contiguous and auditable.

One chain per game, per environment

Each game keeps its own independent chain. A round's reveal names the exact chain root and index it came from, so verification is always unambiguous.

Commit, then reveal

At round open — the commitment

The round publishes serverSeedHash = SHA-256(serverSeed). The seed itself stays secret. Because the seed alone determines the outcome, publishing its fingerprint locks the outcome in before a single bet is placed.

Betting closes

The result is already fixed by the committed seed — nothing about the outcome can change once betting is closed.

At settlement — the reveal

The round publishes the server seed, the chain root + index, and the game-specific result values. Anyone can now recompute the outcome and confirm the seed hashes back to the root.

From seed to outcome

Every draw is a domain-separated value derived from the seed:

message = "<beacon>:<label>:<index>"     // beacon may be empty — the leading ":" is kept
draw    = first 13 hex chars of HMAC-SHA256( key = hex-decoded server-seed bytes,
                                             message as UTF-8 )
r       = int(draw, 16) / 2^52           // a uniform number in [0, 1)

beacon is the round's published public-randomness value, used exactly as it appears in the reveal; rounds without one publish an empty beacon and the message keeps its leading colon — a crash draw's message is literally :crash:0. index is the plain decimal number (0, not zero-padded). Note the deliberate asymmetry: the chain and the commitment hash the seed's ASCII hex text, while the HMAC key is the hex-decoded seed bytes — this is the single detail independent implementations most often get wrong.

The label names which decision is being made (crash, outcome, order, …), so revealing one draw never leaks another, and every draw is provably uniform and unpredictable given a committed seed. From there each game family maps r to its result:

FamilyMapping
Crashr → the crash multiplier via the published house-edge curve.
Counting (CCTV)r → the winning market, drawn against the published probabilities; the boundaries are then placed around the camera-measured count.
Classicr → the winner (a weighted pick for the weighted games) or the full finishing order (a uniform pick over the possible orderings for the podium games).

The exact formulas, cross-language test vectors, and reference code all live in the open-source verifier repository.

What is (and isn't) proven

Provable fairness makes a precise guarantee. It's worth being exact about its edges.

It proves — cryptographically, per round:

  • The outcome was fixed by a seed committed before betting closed, and reproduces exactly from that seed.
  • The seed is a genuine, in-order link in a chain whose root was published in advance.
  • For weighted games, the draw used the published probabilities. A "40% option" occupies 40% of the draw space and, because the draw is provably uniform, hits 40% of the time — verifiably per round (we can't rig the rounds the money is on) and statistically over many rounds (collect the reveals and the frequencies converge).

It does not, by itself, prove:

  • The payout side of RTP. Return-to-player is probability × payout. Provable fairness rigorously covers the probability; the payout multiplier is a separate published figure in the odds table — an auditable number, but not one derived from the seed. So "is the RTP really 90%?" resolves to "is the probability what's published?" (proven and enforced here) × "does it pay what the table says?" (read off the payouts).
  • That a published number matches an advertisement. The cryptography proves the game used the odds it published in the reveal. Confirming those match what the board advertised is a quick comparison a player makes — and the game shows the same odds it bets against.

In short

Provable fairness turns "trust our odds" into "the odds are cryptographically enforced every round and independently checkable." That is the hard, valuable half of fairness — the payout side is a published number that anyone can read.

Independent and open

  • The verifier is open source (marbles-io/155-fairness-verifier) and runs entirely client-side — host your own copy if you prefer.
  • It ships golden test vectors that lock the algorithm across every implementation (the engines, the widget, and the verifier), so a mismatch anywhere fails the build.
  • Nothing about verification depends on 155.io being online or honest at check time — a saved reveal verifies offline, forever.

On this page