Solidity Commit Reveal Schemes
A commit-reveal scheme is a two-step vote or bid that hides the choice until everyone has committed. The commit step stores only a hash, and the reveal step checks the choice against that hash.
The commit stores only a hash
The choice itself is never stored at commit time. Only the hash goes on record, so no one can read the choice from it.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
contract CommitReveal {
mapping(address => bytes32) public commitments;
mapping(address => uint256) public votes;
mapping(address => bool) public revealed;
function commit(bytes32 commitment) public {
require(commitments[msg.sender] == bytes32(0), "already committed");
commitments[msg.sender] = commitment;
}
function reveal(uint256 choice, uint256 secret) public {
require(commitments[msg.sender] != bytes32(0), "no commitment");
require(!revealed[msg.sender], "already revealed");
require(keccak256(abi.encodePacked(msg.sender, choice, secret)) == commitments[msg.sender], "wrong secret");
revealed[msg.sender] = true;
votes[msg.sender] = choice;
}
}A wrong secret fails the reveal
The reveal checks the hash exactly. A wrong secret makes a different hash, so the reveal reverts.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
contract CommitReveal {
mapping(address => bytes32) public commitments;
mapping(address => uint256) public votes;
mapping(address => bool) public revealed;
function commit(bytes32 commitment) public {
require(commitments[msg.sender] == bytes32(0), "already committed");
commitments[msg.sender] = commitment;
}
function reveal(uint256 choice, uint256 secret) public {
require(commitments[msg.sender] != bytes32(0), "no commitment");
require(!revealed[msg.sender], "already revealed");
require(keccak256(abi.encodePacked(msg.sender, choice, secret)) == commitments[msg.sender], "wrong secret");
revealed[msg.sender] = true;
votes[msg.sender] = choice;
}
}The right secret opens the vote
The right choice and secret rebuild the stored hash. The vote is recorded, and a second reveal reverts.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
contract CommitReveal {
mapping(address => bytes32) public commitments;
mapping(address => uint256) public votes;
mapping(address => bool) public revealed;
function commit(bytes32 commitment) public {
require(commitments[msg.sender] == bytes32(0), "already committed");
commitments[msg.sender] = commitment;
}
function reveal(uint256 choice, uint256 secret) public {
require(commitments[msg.sender] != bytes32(0), "no commitment");
require(!revealed[msg.sender], "already revealed");
require(keccak256(abi.encodePacked(msg.sender, choice, secret)) == commitments[msg.sender], "wrong secret");
revealed[msg.sender] = true;
votes[msg.sender] = choice;
}
}Example
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
contract SealedVote {
mapping(address => bytes32) public commitments;
mapping(address => bool) public revealed;
mapping(uint256 => uint256) public tally;
function commit(bytes32 commitment) public {
require(commitments[msg.sender] == bytes32(0), "already committed");
commitments[msg.sender] = commitment;
}
function reveal(uint256 choice, uint256 secret) public {
require(commitments[msg.sender] != bytes32(0), "no commitment");
require(!revealed[msg.sender], "already revealed");
require(keccak256(abi.encodePacked(msg.sender, choice, secret)) == commitments[msg.sender], "wrong secret");
revealed[msg.sender] = true;
tally[choice] += 1;
}
}Key points
- Commit: a hash of a choice plus a secret, stored before anyone can see the choice.
- Reveal: the later step where the choice and the secret are shown and checked against the stored hash.
- Secret: a private value mixed into the hash so the choice cannot be guessed from the hash alone.
- Use a commit-reveal scheme to stop early voters from copying or reacting to later voters choices.
- Binding: a commitment binds its sender, because the sender address is mixed into the hash.
In this example, two voters commit hashes first, then each reveals a choice and a secret, and the tally counts one vote for choice 1 and one vote for choice 2.
Note
the hash hides the choice, and it does not hide the fact that a commitment was made.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Remix IDE
Exercise
A voter committed a hash of a choice and a secret. What must the reveal step show to open the vote?