🎓 A1Learner
🔍
0 of 45 lessons completed

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?