Skip to content

Latest commit

 

History

History
112 lines (89 loc) · 6.96 KB

eip0000.md

File metadata and controls

112 lines (89 loc) · 6.96 KB
eip title description author discussions-to status type category created requires
<to be assigned>
ERC-721 zk-Provable Token Extension
An extension to the ERC-721 standard to add merkle trees for storing account balances
Anton Wahrstätter (@Nerolation)
Idea
Standards Track
ERC
2022-08-04
EIP 165, 721

Abstract

This specification defines an extension to the ERC-721 standard. The extension adds a standardized API for storing ownership information in merkle roots for s.

Motivation

A standard interface for zk succinct non-interactive ownership proofs that allows users to prove ownership of an asset without revealing private account-related information.

Using merkle trees for storing a hash of $UserAddress$, $tokenId$ and $secret$. zk-SNARKs can be employed for verifying private information. By proving the ability to reconstruct the merkle tree, a user can demonstrate to own a certain asset without revealing any account information.

Specification

The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.

Every ERC0000 compliant contract MUST implement the ERC-721 (0x80ac58cd) and ERC165 (0x01ffc9a7) interfaces:

// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.6;
...
interface ERC0001 /* is ERC721, ERC165 */ {

    /// @notice Mints token to a stealth address `stA` if proof is valid. stA is derived from
    ///  stealthAddressBytes which is the MIMC Sponge hash `h` (220 rounds) of the user address `eoa`,
    ///  the token id `tid` and a user-generated secret `s`, such that stA <== address() <== h(eoa,tid,s).
    /// @dev Requires a proof that verifies the following:
   ///        - prover can generate the StealthAddress (e.g. user signs msg => computePublicKey() => computeStealthAddress() ).
    ///       - prover can generate the merkle root from an empty leaf.
    ///       - prover can generate the merkle root after updating the empty leaf.
    /// @param currentRoot A known (historic) root.
    /// @param newRoot Updated root.
    /// @param stealthAddressBytes Hash of user address, tokenId and secret.
    /// @param tokenId The Id of the token.
    /// @param proof The zk-SNARK.
    function _mint(bytes32 currentRoot, bytes32 newRoot, bytes32 stealthAddressBytes, uint256 tokenId, bytes proof) external;

    /// @notice Burns token with specified Id from stealth address `stA` if proof is valid.
    /// @dev Requires a proof that verifies the following:
    ///       - prover can generate the StealthAddress (e.g. user signs msg => computePublicKey() => computeStealthAddress() )
    ///       - prover can generate the merkle root from an non-empty leaf.
    ///       - prover can generate the merkle root after nullifieing the non-empty leaf.
    /// @param currentRoot A known (historic) root.
    /// @param newRoot Updated root.
    /// @param stealthAddressBytes Hash of user address, tokenId and secret.
    /// @param tokenId The Id of the token.
    /// @param proof The zk-SNARK.
    function _burn(bytes32 currentRoot, bytes32 newRoot, bytes32 stealthAddressBytes, uint256 tokenId, bytes proof) external;

    /// @notice Transfers token with specified Id from current owner to the recipient's
    /// stealth address, if proof is valid.
    /// @dev Requires a proof that verifies the following:
    ///       - prover can generate the StealthAddress (e.g. user signs msg => computePublicKey() => computeStealthAddress() ).
    ///       - prover can generate the merkle root from an non-empty leaf.
    ///       - prover can generate the merkle root after updating the non-empty leaf.
    /// @param currentRoot A known (historic) root.
    /// @param newRoot Updated root.
    /// @param stealthAddressBytes Hash of user address, tokenId and secret.
    /// @param tokenId The Id of the token.
    /// @param proof The zk-SNARK.
    function _transfer(bytes32 currentRoot, bytes32 newRoot, bytes32 stealthAddressBytes, uint256 tokenId, bytes proof) external;

    /// @notice Verifies zk-SNARKs
    /// @dev Forwards the different proofs to the right `Verifier` contracts.
    ///  Different Verifiers are required for each action, because of the merkle-tree logic involved.
    /// @param currentRoot A known (historic) root.
    /// @param newRoot Updated root.
    /// @param stealthAddressBytes Hash of user address, tokenId and secret.
    /// @param tokenId The Id of the token.
    /// @param proof The zk-SNARK.
    /// @return Validity of the provided proof.
    function _verifyProof(bytes32 currentRoot, bytes32 newRoot, bytes32 stealthAddressBytes, uint256 tokenId, bytes proof) external returns (bool);
}

Rationale

EIP-0000 emerged from the need to proof ownership of non-fungible tokens without revealing private information. While users might want to prove the ownership of a NFT-concert ticket, they might not want to reveal personal account-related information at the same time. Privacy-preserving solutions require standards to gain adoption, therefore it is critical to focus on generalisable ways of implement ownership-proofs into related contracts.

This extension enables to implement the framework required for zero-knowledge ownership proofs. EIP-0000 extends ERC-721 contracts in a way to provide users possibilities to prove ownership in privacy-preserving ways. Basically, the contract maintains information about ownership in a merkle tree, in addition to commonly used mappings. Using zk-SNARKs (e.g. EdDSAMiMCVerifier of circom, or see github at 0xPARC/circom-ecdsa), users can generate off-chain merkle-proofs and submit them to the contract for verification. Implementing EIP-0000 adds a state variable storing the most recent merkle root to the ERC-721 standard. Further, an array with historic roots must be maintained.

Stealth Addresses are used to hide the owner of a certain token. The Stealth Address is generated from the ownerAddress $eoa$, the tokenID $tid$ and a secret $s$, such that $leaf = hash(eoa, tid, s)$ and stealthAddress = address(uint160(uint256(stealthAddressBytes))).

The different functions require different circuits for proving. The verifyProof MUST ensure to forward the respective proofs to the right Verifier contract

All Verifier contracts MUST represent a immutable contract.

Backwards Compatibility

EIP--- is not backwards compatibly with basic ERC-721 contracts. EIP--- implements the EIP-165 standard.

Reference Implementation

You can find an implementation of this standard in assets/eip-0000.sol.

Security Considerations

Merkle-trees are defined and created during deployment and cannot be enlarged.

Copyright

Copyright and related rights waived via CC0.