ERC-8229: FHE Computation Verification

Summary

I’m proposing a new ERC that defines a standard interface for on-chain verification of Fully Homomorphic Encryption (FHE) computation via recursive zero-knowledge proofs (IVC).

When a smart contract executes on encrypted data, nobody can inspect the computation — not the validators, not the deployer, not the users. This is the promise of FHE. But it introduces a trust problem: how do you know the computation was performed correctly if you can’t see it?

This standard solves it. A single compact proof (≤ 1 KB) attests to arbitrarily complex encrypted computations. Verified on-chain in O(1). The co-processor that ran the computation is untrusted by design — the proof is the only source of truth.

Repository: https://github.com/Valisthea/styx-erc-fhe-verification Author: Valisthea (@Valisthea) Related: ERC: Encrypted Token Standard, ERC: Cryptographic Amnesia Interface (submitted separately)

The Problem

FHE enables computation on encrypted data. Several projects are building FHE execution environments (Fhenix, Zama fhEVM, Sunscreen, TFHE-rs). But none of them have proposed a standard interface for verifying that the computation was correct.

Without verification, FHE on blockchain is just encrypted cloud computing with extra steps. You’re trusting the co-processor the same way you’d trust AWS — which defeats the entire purpose of decentralization.

The specific problems:

  1. Co-processor honesty — FHE computation is delegated to specialized hardware (GPUs, FPGAs). These machines are off-chain. Without verification, a malicious operator could return any result they want. An encrypted DeFi vault asks “is this position below the liquidation threshold?” — the co-processor could lie to protect a friendly whale, or lie to force-liquidate a competitor.

  2. Result integrity across contracts — When Contract A consumes the output of Contract B’s encrypted computation, A needs assurance that B’s result is correct. Today, each contract pair invents custom trust assumptions. There’s no standard way for one contract to verify another’s encrypted computation.

  3. Auditability — Regulators need to verify that encrypted financial computations (tax calculations, solvency proofs, compliance checks) were performed correctly — without seeing the underlying data. No standard interface exists for this.

  4. Multi-step pipelines — Complex DeFi protocols chain encrypted computations: encrypted oracle feed → encrypted risk calculation → encrypted liquidation check → encrypted settlement. If step 2 is wrong, everything downstream is wrong. Verification at each step prevents cascading failures.

How It Works

Three-phase architecture

Phase 1 — Circuit Registration. A developer writes an encrypted smart contract (in SSL, fhEVM Solidity, or any FHE-compatible language). The compiler produces a circuit (sequence of FHE gates). The circuit’s hash and verification key hash are registered on-chain via registerCircuit(). This binds a specific computation to a specific verification key — preventing a prover from substituting a different (trivial) circuit.

Phase 2 — Execution & Proof Generation. A user submits an encrypted transaction. The FHE co-processor executes the circuit on the encrypted inputs. As it executes, it generates an IVC (Incrementally Verifiable Computation) proof: each gate-level proof is “folded” into a running accumulator. The final output is a single constant-size SNARK — regardless of how many gates the circuit has. 10 gates or 10 million gates: same proof size, same verification cost.

Phase 3 — On-chain Verification. The co-processor submits the proof via submitProof(). The contract verifies it against the registered verification key. If valid, the result commitment is stored on-chain. Any dependent contract can check isResultVerified(executionId) before consuming the result.

Core Interface

solidity

interface IERCZZZZ {
    // Circuit registry
    function registerCircuit(bytes32 circuitHash, bytes32 verificationKeyHash, uint256 gateCount, string calldata schemeId) external;
    function isCircuitActive(bytes32 circuitHash) external view returns (bool);
    
    // Proof submission (executionId computed on-chain, not caller-provided)
    function submitProof(bytes32 circuitHash, bytes32 inputCommitment, bytes32 resultCommitment, bytes calldata proof, bytes calldata verificationKey) external returns (bytes32 executionId);
    
    // Result queries
    function isResultVerified(bytes32 executionId) external view returns (bool);
    function verifiedResultCommitment(bytes32 executionId) external view returns (bytes32);
    function verifyResultProvenance(bytes32 executionId, bytes32 circuitHash, bytes32 inputCommitment, bytes32 resultCommitment) external view returns (bool);
    
    // Dispute (optimistic verification)
    function disputeResult(bytes32 executionId, bytes calldata counterProof) external;
    function isDisputed(bytes32 executionId) external view returns (bool);
    
    // Events
    event CircuitRegistered(bytes32 indexed circuitHash, address indexed registrant, uint256 gateCount, string schemeId);
    event ExecutionVerified(bytes32 indexed executionId, bytes32 indexed circuitHash, address indexed prover, bytes32 resultCommitment);
    event ResultDisputed(bytes32 indexed executionId, address indexed challenger, bytes32 counterProofHash);
}

Extensions

Prover Registry — stake-and-slash mechanism for co-processors. Provers register with collateral. Bad proofs get slashed, with a reward to the challenger. Aligns co-processor incentives with honest computation.

Computation ChainingisChainedExecution(executionA, executionB) verifies that B’s input is provably A’s verified output. verifyExecutionChain(executionIds[]) validates an entire pipeline atomically.

Key Design Decisions

Verification key hash on-chain, full key at proof time. Verification keys for IVC circuits can be 1-10 MB. Storing them on-chain would exceed block gas limits. We store only the hash on-chain; the prover provides the full key when submitting a proof, and the contract checks it against the stored hash. This is the standard approach used by Aztec, zkSync, and RiscZero.

ExecutionId computed on-chain. Earlier designs accepted caller-provided execution IDs. This creates a front-running vector: an attacker predicts a future executionId and pre-submits a proof with a different result. On-chain computation (keccak256(circuitHash, inputCommitment, resultCommitment, msg.sender, block.number)) makes IDs non-predictable.

Optimistic verification with dispute. Once a proof is verified, the result is trusted immediately. But a dispute mechanism allows challenging results if a verifier bug or proof system flaw is discovered later. This mirrors the optimistic rollup model: fast finality with a safety net. isResultVerified(id) && !isDisputed(id) is the complete trust check.

Proof binds to prover address. The proof’s public inputs include the prover’s address. A proof generated by Prover A cannot be submitted by Prover B. This prevents MEV searchers from extracting proofs from the mempool and front-running the legitimate prover.

Batch submission is atomic. In a multi-step pipeline, partial verification is worse than no verification. If step 3 of 5 fails, the entire batch reverts. All-or-nothing.

Interaction with Other Standards

This is the verification layer (Prism / L2) of the STYX Protocol stack:

With the Encrypted Token Standard: Every blindTransfer() involves an FHE computation (homomorphic balance update). The balance update circuit is registered here. The co-processor generates an IVC proof for each transfer. The Encrypted Token contract checks isResultVerified() before applying the balance change.

With the Cryptographic Amnesia Interface: After an amnesia ceremony destroys the encryption key, the verification keys for those circuits remain valid. Historical proofs can still be verified (audit trail preserved), but no new computations can be performed (the encryption key is gone). This is intentional: you can prove past computations were correct, but you can’t decrypt the data.

Prior Art

I’m not aware of any ERC standardizing FHE computation verification. Related work in the ZK verification space:

  • ERC-7520 (zk-SNARK Verifier Standard) — focuses on generic SNARK verification, not FHE-specific computation. Doesn’t address circuit registration, result consumption, or computation chaining.

  • Axiom — on-chain ZK co-processor for historical Ethereum state proofs. Different scope (reading historical data, not FHE computation).

  • Brevis — ZK proof marketplace. Application-level, not a standard interface.

  • RiscZero Bonsai — off-chain ZK compute with on-chain verification. Proprietary API, not a standard.

None of these define a standard for FHE-specific verification with circuit registration, computation chaining, and dispute mechanisms.

Questions for the Community

  1. Proof system mandate — should the standard RECOMMEND a specific IVC system (Nova) or remain fully agnostic? Agnostic is more flexible but complicates interop (different provers may use different proof systems for the same circuit).

  2. Verification key storage — hash on-chain (our approach) vs. deploying a per-circuit verifier contract (like some zkRollups do). The verifier contract approach is more expensive to deploy but cheaper per-verification. Which model does the community prefer?

  3. Dispute window — should there be a mandatory dispute window (e.g., 7 days) during which results can be challenged? Or should dispute be perpetual (challengeable forever)? Perpetual is safer but creates indefinite uncertainty for dependent contracts.

  4. Gas cost transparency — should submitProof() have a predictable, constant gas cost regardless of circuit complexity? The IVC folding guarantees constant proof size, but some implementations may have variable verification costs. Should constant gas be a MUST or a SHOULD?

  5. Circuit upgradeability — the draft includes registerCircuitUpgrade() for patching buggy circuits while preserving historical verification. Is this over-engineering, or essential for production deployments?

  6. Cross-standard dependencies — this standard interacts heavily with the Encrypted Token Standard and the Cryptographic Amnesia Interface. Should all three reference each other formally in the requires field, or should they remain independent and describe interactions only in the Rationale?

If anyone from the Fhenix, Zama, Sunscreen, or Inco teams is reading this — I’d especially value your input on the FHE-specific aspects.

— Valisthea

1 Like