I am working on a smart contract that uses Risc Zero to verify proofs generated from a Rust program. I have successfully generated the seal, imageID, and journalDigest from the Risc Zero Rust program. The proof can be verified off-chain using a Rust script, but I encounter an error when trying to verify it on-chain.
Steps to Reproduce
- Generate the proof (seal, imageID, and journalDigest) using the Risc Zero Rust program.
- Verify the proof off-chain using a Rust script (successful).
- Deploy the RiscZeroGroth16Verifier contract on a local testnet and Binance Smart Chain testnet.
- Submit the seal, imageID, and journalDigest to the on-chain verifier contract.
Expected Behavior
The proof should be verified successfully on-chain, just as it is verified off-chain.
Actual Behavior
The transaction reverts with the following error: call to RiscZeroGroth16Verifier.verify errored: Error occurred: revert. revert The transaction has been reverted to the initial state. Error provided by the contract: VerificationFailed : Error raised when cryptographic verification of the zero-knowledge proof fails. Parameters: {} Additional Information
- I am using the exact source code from the RiscZeroGroth16Verifier contract deployed on Ethereum by Risc Zero.
- The proof verification works off-chain with the same seal, imageID, and journalDigest.
- I have tried increasing the gas limit, but the issue persists.
Code Snippets
Smart Contract Code (simplified):
function _verifyIntegrity(bytes calldata seal, bytes32 claimDigest) internal view { // Check that the seal has a matching selector. Mismatch generally indicates that the // prover and this verifier are using different parameters, and so the verification // will not succeed. if (SELECTOR != bytes4(seal[:4])) { revert SelectorMismatch({received: bytes4(seal[:4]), expected: SELECTOR}); } // Run the Groth16 verify procedure. (bytes16 claim0, bytes16 claim1) = splitDigest(claimDigest); Seal memory decodedSeal = abi.decode(seal[4:], (Seal)); bool verified = this.verifyProof( decodedSeal.a, decodedSeal.b, decodedSeal.c, [ uint256(uint128(CONTROL_ROOT_0)), uint256(uint128(CONTROL_ROOT_1)), uint256(uint128(claim0)), uint256(uint128(claim1)), uint256(BN254_CONTROL_ID) ] ); // Revert is verification failed. if (!verified) { revert VerificationFailed(); } } function verify(bytes calldata seal, bytes32 imageId, bytes32 journalDigest) external view { _verifyIntegrity(seal, ReceiptClaimLib.ok(imageId, journalDigest).digest()); } Verifier Parameters Used During Deployment
control_root (bytes32): 0x8cdad9242664be3112aba377c5425a4df735eb1c6966472b561d2855932c0469 bn254_control_id (bytes32): 0x04446e66d300eb7fb45c9726bb53c793dda407a62e9601618bb43c5c14657ac0 Transaction Details:
call to RiscZeroGroth16Verifier.verify errored: Error occurred: revert. revert The transaction has been reverted to the initial state. Error provided by the contract: VerificationFailed : Error raised when cryptographic verification of the zero-knowledge proof fails. Parameters: {} If the transaction failed for not having enough gas, try increasing the gas limit gently. Environment
- Solidity Version: 0.8.26+commit.8a97fa7a
- Ethereum Testnet Contract: https://etherscan.io/address/0xAC292cF957Dd5BA174cdA13b05C16aFC71700327
- Binance Smart Chain Testnet: https://testnet.bscscan.com/address/0x21ee758fe996c8b6bef57cf81b35fe9f4057e037
Question
Why does the proof verification fail on-chain but succeed off-chain? What could be the potential issues, and how can I resolve them?
Are there additional parameters (e.g., public inputs, verification keys) that need to be set before calling the verify function?
Are there differences in how verifier parameters should be configured on-chain vs. off-chain?