<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Rishi</title>
    <description>The latest articles on DEV Community by Rishi (@rishiw).</description>
    <link>https://dev.to/rishiw</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3765730%2F9b2e132f-0ee7-465d-81a9-a46382ecce94.png</url>
      <title>DEV Community: Rishi</title>
      <link>https://dev.to/rishiw</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rishiw"/>
    <language>en</language>
    <item>
      <title>Commit–Reveal Isn’t Enough: Designing Provably Fair Systems That Actually Survive Verification</title>
      <dc:creator>Rishi</dc:creator>
      <pubDate>Wed, 11 Feb 2026 08:00:40 +0000</pubDate>
      <link>https://dev.to/rishiw/commit-reveal-isnt-enough-designing-provably-fair-systems-that-actually-survive-verification-40nl</link>
      <guid>https://dev.to/rishiw/commit-reveal-isnt-enough-designing-provably-fair-systems-that-actually-survive-verification-40nl</guid>
      <description>&lt;p&gt;"Provably fair" has become a common label in systems that generate public outcomes — games, raffles, lotteries, NFT mints, even allocation engines.&lt;/p&gt;

&lt;p&gt;Most implementations follow the same pattern:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Server generates a secret seed&lt;/li&gt;
&lt;li&gt;Server publishes a hash of the seed&lt;/li&gt;
&lt;li&gt;Later, the seed is revealed&lt;/li&gt;
&lt;li&gt;Users verify the hash matches&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This prevents the server from changing the seed after publishing the commitment.&lt;/p&gt;

&lt;p&gt;And that's where most systems stop.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The problem?&lt;/strong&gt; That only prevents one very specific type of cheating. It does not eliminate trust.&lt;/p&gt;

&lt;p&gt;If you're building a system that claims to be provably fair, here's what actually matters at the architectural level.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. The Real Enemy: Timing Asymmetry
&lt;/h2&gt;

&lt;p&gt;Fairness failures almost never come from broken cryptography.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;They come from who moves last.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Consider a simplified flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Server commits: &lt;code&gt;H(server_seed)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;User sends &lt;code&gt;user_seed&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Server reveals &lt;code&gt;server_seed&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Outcome = &lt;code&gt;f(server_seed, user_seed)&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Looks fine.&lt;/p&gt;

&lt;p&gt;But notice something subtle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The server chose its seed before seeing the user seed.&lt;/li&gt;
&lt;li&gt;The user chose their seed after seeing the commitment — but not the actual value.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now flip it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Server commits: &lt;code&gt;H(server_seed)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;User reveals &lt;code&gt;user_seed&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Server decides whether to proceed&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If the server can abort silently after seeing the user's input, it can simulate outcomes offline and only continue favorable rounds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No hash is broken.&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;No cryptography is violated.&lt;/strong&gt;&lt;br&gt;
But fairness is compromised.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Architectural Rule #1:&lt;/strong&gt;&lt;br&gt;
No party should be able to influence continuation after observing partial entropy.&lt;/p&gt;

&lt;p&gt;That's a protocol design constraint — not a hashing problem.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Commit–Reveal Is Necessary — Not Sufficient
&lt;/h2&gt;

&lt;p&gt;A single-sided commitment only prevents post-hoc manipulation.&lt;/p&gt;

&lt;p&gt;It does not prevent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Abort-and-retry attacks&lt;/li&gt;
&lt;li&gt;Selective round publication&lt;/li&gt;
&lt;li&gt;Pre-computation and discarding&lt;/li&gt;
&lt;li&gt;Biased entropy timing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A stronger pattern is &lt;strong&gt;double-blind commitment&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Server commits: &lt;code&gt;H(server_seed)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;User commits: &lt;code&gt;H(user_seed)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Server reveals &lt;code&gt;server_seed&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;User reveals &lt;code&gt;user_seed&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;combined = H(server_seed || user_seed)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;outcome = deterministic_map(combined)&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now neither party sees the other's entropy before locking in their own.&lt;/p&gt;

&lt;p&gt;You've removed "last-move advantage".&lt;/p&gt;

&lt;p&gt;That's a protocol improvement — not a cosmetic change.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Determinism Is Non-Negotiable
&lt;/h2&gt;

&lt;p&gt;Even with perfect entropy symmetry, systems fail if the mapping function isn't strictly deterministic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Common anti-patterns:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;if (result &amp;lt; 0.01) {&lt;br&gt;
result = reroll();&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;OR&lt;/p&gt;

&lt;p&gt;while (value &amp;gt; max) {&lt;br&gt;
value = hash_again();&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;Or hidden "edge-case" handling buried in business logic.&lt;/p&gt;

&lt;p&gt;If running the same inputs twice can produce different outputs, verification becomes probabilistic instead of mathematical.&lt;/p&gt;

&lt;p&gt;If your system uses Math.random() anywhere in the verification path, it is not verifiable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Architectural Rule #2:&lt;/strong&gt;&lt;br&gt;
The outcome function must be pure, stateless, and deterministic.&lt;/p&gt;

&lt;p&gt;Given identical inputs, it must produce identical outputs — forever.&lt;/p&gt;

&lt;p&gt;No retries.&lt;br&gt;
No hidden branching.&lt;br&gt;
No external state.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Range Mapping Is Where Fairness Dies Quietly
&lt;/h2&gt;

&lt;p&gt;Even with strong entropy, biased mapping breaks fairness.&lt;/p&gt;

&lt;p&gt;The classic example:&lt;/p&gt;

&lt;p&gt;roll = random_value % 10;&lt;/p&gt;

&lt;p&gt;If &lt;code&gt;random_value&lt;/code&gt; comes from a space not evenly divisible by 10, some results will be slightly more likely.&lt;/p&gt;

&lt;p&gt;This is called &lt;strong&gt;modulo bias&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It's subtle.&lt;br&gt;
It passes casual inspection.&lt;br&gt;
But over time, distribution skews measurably.&lt;/p&gt;

&lt;p&gt;Correct range mapping requires:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rejection sampling&lt;/li&gt;
&lt;li&gt;Bit slicing with proper bounds&lt;/li&gt;
&lt;li&gt;Careful handling of entropy exhaustion&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Architectural Rule #3:&lt;/strong&gt;&lt;br&gt;
Entropy extraction and range mapping must be mathematically uniform.&lt;/p&gt;

&lt;p&gt;Most "provably fair" implementations fail here — not at hashing.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Public Entropy Isn't a Silver Bullet
&lt;/h2&gt;

&lt;p&gt;Some systems attempt to strengthen fairness by mixing in public entropy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Block hashes&lt;/li&gt;
&lt;li&gt;Randomness beacons&lt;/li&gt;
&lt;li&gt;External APIs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This can improve robustness — but only if done correctly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Common mistake:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;combined = H(server_seed || block_hash)&lt;/p&gt;

&lt;p&gt;Where the server chooses which block hash to use after observing it.&lt;/p&gt;

&lt;p&gt;That reintroduces timing control.&lt;/p&gt;

&lt;p&gt;For public entropy to add security:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The round must commit to a future entropy source before the entropy is published. Otherwise, selection bias reappears.&lt;/li&gt;
&lt;li&gt;It must be independently verifiable&lt;/li&gt;
&lt;li&gt;It must be combined deterministically&lt;/li&gt;
&lt;li&gt;It must not be optional&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Otherwise, it's theater.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. The Offline Verification Test
&lt;/h2&gt;

&lt;p&gt;Here's a simple litmus test for any "provably fair" system:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can a third party verify an outcome offline, years later, using only:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the published commitments&lt;/li&gt;
&lt;li&gt;the revealed seeds&lt;/li&gt;
&lt;li&gt;the documented algorithm&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If verification requires:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an API call&lt;/li&gt;
&lt;li&gt;platform permission&lt;/li&gt;
&lt;li&gt;special tooling&lt;/li&gt;
&lt;li&gt;hidden parameters&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then the system still depends on trust.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Architectural Rule #4:&lt;/strong&gt;&lt;br&gt;
Verification must be permissionless and reproducible without infrastructure.&lt;/p&gt;

&lt;p&gt;If your server disappears, verification should still work.&lt;/p&gt;

&lt;p&gt;That's the difference between auditability and availability.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Logs and Non-Repudiation
&lt;/h2&gt;

&lt;p&gt;Even perfect randomness fails if rounds can disappear.&lt;/p&gt;

&lt;p&gt;If a system can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Drop unfavorable rounds&lt;/li&gt;
&lt;li&gt;Reorder round IDs&lt;/li&gt;
&lt;li&gt;Omit commitments&lt;/li&gt;
&lt;li&gt;Retroactively insert data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then fairness becomes unverifiable.&lt;/p&gt;

&lt;p&gt;Strong designs include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sequential round identifiers&lt;/li&gt;
&lt;li&gt;Immutable public logs&lt;/li&gt;
&lt;li&gt;Time-anchored commitments&lt;/li&gt;
&lt;li&gt;Transparent lifecycle states&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fairness is not just about randomness — it's about event integrity.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. What "Provably Fair" Actually Requires
&lt;/h2&gt;

&lt;p&gt;At the architecture level, a robust system needs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Symmetric entropy commitment&lt;/strong&gt; (no last-move advantage)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deterministic entropy combination&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pure outcome mapping&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Uniform range extraction&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Immutable round logging&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Offline reproducibility&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Remove any one of these, and trust creeps back in.&lt;/p&gt;

&lt;p&gt;And once trust re-enters, the system becomes reputation-based — not proof-based.&lt;/p&gt;




&lt;h2&gt;
  
  
  9. Why Systems Eventually Get Caught
&lt;/h2&gt;

&lt;p&gt;Most fairness failures aren't dramatic.&lt;/p&gt;

&lt;p&gt;They're statistical.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Slight edge-case skew&lt;/li&gt;
&lt;li&gt;Missing commitments&lt;/li&gt;
&lt;li&gt;Biased modulo mapping&lt;/li&gt;
&lt;li&gt;Silent retries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Over thousands of rounds, small biases compound.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cheating requires perfection forever.&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Verification requires one curious engineer with a script.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Time favors math.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thought
&lt;/h2&gt;

&lt;p&gt;Provable fairness is not about adding a hash and calling it secure.&lt;/p&gt;

&lt;p&gt;It's about designing protocols where:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;no one gets the last move&lt;/li&gt;
&lt;li&gt;entropy is symmetric&lt;/li&gt;
&lt;li&gt;algorithms are deterministic&lt;/li&gt;
&lt;li&gt;mapping is uniform&lt;/li&gt;
&lt;li&gt;logs are immutable&lt;/li&gt;
&lt;li&gt;verification is independent&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If verification requires trust, it isn't provably fair.&lt;/p&gt;

&lt;p&gt;It's just harder to cheat.&lt;/p&gt;

&lt;p&gt;And engineering systems that are merely "hard to cheat" is very different from engineering systems that are &lt;strong&gt;impossible to cheat without detection&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;One is security through friction.&lt;br&gt;
The other is security through math.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Practical Example
&lt;/h2&gt;

&lt;p&gt;If you're curious what this looks like in practice, I built a minimal live demo of a provably fair dice roll using commit–reveal combined with future-time-locked drand entropy.&lt;/p&gt;

&lt;p&gt;You can inspect the inputs, recompute the hashes, and verify the result independently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Demo:&lt;/strong&gt; &lt;a href="https://blockrand.net/live.html" rel="noopener noreferrer"&gt;https://blockrand.net/live.html&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Source:&lt;/strong&gt; &lt;a href="https://github.com/blockrand-api/blockrand-js" rel="noopener noreferrer"&gt;https://github.com/blockrand-api/blockrand-js&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No APIs required for verification — everything is reproducible client-side.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>blockchain</category>
      <category>security</category>
      <category>systemdesign</category>
    </item>
  </channel>
</rss>
