diff --git a/src/solidity/.env.example b/src/solidity/.env.example index 36af67f64..36bda4458 100644 --- a/src/solidity/.env.example +++ b/src/solidity/.env.example @@ -5,4 +5,13 @@ PRIVATE_KEY=your_private_key_here # Required for Sepolia, not needed for Anvil SEPOLIA_RPC_URL=https://sepolia.infura.io/v3/your_infura_key_here # Required for Sepolia, not needed for Anvil # API Keys -ETHERSCAN_API_KEY=your_etherscan_api_key_here # Optional: Only needed for contract verification on Sepolia \ No newline at end of file +ETHERSCAN_API_KEY=your_etherscan_api_key_here # Optional: Only needed for contract verification on Sepolia + +# Root registry address +ROOT_REGISTRY_ADDRESS=0xB6bF4a45D5Ed1363C45BD0e4cbaDCcd48F8D3FaB + +# Root verifier admin address +ROOT_VERIFIER_ADMIN_ADDRESS=0x1111111111111111111111111111111111111111 + +# Root verifier guardian address +ROOT_VERIFIER_GUARDIAN_ADDRESS=0x2222222222222222222222222222222222222222 diff --git a/src/solidity/script/AddVerifiers.s.sol b/src/solidity/script/AddSubVerifiers.s.sol similarity index 97% rename from src/solidity/script/AddVerifiers.s.sol rename to src/solidity/script/AddSubVerifiers.s.sol index 71e5ed73a..ae614864b 100644 --- a/src/solidity/script/AddVerifiers.s.sol +++ b/src/solidity/script/AddSubVerifiers.s.sol @@ -102,9 +102,9 @@ contract Deploy is Script { console.log("Outer (13 subproofs) verifier deployed at:", verifierAddresses[9]); // Add verifiers to ZKPassportVerifier - console.log("Adding verifiers to ZKPassportVerifier..."); - zkPassportVerifier.addVerifiers(vkeyHashes, verifierAddresses); - console.log("Verifiers added to ZKPassportVerifier"); + console.log("Adding sub-verifiers to ZKPassportVerifier..."); + zkPassportVerifier.addSubVerifiers(vkeyHashes, verifierAddresses); + console.log("Sub-verifiers added to ZKPassportVerifier"); // Stop broadcasting transactions vm.stopBroadcast(); diff --git a/src/solidity/script/Deploy.s.sol b/src/solidity/script/Deploy.s.sol index 0b960cf92..98e264046 100644 --- a/src/solidity/script/Deploy.s.sol +++ b/src/solidity/script/Deploy.s.sol @@ -99,9 +99,9 @@ contract Deploy is Script { console.log("ZKPassportVerifier deployed at:", address(zkPassportVerifier)); // Add verifiers to ZKPassportVerifier - console.log("Adding verifiers to ZKPassportVerifier..."); - zkPassportVerifier.addVerifiers(vkeyHashes, verifierAddresses); - console.log("Verifiers added to ZKPassportVerifier"); + console.log("Adding sub-verifiers to ZKPassportVerifier..."); + zkPassportVerifier.addSubVerifiers(vkeyHashes, verifierAddresses); + console.log("Sub-verifiers added to ZKPassportVerifier"); // Stop broadcasting transactions vm.stopBroadcast(); diff --git a/src/solidity/script/DeployWithExistingVerifiers.s.sol b/src/solidity/script/DeployWithExistingVerifiers.s.sol index dc27bea97..9c04059c3 100644 --- a/src/solidity/script/DeployWithExistingVerifiers.s.sol +++ b/src/solidity/script/DeployWithExistingVerifiers.s.sol @@ -57,15 +57,15 @@ contract Deploy is Script { ZKPassportVerifier zkPassportVerifier = new ZKPassportVerifier(rootRegistry); console.log("ZKPassportVerifier deployed at:", address(zkPassportVerifier)); - // Add verifiers to ZKPassportVerifier - console.log("Adding verifiers to ZKPassportVerifier..."); - zkPassportVerifier.addVerifiers(vkeyHashes, verifierAddresses); - console.log("Verifiers added to ZKPassportVerifier"); + // Add sub-verifiers to ZKPassportVerifier + console.log("Adding sub-verifiers to ZKPassportVerifier..."); + zkPassportVerifier.addSubVerifiers(vkeyHashes, verifierAddresses); + console.log("Sub-verifiers added to ZKPassportVerifier"); // Stop broadcasting transactions vm.stopBroadcast(); - // Create JSON for verifiers + // Create JSON for sub-verifiers string memory verifiers = "verifiers"; vm.serializeAddress(verifiers, "outer_count_4", verifierAddresses[0]); vm.serializeAddress(verifiers, "outer_count_5", verifierAddresses[1]); diff --git a/src/solidity/src/SampleContract.sol b/src/solidity/src/SampleContract.sol index 1a348c972..a84b43e19 100644 --- a/src/solidity/src/SampleContract.sol +++ b/src/solidity/src/SampleContract.sol @@ -4,14 +4,15 @@ pragma solidity >=0.8.21; import {DateUtils} from "../src/DateUtils.sol"; import {StringUtils} from "../src/StringUtils.sol"; -import {DisclosedData} from "../src/Types.sol"; -import {ZKPassportVerifier, ProofType, ProofVerificationParams, DisclosedData} from "../src/ZKPassportVerifier.sol"; +import {DisclosedData, ProofVerificationParams} from "../src/Types.sol"; +import {ZKPassportVerifier} from "../src/ZKPassportVerifier.sol"; +import {ZKPassportRootVerifier} from "../src/ZKPassportRootVerifier.sol"; import {console} from "forge-std/console.sol"; contract SampleContract { address public admin; - ZKPassportVerifier public zkPassportVerifier; - + uint256 public constant ZKPASSPORT_VERIFIER_VERSION = 1; + ZKPassportRootVerifier public zkPassportRootVerifier; // Unique Identifier => whether it was verified or not mapping(bytes32 => bool) public isVerified; // Unique Identifier => nationality @@ -34,8 +35,8 @@ contract SampleContract { _; } - function setZKPassportVerifier(address _zkPassportVerifier) public onlyAdmin { - zkPassportVerifier = ZKPassportVerifier(_zkPassportVerifier); + function setZKPassportVerifier(address _verifier) public onlyAdmin { + zkPassportRootVerifier = ZKPassportRootVerifier(_verifier); } function setDomain(string calldata _domain) public onlyAdmin { @@ -59,25 +60,28 @@ contract SampleContract { // and the SDK will tell you which one they have bool isIDCard ) public returns (bytes32) { - (bool verified, bytes32 uniqueIdentifier) = zkPassportVerifier.verifyProof(params); + // Get the current verifier from the ZKPassportRootVerifier + ZKPassportVerifier verifier = ZKPassportVerifier(zkPassportRootVerifier.getVerifier(ZKPASSPORT_VERIFIER_VERSION)); + // Verify the proof + (bool verified, bytes32 uniqueIdentifier) = verifier.verifyProof(params); require(verified, "Proof is invalid"); require(!isVerified[uniqueIdentifier], "User already verified"); // Check the proof was generated using your domain name (scope) and the subscope // you specified require( - zkPassportVerifier.verifyScopes(params.proofVerificationData.publicInputs, validDomain, validScope), + verifier.verifyScopes(params.proofVerificationData.publicInputs, validDomain, validScope), "Invalid domain or scope" ); - require(zkPassportVerifier.isAgeAboveOrEqual(18, params.commitments), "Age is not 18+"); - DisclosedData memory disclosedData = zkPassportVerifier.getDisclosedData( + require(verifier.isAgeAboveOrEqual(18, params.commitments), "Age is not 18+"); + DisclosedData memory disclosedData = verifier.getDisclosedData( params.commitments, - isIDCard + isIDCard ); string[] memory nationalityExclusionList = new string[](3); nationalityExclusionList[0] = "ESP"; nationalityExclusionList[1] = "ITA"; nationalityExclusionList[2] = "PRT"; - require(zkPassportVerifier.isNationalityOut(nationalityExclusionList, params.commitments), "Nationality is part of the exclusion list"); + require(verifier.isNationalityOut(nationalityExclusionList, params.commitments), "Nationality is part of the exclusion list"); // If all good, mark the user as verified isVerified[uniqueIdentifier] = true; diff --git a/src/solidity/src/Types.sol b/src/solidity/src/Types.sol index f458c80d8..2a6b656c9 100644 --- a/src/solidity/src/Types.sol +++ b/src/solidity/src/Types.sol @@ -2,6 +2,12 @@ // Copyright 2025 ZKPassport pragma solidity >=0.8.21; +interface IZKPassportVerifier { + function verifyProof( + ProofVerificationParams calldata params + ) external view returns (bool valid, bytes32 uniqueIdentifier); +} + enum ProofType { DISCLOSE, AGE, @@ -99,4 +105,4 @@ struct BoundData { address senderAddress; uint256 chainId; string customData; -} \ No newline at end of file +} diff --git a/src/solidity/src/ZKPassportRootVerifier.sol b/src/solidity/src/ZKPassportRootVerifier.sol new file mode 100644 index 000000000..e9edb0ed0 --- /dev/null +++ b/src/solidity/src/ZKPassportRootVerifier.sol @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2025 ZKPassport +pragma solidity >=0.8.21; + +import {IZKPassportVerifier, ProofVerificationParams} from "./Types.sol"; + +contract ZKPassportRootVerifier { + address public admin; + address public guardian; + bool public paused; + + mapping(uint256 => IZKPassportVerifier) public verifiers; + + // Events + event AdminUpdated(address indexed oldAdmin, address indexed newAdmin); + event GuardianUpdated(address indexed oldGuardian, address indexed newGuardian); + event VerifierAdded(uint256 indexed version, address indexed verifier); + event VerifierRemoved(uint256 indexed version, address indexed verifier); + event VerifierUpdated(uint256 indexed version, address indexed oldVerifier, address indexed newVerifier); + event PausedStatusChanged(bool paused); + + /** + * @dev Constructor + * @param _admin The address of the admin + * @param _guardian The address of the guardian + * @param _version The initial version number for the verifier + * @param _verifier The address of the ZKPassport verifier implementation + */ + constructor(address _admin, address _guardian, uint256 _version, address _verifier) { + require(_admin != address(0), "Admin cannot be zero address"); + require(_verifier != address(0), "Verifier cannot be zero address"); + require(_version > 0, "Version must be greater than 0"); + admin = _admin; + guardian = _guardian; + verifiers[_version] = IZKPassportVerifier(_verifier); + emit VerifierAdded(_version, _verifier); + } + + /** + * @notice Verifies a ZKPassport proof using a specific verifier version + * @param version The version number of the verifier to use + * @param params The proof verification parameters + * @return valid True if the proof is valid or false otherwise + * @return uniqueIdentifier The unique identifier associated with the ID used to generate the proof + */ + function verify( + uint256 version, + ProofVerificationParams calldata params + ) external view whenNotPaused returns (bool valid, bytes32 uniqueIdentifier) { + IZKPassportVerifier verifier = verifiers[version]; + require(address(verifier) != address(0), "Verifier not found for version"); + (valid, uniqueIdentifier) = verifier.verifyProof(params); + return (valid, uniqueIdentifier); + } + + modifier onlyAdmin() { + require(msg.sender == admin, "Not authorized: admin only"); + _; + } + + modifier onlyGuardian() { + require(msg.sender == guardian, "Not authorized: guardian only"); + _; + } + + modifier whenNotPaused() { + require(!paused, "Contract is paused"); + _; + } + + function transferAdmin(address newAdmin) external onlyAdmin { + require(newAdmin != address(0), "Admin cannot be zero address"); + address oldAdmin = admin; + admin = newAdmin; + emit AdminUpdated(oldAdmin, newAdmin); + } + + function setGuardian(address newGuardian) external onlyAdmin { + address oldGuardian = guardian; + guardian = newGuardian; + emit GuardianUpdated(oldGuardian, newGuardian); + } + + /** + * @notice Gets the verifier address for a specific version + * @param version The version number + * @return The verifier address for the given version + */ + function getVerifier(uint256 version) external view returns (address) { + return address(verifiers[version]); + } + + /** + * @notice Adds a verifier for a specific version + * @param version The version number + * @param newVerifier The address of the verifier + */ + function addVerifier(uint256 version, address newVerifier) external onlyAdmin { + require(newVerifier != address(0), "Verifier cannot be zero address"); + require(version > 0, "Version must be greater than 0"); + require((address)(verifiers[version]) == address(0), "Verifier already exists for version"); + verifiers[version] = IZKPassportVerifier(newVerifier); + emit VerifierAdded(version, newVerifier); + } + + /** + * @notice Removes a verifier for a specific version + * @param version The version number + */ + function removeVerifier(uint256 version) external onlyAdmin { + require(version > 0, "Version must be greater than 0"); + address verifierAddress = address(verifiers[version]); + require(verifierAddress != address(0), "Verifier not found for version"); + delete verifiers[version]; + emit VerifierRemoved(version, verifierAddress); + } + + /** + * @notice Updates a verifier for a specific version + * @param version The version number + * @param newVerifier The address of the verifier + */ + function updateVerifier(uint256 version, address newVerifier) external onlyAdmin { + require(version > 0, "Version must be greater than 0"); + require(newVerifier != address(0), "Verifier cannot be zero address"); + require((address)(verifiers[version]) != address(0), "Verifier not found for version"); + address oldVerifier = address(verifiers[version]); + verifiers[version] = IZKPassportVerifier(newVerifier); + emit VerifierUpdated(version, oldVerifier, newVerifier); + } + + function setPaused(bool _paused) external onlyGuardian { + paused = _paused; + emit PausedStatusChanged(_paused); + } +} diff --git a/src/solidity/src/ZKPassportVerifier.sol b/src/solidity/src/ZKPassportVerifier.sol index 2ad49a969..2e605f55f 100644 --- a/src/solidity/src/ZKPassportVerifier.sol +++ b/src/solidity/src/ZKPassportVerifier.sol @@ -19,7 +19,7 @@ contract ZKPassportVerifier { bool public paused; // Mapping from vkey hash of each Outer Circuit to its Ultra Honk Verifier address - mapping(bytes32 => address) public vkeyHashToVerifier; + mapping(bytes32 => address) public vkeyHashToSubVerifier; // Maybe make this immutable as this should most likely not change IRootRegistry public rootRegistry; @@ -28,8 +28,8 @@ contract ZKPassportVerifier { event AdminUpdated(address indexed oldAdmin, address indexed newAdmin); event PausedStatusChanged(bool paused); event ZKPassportVerifierDeployed(address indexed admin, uint256 timestamp); - event VerifierAdded(bytes32 indexed vkeyHash, address indexed verifier); - event VerifierRemoved(bytes32 indexed vkeyHash); + event SubVerifierAdded(bytes32 indexed vkeyHash, address indexed verifier); + event SubVerifierRemoved(bytes32 indexed vkeyHash); event CertificateRegistryRootAdded(bytes32 indexed certificateRegistryRoot); event CertificateRegistryRootRemoved(bytes32 indexed certificateRegistryRoot); event SanctionsTreesRootUpdates(bytes32 indexed _sanctionsTreesRoot); @@ -66,20 +66,20 @@ contract ZKPassportVerifier { emit PausedStatusChanged(_paused); } - function addVerifiers( + function addSubVerifiers( bytes32[] calldata vkeyHashes, address[] calldata verifiers ) external onlyAdmin { for (uint256 i = 0; i < vkeyHashes.length; i++) { - vkeyHashToVerifier[vkeyHashes[i]] = verifiers[i]; - emit VerifierAdded(vkeyHashes[i], verifiers[i]); + vkeyHashToSubVerifier[vkeyHashes[i]] = verifiers[i]; + emit SubVerifierAdded(vkeyHashes[i], verifiers[i]); } } - function removeVerifiers(bytes32[] calldata vkeyHashes) external onlyAdmin { + function removeSubVerifiers(bytes32[] calldata vkeyHashes) external onlyAdmin { for (uint256 i = 0; i < vkeyHashes.length; i++) { - delete vkeyHashToVerifier[vkeyHashes[i]]; - emit VerifierRemoved(vkeyHashes[i]); + delete vkeyHashToSubVerifier[vkeyHashes[i]]; + emit SubVerifierRemoved(vkeyHashes[i]); } } @@ -102,7 +102,7 @@ contract ZKPassportVerifier { * @param isIDCard Whether the proof is an ID card * @return disclosedData The data disclosed by the proof */ - function getDisclosedData( + function getDisclosedData( Commitments calldata commitments, bool isIDCard ) public pure returns (DisclosedData memory disclosedData) { @@ -589,7 +589,7 @@ contract ZKPassportVerifier { } function _getVerifier(bytes32 vkeyHash) internal view returns (address) { - address verifier = vkeyHashToVerifier[vkeyHash]; + address verifier = vkeyHashToSubVerifier[vkeyHash]; require(verifier != address(0), "Verifier not found"); return verifier; } @@ -665,7 +665,7 @@ contract ZKPassportVerifier { || params.serviceConfig.devMode, "Mock proofs are only allowed in dev mode" ); - + // For now, only non-salted nullifiers are supported // but salted nullifiers can be used in dev mode // They will be later once a proper registration mechanism is implemented diff --git a/src/solidity/src/ultra-honk-verifiers/OuterCount5.sol b/src/solidity/src/ultra-honk-verifiers/OuterCount5.sol index d851bcad6..f898925db 100644 --- a/src/solidity/src/ultra-honk-verifiers/OuterCount5.sol +++ b/src/solidity/src/ultra-honk-verifiers/OuterCount5.sol @@ -12,115 +12,115 @@ library HonkVerificationKey { circuitSize: uint256(4194304), logCircuitSize: uint256(22), publicInputsSize: uint256(25), - ql: Honk.G1Point({ + ql: Honk.G1Point({ x: uint256(0x0f6f6fc87950e3ca69783aa4d77e7a735b17fce6dde4f303a5de1fb37847a1ff), y: uint256(0x19c3937a312458c895c3501c0ca5e4531a7ea7a33aedf8efad898a09bcbd3550) }), - qr: Honk.G1Point({ + qr: Honk.G1Point({ x: uint256(0x1c9647a894e84339d443b1e225a224122e0f17bd5a3e020c1f8a6c100c890d8c), y: uint256(0x196f73efe001cc147b33b98b673b35c817a42a40536cecc5fac0f6197fc6b0c6) }), - qo: Honk.G1Point({ + qo: Honk.G1Point({ x: uint256(0x086dcdf90d7ca2a1a830879f58c34b0a83fb765c920ad1cc85be9d4e9dd405a8), y: uint256(0x2307f9d61a50fbe4c39f5066e50596625ccc80d5282425cd6b4c3db13e86051d) }), - q4: Honk.G1Point({ + q4: Honk.G1Point({ x: uint256(0x08ce1b25cb148283e18628d9827c3452d0266c7cee764b2ed5238ffdc1347828), y: uint256(0x24503cbd0eba60d85aab34b1efb9de20c8684d05e06af32a0394ab0e03b1c2e9) }), - qm: Honk.G1Point({ + qm: Honk.G1Point({ x: uint256(0x1984ab725314f6c5989c6415f0385380e5f98c89b66042cb1fddee1a873aa700), y: uint256(0x0538589fcc0aea6cf1c57ba0a0198a41bce13fac4a09c7441598dbae9eaa40da) }), - qc: Honk.G1Point({ + qc: Honk.G1Point({ x: uint256(0x2d4fdabf9686a9a9f6b9063fdc5ff2e373e9cc5bad962750c28da0e12dcfe057), y: uint256(0x07d86cc99842a46d8eb892d7ba332b7ec6c5269d0eecdaaa0f69d39d5f77e820) }), - qLookup: Honk.G1Point({ + qLookup: Honk.G1Point({ x: uint256(0x2e57b44b90da66faa200db697385d8231b6e8223f1073f97628acac5e2e0b28b), y: uint256(0x1f9f2a5af3fb5b557da39bd9120c9a4d0a4090a5c806ffff96310036df7fde40) }), - qArith: Honk.G1Point({ + qArith: Honk.G1Point({ x: uint256(0x044bb6406f7b91f5305ce10135ad122270d8e7685a4076360426b663cfc654cd), y: uint256(0x04f3dfb91d5c57b6dd1767c452120e0ff8c672dfe28303e57262aa4bdf919da6) }), - qDeltaRange: Honk.G1Point({ + qDeltaRange: Honk.G1Point({ x: uint256(0x071783da995d53ae821608033bed244822a25af1d20116a5dbc38a853df977de), y: uint256(0x2d963e4beb46e81d34f3ecd4236e283f82e32118e81c6e0d5bb5270275a6ddec) }), - qElliptic: Honk.G1Point({ + qElliptic: Honk.G1Point({ x: uint256(0x248aded3d1d8e4c8442c37b45f7179632f7d1b755c1f627f582533a81b085c71), y: uint256(0x2925a9238c179ba7ae4d88df142dd7612e181a4a02e3b05a5d930cf28f4b77cb) }), - qMemory: Honk.G1Point({ + qMemory: Honk.G1Point({ x: uint256(0x15fb05ef6cbe824fc9b484d988d7667491d53c75806fb2429966742402b631e6), y: uint256(0x144e9a407aebdfb3867d162c19f645702419aa6d9eae3b9f7c163bddc0107ee9) }), - qNnf: Honk.G1Point({ + qNnf: Honk.G1Point({ x: uint256(0x2fba29170e29f1f61fb4cec27ea5122d68e51586deb2fbc00374ceaa91659ef6), y: uint256(0x00f588bf177290f7593b7c85a765c3162307a13edcdadb7ed7451e67e2b4bab2) }), - qPoseidon2External: Honk.G1Point({ + qPoseidon2External: Honk.G1Point({ x: uint256(0x2096d53f1e57a2df8ed3ecf53c93e05d8c7fc0e5abde18d0ec3dbf3018eec0f3), y: uint256(0x065b8a9893e467c4289704516aa5d06e24f99c3085ebe6e19e84bf1220faf679) }), - qPoseidon2Internal: Honk.G1Point({ + qPoseidon2Internal: Honk.G1Point({ x: uint256(0x07aacefd12d904e9bf2ad73020d6a2b618acbeeaee59135e989b07da796c6a40), y: uint256(0x1fdbccb49979ea18c010e59c377eb5c26901671d836ec995853d8bd05f91951b) }), - s1: Honk.G1Point({ + s1: Honk.G1Point({ x: uint256(0x05e2113e71a2a71562f02492d12d4da7915377636a0b7b0b33aa7fd48da0f12a), y: uint256(0x020131db1910828dae674b549722d21983a4a10b15e9292b583ed8867f037ac0) }), - s2: Honk.G1Point({ + s2: Honk.G1Point({ x: uint256(0x15531eac0ced61422889242978b354afa00f46c35225c0879360ca64468c8080), y: uint256(0x3062e9b6926b520c97c2e674a46fdb89b93688cf0e350064bb5e7cf7f4ba1bd5) }), - s3: Honk.G1Point({ + s3: Honk.G1Point({ x: uint256(0x2045addf45ce01322a6b5644a88bbcf05a5c96e9aed9c5b2fba4c860219015bc), y: uint256(0x0776e97f2c0f13d41447e1139ce8874dd7758d03dbfc3d4dd6ba9766fd3890bf) }), - s4: Honk.G1Point({ + s4: Honk.G1Point({ x: uint256(0x21a07941ef2d902a527c17c588d81d01b95b94ea2b0492094dcd54136ca995ed), y: uint256(0x04e2dc9a14c2fe289492d7e20920a84d4d0367c141e6e40558937fedbdcc40c5) }), - t1: Honk.G1Point({ + t1: Honk.G1Point({ x: uint256(0x2a2151a981bb85afda957beeb8ee245883ab86e97482786e90275194bb8e80cd), y: uint256(0x2b87f0bc586ed756610eca48de9d4d3c56f54404708eabe1bb6dabd7696203cb) }), - t2: Honk.G1Point({ + t2: Honk.G1Point({ x: uint256(0x2fa75dcd73148b6cab95e82a24769ca1d1d3280dd5b9c2700c56eb44684b83d2), y: uint256(0x247273927cfad5b89f2f6ba25e364acc2ccd40e9b1da53772dac5fcea42ef432) }), - t3: Honk.G1Point({ + t3: Honk.G1Point({ x: uint256(0x0dd736c04e5e2cbbbc0fb0d929f5279c748d445232319f9f881e293a3670403e), y: uint256(0x1fa689f2550eb43257a026fdb66b7fa5417eb612170baec5cd3adc2822903750) }), - t4: Honk.G1Point({ + t4: Honk.G1Point({ x: uint256(0x11460f1cb9210508c275ec98009facc06544875226c2ef12eb33610665b1c523), y: uint256(0x021b2c1b6d951e023a6a8d2e9cbfc72c35c7a07ca516ed60d7f02e52671a612e) }), - id1: Honk.G1Point({ + id1: Honk.G1Point({ x: uint256(0x2a2df34804ceb4fceba0d464c9d7395c07e2512779800d0affae2be7c78cb9b3), y: uint256(0x1056a2c612d969590ad6e8fac1655bffd7f2dd1e108e68cb7a650c1e5cf22007) }), - id2: Honk.G1Point({ + id2: Honk.G1Point({ x: uint256(0x197b7f748b5851cc52fe7162431b079ef06e548aa6dcf61f7c285cfb692366e5), y: uint256(0x229de0a0f5b7e896e02223b6944f0334b936955f2317bb959b13a17a8d8dcca0) }), - id3: Honk.G1Point({ + id3: Honk.G1Point({ x: uint256(0x2220d5271509168ee4d3ac1d60dcca0cd7c71558d8173a770be57deed6109f85), y: uint256(0x2f45f9730e9161d57f8bbdf4bb896bff02e2f84395c93776960c0ae129979d7d) }), - id4: Honk.G1Point({ + id4: Honk.G1Point({ x: uint256(0x27f6992fab408dce5972d76c9f6efc6a3df47db3c93a8402f340a01e95f2178e), y: uint256(0x2dbb9218f0d9777f689f58475fe13c837a2d79c5e0ba2dfec6864307f045e0d1) }), - lagrangeFirst: Honk.G1Point({ + lagrangeFirst: Honk.G1Point({ x: uint256(0x0000000000000000000000000000000000000000000000000000000000000001), y: uint256(0x0000000000000000000000000000000000000000000000000000000000000002) }), - lagrangeLast: Honk.G1Point({ + lagrangeLast: Honk.G1Point({ x: uint256(0x015150bdeb71a0330b9f4f608149535e7f7e3e97fb2f1f22c1705681575eddd4), y: uint256(0x2a9efbef1eedca2b36673786633186f225ce8f8bd9be4050d1f7b5e2fef333c7) }) @@ -185,7 +185,7 @@ library FrLib { mstore(add(free, 0x20), 0x20) mstore(add(free, 0x40), 0x20) mstore(add(free, 0x60), v) - mstore(add(free, 0x80), sub(MODULUS, 2)) + mstore(add(free, 0x80), sub(MODULUS, 2)) mstore(add(free, 0xa0), MODULUS) let success := staticcall(gas(), 0x05, free, 0xc0, 0x00, 0x20) if iszero(success) { @@ -209,7 +209,7 @@ library FrLib { mstore(add(free, 0x20), 0x20) mstore(add(free, 0x40), 0x20) mstore(add(free, 0x60), b) - mstore(add(free, 0x80), v) + mstore(add(free, 0x80), v) mstore(add(free, 0xa0), MODULUS) let success := staticcall(gas(), 0x05, free, 0xc0, 0x00, 0x20) if iszero(success) { @@ -484,7 +484,7 @@ library TranscriptLib { uint256 vkHash, uint256 publicInputsSize, uint256 logN - ) internal view returns (Transcript memory t) { + ) internal pure returns (Transcript memory t) { Fr previousChallenge; (t.relationParameters, previousChallenge) = generateRelationParametersChallenges(proof, publicInputs, vkHash, publicInputsSize, previousChallenge); @@ -692,7 +692,7 @@ library TranscriptLib { function generateShplonkZChallenge(Honk.Proof memory proof, Fr prevChallenge) internal - view + pure returns (Fr shplonkZ, Fr nextPreviousChallenge) { uint256[3] memory shplonkZChallengeElements; diff --git a/src/solidity/test/SampleContract.t.sol b/src/solidity/test/SampleContract.t.sol index 925dafa7a..e4ff50bed 100644 --- a/src/solidity/test/SampleContract.t.sol +++ b/src/solidity/test/SampleContract.t.sol @@ -3,6 +3,7 @@ pragma solidity >=0.8.21; import {Test, console} from "forge-std/Test.sol"; +import {ZKPassportRootVerifier} from "../src/ZKPassportRootVerifier.sol"; import {ZKPassportVerifier, ProofType, ProofVerificationParams} from "../src/ZKPassportVerifier.sol"; import {ProofVerificationData, Commitments, ServiceConfig} from "../src/Types.sol"; import {HonkVerifier as OuterVerifier13} from "../src/ultra-honk-verifiers/OuterCount13.sol"; @@ -11,31 +12,33 @@ import {TestUtils} from "./Utils.t.sol"; import {CommittedInputLen} from "../src/Constants.sol"; contract SampleContractTest is TestUtils { - OuterVerifier13 public verifier; - ZKPassportVerifier public zkPassportVerifier; SampleContract public sampleContract; // Path to the proof file - using files directly in project root string constant PROOF_PATH = "./test/fixtures/all_subproofs_proof.hex"; string constant PUBLIC_INPUTS_PATH = "./test/fixtures/all_subproofs_public_inputs.json"; string constant COMMITTED_INPUTS_PATH = "./test/fixtures/all_subproofs_committed_inputs.hex"; bytes32 constant VKEY_HASH = 0x048f929a5be0814a81e5c4e62305e5cd4d203fb5e56c9ae5f5990aeee8fcabb4; + // TODO: Add automatic update of this timestamp for testing uint256 constant CURRENT_DATE = 1761776121; + // The version of the ZKPassportVerifier + uint256 constant VERIFIER_VERSION = 1; function setUp() public { // Deploy the ZKPassportVerifier - zkPassportVerifier = new ZKPassportVerifier(vm.envAddress("ROOT_REGISTRY_ADDRESS")); + ZKPassportVerifier zkPassportVerifier = new ZKPassportVerifier(vm.envAddress("ROOT_REGISTRY_ADDRESS")); // Deploy the UltraHonkVerifier - verifier = new OuterVerifier13(); - - // Add the verifier to the ZKPassportVerifier + OuterVerifier13 subverifier = new OuterVerifier13(); + // Add the sub-verifier to the ZKPassportVerifier bytes32[] memory vkeyHashes = new bytes32[](1); vkeyHashes[0] = VKEY_HASH; - address[] memory verifiers = new address[](1); - verifiers[0] = address(verifier); - zkPassportVerifier.addVerifiers(vkeyHashes, verifiers); - + address[] memory subverifiers = new address[](1); + subverifiers[0] = address(subverifier); + zkPassportVerifier.addSubVerifiers(vkeyHashes, subverifiers); + // Deploy the ZKPassportRootVerifier + ZKPassportRootVerifier rootVerifier = new ZKPassportRootVerifier(vm.envAddress("ROOT_VERIFIER_ADMIN_ADDRESS"), vm.envAddress("ROOT_VERIFIER_GUARDIAN_ADDRESS"), VERIFIER_VERSION, address(zkPassportVerifier)); + // Deploy the SampleContract sampleContract = new SampleContract(); - sampleContract.setZKPassportVerifier(address(zkPassportVerifier)); + sampleContract.setZKPassportVerifier(address(rootVerifier)); } function test_Register() public { diff --git a/src/solidity/test/Verifier.t.sol b/src/solidity/test/SubVerifier.t.sol similarity index 91% rename from src/solidity/test/Verifier.t.sol rename to src/solidity/test/SubVerifier.t.sol index 01ffe6a4e..4d12c16d9 100644 --- a/src/solidity/test/Verifier.t.sol +++ b/src/solidity/test/SubVerifier.t.sol @@ -3,11 +3,11 @@ pragma solidity >=0.8.21; import {Test, console} from "forge-std/Test.sol"; -import {IVerifier, HonkVerifier, BaseHonkVerifier} from "../src/ultra-honk-verifiers/OuterCount5.sol"; +import {IVerifier as ISubVerifier, HonkVerifier, BaseHonkVerifier} from "../src/ultra-honk-verifiers/OuterCount5.sol"; import {TestUtils} from "./Utils.t.sol"; -contract VerifierTest is TestUtils { - IVerifier public verifier; +contract SubVerifierTest is TestUtils { + ISubVerifier public verifier; // Path to the proof file - using files directly in project root string constant PROOF_PATH = "./test/fixtures/valid_proof.hex"; diff --git a/src/solidity/test/Utils.t.sol b/src/solidity/test/Utils.t.sol index a201a0b11..0fde80e33 100644 --- a/src/solidity/test/Utils.t.sol +++ b/src/solidity/test/Utils.t.sol @@ -5,6 +5,16 @@ pragma solidity >=0.8.21; import {Test, console} from "forge-std/Test.sol"; abstract contract TestUtils is Test { + + /** + * @dev Helper function to log the approx gas cost for an operation + */ + function logGas(string memory name) internal { + uint256 gasUsed = vm.stopSnapshotGas(); + console.log(name); + console.log(gasUsed); + } + /** * @dev Helper function to load proof data from a file */ @@ -23,12 +33,12 @@ abstract contract TestUtils is Test { // Try to parse the bytes try vm.parseBytes(proofHex) returns (bytes memory parsedBytes) { return parsedBytes; - } catch Error(string memory reason) { + } catch Error(string memory) { revert("Failed to parse proof bytes"); } catch { revert("Failed to parse proof bytes"); } - } catch Error(string memory reason) { + } catch Error(string memory) { revert("Failed to load proof from file"); } catch { revert("Failed to load proof from file"); @@ -49,7 +59,7 @@ abstract contract TestUtils is Test { } return result; - } catch Error(string memory reason) { + } catch Error(string memory) { revert("Failed to load inputs from file"); } catch { revert("Failed to load inputs from file"); diff --git a/src/solidity/test/ZKPassportVerifier.t.sol b/src/solidity/test/ZKPassportRootVerifier.t.sol similarity index 63% rename from src/solidity/test/ZKPassportVerifier.t.sol rename to src/solidity/test/ZKPassportRootVerifier.t.sol index a2e5a0cb1..741407bf2 100644 --- a/src/solidity/test/ZKPassportVerifier.t.sol +++ b/src/solidity/test/ZKPassportRootVerifier.t.sol @@ -3,17 +3,18 @@ pragma solidity >=0.8.21; import {Test, console} from "forge-std/Test.sol"; -import {ZKPassportVerifier, ProofType, ProofVerificationParams} from "../src/ZKPassportVerifier.sol"; +import {ZKPassportRootVerifier} from "../src/ZKPassportRootVerifier.sol"; +import {ZKPassportVerifier} from "../src/ZKPassportVerifier.sol"; import {HonkVerifier as OuterVerifier5} from "../src/ultra-honk-verifiers/OuterCount5.sol"; import {HonkVerifier as OuterVerifier13} from "../src/ultra-honk-verifiers/OuterCount13.sol"; import {TestUtils} from "./Utils.t.sol"; import {CommittedInputLen} from "../src/Constants.sol"; -import {DisclosedData, BoundData, FaceMatchMode, ProofVerificationData, Commitments, ServiceConfig, OS} from "../src/Types.sol"; +import {DisclosedData, BoundData, FaceMatchMode, ProofVerificationData, Commitments, ServiceConfig, OS, ProofType, ProofVerificationParams} from "../src/Types.sol"; -contract ZKPassportVerifierTest is TestUtils { - OuterVerifier5 public verifier5; - OuterVerifier13 public verifier13; - ZKPassportVerifier public zkPassportVerifier; + +contract ZKPassportRootVerifierTest is TestUtils { + ZKPassportRootVerifier public rootVerifier; + ZKPassportVerifier public verifier; // Path to the proof file - using files directly in project root string constant PROOF_PATH = "./test/fixtures/valid_proof.hex"; @@ -25,6 +26,7 @@ contract ZKPassportVerifierTest is TestUtils { string constant ALL_SUBPROOFS_COMMITTED_INPUTS_PATH = "./test/fixtures/all_subproofs_committed_inputs.hex"; + uint256 constant VERIFIER_VERSION = 1; bytes32 constant VKEY_HASH = bytes32(uint256(0x04b98c6f867d6a7f86d514b72c3be8f41b7aa6f49fdc17514c9f9f0a2ac3ef9a)); bytes32 constant OUTER_PROOF_13_VKEY_HASH = @@ -33,19 +35,22 @@ contract ZKPassportVerifierTest is TestUtils { function setUp() public { // Deploy the ZKPassportVerifier - zkPassportVerifier = new ZKPassportVerifier(vm.envAddress("ROOT_REGISTRY_ADDRESS")); - // Deploy the UltraHonkVerifier - verifier5 = new OuterVerifier5(); - verifier13 = new OuterVerifier13(); - - // Add the verifier to the ZKPassportVerifier + ZKPassportVerifier _verifier = new ZKPassportVerifier(vm.envAddress("ROOT_REGISTRY_ADDRESS")); + // Deploy the sub-verifiers + OuterVerifier5 verifier5 = new OuterVerifier5(); + OuterVerifier13 verifier13 = new OuterVerifier13(); + // Add the sub-verifiers to the ZKPassportVerifier bytes32[] memory vkeyHashes = new bytes32[](2); vkeyHashes[0] = VKEY_HASH; vkeyHashes[1] = OUTER_PROOF_13_VKEY_HASH; - address[] memory verifiers = new address[](2); - verifiers[0] = address(verifier5); - verifiers[1] = address(verifier13); - zkPassportVerifier.addVerifiers(vkeyHashes, verifiers); + address[] memory subverifiers = new address[](2); + subverifiers[0] = address(verifier5); + subverifiers[1] = address(verifier13); + _verifier.addSubVerifiers(vkeyHashes, subverifiers); + // Deploy the ZKPassportRootVerifier + rootVerifier = new ZKPassportRootVerifier(vm.envAddress("ROOT_VERIFIER_ADMIN_ADDRESS"), vm.envAddress("ROOT_VERIFIER_GUARDIAN_ADDRESS"), VERIFIER_VERSION, address(_verifier)); + // Get the current verifier from the ZKPassportRootVerifier + verifier = ZKPassportVerifier(rootVerifier.getVerifier(VERIFIER_VERSION)); } function test_VerifyValidProof() public { @@ -55,7 +60,7 @@ contract ZKPassportVerifierTest is TestUtils { bytes memory committedInputs = loadBytesFromFile(COMMITTED_INPUTS_PATH); // Verify the proof - vm.startSnapshotGas("ZKPassportVerifier verifyProof"); + vm.startSnapshotGas("ZKPassportRootVerifier verify"); vm.warp(CURRENT_DATE); ProofVerificationParams memory params = ProofVerificationParams({ proofVerificationData: ProofVerificationData({ @@ -73,10 +78,8 @@ contract ZKPassportVerifierTest is TestUtils { devMode: false }) }); - (bool result, bytes32 scopedNullifier) = zkPassportVerifier.verifyProof(params); - uint256 gasUsed = vm.stopSnapshotGas(); - console.log("Gas used in ZKPassportVerifier verifyProof"); - console.log(gasUsed); + (bool result, bytes32 scopedNullifier) = rootVerifier.verify(VERIFIER_VERSION, params); + logGas("ZKPassportRootVerifier verify"); assertEq(result, true); assertEq( scopedNullifier, @@ -84,10 +87,8 @@ contract ZKPassportVerifierTest is TestUtils { ); vm.startSnapshotGas("ZKPassportVerifier getDisclosedData"); - DisclosedData memory disclosedData = zkPassportVerifier.getDisclosedData(params.commitments, false); - uint256 gasUsedGetDisclosedData = vm.stopSnapshotGas(); - console.log("Gas used in ZKPassportVerifier getDisclosedData"); - console.log(gasUsedGetDisclosedData); + DisclosedData memory disclosedData = verifier.getDisclosedData(params.commitments, false); + logGas("ZKPassportVerifier getDisclosedData"); assertEq(disclosedData.name, "SILVERHAND<