DEV Community

Cover image for One nonce, many numbers: how a provably-fair round draws every card (the cursor, explained)
Betkyo Research
Betkyo Research

Posted on Originally published at betkyo.com

One nonce, many numbers: how a provably-fair round draws every card (the cursor, explained)

Originally published on the Betkyo Journal, where every figure is read from the game engine's source.

On this site a round’s randomness is HMAC-SHA256(server seed, “client seed-nonce-cursor”). The nonce counts rounds: it goes up by one each bet, across every original, so no two rounds share it. The cursor counts draws inside a round: it starts at 0 and goes up by one for each random number the game needs. A game that needs one number reads cursor 0 and stops. Roulette, Limbo and the lucky bag do that. Sic Bo reads cursors 0, 1 and 2 for its three dice. Bingo reads 0 to 24 to lay out the card and 25 to 62 for the 38 balls. Blackjack reads one cursor per card in deal order, for as many cards as the hand takes. Each cursor gives an independent uniform number, and each game’s published derivation turns those numbers into a pocket, a card or a symbol. Because the whole stream is fixed the moment the seed pair and nonce are, every card of a round already exists before the first one is turned, and after the server seed is revealed you can recompute all of them.

The fourth input

Every explanation of provably fair gaming, including ours, names three inputs: a server seed the house commits to in advance, a client seed you can set yourself, and a nonce that goes up by one with every bet. Feed them into a keyed hash and out comes the round’s randomness. That description is complete for a coin flip. It is incomplete for almost everything else, because a hash produces one number and most games need several.

The fourth input is the cursor. On this site the message that is hashed is not “client seed-nonce” but “client seed-nonce-cursor”, and the cursor is a counter inside the round: 0 for the first random number the game needs, 1 for the second, and so on. Each cursor value gives a fresh 256-bit digest, unrelated to its neighbours in any way you could exploit, and the engine reads the first eight bytes of each digest as a number between 0 and 1.

Verified in source. _shared/demoLocal.ts: uAt(pair, cursor) returns u64(HMAC-SHA256(serverSeed, ${clientSeed}-${nonce}-${cursor})), described as matching the server’s RandomUtils.generateHash(“clientSeed-nonce-cursor”). _shared/rng.ts: u64 reads the first eight bytes of the digest as hi/2^32 + lo/2^64, “the same single-rounding construction” as the server’s ULong→Double conversion. The nonce is one stream per seed pair across all games: each bet consumes the current nonce and stores nonce + 1.

So a round is not one random number but a numbered list of them, as long as the game needs, and every entry on the list is fixed the instant the seed pair and nonce are. The game does not draw as it goes. It reads down a list that already exists.

What each game reads

The interesting part is that the list is different for every game, and each derivation module states its layout in its header. Here is the cursor stream of every original that draws from the seed pair, taken from the engine source.

Cursor use per round, from each game’s derivation module

GAME CURSORS READ WHAT THEY BECOME
Limbo 0 One number through the 0.99 ÷ (1 − u) curve
Roulette 0 The pocket: floor(u × 37)
Fukubukuro 0 The item, against the bag’s weight table
Sic Bo 0, 1, 2 The three dice
Chinchiro side × 9 + attempt × 3 + die Up to 18: dealer then player, up to three throws of three dice each; a throw that makes a hand ends the side
Koban Flip one per flip Flip i reads cursor i; every face of the ride is fixed when the first coin is bought
Omikuji 0, then 1–3, then 4 onward Fortune tier; the three winning cells on a win; filler glyphs for the rest
Pagoda one per floor The trap tile on each floor: floor(u × tiles)
Video Poker 5, plus one per replaced card One without-replacement sequence over 52 cards; hold everything and no further cursor is read
Casino Hold’em up to 9 player 1, player 2, dealer 1, dealer 2, flop 1–3, turn, river; the turn and river are only read on CALL
Blackjack one per card Infinite shoe, card = floor(u × 52), in deal order: player, dealer up, player, dealer hole, then every further draw
Andar Bahar 0–4, then one per raced card Two boosted buckets, their magnitudes, the joker, then the race until the rank repeats
Hanabi 15 per spin Cell = reel × 3 + row; free spin j reads 15 + 15j onward
Bingo 0–24, then 25–62 The card, five draws per column; then the 38 balls

Keno and Crash are the exceptions: their demo rounds use a per-ticket hash chain rather than the seed-pair nonce, described below. Mines and Plinko are settled by the house service; the mines API notes that the whole layout comes off one nonce.

Verified in source. Header comments in bingo/derive.ts (“Cursor stream: 0..24 — the card … 25..62 — the 38 balls”), hanabi/engine.ts (“base spin = cells 0..14 … free spin j = cells 15+15j .. 29+15j”), blackjack/derive.ts (“card i = floor(u × 52) at cursor i … Deal order (cursor): player1, dealer-up, player2, dealer-hole”), vpoker/derive.ts (“the first five are the deal, replacements continue the same sequence … Hold everything and no further cursor is read”), holdem/derive.ts (“player1, player2, dealer1, dealer2, flop1, flop2, flop3, turn, river. The turn/river cursors are only consumed on CALL”), andar/derive.ts (cursors 0–4 then “5.. one cursor per raced card”), koban/derive.ts (“flip i reads cursor i”), sicbo/derive.ts (“cursors 0/1/2 are the three dice”), roulette/derive.ts (“cursor 0 alone decides the pocket: floor(u × 37)”), fuku/derive.ts (“cursor 0 alone draws the item”), omikuji/derive.ts (cursor 0 tier, 1..3 winning cells, 4.. fillers); demoLocal.ts rollSide (chinchiro cursor = side*9 + attempt*3 + d), trapAt (pagoda: the floor number is the cursor), demoLimboBet (uAt(p, 0)); mines/minesApi.ts (“the whole mine layout comes off this one nonce”).

Three things stand out in that table. First, the games that deal cards read one cursor per card in a fixed order, which is why a blackjack hand can be replayed from its action list: your decision to hit does not create a card, it turns over the next one on a list that was complete before the deal. Second, several games stop reading early. A video poker hand where you hold all five cards never touches cursor 5; a Hold’em fold never reads the turn or river. The numbers were there; the game did not need them. Third, the two boosted buckets in Andar Bahar and the fortune tier in Omikuji are drawn before the cards and cells, at the head of the stream, so the round’s special features are fixed by the same commitment as its ordinary ones.

Drawing without replacement

A uniform number between 0 and 1 is easy to turn into a roulette pocket: multiply by 37 and round down. A deck is harder, because the second card must not be the first card again. The engine handles this with what its comments call a pool hop: keep a list of the cards still in the pool, use the cursor’s number to pick a position in it, and swap the chosen card to the end of the list so the next draw picks from one fewer. Video Poker, Casino Hold’em, Andar Bahar and the Bingo card all draw this way, so no card or number repeats within a round.

Blackjack is the deliberate exception. Its shoe is infinite: every card is floor(u × 52) from its own cursor, and the same card can appear twice in a hand. That is a design choice with a published cost, discussed in the blackjack payout article, and it is exactly the kind of thing the cursor layout tells you that a marketing page would not.

Verified in source. vpoker/derive.ts: “Cards come from ONE without-replacement sequence over the 52-card deck (pool-hop, cursor per draw)”. holdem/derive.ts: “ONE without-replacement pool-hop sequence (cursor per draw)”. bingo/derive.ts: “five pool-hop draws per column” and “the 38 balls, pool-hops over 1..75”. blackjack/derive.ts: “Infinite shoe: card i = floor(u × 52) at cursor i”.

The two exceptions

Keno and Crash do not read the seed-pair nonce at all in the demo engine. Keno uses a per-ticket scheme: the ticket gets its own random hash and a salt, and each of the ten balls is drawn by hashing the previous hash again, taking the leading bits as a number and picking from the shrinking pool of forty. The fairness panel verifies a keno ticket from those two strings rather than from the seed pair. Crash follows the bustabit convention, a chain of hashes where each round’s crash point is computed from its own link with exact integer arithmetic.

Verified in source. demoLocal.ts kenoDraws: “per-bet randomHash chain + salt (verifiable per ticket via the fairness modal) … Does not touch the seed-pair nonce”; the loop hashes HMAC-SHA256(salt, previous hash) ten times, reads the leading 52 bits and picks index floor(norm × remaining) from a 40-number pool with a swap to the end. rng.ts h52 and the crash comment: “bustabit crash point in hundredths: floor((n·e − h)/(e − h)), e = 2^52, n = 100 … Exact BigInt math to match the server”.

Neither exception is weaker than the cursor scheme; both are older and better known. What matters for a player is only that the panel shows which scheme a round used, and that each can be recomputed from the strings it shows.

What it means at the table

  • The round exists before you act. Every card, ball and reel symbol of a round is a function of the seed pair, the nonce and its cursor position. Hitting, calling or holding chooses which entries get read, not what they contain.
  • Your choices cannot change the list, and neither can the house. Once the server seed is committed and the nonce is fixed, no one on either side can alter entry 7 without altering the seed, which would break the fingerprint you were shown.
  • A verifier has to read the same list the same way. Recomputing a bingo round means deriving all 63 cursors in the published order; recomputing a Hold’em fold means deriving 7. The seed panel does this in your browser, and the layouts in the table above are what it follows.
  • A streak is a list, not a mood. Two rounds share nothing but the seed pair; their nonces differ and so every digest differs. This is the same point the hot hand article makes from the other direction.

Three inputs decide the round. The fourth decides how long the round is.

— the cursor, in one line

Note. The cursor layouts above are read from the client-side derivation modules that the browser verifier and the demo engine share. The house service is the paying authority; where a comment in the source says a module mirrors the server, that is quoted as written, not certified by us.

FAQ

What is the cursor in a provably fair round?

A counter inside the round. The message hashed with the server seed is “client seed-nonce-cursor”; cursor 0 gives the first random number the game needs, cursor 1 the second, and so on. A game that needs one number reads cursor 0 only.

How is the cursor different from the nonce?

The nonce counts rounds and goes up by one with each bet across every original. The cursor counts draws within one round and restarts at 0 each round. Together with the seed pair they identify every random number the site ever produces.

How many random numbers does a bingo round use?

Sixty-three: cursors 0 to 24 lay out the 25 cells of the card and cursors 25 to 62 draw the 38 balls, all without replacement within their pools.

Does hitting in blackjack change the next card?

No. Each card is derived from its own cursor in deal order, and the whole list exists once the seed pair and nonce are fixed. Hitting reads the next entry; standing leaves it unread.

Do all games use the cursor scheme?

Keno and Crash use hash chains instead: keno hashes a per-ticket random hash and salt ten times, and crash follows the bustabit chain with exact integer arithmetic. The fairness panel verifies each from the strings it shows.

Sources

  • Betkyo engine source: _shared/demoLocal.ts (uAt, rollSide, trapAt, kenoDraws), _shared/rng.ts (u64, h52, crash point), and the derivation modules for bingo, hanabi, blackjack, vpoker, holdem, andar, koban, sicbo, roulette, fuku and omikuji
  • RFC 2104 — HMAC: Keyed-Hashing for Message Authentication — https://www.rfc-editor.org/rfc/rfc2104
  • Bustabit provably fair scheme (the crash-point construction the engine mirrors)

This article first appeared on the Betkyo Journal: https://betkyo.com/en/blog/one-nonce-many-numbers-what-the-cursor-counts/?utm_source=devto&utm_medium=repost&utm_campaign=journal. Betkyo is a crypto casino with provably fair original games. 18+. Educational content, not betting advice.

Top comments (0)