ERC-5732 Common commit interface (Commit Interface)
Final · Other · created 2022-09-29
Defines a common interface containing only the commit function, so that the commit-reveal scheme, in which a choice is first submitted as a hash (commit) and revealed later, can be attached to any contract.
Why it is needed
On a blockchain, anyone can see the contents of a transaction before it is processed. Actions that need secrecy, such as voting, therefore expose their contents and suffer harms such as front-running, where someone sees another party's transaction and jumps ahead of it, back-running, where someone follows right behind it, and sandwich attacks, which wrap a transaction on both sides. The long-standing remedy is commit-reveal. You first submit only the hash of the content to lock in a commitment, then reveal the original later to have it verified. However, if each standard, such as voting standards and token standards, designs its own commit scheme, they are incompatible with one another. This standard fixes a single commit function as the common piece and leaves the reveal method to each application, so commit-reveal can be added anywhere without changing the function signatures of existing standards.
What to implement
function commit(bytes32 _commitment) payable external;: Submits a single commitment (commit) hash. This is the core function that a compliant contract must implement.
In depth
Two interfaces: core and general
A compliant contract MUST implement IERC_COMMIT_CORE (EIP-165 identifier 0xf14fcbc8), and SHOULD implement IERC_COMMIT_GENERAL (identifier 0x67b2ec2c). A contract that implements the general interface MUST treat a commit(_commitment) call as equivalent to commitFrom(msg.sender, _commitment, an empty array). The core interface is kept simple with a single commit parameter for backward compatibility with prior implementations such as ENS.
timePoint and the Commit event
A contract implementing IERC_COMMIT_GENERAL MUST emit a Commit event when a commit is accepted and recorded. The return value of commitFrom and the event's _timePoint indicate the ordering between commits, and a later commit always has a value greater than or equal to that of an earlier commit. Using block.timestamp, block.number, or another time scale chosen by the implementing contract is RECOMMENDED.
commitFrom and submission on someone's behalf
commitFrom is a function that lets the commitment be built off-chain and submitted by a different account on the owner's behalf. If _from differs from the transaction signer, it is recommended to verify the signature of _from. For an externally owned account (EOA), verifying an ECDSA signature on-chain is recommended, and for a smart contract account, verifying the signature with EIP-1271 is recommended. The _extraData field is reserved for extending behavior in the future.
The reveal method is not standardized
This standard defines only commit and does not define a reveal function. One or more methods of a compliant contract MAY be used for revealing, and an existing function such as vote in a voting standard or transfer in a token standard can be used as the reveal as is. However, there must be a way for the committer to disclose secret_salt in the later reveal transaction. The size and location of secret_salt are deliberately left unspecified to maximize flexibility of integration.
Backward compatibility and the ENS case
This standard is backward compatible with the method signatures of existing ERCs that have extraData. New EIPs can add a salt field to make supporting this standard easier, but it is not required. The reference implementation presents the commit function that ENS's ETHRegistrarController uses to prevent registration front-running, showing that existing code can conform to this standard by adding emission of the Commit event. This reference implementation is for demonstration, so it must not be used as is in a production service.
EIP-165 support recommended
A compliant contract is recommended to implement EIP-165 (the standard for querying whether an interface is supported). The identifier of the core interface is 0xf14fcbc8 and that of the general interface is 0x67b2ec2c. This standard lists EIP-165 and EIP-1271 as prerequisites (requires), and its status is Final, a settled standard.
Things to watch out for
- Keep it secret until the reveal: The reveal transaction and its parameters, especially secret_salt, must be kept secret until the time of disclosure.
- Salt length and randomness quality: secret_salt must be cryptographically long enough, and the randomness used to generate it must also be cryptographically secure. In addition, a secret_salt that has been used once must never be reused, and client apps are recommended to warn about reuse attempts.
- Consider deleting the commit immediately: To reduce the chance of replay or re-entry attacks, the implementing contract should consider deleting that submitter's commit immediately.
- Cross-chain reuse attacks: A commit can be reused on a different chainId or on a forked chain, so the chainId must be included when building the commit. Before inventing their own scheme, high-risk applications should consider composing the commit with a proven approach such as EIP-712.
- Duplicate timePoint within the same block: For a contract in which the next commit's _timePoint must be strictly greater than the previous one, block.timestamp and block.number cannot be trusted, because two transactions can land in the same block and have the same value. Additional measures are needed, such as keeping a separate state variable that counts commits or rejecting a second transaction with the same value.