---
eip: 7992
title: Verifiable ML Model Inference (ZKML)
description: Interfaces for registering ML model commitments and verifying zero-knowledge proofs of their inferences on-chain.
author: Aryaethn (@aryaethn)
discussions-to: https://ethereum-magicians.org/t/erc-7992-verifiable-ml-model-inference-zkml/24896
status: Draft
type: Standards Track
category: ERC
created: 2025-07-23
requires: 165
---

## Abstract

This ERC standardizes how smart contracts reference machine-learning (ML) models and accept zero-knowledge attestations of their inferences. It defines a registry that issues a `modelId` for a `ModelCommitment`, hashes of the model’s weights/architecture, proving circuit/AIR, and verifying key, along with a `proofSystemId` for the proving system, and exposes discoverability via [ERC-165](./eip-165.md). A verifier interface provides `verifyInference(modelId, inputCommitment, output, proof)`: it retrieves the model commitment, dispatches verification to the declared proof system, and reverts on any mismatch or invalid proof; success implies validity and emits `InferenceVerified`. Inputs are bound by domain-separated commitments (nonceable for replay protection), outputs are ABI-encoded bytes whose schema can be application-defined or additionally committed on-chain, and proof systems (e.g., Groth16/Plonk/STARK) are pluggable without ABI changes. An optional extension persists verified inference records to enable auditability and deterministic settlement.


## Motivation

Smart contracts can’t run large ML models, and they can’t trust an oracle’s claim about a model’s output. 
Today, projects either (1) trust a centralized server, (2) cripple models to fit on-chain,  or (3) rely on social committees. None provide cryptographic assurance.

Zero-knowledge ML (ZKML) fixes the trust gap by letting a prover show—succinctly and privately—that a specific model, with specific inputs, produced a specific output. 
But without a shared interface, every dApp/verifier pair is bespoke: different ABIs, different registry schemas, poor composability.

This ERC standardizes that on-chain boundary:
- Registry: publish immutable commitments to model weights/architecture/circuits so callers know exactly which model they’re referencing.
- Verifier: a uniform function to validate inference proofs, independent of proof system (Groth16, Plonk, STARKs, …).

Benefits include:
- Trustless, composable “AI oracles” for DeFi risk, prediction markets, insurance, etc.
- Protection of proprietary models and private inputs while still guaranteeing correctness.
- Deterministic, dispute-free settlement for complex computations.
- Lower integration and audit overhead via consistent events, structs, and revert semantics.
- Future-proofing as proving systems evolve—only implementations change, not integrators.
- Clear security expectations (e.g., nonce usage to prevent replays) baked into the spec.

In short, this ERC turns verifiable ML inference into a reusable primitive—doing for AI outputs what [ERC-20](./eip-20.md)/[ERC-721](./eip-721.md) did for assets.

## 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).

### Terminology & IDs

-	Model Commitment: A bundle of hashes that ties together the model’s weights, architecture, proving circuit/Algebraic intermediate representation (AIR), and verifying key.
-	`modelId` (`uint256`): A unique identifier returned by the registry upon model registration.
-	`inputCommitment` (`bytes32`): A hash commitment to all private inputs (and any declared public inputs) for an inference. Implementations MUST domain-separate and SHOULD include a nonce/salt when single-use or non-deterministic behavior is possible.
-	`output` (`bytes`): ABI-encoded public outputs of the inference. Consumers MUST agree on its schema and MAY validate it via an outputCommitment (not included in the minimal interface).
-	`proof` (`bytes`): The ZK proof blob.
-	`proofSystemId` (`bytes4`): Identifier of the proving system + curve + version used by the circuit.

`proofSystemId` MUST equal the first four bytes of:

```bytes4(keccak256(abi.encodePacked(<canonical-proof-system-name-and-version>)))```

Where `<canonical-proof-system-name-and-version>` is a lowercase, hyphen-separated string, e.g.:
	-	"groth16-bn254-v1"
	-	"plonk-bn254-v2"
	-	"stark-airfoo-v1"

This method ensures deterministic, collision-resistant identifiers across implementations.

### Interfaces

#### ZKML Registry 

``` solidity
// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.20;

interface IERCZKMLRegistry /* is IERC165 */ {
    struct ModelCommitment {
        bytes32 modelHash;     // weights + architecture hash
        bytes32 circuitHash;   // arithmetic circuit / AIR hash
        bytes32 vkHash;        // verifying key hash
        bytes4  proofSystemId; // keccak-based identifier
        string  uri;           // optional off-chain metadata (IPFS/HTTP)
    }

    event ModelRegistered(
        uint256 indexed modelId,
        address indexed owner,
        ModelCommitment commitment
    );

    event ModelUpdated(
        uint256 indexed modelId,
        ModelCommitment oldCommitment,
        ModelCommitment newCommitment
    );

    event ModelDeprecated(uint256 indexed modelId);

    error ModelNotFound(uint256 modelId);
    error NotModelOwner(uint256 modelId, address caller);
    error ModelDeprecated(uint256 modelId);

    function registerModel(ModelCommitment calldata commitment)
        external
        returns (uint256 modelId);

    function updateModel(uint256 modelId, ModelCommitment calldata newCommitment)
        external;

    function deprecateModel(uint256 modelId) external;

    function getModel(uint256 modelId)
        external
        view
        returns (ModelCommitment memory commitment, bool deprecated, address owner);
}
```
- `registerModel` MUST return a unique `modelId`.
- `updateModel` and `deprecateModel` MUST only be callable by the model `owner`.
-	`getModel` MUST return the current `commitment`, deprecation status, and `owner` address.
-	Implementations MAY allow versioning under one `modelId` or require a new `modelId` per change. The chosen policy MUST be documented.

#### ZKML Verifier

``` solidity
// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.20;

interface IERCZKMLVerifier /* is IERC165 */ {
    event InferenceVerified(
        uint256 indexed modelId,
        bytes32 indexed inputCommitment,
        bytes   output,
        address indexed caller
    );

    error InvalidProof();
    error ModelMismatch();            // proof verifies but not tied to given modelId
    error InputCommitmentMismatch();
    error OutputMismatch();           // if verifier checks output commitment/schema
    error UnsupportedProofSystem(bytes4 proofSystemId);
    error VerificationRefused_ModelDeprecated(uint256 modelId);

    /**
     * @notice Verifies a ZK proof for an inference.
     * @dev MUST revert on any failure path. Successful execution implies validity.
     * @param modelId         Registry model identifier
     * @param inputCommitment Commitment to private inputs
     * @param output          ABI-encoded public outputs
     * @param proof           ZK proof bytes
     */
    function verifyInference(
        uint256 modelId,
        bytes32 inputCommitment,
        bytes calldata output,
        bytes calldata proof
    ) external;

    /// Optional helper views:
    function proofSystemOf(uint256 modelId) external view returns (bytes4);
    function registry() external view returns (address registryAddress);
}
```
The `verifyInference`:

- MUST fetch the model `commitment` from the registry and validate the `proof` against it.
-	MUST revert on any failure (invalid proof, mismatched commitments, unsupported proof system, deprecated model, etc.).
-	SHOULD emit  `InferenceVerified` on success.
-	SHOULD remain stateless except for emitting events. Statefulness MAY be introduced by extensions.

#### Optional Storage Extension 

``` solidity
// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.20;

interface IERCZKMLStorageExtension {
    event InferenceStored(bytes32 indexed inferenceId);

    /**
     * @notice Verify and store an inference record.
     * @dev MUST revert on invalid proof.
     * @return inferenceId keccak256(abi.encodePacked(modelId, inputCommitment, output))
     */
    function verifyAndStoreInference(
        uint256 modelId,
        bytes32 inputCommitment,
        bytes calldata output,
        bytes calldata proof
    ) external returns (bytes32 inferenceId);

    function getInference(bytes32 inferenceId)
        external
        view
        returns (uint256 modelId, bytes32 inputCommitment, bytes memory output);
}
```

Replay Protection Note: Implementations that rely on 
`inferenceId = keccak256(modelId, inputCommitment, output)` 
MUST ensure that `inputCommitment` embeds a `nonce/salt` or other uniqueness source 
if replays are a concern (e.g., non-deterministic models or single-use inferences).

The registry interface exposes an `owner` per `modelId`. 
Implementations MUST include some ownership/access-control mechanism 
(e.g., simple owner storage, [ERC-173](./eip-173.md), [ERC-721](./eip-173.md) representation, or role-based control). 
The returned `owner` address SHOULD be treated as the canonical authority to mutate or deprecate that model.


## Rationale

-	Void Return on `verifyInference`: Reverting on failure and returning nothing on success removes redundant gas-expensive booleans and matches modern Solidity patterns (e.g., OpenZeppelin’s `SafeERC20`).
-	Separated Registry & Verifier: Encourages modularity—teams can upgrade verifiers or registries independently.
-	Opaque bytes for Proof/Output: Avoids lock-in to a specific proof system or output schema.
-	Deterministic `proofSystemId`: Prevents collisions and ambiguity; enables predictable dispatch in mixed-system verifiers.
-	Nonce in `inputCommitment`: Explicitly mitigates replay attacks when inference uniqueness matters.


## Backwards Compatibility

Fully backwards compatible:

- Uses [ERC-165](./eip-165.md) like popular [ERC-721](./eip-721.md) or [ERC-1155](./eip-1155.md) for discoverability.
- No dependency on token ownership standards; minimal collision with existing protocols.

## Reference Implementation

``` solidity
// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.20;

import "./IERCZKMLRegistry.sol";
import "./IERCZKMLVerifier.sol";

contract ZKMLVerifier is IERCZKMLVerifier {
    IERCZKMLRegistry public immutable override registry;

    constructor(IERCZKMLRegistry _registry) {
        registry = _registry;
    }

    function verifyInference(
        uint256 modelId,
        bytes32 inputCommitment,
        bytes calldata output,
        bytes calldata proof
    ) external override {
        (IERCZKMLRegistry.ModelCommitment memory cm, bool deprecated,) =
            registry.getModel(modelId);

        if (deprecated) revert ModelDeprecated(modelId);

        // Dispatch based on proofSystemId. Example only.
        // bool ok = VerifierLib.verify(proof, cm.vkHash, inputCommitment, output);
        bool ok = _dummyVerify(proof, cm.vkHash, inputCommitment, output);
        if (!ok) revert InvalidProof();

        emit InferenceVerified(modelId, inputCommitment, output, msg.sender);
    }

    function proofSystemOf(uint256 modelId) external view override returns (bytes4) {
        (IERCZKMLRegistry.ModelCommitment memory cm,,) = registry.getModel(modelId);
        return cm.proofSystemId;
    }

    function _dummyVerify(
        bytes calldata,
        bytes32,
        bytes32,
        bytes calldata
    ) private pure returns (bool) {
        return true;
    }
}
```


## Security Considerations

### Security

<!-- Editor's Note: any requirements (defined with UPPERCASE keywords) should go in the specification section, but they should be discussed here in more detail. -->

- Replay Attacks: Inputs must embed a nonce/salt where uniqueness matters. Contracts may also track consumed `inferenceIds`.
-	Model Commitment Drift: Updating commitments can invalidate proofs; consumers should pin specific `modelId` + `commitment` hashes or check deprecated.
-	Hash Domain Separation: Use distinct prefixes (e.g., "ZKML_MODEL_V1") to avoid collisions across contexts.
-	Output Ambiguity: Contracts must validate or commit to output schemas to avoid maliciously crafted bytes.
-	DoS via Heavy Verification: Consider off-chain verification with on-chain succinct attestations, or batching/aggregation.

### Gas

- Proof verification can dominate gas costs; splitting verification-only calls from storage writes lets integrators choose.
-	Events are cheaper than persistent storage for audit trails.


## Copyright

Copyright and related rights waived via [CC0](../LICENSE.md).

