Patterns worth knowing · ≈ 16 min

Commit-reveal: keeping a secret on a public chain

Everything in storage is public forever. This is how you run a secret ballot anyway.

The voting lesson ended on a warning: a ballot in contract storage is readable by anyone, forever. If people can see the running tally, late voters vote strategically and early voters get targeted. This is the fix.

Commit-reveal splits one action into two transactions:

Phase 1 — commit. You publish only a digest:

digest = sha3_256(choice + secret)

Nobody can invert it, so your choice stays private. But you are bound by it: you cannot later claim a different choice, because no other input produces that same digest.

Phase 2 — reveal. After commits close, you publish the choice and the secret. The contract recomputes the digest and checks it matches what you committed. Only then does your vote count.

The secret is what makes it secret. Without one, a commitment to a choice from a small set is trivially broken: there are only two possible digests for a yes/no vote, so anyone can hash both and read every "hidden" ballot instantly.

The secret must be long and unpredictable — 32 random bytes. And it must be different for every voter, or one revealed secret unmasks everyone who reused it.

Commit-reveal binds; it does not compel. A voter who dislikes the way the reveal phase is going can simply never reveal. Their vote does not count, but they got to make that decision after seeing partial results.

Real systems fix this with a deposit that is forfeited by non-revealers. Know that the plain pattern has this hole before you rely on it for anything where selective abstention is valuable.

Your turn

Build commit-reveal voting. commit(digest) stores one commitment per address. reveal(choice, secret) must recompute sha3_256(choice + secret), reject a mismatch with b"bad_reveal", reject a second reveal, and count the vote. tally() returns yes*1000 + no.

Hints

Stuck? Ask

Answered by Animica's own free inference network. It is donated GPU capacity, so give it 20-30 seconds.