ERC-7812 ZK identity registry

Review · Signatures & auth · created 2024-11-08

Commitments to secret data are stored in a single on-chain registry at a fixed address, so that later, without disclosing the data, their validity and authenticity can be proven via zero-knowledge proofs.

Why it is needed

Many privacy protocols use zero-knowledge proofs, but where and how a provable statement is stored has varied widely. When each protocol builds its own storage, a statement created in one place is hard for another protocol to reuse, and moving it across chains is cumbersome. The source states that this proposal arose from the need to consolidate such storage and issuance in one place. Gathering provable statements into a single standardized registry greatly improves reusability, portability, and security, and lets future protocols connect to this registry to make use of each other's data. The registry is also deliberately defined abstractly, so identity-based, reputation-based, proof-of-attendance-based, and other protocols can be implemented with few constraints. The proposal lays only the foundation, leaving concrete statement and commitment structures to be defined in separate EIPs.

What to implement

In depth

Three components: EvidenceRegistry, EvidenceDB, Registrar

This system consists of EvidenceRegistry, EvidenceDB, and Registrar. EvidenceRegistry is the entry point to EvidenceDB, a provable database shared by the entire protocol, and it can record arbitrary 32 byte data and prove it later. Registrar is the entity that implements concrete business use cases such as identity, reputation, or proof of attendance, structuring data and inserting it into EvidenceDB through EvidenceRegistry. This EIP standardizes only the first two components and treats the Registrar specification as a separate proposal. Inclusion or exclusion of data is proven with Merkle proofs, and knowing how a Registrar inserts data lets anyone implement an on chain ZK verifier using any stack, such as Circom.

Requirements for EvidenceDB

EvidenceDB can use any provable key/value data structure, but it must support addition, update, and removal of elements. All write operations must preserve idempotence, and adding then removing an element must leave the database state unchanged. The chosen data structure must be able to provide both inclusion and exclusion proofs for elements, and only EvidenceRegistry may call the state-changing functions. The reference interface is based on a sparse Merkle tree and defines a Proof struct that holds the root, an array of sibling hashes, an existence flag, and auxiliary node information (auxKey, auxValue, auxExistence).

Per-caller key isolation

addStatement, removeStatement, and updateStatement must isolate statement keys, assigning each caller a dedicated namespace. The isolation scheme is isolatedKey = hash(msg.sender, key), and the call must revert if the isolated key to be added already exists, or if the isolated key to be removed or updated does not exist. This namespace separation is designed to partition write access to database cells so that no one but the issuer can change their contents. However, this decision places access control management entirely on the Registrar, which is an important consideration when developing a Registrar.

Root history management and getRootTimestamp

EvidenceRegistry must maintain a linear history of EvidenceDB roots. getRootTimestamp must never revert, and must return 0 if the queried root does not exist, or block.timestamp if the latest root is queried. On chain, only minimal root history is kept to limit gas cost, and if more detailed history is needed, it is recommended to implement an off chain service that parses the RootUpdated event.

Hash function and prime field constraints

EvidenceRegistry and EvidenceDB must use the same secure hash function, and a ZK friendly hash function such as Poseidon is recommended to make database proofs easier. When using a ZK friendly hash, EvidenceRegistry must not accept keys or values that exceed the prime field size of the underlying elliptic curve (21888242871839275222246405745257275088548364400416034343698204186575808495617 for BN128). The reference implementation uses the Poseidon hash and includes an onlyInPrimeField check that enforces this range.

Singleton design and deterministic deployment

During specification, a per-protocol registry approach was compared against a shared singleton registry used by all protocols, and the latter was chosen. The rationale rests on three points: cross chain portability, since proving registry state across chains only requires sending a single bytes32 SMT root; concentrated trust, since users only need to trust one permissionless, immutable contract; and simplified integration, since formalizing the interface, hash function, and proof structure makes integration straightforward. EvidenceRegistry is deployed at 0x781246D2256dc0C1d8357c9dDc1eEe926a9c7812 using the deterministic deployment proxy at 0x4e59b44847b379578588920ca78fbf26c0b4956c with salt 0x04834e077c463de76a20df3770a7b96a5e5eb826922d1514f943cd5b41ccaed0.

Reference implementation and follow-up standards

The reference implementation provides Solidity contracts for EvidenceRegistry and EvidenceDB, plus a Circom circuit that verifies registry state. The SMT height is set to 80, and the circuit takes the root as a public input, recomputes the isolated key from the address and key, and checks the inclusion or exclusion proof. The implementation depends on @openzeppelin/contracts v5.1.0 and circomlib v2.0.5. Building on this proposal, follow-up EIPs are expected that specify commitment generation, commitment key management, and statement proof procedures for registrars such as one for recording national passports on chain, one for EIP-4337 confidential account identity management, and one for POAP.

Proposal status: Review

This proposal is still at the Review stage. Since the content may change, check the latest source before implementing.

Things to watch out for

Original ERC · ← ERCs