Skip to main content

Provably fair maths

A provably fair game lets a player verify, after the fact, that the outcome was decided before they bet and was not changed afterwards. XGENIA ships this as nodes, so you can build the pipeline in a graph and deploy it as a Maths Component.

The scheme is the standard commit/reveal:

  1. The server generates a secret server seed and publishes only its hash (the commitment).
  2. The player contributes a client seed.
  3. The outcome is derived by hashing serverSeed:clientSeed:nonce.
  4. Afterwards the server reveals the seed. Anyone can hash it, compare against the commitment, and re-derive the outcome.
Game serverPlayercommitment = SHA256(serverSeed)published before any betclient seedoutcome = SHA256(seed:client:nonce)round result, with its noncereveal the server seedThe player checks:SHA256(seed) = commitment?re-derived outcome matches?

The nodes​

NodeRole
Server Seed GeneratorA fresh 32-byte server seed, its SHA-256 commitment, and an incrementing nonce.
Verify CommitmentChecks a revealed seed against a published hash.
Calculate RollDerives the outcome: hashes the seeds and nonce, maps into a range.
Verify FairnessRe-derives the outcome hash and compares it to what the game published.
Validate OutcomeJust the range mapping, when you already hold the hash.
Random UUID GeneratorRound and bet identifiers.

The randomness primitives sit alongside them: ISAAC RNG, ISAAC RNG Array, True Random Number Generator, MFAG and SPF.

The exact hashing rules​

Players verifying your game will reproduce these themselves, so they matter:

Commitment — the server seed is 32 random bytes, hex-encoded (64 characters):

committedHash = SHA256(serverSeed)

Outcome — seeds and nonce joined with colons, in this order:

combined = serverSeed + ":" + clientSeed + ":" + nonce
fairHash = SHA256(combined)

Range mapping — the first 8 hex characters, read as a 32-bit integer, reduced into the range (inclusive on both ends):

roll = (parseInt(fairHash.slice(0, 8), 16) % (max - min + 1)) + min

Calculate Roll does all three steps. Validate Outcome does only the last.

note

Seeds are generated locally by the node — in the editor preview and on the server alike. No external service is involved.

Letting players verify​

Publish the commitment before the round and the seed after it. Verification is then two checks, cheap enough to build into the game's own UI:

  • Verify Commitment — the revealed Server Seed plus the Committed Hash you published. Result is true when the seed is the one you committed to.
  • Verify Fairness — seed, client seed, nonce, and the outcome hash you published. isFair is true when the outcome follows from them.

Monitoring​

Three nodes accumulate statistics across rounds — useful in a local test harness or dashboard:

To measure the deployed maths rather than a local graph, use Simulate — it runs on the platform.

Building a slot on top​

For slot maths, the standard pipeline is:

True Random Number Generator → ISAAC RNG → Generate Symbol Weights
→ Reel Strips Generator → Get Paytable → Weighted Reels
→ Check Wins → Calculate Winnings → Spin Calculate → Spin Result
caution

The old SlotMainEngine node is deprecated — a prototyping-only monolith built on Math.random(). It is neither provably fair nor RGS-compatible. Use the pipeline above for anything real.

The reel and column counts must agree across every node in the chain and the visuals. A mismatch shows up as Weighted Reels throwing "Seeds array must contain at least N elements" — and no reels on screen.