Skip to main content

Fairness and competitive integrity

A technical explanation of how VPR locks race inputs, records every event, and enables independent verification after a race finishes.

Written by VPR
Updated over a week ago

Virtual Pigeon Racing is built so that race outcomes cannot be secretly changed. Not by players, and not by us.

We achieve this with three ideas working together:

  1. Lock race inputs before the race starts

  2. Record a tamper-evident event log during the race

  3. Reveal proof data after the race so anyone can verify the result

Fairness guarantees at a glance

This table summarizes what we guarantee in every official race and the mechanism that enforces it.

Guarantee

What it means

Technical

Immutable outcome

Race result cannot change retroactively.

Hash chain + cryptographic seal

No secret modification

Stats, traits, AI decisions cannot be silently altered.

HMAC-SHA256 payload binding

No randomness re-roll

Server cannot retry RNG to pick a preferred winner.

Deterministic PRNG seeded pre-race

Independent verifiability

Anyone can audit every step post-race.

Full reveal + public event log

Live race support

Race evolves in real-time. No T-10 lock needed.

Event sourcing + streaming

The exact lock timing can vary by race, but the principle stays the same. Race inputs are locked before the race begins and cannot be changed afterward.

The deterministic model

Our fairness model is deterministic, which means the same inputs always produce the same output:

Result = f(EventLog, EngineVersion, Seed)

Seed: the locked randomness source

  • EngineVersion: the exact race engine version used

  • EventLog: the ordered list of race events (weather ticks, AI decisions, position updates, random usage)

If you replay the race with the same Seed, EngineVersion, and EventLog, you get the exact same finish order and timestamps.

The table below shows the architecture layers of our fairness system, from locking randomness before a race to independent verification after the race finishes.

Layer

Name

Timing

Responsibility

1

Commit Layer

Before race starts

Seal serverSecret with SHA256 commitment

2

Event Log Layer

During race

Record, hash, and chain every event

3

Reveal Layer

Immediately after race

Publish serverSecret + all plaintext payloads

4

Verification Layer

Any time after reveal

Independent audit and full replay

Step 1: Race inputs are locked before the race

Before the race starts, we lock the “RaceLock” state, which includes:

  • All entries and placements

  • The selected pigeons and their attributes

  • The official weather snapshot used for the race

  • The race engine and rules version

  • A cryptographic commitment to race randomness

Blockchain anchor (timestamp proof)

At lock time, we store a cryptographic fingerprint of the RaceLock package on Polygon. This creates a public, timestamped record proving the inputs were locked before the race started.

Step 2: Randomness uses commit–reveal

If randomness is involved, we use a commit–reveal system to prevent rerolls and selective outcomes.

Commit phase (before the race)

We generate a secret value:

serverSecret = random(256-bit)

We publish only its fingerprint:

commitHash = SHA256(serverSecret)

The serverSecret is kept private until after the race. This locks the randomness upfront and prevents “try again until the preferred pigeon wins.”

Step 3: Weather is frozen and verifiable

Weather affects races, so we freeze it equally for everyone.

  • We capture a weather snapshot before the race using real-world conditions at that moment.

  • Weather is not live-polled during the race. The simulation uses the frozen snapshot.

  • The weather snapshot is included in the locked race data so any modification would be detectable.

Step 4: Every race event is logged and chained

We do not store “Bird A won” as the source of truth. Instead, we record the sequence of events that leads to the final state. This is called event sourcing.

Example event sequence:

seq=1  RACE_STARTED        
seq=2 POSITION_UPDATED
seq=3 WEATHER_TICK
seq=4 RANDOM_CONSUMED
seq=5 POSITION_UPDATED
...
seq=N RACE_FINISHED

Tamper-evident event log

Each event is cryptographically sealed and linked to the previous event (hash chaining). If any single value is changed afterward, verification fails.

The event log uses hashes to bind data to the pre-race secret. Example schema includes:

payloadHash = HMAC_SHA256(serverSecret, canonical(payload))

prevEventHash and eventHash to chain events together

Canonical payload rules

To ensure replays are identical, payloads are canonicalized (standardized), for example with fixed key order and fixed float precision. This is what prevents silent changes to weather inputs, pigeon stats, AI decisions, or random number usage after the race.

Step 5: Reveal and verification after the race

After the race ends, we reveal the sealed data so anyone can verify it.

Reveal phase

After the race ends, we publish the serverSecret and the full plaintext payloads for every recorded event, so anyone can verify the outcome independently.

Verification process

Anyone can verify the race by:

  1. Commit verification: check that SHA256(serverSecret) matches the published commitHash

  2. Event verification: recompute payload hashes and validate the event hash chain

  3. Race replay: load the exact EngineVersion, apply events in order, and confirm the final result matches

Verification tooling

We are building a dedicated verification tool so players can independently validate race outcomes.

Note: Race calculations run on game infrastructure for performance, but the inputs are locked and the outcome is designed to be verifiable through cryptographic proofs and deterministic replay.

Did this answer your question?