--- eip: 8084 title: Zero-knowledge proof metadata description: ZKmeta is a method to discover ZK circuit identifiers, semantic versions, verification keys, public input schemas. author: cococay (@zwowo1997) discussions-to: https://ethereum-magicians.org/t/eip-0000-standardized-zk-metadata-interface/26511 status: Draft type: Standards Track category: ERC created: 2025-11-10 --- ## Abstract This standard formalizes ZKMeta, a minimal interface for contracts that verify or depend on zero-knowledge proofs. It defines how to expose a proof-system identifier, circuit identifier, circuit version, public-input schema (hash and URI), and verification-key URI. The goal is to enable interoperable wallet, relayer, explorer, rollup, and dApp tooling without prescribing any specific proof format. ## Motivation Zero-knowledge applications currently lack a standard way to publish the "shape" of their proofs. Projects using Groth16, Plonk, Halo2, or zkVMs each deploy custom artifacts, leaving integrators unable to reliably determine how to interact with a system without bespoke adapters. Unlike previous attempts that aimed to standardize verification logic (e.g., [ERC-1923](./eip-1923.md)), ZKMeta focuses solely on metadata discovery. By standardizing how to locate the *verification key*, *public input schema*, and *circuit identity*, this standard creates a "ZK ABI" that unlocks capabilities previously impossible in a fragmented ecosystem: - **Universal ZK Explorers:** Block explorers can automatically fetch input schemas to decode and display opaque proof inputs (e.g., "decoding" a ZK-tx's public signals similar to how ABIs decode call data), rather than showing raw hex strings. - **Decentralized Proving Markets:** Solvers and prover networks can programmatically discover new jobs, fetch the required circuit artifacts (Wasm/zkey) via URI, and submit proofs without needing manual integration for every new dApp. - **Automated Security Auditing:** Security tooling can track `circuitVersion` changes to alert users if a protocol silently downgrades to an older, vulnerable circuit or changes constraints without announcement. ## Specification The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.rfc-editor.org/rfc/rfc2119) and [RFC 8174](https://www.rfc-editor.org/rfc/rfc8174). ### Interface ```solidity /// @title ZKMeta Interface interface IZKMetadata { /// Emitted when the stored circuit metadata is modified. event CircuitMetadataUpdated(bytes32 indexed circuitId, uint64 circuitVersion, bytes4 proofSystem); /// Content-addressed identifier of the circuit definition artifact. function circuitId() external view returns (bytes32); /// Monotonically increasing semver-style version (major.minor -> uint32.uint32 packed into uint64). function circuitVersion() external view returns (uint64); /// Hash of the canonical public-input schema document. function publicInputsSchemaHash() external view returns (bytes32); /// URI of the canonical public-input schema document. function publicInputsSchemaURI() external view returns (string memory); /// URI for verification key discovery (content-addressed or URI with trailing #hash). function verificationKeyURI() external view returns (string memory); /// Proof-system identifier (e.g., 0x0001 Groth16, 0x0002 Plonk, 0x0003 Halo2). function proofSystem() external view returns (bytes4); } ``` ### Proof-System Registry | Identifier | Proof System | Notes | |-----------:|--------------|-----------------------------------| | `0x0001` | Groth16 | BN254 (e.g., snarkjs) | | `0x0002` | Plonk | BN254 variant | | `0x0003` | Halo2 | Plonkish family | | `0x0004` | zkSTARK | General STARK provers | | `0x0005` | zkVM | e.g., RISC-V/Jolt/RISC Zero | Additional identifiers MUST be proposed in the discussion thread and MUST NOT collide. Unknown identifiers SHOULD be treated as unsupported by tooling. ### Requirements - `circuitId()` MUST be a content hash (e.g., keccak256 or multihash) of the canonical circuit artifact used to derive the verification key. - `circuitVersion()` MUST increase upon any breaking change to constraints or public-input semantics. Projects SHOULD use the high 32 bits for major and low 32 bits for minor. - `publicInputsSchemaHash()` MUST match the document at `publicInputsSchemaURI()`. - `publicInputsSchemaURI()` and `verificationKeyURI()` SHOULD be content-addressed (`ipfs://`, `ar://`, `bzz://`). If HTTPS is used, the URI MUST include a URL fragment containing the Keccak-256 hash of the resource content in hexadecimal format (e.g., `https://example.com/schema.json#0x...`) to ensure integrity. - `CircuitMetadataUpdated` SHOULD be emitted in the same transaction that makes new metadata observable. - Tooling MUST verify that `publicInputsSchemaHash()` matches the hash of the fetched schema document. - Tooling SHOULD verify that the verification key’s content hash matches `verificationKeyURI()`’s hash fragment. - `CircuitMetadataUpdated` MUST be emitted in the same transaction that makes new metadata observable to prevent indexer race conditions. ### Recommendations 1. **Content addressing** Prefer IPFS/Arweave/Swarm CIDs or HTTPS with `#hash` to ensure immutability and reproducibility. 2. **Versioning discipline** Treat schema or circuit constraint changes as *major*; cosmetic or doc updates as *minor*. 3. **Indexing** Indexers and explorers SHOULD subscribe to `CircuitMetadataUpdated` instead of polling getters. ## Rationale - **Hash + URI for schemas/keys** enables automatic discovery while preserving integrity. - **Event-based updates** let tooling react to changes without polling. - **Compact `bytes4` proof-system code** minimizes calldata while remaining extensible. - **Proof-system neutrality** avoids locking the ecosystem to any single proving stack. ## Backwards Compatibility Existing contracts may expose an adapter that implements `IZKMetadata`. Legacy systems can deploy a read-only facade or off-chain router for downstream tooling. No existing ERCs are modified. ## Reference Implementation ```solidity // SPDX-License-Identifier: CC0-1.0 pragma solidity ^0.8.20; interface IZKMetadata { event CircuitMetadataUpdated(bytes32 indexed circuitId, uint64 circuitVersion, bytes4 proofSystem); function circuitId() external view returns (bytes32); function circuitVersion() external view returns (uint64); function publicInputsSchemaHash() external view returns (bytes32); function publicInputsSchemaURI() external view returns (string memory); function verificationKeyURI() external view returns (string memory); function proofSystem() external view returns (bytes4); } contract ZKMetadataAdapter is IZKMetadata { bytes32 private _cid; uint64 private _version; bytes32 private _schemaHash; string private _schemaURI; string private _vkURI; bytes4 private _ps; constructor( bytes32 cid, uint64 version, bytes32 schemaHash, string memory schemaURI, string memory vkURI, bytes4 proofSystemId ) { _cid = cid; _version = version; _schemaHash = schemaHash; _schemaURI = schemaURI; _vkURI = vkURI; _ps = proofSystemId; emit CircuitMetadataUpdated(_cid, _version, _ps); } function circuitId() external view returns (bytes32) { return _cid; } function circuitVersion() external view returns (uint64) { return _version; } function publicInputsSchemaHash() external view returns (bytes32) { return _schemaHash; } function publicInputsSchemaURI() external view returns (string memory) { return _schemaURI; } function verificationKeyURI() external view returns (string memory) { return _vkURI; } function proofSystem() external view returns (bytes4) { return _ps; } /// Example admin update; replace with proper access control in production. function _adminUpdate( bytes32 cid, uint64 version, bytes32 schemaHash, string calldata schemaURI, string calldata vkURI, bytes4 proofSystemId ) external { _cid = cid; _version = version; _schemaHash = schemaHash; _schemaURI = schemaURI; _vkURI = vkURI; _ps = proofSystemId; emit CircuitMetadataUpdated(_cid, _version, _ps); } } ``` ## Security Considerations ### Schema Integrity If tooling does not verify `publicInputsSchemaHash()` against the downloaded document, a malicious frontend or gateway could serve a modified schema. This would allow an attacker to mislabel public inputs (e.g., swapping "Token Amount" for "Nonce"), deceiving users about what the proof actually attests to. ### Verification Key Mutability Relying on HTTP URIs without hash fragments allows a server administrator to silently swap the verification key. This could enable the server admin to create fake proofs for a new (compromised) circuit while the contract still points to the old URI. ### Indexer Race Conditions If the `CircuitMetadataUpdated` event is not emitted atomically with the state change, off-chain indexers might read stale metadata values (e.g., an old verification key) while processing the update event, leading to widespread denial of service for proof generation. ## Copyright Copyright and related rights waived via [CC0](../LICENSE.md).