To verify a roll you need four things: the revealed server seed, the hash of it you were shown before playing, your client seed and the nonce. You hash the server seed and compare it with the commitment, then compute HMAC-SHA256 of client_seed:nonce keyed with the server seed, convert the start of the result into a number, scale it to a roll, and check it against the outcome you were given. This guide walks through each step with real computed values.
The method below is one common convention. Sites differ in the details, so always compare it with the documentation of the game you're checking. All seeds are examples made up for this guide.
What you need before you start
| Item | Example value | Where it comes from |
|---|---|---|
| Commitment (server seed hash) | a5e39de9361cb47dd6ccdf84bb0cbeff1e402575f217b91bb2e560cfe1ac582d | Shown before your first bet — save a copy |
| Revealed server seed | 9f2c4e1a7b3d8e6f0a5c2b9d4e7f1a3c | Shown after you rotate seeds |
| Client seed | lab-reader-01 | Your setting |
| Nonce | 0 | Your bet history |
| Result shown by the game | Roll 77.61 | Your bet history |
If any of these are missing, you can't complete the check. A site that hides one of them is on our red-flags checklist.
Step 1: Check the commitment
Hash the revealed server seed with SHA-256 and compare the result, character by character, with the commitment you saved.
sha256("9f2c4e1a7b3d8e6f0a5c2b9d4e7f1a3c")
= a5e39de9361cb47dd6ccdf84bb0cbeff1e402575f217b91bb2e560cfe1ac582d
commitment
= a5e39de9361cb47dd6ccdf84bb0cbeff1e402575f217b91bb2e560cfe1ac582d ✓ matchThis is the most important step. If it fails, the seed you've been given isn't the one that was committed, and nothing that follows can be trusted.
Be exact about the input. Hashing the same seed with a trailing newline (what you get from some copy-paste or echo commands) gives 2fe7323ff3e99f18…, a completely different hash. If you get a mismatch, check for stray whitespace before drawing conclusions.
Step 2: Build the message
In this method, the message is the client seed, a colon and the nonce:
message = "lab-reader-01:0"Other methods use different separators, put the nonce first, or add a round counter. The message must match the site's format exactly.
Step 3: Compute HMAC-SHA256
HMAC-SHA256 is a keyed hash. Here the key is the server seed and the data is the message.
HMAC-SHA256(key = "9f2c4e1a7b3d8e6f0a5c2b9d4e7f1a3c", data = "lab-reader-01:0")
= c6b1e6abfb75912f63fb7d8e96d037910dd26cd81e57d5e70c7fc855c3fdb59eThe result is 64 hex characters, or 32 bytes. Its first four bytes are c6, b1, e6, ab — in decimal, 198, 177, 230 and 171.
Step 4: Turn hex into a number
Take the first 8 hex characters (those four bytes) and read them as one unsigned 32-bit integer:
0xc6b1e6ab = 3,333,547,691The largest possible value is 2³² − 1 = 4,294,967,295, so this number sits about 77.6% of the way through the range.
Step 5: Scale to a roll
Multiply by 10,000, divide by 2³² (rounding down), then divide by 100:
floor(3,333,547,691 × 10,000 ÷ 4,294,967,296) ÷ 100
= floor(7761.55…) ÷ 100
= 77.61That gives a roll between 0.00 and 99.99. The game showed 77.61, so the roll is verified.
Step 6: Map the roll to an outcome
The roll itself isn't the prize. The game turns it into an outcome with a rule, and that rule is something you check by reading, not by hashing.
Dice example. A fictional dice bet "roll under 49.50" wins on rolls 0.00–49.49. A roll of 77.61 loses. On nonce 1 the same seeds give 18.35, which would win.
Case example. A fictional case publishes this range table:
| Outcome tier | Roll range | Share of range |
|---|---|---|
| Common | 0.00 – 79.99 | 80.00% |
| Uncommon | 80.00 – 95.99 | 16.00% |
| Rare | 96.00 – 99.49 | 3.50% |
| Very rare | 99.50 – 99.99 | 0.50% |
| Total | 0.00 – 99.99 | 100.00% |
Roll 77.61 falls in 0.00–79.99, so the outcome is Common. Two separate checks have happened here: the cryptography confirmed the roll, and the table confirmed what the roll should have paid. The ranges also add up to 100% and their widths match the stated shares — worth checking on any real table, because the proof can't do it for you.
Do the whole thing in Python
import hmac, hashlib
server_seed = "9f2c4e1a7b3d8e6f0a5c2b9d4e7f1a3c"
commitment = "a5e39de9361cb47dd6ccdf84bb0cbeff1e402575f217b91bb2e560cfe1ac582d"
client_seed, nonce = "lab-reader-01", 0
assert hashlib.sha256(server_seed.encode()).hexdigest() == commitment
h = hmac.new(server_seed.encode(), f"{client_seed}:{nonce}".encode(), hashlib.sha256).hexdigest()
n = int(h[:8], 16)
roll = (n * 10000 >> 32) / 100
print(h) # c6b1e6abfb75912f63fb7d8e96d037910dd26cd81e57d5e70c7fc855c3fdb59e
print(n) # 3333547691
print(roll) # 77.61Or skip the code and paste the same values into our browser verifier, which uses WebCrypto and gives identical output.
When it doesn't match: a troubleshooting table
| Symptom | Likely cause | What to do |
|---|---|---|
| Commitment hash doesn't match | Wrong seed, stray whitespace, or the seed really was changed | Re-copy carefully; if it still fails, raise it with the site and stop playing |
| Hash matches, roll differs slightly | Different scaling or rounding rule | Read the site's formula for converting bytes to a roll |
| Hash matches, roll completely different | Different message format or byte count | Check separator, order and how many bytes are read |
| Off by one bet | Nonce starts at 1, not 0 | Try the adjacent nonce |
| Client seed has a space in it | Copy-paste error | lab-reader-01 :0 gives 38.16, not 77.61 — exactness matters |
What you've proved
A successful check shows the roll was produced from a seed fixed before your bet, using your client seed, by the stated formula. It doesn't show the odds are good, that the payout table is generous, or that the site will honour withdrawals; for that distinction, see what provably fair means and what it doesn't prove. The roll is checked; the house edge is still there.
FAQ
My result doesn't match. Does that mean the site cheated?
Not necessarily. The most common cause is using a different method from the site's: message format, byte count or scaling. Check the site's documentation first, then check for stray spaces or the wrong nonce. If the commitment hash itself doesn't match, that is far more serious.
Why use only the first 8 hex characters?
Eight hex characters are 32 bits, which gives over four billion possible values — plenty of precision for a roll to two decimal places. Some methods read more bytes; follow the documentation of the game you're checking.
Is HMAC-SHA256 the only method used?
No. It is common, but some systems use plain SHA-256 of a combined string, SHA-512, or different byte-to-number rules. The verification steps are the same in spirit; the details must match the site's documentation.
Can I verify without code?
Yes. Our browser verifier runs the method described here using WebCrypto, so nothing leaves your device. For other methods you'd need the site's own formula.