Ever wanted to store a piece of information on-chain but keep it a secret until later? In this quick post, we'll explore the commit-reveal scheme and provide an example of how you can implement it within a smart contract using Solidity.
What is the Commit Reveal Scheme?
The commit-reveal scheme is a type of commitment scheme that can be used to store values in a smart contract and keep them secret until you choose to reveal them later.
One common example of this is Delayed reveal NFT collections; where users mint NFTs, but the underlying data (such as the image, name, traits, etc.) of those NFTs are not revealed until later.
In this post, we'll use the popular rock paper scissors game as an example of how and why you might want to implement this paradigm into your smart contracts.
How Does the Commit Reveal Scheme Work?
As the name suggests, the commit reveal scheme uses a two-step process:
Commit: write the data to the smart contract, but keep it hidden; by hashing the data itself along combined with a secret phrase, i.e.
hash(data, secret)
.Reveal: later, reveal the data by providing the same
data
andsecret
values. If the hash of the newly provideddata
andsecret
value is equal to the hash of the originaldata
andsecret
value, then the data is revealed.
For example, in a game of rock paper scissors, we want to submit our move inside a transaction without the other player knowing what move we made; otherwise, they could easily win every game by inspecting the move from our transaction.
Instead, we create a secret key or password and hash the combination of these two values. Using solidity, we can achieve this by using keccak256
and abi.encodePacked
:
// Generate a hash of the combination of the data and secret
bytes32 hash = keccak256(
abi.encodePacked(
"scissors" // Our move
bytes32("player1-secret") // Our secret key or password
)
);
Now, we can safely store this information inside of the smart contract. Other people will be able to see the hash
value, however, they won't be able to decode it; as they don't know our secret value.
Only users who know both our original move and our secret key will be able to reveal our commitment at a later time; since the hash output of data
and secret
will always be the same value.
This also means we cannot change our data
later; for example, we can't change our move from scissors to paper, since the output of hash(paper,secret)
will be different to hash(scissors,secret)
; i.e. we "committed" to our answer.
Later, for example, when both players have committed their move in rock paper scissors, we can safely reveal our commitment; by providing both the original data and the secret key to produce the same hash, for example:
// Produce a new hash by providing the value and secret we did originally
bytes32 newHash = keccak256(
abi.encodePacked(newMove, newSecret)
);
// If hash(newMove, newSecret) == hash(originalMove, originalSecret), reveal!
require(
hash == newHash, "Wrong move or wrong pasword: Hashes do not match."
);
At this point, if the two hashes match, the move provided in the reveal phase (newMove
in the code snippet above) is the "revealed" data; the user has decoded their hash by proving that they know the secret as well as the original move.
To summarise, the commit-reveal scheme is a two-step process effective for storing data in a smart contract in a hidden manner that can be revealed later using hashing:
Commit: Store a hash of a combination of the data + a secret key
Reveal: Provide the data and the secret key to recompute a new hash. If the hash is the same as the hash from the commit phase, the data is revealed.
Wrapping Up
That's a quick introduction to the commit-reveal scheme. For a full code example in Solidity, check out the repository below:
For any questions, feel free to reach out to me on Twitter @jarrodwattsdev.