Commit-reveal vs VRF: when each is the right primitive
Commit-reveal and Verifiable Random Functions (VRFs) keep showing up in the same conversations. They both produce a value that can be verified after the fact but not predicted in advance. They both get reached for when a protocol needs randomness, sealed bids, or anti-front-running. They are not, however, the same primitive, and picking the wrong one produces protocols that either over-trust a participant or burn engineering effort solving the wrong problem.
This is a short, opinionated guide for protocol engineers picking between them.
The two primitives
A commit-reveal scheme is two phases. In the commit phase, a party publishes H(value || salt). The commitment is binding (you cannot later claim a different value matches it) and hiding (nobody can extract the value from the hash). In the reveal phase, the party publishes (value, salt). Anyone can recompute H(value || salt) and compare it to the original commitment.
This library implements exactly that, with eight selectable hash algorithms (sha256, sha384, sha512, sha3_256, sha3_384, sha3_512, blake2b, blake2s) and an optional Schnorr zero-knowledge proof on secp256k1 for the case where you want to prove “I know the committed value” without performing the reveal.
A Verifiable Random Function takes a secret key and an input and produces an output plus a proof. The output is unique (only the holder of the secret key can produce it for a given input), pseudorandom (computationally indistinguishable from uniform for anyone without the key), and verifiable (anyone with the matching public key can confirm the output is correct for the input).
VRFs are the engine behind the leader election in Algorand, the randomness beacon in some Solana designs, and the proof-of-replication checks in Filecoin. They are not implemented in this library; the closest thing here is the Schnorr ZKP, which is a proof of knowledge, not a function output.
What they have in common
Both primitives produce a value that is committed to before it is revealed.
For commit-reveal, the binding is at the point of publishing the hash; nothing about the input is known to anyone else, but the committer is locked in.
For a VRF, the binding is at the point of generating the keypair; for a given input and secret key, the output is uniquely determined, but only the key-holder can compute it.
Both are useful for the same broad class of problems: sealed-bid auctions, randomness beacons, leader election, fair-launch ordering. Both produce outputs that the producer cannot retroactively change.
What is genuinely different
The first difference is the number of parties involved in producing the hidden value.
A commit-reveal RNG needs multiple participants to be unmanipulable. If a single party commits and then reveals, they have just hashed a value they chose; the result is whatever they wanted it to be. The protection comes from XOR-ing or hashing the reveals of many parties: as long as at least one is honest, the final output is uniformly random, because the honest party’s contribution randomises the rest.
A VRF produces a fresh random-looking output from a single key-holder, given a public input. There is no need for many participants. This is why VRFs are the right tool for slot-based consensus: every slot has a known input (the slot number), every validator can compute their VRF output for it, and the output is the same one anyone with the public key would verify.
The second difference is the interaction pattern.
Commit-reveal is two messages per participant per round: commitment and reveal. The reveal can be aborted (the committer simply does not publish the reveal). Protocols using commit-reveal need to specify what happens to a participant who commits and then refuses to reveal; usually this is slashing, forfeiture, or treating the participant as having committed to a default value.
VRFs are one message: input goes in, output and proof come out. There is no equivalent of “refusing to reveal” because there is nothing to reveal — the proof is published alongside the output.
The third difference is what is hidden, and from whom.
Commit-reveal hides the value from everyone, including the committer’s collaborators, until reveal time. This is what you want for a sealed-bid auction: even your auction partner cannot peek at your bid before the deadline.
A VRF hides the future outputs from everyone who does not hold the secret key, but the key-holder knows the output as soon as they know the input. This is not what you want for a sealed bid; the key-holder is not blind to their own future bids.
How to pick
Use commit-reveal when:
- You need multiple participants to contribute to a single hidden value (multi-party randomness, sealed-bid auction with multiple bidders).
- The hidden value is genuinely arbitrary (a bid, a vote, an order), not deterministically derivable from a public input.
- You can tolerate the two-phase interaction, including a defined behaviour for non-revealing participants.
- You want a small, audit-friendly primitive. A commit-reveal scheme over SHA-256 fits on half a sheet of paper.
Use a VRF when:
- The hidden value is a function of a public input (slot number, block height) and a secret key.
- You need single-message production: the producer publishes once and the value is fixed.
- You need on-demand output that anyone with the public key can verify, without a back-and-forth.
- You are doing leader election in a consensus algorithm, generating randomness per block, or proving unique participation.
If your protocol fits both, use commit-reveal. It is simpler to reason about, simpler to audit, and the primitives required (a hash, optionally a Schnorr proof) are mature.
When you want both
Some protocols use both. The classical pattern is: each participant has a long-lived VRF key. Each round, they commit to a fresh random seed using a hash commitment. After the commit deadline they reveal. The protocol XORs the reveals to produce a fresh per-round seed, then uses the VRF on that seed for whatever needs to happen (e.g., randomly assigning slots to participants).
The VRF in this picture is doing the deterministic function from key to output job; commit-reveal is doing the multi-party agreement on the input job. The two are complementary because they solve different parts of the problem.
If you are building such a protocol on top of this library, the relevant API is:
from commit_reveal import CommitRevealScheme
import secrets
scheme = CommitRevealScheme(hash_algorithm="sha3_256", use_zkp=True)
# round 1: each participant commits to a 32-byte random seed
my_seed = secrets.token_bytes(32)
commitment, salt = scheme.commit(my_seed)
# the commitment is broadcast; salt is kept secret until reveal time
# round 2: each participant reveals; the protocol XORs all reveals
# to produce a single per-round random output
The Schnorr ZKP becomes useful if a participant wants to prove they have a valid commitment without yet publishing the seed — for instance, to claim a slot in the reveal queue without leaking their seed early.
Failure modes to think about
For commit-reveal, the dominant failure mode is selective non-reveal. A participant who commits and then sees how others’ reveals would affect the final output may refuse to reveal in order to bias the result. This is fixable but only if the protocol specifies it: a default reveal value, a slashing condition, or a fallback that excludes non-revealers from the output computation.
For VRFs, the dominant failure mode is key compromise. If the key-holder leaks the secret, every output it could ever produce is now predictable. Key rotation, hardware isolation, and threshold VRFs are the standard mitigations.
Neither failure mode is in this library’s scope to solve. What the library provides is the primitive; what your protocol does about non-revealers, or key custody, is your protocol’s problem. The README and SECURITY.md are explicit about the boundary.
A short summary
If you are coordinating sealed input from many participants, use commit-reveal. If you need a single party to produce a deterministic, verifiable, pseudorandom output from a public input, use a VRF. If you need both, use both — they compose cleanly.
This library implements the commit-reveal half, with a Schnorr ZKP on secp256k1 if you need proof of knowledge without revelation. The VRF half lives elsewhere; pick a library with constant-time scalar arithmetic and a published audit. We will not pretend to be that library.