// SPDX-License-Identifier: MIT pragma solidity 0.8.23; import "../zkverify/ISP1Verifier.sol"; /// @title AereCompliancePoolSP1Verifier /// @notice SP1-backed verifier adapter for AereCompliancePool. Bridges the /// pool's `IPrivacyPoolVerifier.verify(bytes proof, uint256[] pubs)` /// interface to the Succinct Labs SP1 verifier gateway. /// /// The off-chain prover is the Rust SP1 program at /// `zk-circuits/compliance-pool/program`. Its compiled VKEY is /// stored as a constructor-immutable here. The 8 public inputs /// the pool passes (poolId, depositRoot, associationRoot, /// nullifierHash, recipient, feeRecipient, refund, fee) are /// abi-encoded into the same struct the program commits to. /// /// @dev Drop-in replacement for `MockPrivacyPoolVerifier` in production /// deploys. Test deploys may continue to use the mock to avoid /// needing a real SP1 proof per test. contract AereCompliancePoolSP1Verifier { ISP1Verifier public immutable SP1; bytes32 public immutable PROGRAM_VKEY; error InvalidPublicInputCount(); error InvalidPublicValues(); constructor(ISP1Verifier sp1, bytes32 programVKey) { require(address(sp1) != address(0) && programVKey != bytes32(0), "zero"); SP1 = sp1; PROGRAM_VKEY = programVKey; } /// @notice Verify a withdraw proof. /// @param proof SP1-encoded proof bytes (Groth16 or PLONK over SP1 zkVM) /// @param pubs [poolId, depositRoot, associationRoot, nullifierHash, /// recipient, feeRecipient, refund, fee] /// @return ok True iff SP1 verifier accepts the proof against the /// ABI-encoded public-values struct. function verify(bytes calldata proof, uint256[] calldata pubs) external view returns (bool ok) { if (pubs.length != 8) revert InvalidPublicInputCount(); // The Rust program commits PublicValues = (bytes32 pool_id, // bytes32 deposit_root, bytes32 association_root, // bytes32 nullifier_hash, address recipient, address fee_recipient, // uint256 refund, uint256 fee). bytes memory publicValues = abi.encode( bytes32(pubs[0]), bytes32(pubs[1]), bytes32(pubs[2]), bytes32(pubs[3]), address(uint160(pubs[4])), address(uint160(pubs[5])), pubs[6], pubs[7] ); // SP1Verifier.verifyProof reverts on failure. Wrap in a low-level // staticcall so we can return bool to fit the existing pool // interface. (bool success, ) = address(SP1).staticcall( abi.encodeCall(ISP1Verifier.verifyProof, (PROGRAM_VKEY, publicValues, proof)) ); return success; } }