Contents
Provably Fair is a verification protocol around deterministic random-output generation: once the required inputs are revealed, you can repeat the declared calculations and compare the result with the game history. It is not a promise of profit. The Provably Fair introduction explains the concept; this guide follows the technical chain from commitment hash to HMAC, bytes, a number and a game-specific result.
Two checks matter. Commitment verification establishes whether the revealed server seed matches a previously saved hash. Result replay establishes whether the historical round's inputs produce the recorded outcome. The second check requires the exact provider protocol, its version and its output-mapping rules.
How RNG differs from Provably Fair
A random number generator produces random or pseudorandom values; Provably Fair makes a declared method of producing an outcome verifiable. They are complementary concepts, not competing alternatives.
| Mechanism | What it does | What it does not establish by itself |
|---|---|---|
| Random number generator (RNG) | A general term for mechanisms that produce random values | Source quality, unpredictability or the ability to verify a particular result |
| Pseudorandom number generator (PRNG) | Deterministically expands an initial state into a sequence | Cryptographic security: not every PRNG is secure, but not every PRNG is weak |
| Cryptographically secure generator | Is designed to resist prediction when its initial-state and implementation requirements are met | Correct implementation or sufficient unpredictability of the initial secret |
| HMAC-based stream | Produces reproducible bytes from a key and messages | Compliance with HMAC_DRBG merely because HMAC is called repeatedly |
| Provably Fair | Connects a prior commitment, disclosure and outcome verification | Unbiased input selection, payment or protection against every attack |
HMAC_DRBG is a specific construction with defined state, instantiation, generation and reseeding operations. An arbitrary HMAC loop is not enough to establish compliance with NIST SP 800-90A Rev. 1.
Security also depends on the source of the server seed. It needs sufficient entropy—unpredictability in the underlying secret—and must remain secret while active. A neat-looking distribution does not replace either condition. For example, UKGC RTS 7 addresses both statistical quality and unpredictability. That is a jurisdiction-specific standard, not evidence that any operator mentioned here has been certified.
Why use both a hash and HMAC?
The hash commits to the string that will later be revealed; HMAC uses a secret key to produce reproducible bytes. These are different operations, and neither is decryption.
In our example, the commitment hash is SHA-256 of the server seed. Save it before betting, then calculate SHA-256 again after the seed is revealed and compare the complete hashes.
HMAC-SHA256 takes a key and a separate message. Here, the server seed is the key, while the message combines the client seed, nonce and HMAC block counter. The keyed construction is defined in RFC 2104.
A hash obtained only after a bet does not establish that the commitment existed beforehand. Saving it at the right time is part of verification.
Which inputs generate the byte stream?
This particular scheme needs the server seed, client seed, nonce, HMAC block counter and exact payload serialization. If the game starts reading at a particular position, you also need its byte-stream cursor.
The documented Stake implementation uses HMAC-SHA256 with the server seed as the key and a message shaped as clientSeed:nonce:block. This supports the byte-generation stages below; it is not an audit of the deployed casino or confirmation of current Stake Dice settlement rules.
| Input | Purpose | Our synthetic example |
|---|---|---|
| Server seed | Secret HMAC key; enables replay after disclosure | tg-rng-demo-server |
| Client seed | Additional input in the message | reader-example |
| Nonce | Distinguishes calculations in a sequence | 7 |
| HMAC block counter | Selects a 32-byte block | 0 |
| Byte-stream cursor | Determines the block and the offset within it | 0, the beginning of the first block |
| Exact payload serialization | Specifies encoding, separators and field order | Literal UTF-8 string reader-example:7:0 |
| Commitment hash | Commits to the server seed for later comparison | The full SHA-256 value shown below |
One HMAC-SHA256 block contains 32 bytes, enough for eight non-overlapping four-byte values. In the documented implementation:
currentRound = floor(cursor / 32)
withinBlockOffset = cursor % 32
Here, currentRound selects the block and withinBlockOffset selects a byte inside it. Cursor 32 therefore means block 1, offset 0—not nonce 32.
Encoding matters. The literal UTF-8 string ab is different from the single byte decoded from hexadecimal ab. Spaces, newlines, separators and field order also affect the calculation.
Do not assume every provider uses this scheme. BGaming describes a Result + Secret protocol, while Gamba converts mnemonic strings to binary data. Neither should be replaced with our literal UTF-8 serialization.
An exact example: from a server seed to 6.73
Our synthetic inputs produce four initial HMAC bytes that convert to 0.06736633484251797; under our explicitly declared teaching Dice rule, that becomes 6.73. This is not a casino wager or a verified current Stake Dice result.
The teaching rule is:
floor(u * 10000) / 100
It defines 10000 bins with displayed values from 0.00 to 99.99. The provider documentation supports the byte-stream stages and four-byte conversion, not this illustrative settlement rule.
Complete inputs and expected outputs
Use the strings exactly as shown, without quotation marks or trailing whitespace. The server seed is a literal UTF-8 string, not decoded hexadecimal.
Server seed: tg-rng-demo-server
Client seed: reader-example
Nonce: 7
HMAC block counter: 0
Message: reader-example:7:0
SHA-256 of the server seed:
af44fbb9b3710c9bb38aec8a7ac0b25140f34033376c7bb894626d34cfa5a026
HMAC-SHA256:
113eeb8d4b56244a2674b08b1332e4baf7552c927271d397b5963d868cd0b0bb
The first four decimal bytes are 17, 62, 235, 141. Reading them as a big-endian integer gives:
17 × 256³ + 62 × 256² + 235 × 256 + 141 = 289336205
The Stake conversion documentation describes combining four bytes as fractional terms:
u = 17/256 + 62/256² + 235/256³ + 141/256⁴
= 289336205 / 4294967296
= 0.06736633484251797
u × 10000 = 673.6633484251797
floor(u × 10000) / 100 = 6.73
Our rule rounds down when selecting the teaching bin; it does not simply round a number to two decimal places. Although u lies in [0, 1), it is on a finite grid with spacing 1 / 4294967296, not a continuous scale. That finite grid and the chosen bin boundaries are part of the mapping.
Browser code to reproduce the calculation
The following executable JavaScript calculates the commitment, HMAC, four-byte fraction and teaching roll, then reproduces the 10000-output sample. Run it in a modern browser console on an HTTPS page or localhost where Web Crypto is available.
(async () => {
const encoder = new TextEncoder();
const serverSeed = "tg-rng-demo-server";
const clientSeed = "reader-example";
const nonce = 7;
const blockIndex = 0;
function toHex(bytes) {
return Array.from(bytes, byte =>
byte.toString(16).padStart(2, "0")
).join("");
}
// Hash the literal UTF-8 server seed.
const commitmentBytes = new Uint8Array(
await crypto.subtle.digest(
"SHA-256",
encoder.encode(serverSeed)
)
);
// The server seed is the HMAC key, not part of the message.
const key = await crypto.subtle.importKey(
"raw",
encoder.encode(serverSeed),
{ name: "HMAC", hash: "SHA-256" },
false,
["sign"]
);
async function calculate(counter, block) {
const message = `${clientSeed}:${counter}:${block}`;
const bytes = new Uint8Array(
await crypto.subtle.sign(
"HMAC",
key,
encoder.encode(message)
)
);
// Interpret the first four bytes in big-endian order.
const first4 = Array.from(bytes.slice(0, 4));
const integer =
first4[0] * 256 ** 3 +
first4[1] * 256 ** 2 +
first4[2] * 256 +
first4[3];
const u = first4.reduce(
(sum, byte, index) => sum + byte / 256 ** (index + 1),
0
);
// Our teaching rule, not a verified casino settlement rule.
const teachingRoll = Math.floor(u * 10000) / 100;
return {
message,
hmac: toHex(bytes),
first4,
integer,
u,
scaled: u * 10000,
teachingRoll
};
}
console.log("Commitment hash:", toHex(commitmentBytes));
console.log("Teaching example:", await calculate(nonce, blockIndex));
// Ten bins: [0, 0.1), ..., [0.9, 1).
const counts = Array(10).fill(0);
for (let counter = 0; counter < 10000; counter++) {
const { u } = await calculate(counter, 0);
counts[Math.floor(u * 10)]++;
}
console.log("Counts for nonces 0–9999:", counts);
// Enumerate all equally likely inputs in the 8-bit toy model.
const remainderCounts = Array(6).fill(0);
const acceptedCounts = Array(6).fill(0);
for (let value = 0; value < 256; value++) {
remainderCounts[value % 6]++;
if (value < 252) acceptedCounts[value % 6]++;
}
console.log("Remainder counts:", remainderCounts);
console.log("Counts after rejection:", acceptedCounts);
})();
The first calculation should return the full hash and HMAC shown above, bytes [17, 62, 235, 141], integer 289336205, fraction 0.06736633484251797 and teaching roll 6.73. The sample should return [977, 1012, 1037, 1027, 998, 1026, 973, 969, 1006, 975].
Changing the nonce to 8 changes the message to reader-example:8:0 and produces a different HMAC. You can perform that calculation because this teaching server seed is already known. It does not demonstrate prediction of future rounds with an unknown active server seed.
Why the same bytes can produce different game results
Bytes become a game result only after the game's mapping rules are applied. Matching the HMAC is insufficient if the mapping, byte-consumption order or payout parameters differ.
| Game type | What result replay must reconstruct | Why there is no universal formula |
|---|---|---|
| Dice | Discrete-bin selection and comparison with the player's prediction | Bin counts and rounding can differ |
| Crash | A nonlinear conversion into a multiplier | Distribution tails, house-edge parameters and limits matter |
| Card game | A card index or a deck shuffle | Independent index selection differs from drawing without replacement |
| Mines | A set of unique positions | Duplicate handling and selection order must match |
| Plinko | A sequence of directions and the payout table | A path alone does not determine payment without the game settings |
The Stake conversion documentation describes different ways to consume values, including selection without replacement. This does not mean all card games require unique cards, or all providers use the same HMAC protocol.
For an actual round, obtain the protocol version, rounding rules and payout parameters. Current Stake Dice settlement is not verified by this example: 6.73 belongs only to our stated teaching model.
What frequency tests can—and cannot—show
Frequency tests can reveal some mapping errors, but they cannot establish generator security. A simple remainder example illustrates bias, while our synthetic sample illustrates the limits of a nearly flat distribution.
Assume each 8-bit input from 0 through 255 is equally likely. Taking its remainder modulo 6 gives:
| Residue | Number of source values |
|---|---|
| 0 | 43 |
| 1 | 43 |
| 2 | 43 |
| 3 | 43 |
| 4 | 42 |
| 5 | 42 |
The imbalance occurs because 256 is not divisible by 6. Rejecting values 252–255 and drawing again leaves 252 accepted values, with 42 for each residue. This equality depends on the stated uniform-source assumption and correct redrawing. It is a toy example, not an allegation about a provider.
A reproducible sample of 10000 outputs
Our sample produces similar counts across ten bins, but these are independently generated synthetic outputs, not casino measurements. It uses the same literal seeds, block 0, nonces 0 through 9999 inclusive and the first four bytes of each HMAC.
The bin index is floor(u * 10). The browser code above reproduces the entire sample.
Interval for u | Count |
|---|---|
[0.0, 0.1) | 977 |
[0.1, 0.2) | 1012 |
[0.2, 0.3) | 1037 |
[0.3, 0.4) | 1027 |
[0.4, 0.5) | 998 |
[0.5, 0.6) | 1026 |
[0.6, 0.7) | 973 |
[0.7, 0.8) | 969 |
[0.8, 0.9) | 1006 |
[0.9, 1.0) | 975 |
The equal-probability reference is 1000 outputs per bin. Counts close to that reference do not establish secret entropy, implementation security, real-game RTP, future frequencies or profit. A known deterministic sequence can also look well balanced.
How to verify a historical round
Save the commitment before betting, then check the revealed seed and replay the result separately after the protocol's documented disclosure process. A green tool status cannot replace either the timing evidence or provider-specific reconstruction.
- Save the commitment hash before the bet. Record when it was published and which server seed it identifies.
- Save the calculation details. Keep the protocol and version, client seed, nonce, byte-stream cursor or block counter, game settings and payout parameters.
- Wait for documented seed retirement and revelation. Select an actual historical round associated with that seed pair.
- Check the complete commitment hash. Use the provider's exact payload serialization and specified hash function.
- Replay the result. Reproduce byte generation, byte consumption, mapping and rounding for that game version.
- Compare the history and settlement. Check the outcome, prediction, stake, payout settings and recorded payment—not just one displayed number.
Gamba's documentation likewise distinguishes commitment verification from game-result verification. Its mnemonic-to-binary conversion remains provider-specific and is not interchangeable with our UTF-8 example.
Gamba documentation showing separate commitment verification and game-result verification
Two verification stages; Gamba's protocol must not be replaced with our teaching scheme.
The general Provably Fair verification guide provides a broader workflow. The technical requirement here is agreement across the full calculation chain, not the name of a button.
What to check when results disagree
First check the inputs and mapping rules: a mismatch is not, by itself, proof of fraud.
- Confirm that both seeds belong to the selected historical round.
- Check for a nonce that is off by one.
- Check for a missing block counter or incorrect initial byte-stream cursor.
- Distinguish literal UTF-8 strings from decoded hexadecimal bytes.
- Match separators, field order, spaces and newlines exactly.
- Use the correct protocol version, mapping and rounding.
- Match payout parameters to the saved history.
If the discrepancy remains unexplained, preserve the inputs, history, calculations and screenshots, then ask the provider for an explanation. Do not send active secrets or a wallet recovery phrase.
What the ToolsGambling inline verifier checks
The inline verifier can check whether SHA-256 of a revealed literal seed matches the supplied commitment hash. Its game numbers use a different protocol: it does not reproduce our teaching roll of 6.73 or establish compatibility with current Stake or Spribe games.
To reproduce the local screenshot, fill in these four fields:
| Interface label | Value to enter |
|---|---|
SERVER SEED HASH (PRE-GAME) | af44fbb9b3710c9bb38aec8a7ac0b25140f34033376c7bb894626d34cfa5a026 |
SERVER SEED (REVEALED) | tg-rng-demo-server |
CLIENT SEED | reader-example |
NONCE | 7 |
Under HMAC ALGORITHM, select HMAC-SHA256, then click Verify Round. Do not append :0 to the client seed; this interface has no separate block-counter field.
Inline verifier fields filled with the synthetic commitment hash, server seed, client seed and nonce 7
Synthetic inputs on localhost, not a production page or casino wager.
The brand names beside the algorithm controls are interface hints, not verified compatibility claims. The interface's “Universal” wording and generic-formula explanation do not establish provider-specific result replay.
Why the tool displays 36.03 instead of 6.73
The tool signs reader-example:7, without the block suffix :0, and applies a different mapping. The commitment can therefore match while both the HMAC and displayed Dice result differ from our byte-stream example.
For the inline verifier:
Message:
reader-example:7
HMAC-SHA256:
164923c03bdddda005a0f12178e6b92855e8368c9b77a85d9db3ff63f4187df8
Its Dice calculation interprets the first ten hexadecimal characters as an integer, takes the remainder modulo 1000000 and divides by 10000. That produces 36.0251, which the interface displays as 36.03. It is not our first-four-byte, 10000-bin teaching rule.
Local verification result showing PASS, the full computed SHA-256 hash, inline HMAC and Dice display of 36.03
The commitment matches; the Dice display belongs only to this tool's calculation.
The visible PASS status and green badge establish only that the revealed string matches the supplied commitment. Full verification still requires evidence that the commitment was saved before the bet, followed by provider-specific result replay. The label VERIFIED FAIR does not expand that conclusion.
Commitment verification uses SHA-256 in both HMAC modes. Selecting HMAC-SHA512 changes the HMAC calculation, not the commitment hash function.
The full Provably Fair tool can also serve as a SHA-256 commitment-check helper for literal seeds. It uses HMAC-SHA512 over client:nonce and its own first-four-byte mapping, so it is neither a universal result-replay tool nor a reproduction of our block-0 example.
What verification does not prove about security or RTP
A matching commitment and replayed result support a particular calculation chain; they do not guarantee profit, withdrawals or security of the whole system. RTP is a separate question from byte generation.
Consider a conditional model with win probability 0.5, a gross winning payout of 1.98 times the stake—including the returned stake—and zero payout on a loss:
RTP = 0.5 × 1.98 = 99%
House edge = 1%
For total betting turnover of 1000:
expected gross payout = 990
expected net result = −10
These are expectations under the stated model, not an actual game's RTP or a session forecast. Output mapping and payout rules are distinct parts of the calculation.
A hash check alone does not establish that:
- the server seed was selected without preselection;
- its source had sufficient unpredictability;
- inconvenient rounds were not aborted;
- game and payout rules were implemented correctly;
- withdrawals will be paid;
- the system passed an independent security certification.
Independently choosing a client seed after the server commitment can reduce a specific preselection risk: tailoring the server seed to an already known client input. That conclusion depends on the protocol and its assumptions; it is not protection against every attack.
An active server seed can leak and still match its commitment. The matching hash remains valid even though secrecy has been lost. Rotating seeds after an arbitrary fixed number of bets does not eliminate all risks either. Secret generation, input-selection order, retirement and implementation all matter.
Finally, distrust “predictors” promising future outcomes from a public commitment hash alone. Our reproducible example works because the teaching secret is known; it does not recover an unknown seed from its hash. For real verification, use actual round history and the official protocol for the specific game version—without wallet recovery secrets or promises of profit.
Frequently Asked Questions
Not in the HMAC protocol described here: you need the server seed to reconstruct the byte stream. Before revelation, save the commitment hash and round details. Other protocols may use a different verification process, so follow the specific game's documentation.
These are separate checks. Comparing the full hash establishes whether the revealed string matches the saved commitment. Comparing the roll checks result replay under the game's rules. Many different inputs can produce the same short displayed result.
Reconstruct every required block and the exact byte-stream cursor according to the game's protocol. Each HMAC-SHA256 block in this example contains 32 bytes, enough for eight non-overlapping four-byte values. The byte-stream cursor and HMAC block counter are not interchangeable.
No. It shows frequencies in selected intervals for particular inputs. It does not establish server-seed unpredictability, implementation security, real-game RTP or future frequencies.
No. The verification described here uses gaming seeds, hashes and round records without blockchain. A wallet recovery phrase is not a gaming seed: never enter it into a fairness tool or share it for verification.








