ProbablyFair
HomeDevelopmentSpecAPI Reference
HomeDevelopmentSpecAPI Reference
Github
  1. development
  • development
    • overview
    • sdk
  • ProbablyFair Verifiability Layer API
    • Bets
      • Create a new bet commitment
    • Roots
      • Get the latest Merkle root
    • Proofs
      • Get Merkle proof for a specific bet
    • Settlement
      • Get the full settlement transcript for a bet
    • Anchors
      • Publish an on-chain or external anchor of a Merkle root
  • spec
    • PF-VL-1.0
    • Annex A
    • Vector Generation
  1. development

overview

0) high-level guarantees#

for every bet/round:
RNG integrity: which RNG module/version was used (hash of code + params), and its inputs (seeds, nonces, external beacons) are fixed and public.
Game definition integrity: the exact game math (probability tables, reel strips, board layout, payout rules) is referenced by content hash.
Deterministic mapping: a public, deterministic function maps RNG output → outcome, verifiably using that game definition.
Inclusion: your bet is cryptographically included in the committed set (can’t be dropped/added post hoc).
Reproduction: any third party can recompute the exact result from public artifacts.

1) objects & ids#

1.1 RNG Module#

rng_id: sha256(rng_source_code || build_metadata || param_schema)
rng_params: e.g., algorithm name, reseed interval, CSPRNG impl, DRBG instantiation, HMAC keys (if public), etc.
Test vectors: deterministic vectors published for the module id.

1.2 Game Definition Package (GDP)#

gdp_id: sha256(archive_of_game_logic)
contents depend on game:
plinko: board layout grid, collision rules, payout bands.
keno: number space, draws per round, payout table.
slots: reel strips, reel count, paylines, scatter rules, paytable, wilds/weighting.
dice: payout curve, house edge, precision.
cards/shuffle: deck definition, shuffle algorithm, burn rules.
GDP includes Mapping Spec: canonical pseudocode from rng_stream → outcome.
GDP includes Bias Handling: e.g., rejection sampling to avoid modulo bias.

1.3 External Entropy Beacons (optional but recommended)#

list of sources (e.g., btc_block(n+Δ), eth_block(m+Δ), drand_round(r)).
beacon_mix_id = sha256(concat(all_beacon_values_in_order))
mixed with server seed ensures unbiasable entropy.

2) per-session seeds & per-bet commitments#

2.1 Session (established when user starts playing)#

server_seed_commit = sha256(server_seed_plain)
client_seed (optional, user-provided)
session_id (random)

2.2 Append-only inclusion log (Merkle)#

every bet produces a bet leaf:
bet_leaf = sha256(
  session_id ||
  bet_nonce ||
  stake_amount ||
  gdp_id ||
  rng_id ||
  rng_params_commit ||
  server_seed_commit ||
  client_seed ||
  beacon_plan_id ||
  timestamp_ms
)
operator maintains an append-only Merkle tree over bet leaves.
publishes updated merkle_root every T seconds and signs it.
player gets Merkle proof for their bet at submission time (pre-result).
this prevents silent exclusion or late injection of bets.

3) per-bet result derivation#

3.1 entropy transcript (for that bet)#

after the designated beacon round(s) finalize:
rng_entropy = H(
  server_seed_plain ||
  client_seed ||
  bet_nonce ||
  beacon_mix_value
)
bet_nonce increments per bet to avoid stream reuse.
beacon_mix_value is the concatenation (or KDF’d mix) of chosen external beacon values.
server_seed_plain is revealed either per-settlement or at session close (configurable), but must match server_seed_commit.

3.2 rng stream#

from rng_entropy, derive a byte stream:
use HMAC-DRBG or HKDF-Expand with rng_id as info/context.
document exact KDF / DRBG in rng_id code.

3.3 deterministic mapping (defined in GDP)#

the GDP’s Mapping Spec consumes the RNG stream and returns a canonical outcome.
examples:
plinko
for each row:
  r = next_uniform_0_1()
  go_left if r < 0.5 else right
bucket_index = position_to_bucket()
payout = paytable[bucket_index]
keno
drawn = sample_without_replacement(80, 20, rng_stream)
matches = |drawn ∩ player_numbers|
payout = paytable[matches]
slots
for each reel i:
  stop_i = unbiased_index(next_32_bits, len(reel[i]))
symbols = [reel[i][stop_i] ...]
lines = evaluate(symbols, paytable, wild_rules)
payout = sum(lines)
use rejection sampling for unbiased_index to remove modulo bias if len ∤ 2^k.
dice
x = next_52_bits / 2^52
roll = floor(x * 10000)/100  // example precision
payout = curve(roll)
cards/shuffle
deck = canonical_deck()
shuffle with proven Fisher–Yates driven by rng_stream
apply burn/deal rules
payout = evaluate_hand(...)

4) receipts & verification#

4.1 player receives a #

Bet Receipt#

{
  "version": "PF-VL-1",
  "session_id": "...",
  "bet_nonce": 42,
  "gdp_id": "sha256:...",
  "rng_id": "sha256:...",
  "rng_params_commit": "sha256:...",
  "server_seed_commit": "sha256:...",
  "client_seed": "optional",
  "beacon_plan_id": "btc+drand:v1",
  "bet_merkle_root": "sha256:...",
  "bet_merkle_proof": ["...","..."],
  "stake": "100.00",
  "timestamp_ms": 1738923123123
}

4.2 settlement transcript (revealed when result shown)#

{
  "server_seed_plain": "hex...",
  "beacon_values": {
    "btc_block_863041": "000000...",
    "drand_round_123456": "a9f3..."
  },
  "beacon_mix_id": "sha256:...",
  "rng_entropy": "sha256:...",
  "outcome": {
    "raw": { "roll": 37 },                 // or stops, drawn set, etc.
    "payout": "285.00",
    "aux": { "stops":[12,331,88] }         // game-specific
  },
  "operator_sig": "ed25519:..."
}

4.3 verification steps (anyone can run)#

1.
Verify bet_leaf → Merkle proof against bet_merkle_root.
2.
Verify server_seed_commit == sha256(server_seed_plain).
3.
Fetch beacon values; recompute beacon_mix_id.
4.
Recompute rng_entropy and recreate rng_stream.
5.
Load GDP(gdp_id) and run Mapping Spec with rng_stream.
6.
Check derived outcome and payout match transcript.

5) continuous oversight (watchdogs)#

ProbablyFair Verifier Nodes subscribe to all operator feeds:
bet-root stream (Merkle roots + signed deltas),
server-seed reveals,
settlement transcripts.
nodes independently reproduce outcomes; publish attestations:
PF-ATTEST {bet_id, pass/fail, reason, signatures}
operators can display live PF badges (green/amber/red) per table/game.
optional: SPRT drift tests against GDP probability tables to flag long-term statistical anomalies without false positives.

6) anti-cheat edge cases & mitigations#

Selective refusal / voiding: enforce commit-before-beacon with immutable bet roots. Bets included in root must settle; void events require a public, signed reason code and are rate-limited in the PF badge.
Timing games: use future beacon rounds (e.g., n+2) and minimum cutoff Δ to prevent mempool peeking or race closes.
Stateful games (series hands, bonus rounds): chain a state commitment per step: state_i_commit = sha256(state_{i-1}_commit || move_i || rng_entropy_i).
Privacy: leaves carry salted commitments only; optional ZK Merkle membership if you want private inclusion proofs.
RTP misrepresentation: GDP must include computable RTP; watchdogs run Monte Carlo using the same mapping to compare expected vs observed over windows.

7) minimal operator integration surface#

publish GDPs and RNG modules (open-source or reproducible builds) → get gdp_id, rng_id.
implement bet-leaf + Merkle plumbing and expose:
GET /pf/roots/latest (root + sig)
POST /pf/proofs (returns Merkle proof for bet_leaf)
GET /pf/settlement/:bet_id (transcript)
adopt beacon plan (btc/drand/etc.) and the fixed cutoff rules.

**8) positioning#

Provably Fair proved the outcome.
ProbablyFair proves the process.
Every bet: committed, included, reproduced.

next steps:
a formal RFC-style PDF (PF-VL-1.0) with schemas & reference pseudocode, and
a tiny open-source verifier CLI that ingests a receipt + transcript and replays the outcome?
we can also add game-specific annexes (slots reel hashes, plinko boards, keno tables) so studios can adopt with near-zero friction.
Modified at 2025-10-07 21:16:53
Next
sdk
Built with