diff --git a/script/Deploy.s.sol b/script/Deploy.s.sol index f927e7d..7360505 100644 --- a/script/Deploy.s.sol +++ b/script/Deploy.s.sol @@ -7,6 +7,7 @@ import "../src/WorldIDRouterImplV1.sol"; import "../src/WorldIDIdentityManager.sol"; import "../src/WorldIDIdentityManagerImplV1.sol"; import "../src/WorldIDIdentityManagerImplV2.sol"; +import "../src/WorldIDIdentityManagerImplV3.sol"; import "../src/SemaphoreVerifier.sol"; import {Verifier as InsertionB10} from "../src/verifiers/insertion/b10.sol"; @@ -75,6 +76,7 @@ contract Deploy is Script { WorldIDIdentityManagerImplV1 impl1 = new WorldIDIdentityManagerImplV1(); WorldIDIdentityManagerImplV2 impl2 = new WorldIDIdentityManagerImplV2(); + WorldIDIdentityManagerImplV3 impl3 = new WorldIDIdentityManagerImplV3(); WorldIDIdentityManager worldID = new WorldIDIdentityManager(address(impl1), initializeCall); @@ -82,6 +84,10 @@ contract Deploy is Script { WorldIDIdentityManagerImplV1 worldIDImplV1 = WorldIDIdentityManagerImplV1(address(worldID)); worldIDImplV1.upgradeToAndCall(address(impl2), initializeV2Call); + // Upgrade to V3 + WorldIDIdentityManagerImplV2 worldIDImplV2 = WorldIDIdentityManagerImplV2(address(worldID)); + worldIDImplV2.upgradeTo(address(impl3)); + vm.stopBroadcast(); return worldID; diff --git a/src/WorldIDIdentityManagerImplV1.sol b/src/WorldIDIdentityManagerImplV1.sol index bf03cc4..92f5f4c 100644 --- a/src/WorldIDIdentityManagerImplV1.sol +++ b/src/WorldIDIdentityManagerImplV1.sol @@ -667,12 +667,11 @@ contract WorldIDIdentityManagerImplV1 is WorldIDImpl, IWorldID { /// @dev Note that a double-signaling check is not included here, and should be carried by the /// caller. /// - /// @param proof The zero-knowledge proof /// @param root The of the Merkle tree /// @param signalHash A keccak256 hash of the Semaphore signal /// @param nullifierHash The nullifier hash /// @param externalNullifierHash A keccak256 hash of the external nullifier - /// + /// @param proof The zero-knowledge proof /// @custom:reverts string If the zero-knowledge proof cannot be verified for the public inputs. function verifyProof( uint256 root, diff --git a/src/WorldIDIdentityManagerImplV3.sol b/src/WorldIDIdentityManagerImplV3.sol new file mode 100644 index 0000000..37d0171 --- /dev/null +++ b/src/WorldIDIdentityManagerImplV3.sol @@ -0,0 +1,107 @@ +pragma solidity ^0.8.21; + +import "./WorldIDIdentityManagerImplV2.sol"; + +/// @title WorldID Identity Manager Implementation Version 3 +/// @author Worldcoin +/// @notice An implementation of a batch-based identity manager for the WorldID protocol. +/// @dev The manager is based on the principle of verifying externally-created Zero Knowledge Proofs +/// to perform the deletions. +/// @dev This is the implementation delegated to by a proxy. +contract WorldIDIdentityManagerImplV3 is WorldIDIdentityManagerImplV2 { + /////////////////////////////////////////////////////////////////////////////// + /// A NOTE ON IMPLEMENTATION CONTRACTS /// + /////////////////////////////////////////////////////////////////////////////// + + // This contract is designed explicitly to operate from behind a proxy contract. As a result, + // there are a few important implementation considerations: + // + // - All updates made after deploying a given version of the implementation should inherit from + // the latest version of the implementation. This contract inherits from its previous implementation + // WorldIDIdentityManagerImplV1. This prevents storage clashes. + // - All functions that are less access-restricted than `private` should be marked `virtual` in + // order to enable the fixing of bugs in the existing interface. + // - Any function that reads from or modifies state (i.e. is not marked `pure`) must be + // annotated with the `onlyProxy` and `onlyInitialized` modifiers. This ensures that it can + // only be called when it has access to the data in the proxy, otherwise results are likely to + // be nonsensical. + // - This contract deals with important data for the WorldID system. Ensure that all newly-added + // functionality is carefully access controlled using `onlyOwner`, or a more granular access + // mechanism. + // - Do not assign any contract-level variables at the definition site unless they are + // `constant`. + // + // Additionally, the following notes apply: + // + // - Initialisation and ownership management are not protected behind `onlyProxy` intentionally. + // This ensures that the contract can safely be disposed of after it is no longer used. + // - Carefully consider what data recovery options are presented as new functionality is added. + // Care must be taken to ensure that a migration plan can exist for cases where upgrades + // cannot recover from an issue or vulnerability. + + /////////////////////////////////////////////////////////////////////////////// + /// !!!!! DATA: DO NOT REORDER !!!!! /// + /////////////////////////////////////////////////////////////////////////////// + + // To ensure compatibility between upgrades, it is exceedingly important that no reordering of + // these variables takes place. If reordering happens, a storage clash will occur (effectively a + // memory safety error). + + /////////////////////////////////////////////////////////////////////////////// + /// SEMAPHORE PROOF VALIDATION LOGIC /// + /////////////////////////////////////////////////////////////////////////////// + + /// @notice A verifier for the semaphore protocol that supports compressed proofs and is backwards + /// compatible with the previous implementation. If a proof is compressed, the last 4 uints + /// will be 0. If this condition is met, we will call the semaphore compress proof function + /// instead. + /// @dev Note that a double-signaling check is not included here, and should be carried by the + /// caller. + /// + /// @param root The of the Merkle tree + /// @param signalHash A keccak256 hash of the Semaphore signal + /// @param nullifierHash The nullifier hash + /// @param externalNullifierHash A keccak256 hash of the external nullifier + /// @param proof The zero-knowledge proof + /// @custom:reverts string If the zero-knowledge proof cannot be verified for the public inputs. + function verifyProof( + uint256 root, + uint256 signalHash, + uint256 nullifierHash, + uint256 externalNullifierHash, + uint256[8] calldata proof + ) public view virtual override onlyProxy onlyInitialized { + // Check the preconditions on the inputs. + requireValidRoot(root); + uint256[4] memory input = [root, nullifierHash, signalHash, externalNullifierHash]; + if (proof[4] == 0 && proof[5] == 0 && proof[6] == 0 && proof[7] == 0) { + uint256[4] memory compressedProof = [proof[0], proof[1], proof[2], proof[3]]; + semaphoreVerifier.verifyCompressedProof(compressedProof, input); + } else { + semaphoreVerifier.verifyProof(proof, input); + } + } + + /// @notice A verifier for the semaphore protocol that supports compressed proofs + /// @dev Note that a double-signaling check is not included here, and should be carried by the + /// caller. + /// + /// @param root The of the Merkle tree + /// @param signalHash A keccak256 hash of the Semaphore signal + /// @param nullifierHash The nullifier hash + /// @param externalNullifierHash A keccak256 hash of the external nullifier + /// @param compressedProof The zero-knowledge proof + /// @custom:reverts string If the zero-knowledge proof cannot be verified for the public inputs. + function verifyCompressedProof( + uint256 root, + uint256 signalHash, + uint256 nullifierHash, + uint256 externalNullifierHash, + uint256[4] calldata compressedProof + ) public view virtual onlyProxy onlyInitialized { + // Check the preconditions on the inputs. + requireValidRoot(root); + uint256[4] memory input = [root, nullifierHash, signalHash, externalNullifierHash]; + semaphoreVerifier.verifyCompressedProof(compressedProof, input); + } +} diff --git a/src/interfaces/ISemaphoreVerifier.sol b/src/interfaces/ISemaphoreVerifier.sol index 6213bec..4c05896 100644 --- a/src/interfaces/ISemaphoreVerifier.sol +++ b/src/interfaces/ISemaphoreVerifier.sol @@ -15,4 +15,17 @@ interface ISemaphoreVerifier { /// @param input the public input field elements in the scalar field Fr. /// Elements must be reduced. function verifyProof(uint256[8] calldata proof, uint256[4] calldata input) external view; + + /// Verify a Groth16 proof with compressed points. + /// @notice Reverts with InvalidProof if the proof is invalid or + /// with PublicInputNotInField the public input is not reduced. + /// @notice There is no return value. If the function does not revert, the + /// proof was succesfully verified. + /// @param compressedProof the points (A, B, C) in compressed format + /// matching the output of compressProof. + /// @param input the public input field elements in the scalar field Fr. + /// Elements must be reduced. + function verifyCompressedProof(uint256[4] calldata compressedProof, uint256[4] calldata input) + external + view; } diff --git a/src/test/identity-manager/WorldIDIdentityManagerCalculation.t.sol b/src/test/identity-manager/WorldIDIdentityManagerCalculation.t.sol index f9f4c1f..7d121de 100644 --- a/src/test/identity-manager/WorldIDIdentityManagerCalculation.t.sol +++ b/src/test/identity-manager/WorldIDIdentityManagerCalculation.t.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.21; import {WorldIDIdentityManagerTest} from "./WorldIDIdentityManagerTest.sol"; -import {WorldIDIdentityManagerImplV2 as ManagerImpl} from "../../WorldIDIdentityManagerImplV2.sol"; +import {WorldIDIdentityManagerImplV2 as ManagerImplV2} from "../../WorldIDIdentityManagerImplV2.sol"; import {WorldIDIdentityManagerImplV1 as ManagerImplV1} from "../../WorldIDIdentityManagerImplV1.sol"; /// @title World ID Identity Manager Calculation Tests @@ -32,7 +32,7 @@ contract WorldIDIdentityManagerCalculation is WorldIDIdentityManagerTest { vm.expectRevert("Function must be called through delegatecall"); // Test - managerImpl.calculateIdentityRegistrationInputHash( + managerImplV2.calculateIdentityRegistrationInputHash( startIndex, insertionPreRoot, insertionPostRoot, identityCommitments ); } @@ -42,7 +42,7 @@ contract WorldIDIdentityManagerCalculation is WorldIDIdentityManagerTest { function testCalculateIdentityDeletionInputHashFromParametersOnKnownInput() public { // Setup bytes memory callData = abi.encodeCall( - ManagerImpl.calculateIdentityDeletionInputHash, + ManagerImplV2.calculateIdentityDeletionInputHash, (packedDeletionIndices, deletionPreRoot, deletionPostRoot, deletionBatchSize) ); bytes memory returnData = abi.encode(deletionInputHash); @@ -57,7 +57,7 @@ contract WorldIDIdentityManagerCalculation is WorldIDIdentityManagerTest { vm.expectRevert("Function must be called through delegatecall"); // Test - managerImpl.calculateIdentityDeletionInputHash( + managerImplV2.calculateIdentityDeletionInputHash( packedDeletionIndices, deletionPreRoot, deletionPostRoot, deletionBatchSize ); } diff --git a/src/test/identity-manager/WorldIDIdentityManagerConstruction.t.sol b/src/test/identity-manager/WorldIDIdentityManagerConstruction.t.sol index 385a57c..95aa860 100644 --- a/src/test/identity-manager/WorldIDIdentityManagerConstruction.t.sol +++ b/src/test/identity-manager/WorldIDIdentityManagerConstruction.t.sol @@ -5,7 +5,7 @@ import {WorldIDIdentityManagerTest} from "./WorldIDIdentityManagerTest.sol"; import {UUPSUpgradeable} from "contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import {WorldIDIdentityManager as IdentityManager} from "../../WorldIDIdentityManager.sol"; -import {WorldIDIdentityManagerImplV2 as ManagerImpl} from "../../WorldIDIdentityManagerImplV2.sol"; +import {WorldIDIdentityManagerImplV2 as ManagerImplV2} from "../../WorldIDIdentityManagerImplV2.sol"; import {WorldIDIdentityManagerImplV1 as ManagerImplV1} from "../../WorldIDIdentityManagerImplV1.sol"; /// @title World ID Identity Manager Construction Tests @@ -45,18 +45,18 @@ contract WorldIDIdentityManagerConstruction is WorldIDIdentityManagerTest { ); // Test - identityManager = new IdentityManager(address(managerImpl), callData); + identityManager = new IdentityManager(address(managerImplV2), callData); identityManagerAddress = address(identityManager); // creates Manager Impl V2, which will be used for tests - managerImpl = new ManagerImpl(); - managerImplAddress = address(managerImpl); + managerImplV2 = new ManagerImplV2(); + managerImplV2Address = address(managerImplV2); bytes memory initCallV2 = - abi.encodeCall(ManagerImpl.initializeV2, (defaultDeletionVerifiers)); + abi.encodeCall(ManagerImplV2.initializeV2, (defaultDeletionVerifiers)); bytes memory upgradeCall = abi.encodeCall( - UUPSUpgradeable.upgradeToAndCall, (address(managerImplAddress), initCallV2) + UUPSUpgradeable.upgradeToAndCall, (address(managerImplV2Address), initCallV2) ); // Test diff --git a/src/test/identity-manager/WorldIDIdentityManagerDataQuery.t.sol b/src/test/identity-manager/WorldIDIdentityManagerDataQuery.t.sol index 2df1cb1..a6c1af4 100644 --- a/src/test/identity-manager/WorldIDIdentityManagerDataQuery.t.sol +++ b/src/test/identity-manager/WorldIDIdentityManagerDataQuery.t.sol @@ -128,7 +128,7 @@ contract WorldIDIdentityManagerDataQuery is WorldIDIdentityManagerTest { // Setup vm.assume(badRoot != initialRoot); bytes memory callData = abi.encodeCall(ManagerImplV1.queryRoot, badRoot); - bytes memory returnData = abi.encode(managerImpl.NO_SUCH_ROOT()); + bytes memory returnData = abi.encode(managerImplV2.NO_SUCH_ROOT()); // Test assertCallSucceedsOn(identityManagerAddress, callData, returnData); @@ -140,7 +140,7 @@ contract WorldIDIdentityManagerDataQuery is WorldIDIdentityManagerTest { vm.expectRevert("Function must be called through delegatecall"); // Test - managerImpl.queryRoot(initialRoot); + managerImplV2.queryRoot(initialRoot); } /// @notice Checks that it is possible to get the latest root from the contract. @@ -167,7 +167,7 @@ contract WorldIDIdentityManagerDataQuery is WorldIDIdentityManagerTest { vm.expectRevert("Function must be called through delegatecall"); // Test - managerImpl.latestRoot(); + managerImplV2.latestRoot(); } /// @notice Checks that it is possible to get the tree depth the contract was initialized with. diff --git a/src/test/identity-manager/WorldIDIdentityManagerGettersSetters.t.sol b/src/test/identity-manager/WorldIDIdentityManagerGettersSetters.t.sol index 921e387..eaab1a9 100644 --- a/src/test/identity-manager/WorldIDIdentityManagerGettersSetters.t.sol +++ b/src/test/identity-manager/WorldIDIdentityManagerGettersSetters.t.sol @@ -9,7 +9,7 @@ import {SimpleVerifier, SimpleVerify} from "../mock/SimpleVerifier.sol"; import {TypeConverter as TC} from "../utils/TypeConverter.sol"; import {VerifierLookupTable} from "../../data/VerifierLookupTable.sol"; -import {WorldIDIdentityManagerImplV2 as ManagerImpl} from "../../WorldIDIdentityManagerImplV2.sol"; +import {WorldIDIdentityManagerImplV2 as ManagerImplV2} from "../../WorldIDIdentityManagerImplV2.sol"; import {WorldIDIdentityManagerImplV1 as ManagerImplV1} from "../../WorldIDIdentityManagerImplV1.sol"; /// @title World ID Identity Manager Getter and Setter Tests @@ -20,7 +20,9 @@ import {WorldIDIdentityManagerImplV1 as ManagerImplV1} from "../../WorldIDIdenti contract WorldIDIdentityManagerGettersSetters is WorldIDIdentityManagerTest { /// @notice Taken from WorldIDIdentityManagerImplV1.sol event DependencyUpdated( - ManagerImpl.Dependency indexed kind, address indexed oldAddress, address indexed newAddress + ManagerImplV2.Dependency indexed kind, + address indexed oldAddress, + address indexed newAddress ); event RootHistoryExpirySet(uint256 indexed oldExpiryTime, uint256 indexed newExpiryTime); @@ -43,7 +45,7 @@ contract WorldIDIdentityManagerGettersSetters is WorldIDIdentityManagerTest { vm.expectRevert("Function must be called through delegatecall"); // Test - managerImpl.getRegisterIdentitiesVerifierLookupTableAddress(); + managerImplV2.getRegisterIdentitiesVerifierLookupTableAddress(); } /// @notice Checks that it is possible to set the lookup table currently being used to verify @@ -93,7 +95,7 @@ contract WorldIDIdentityManagerGettersSetters is WorldIDIdentityManagerTest { vm.expectRevert("Function must be called through delegatecall"); // Test - managerImpl.setRegisterIdentitiesVerifierLookupTable(insertionVerifiers); + managerImplV2.setRegisterIdentitiesVerifierLookupTable(insertionVerifiers); } /// @notice Checks that it is possible to get the address of the contract currently being used @@ -101,7 +103,7 @@ contract WorldIDIdentityManagerGettersSetters is WorldIDIdentityManagerTest { function testCanGetDeleteIdentitiesVerifierLookupTableAddress() public { // Setup bytes memory callData = - abi.encodeCall(ManagerImpl.getDeleteIdentitiesVerifierLookupTableAddress, ()); + abi.encodeCall(ManagerImplV2.getDeleteIdentitiesVerifierLookupTableAddress, ()); bytes memory expectedReturn = abi.encode(address(defaultDeletionVerifiers)); // Test @@ -115,7 +117,7 @@ contract WorldIDIdentityManagerGettersSetters is WorldIDIdentityManagerTest { vm.expectRevert("Function must be called through delegatecall"); // Test - managerImpl.getDeleteIdentitiesVerifierLookupTableAddress(); + managerImplV2.getDeleteIdentitiesVerifierLookupTableAddress(); } /// @notice Checks that it is possible to set the lookup table currently being used to verify @@ -124,10 +126,11 @@ contract WorldIDIdentityManagerGettersSetters is WorldIDIdentityManagerTest { // Setup (, VerifierLookupTable deletionVerifiers,) = makeVerifierLookupTables(TC.makeDynArray([40])); address newVerifiersAddress = address(deletionVerifiers); - bytes memory callData = - abi.encodeCall(ManagerImpl.setDeleteIdentitiesVerifierLookupTable, (deletionVerifiers)); + bytes memory callData = abi.encodeCall( + ManagerImplV2.setDeleteIdentitiesVerifierLookupTable, (deletionVerifiers) + ); bytes memory checkCallData = - abi.encodeCall(ManagerImpl.getDeleteIdentitiesVerifierLookupTableAddress, ()); + abi.encodeCall(ManagerImplV2.getDeleteIdentitiesVerifierLookupTableAddress, ()); bytes memory expectedReturn = abi.encode(newVerifiersAddress); vm.expectEmit(true, false, true, true); emit DependencyUpdated( @@ -162,7 +165,7 @@ contract WorldIDIdentityManagerGettersSetters is WorldIDIdentityManagerTest { vm.expectRevert("Function must be called through delegatecall"); // Test - managerImpl.setDeleteIdentitiesVerifierLookupTable(deletionVerifiers); + managerImplV2.setDeleteIdentitiesVerifierLookupTable(deletionVerifiers); } /// @notice Ensures that we can get the address of the semaphore verifier. @@ -181,7 +184,7 @@ contract WorldIDIdentityManagerGettersSetters is WorldIDIdentityManagerTest { vm.expectRevert("Function must be called through delegatecall"); // Test - managerImpl.getSemaphoreVerifierAddress(); + managerImplV2.getSemaphoreVerifierAddress(); } /// @notice Checks that it is possible to set the contract currently being used to verify @@ -224,7 +227,7 @@ contract WorldIDIdentityManagerGettersSetters is WorldIDIdentityManagerTest { vm.expectRevert("Function must be called through delegatecall"); // Test - managerImpl.setSemaphoreVerifier(newVerifier); + managerImplV2.setSemaphoreVerifier(newVerifier); } /// @notice Ensures that it's possible to get the root history expiry time. @@ -243,7 +246,7 @@ contract WorldIDIdentityManagerGettersSetters is WorldIDIdentityManagerTest { vm.expectRevert("Function must be called through delegatecall"); // Test - managerImpl.getRootHistoryExpiry(); + managerImplV2.getRootHistoryExpiry(); } /// @notice Ensures that it is possible to set the root history expiry time. @@ -290,6 +293,6 @@ contract WorldIDIdentityManagerGettersSetters is WorldIDIdentityManagerTest { vm.expectRevert("Function must be called through delegatecall"); // Test - managerImpl.setRootHistoryExpiry(newExpiry); + managerImplV2.setRootHistoryExpiry(newExpiry); } } diff --git a/src/test/identity-manager/WorldIDIdentityManagerIdentityDeletion.t.sol b/src/test/identity-manager/WorldIDIdentityManagerIdentityDeletion.t.sol index fcdd41c..37cb759 100644 --- a/src/test/identity-manager/WorldIDIdentityManagerIdentityDeletion.t.sol +++ b/src/test/identity-manager/WorldIDIdentityManagerIdentityDeletion.t.sol @@ -10,7 +10,7 @@ import {Verifier as TreeVerifier} from "src/test/DeletionTreeVerifier16.sol"; import {VerifierLookupTable} from "../../data/VerifierLookupTable.sol"; import {WorldIDIdentityManager as IdentityManager} from "../../WorldIDIdentityManager.sol"; -import {WorldIDIdentityManagerImplV2 as ManagerImpl} from "../../WorldIDIdentityManagerImplV2.sol"; +import {WorldIDIdentityManagerImplV2 as ManagerImplV2} from "../../WorldIDIdentityManagerImplV2.sol"; import {WorldIDIdentityManagerImplV1 as ManagerImplV1} from "../../WorldIDIdentityManagerImplV1.sol"; import {console} from "forge-std/console.sol"; @@ -27,7 +27,7 @@ contract WorldIDIdentityManagerIdentityDeletion is WorldIDIdentityManagerTest { /// Taken from WorldIDIdentityManagerImplV1.sol event TreeChanged( uint256 indexed deletionPreRoot, - ManagerImpl.TreeChange indexed kind, + ManagerImplV2.TreeChange indexed kind, uint256 indexed deletionPostRoot ); @@ -50,7 +50,7 @@ contract WorldIDIdentityManagerIdentityDeletion is WorldIDIdentityManagerTest { semaphoreVerifier ); bytes memory deleteCallData = abi.encodeCall( - ManagerImpl.deleteIdentities, + ManagerImplV2.deleteIdentities, (deletionProof, packedDeletionIndices, deletionPreRoot, deletionPostRoot) ); bytes memory latestRootCallData = abi.encodeCall(ManagerImplV1.latestRoot, ()); @@ -97,7 +97,7 @@ contract WorldIDIdentityManagerIdentityDeletion is WorldIDIdentityManagerTest { ); uint256[8] memory actualProof = prepareDeleteIdentitiesTestCase(prf); bytes memory callData = abi.encodeCall( - ManagerImpl.deleteIdentities, + ManagerImplV2.deleteIdentities, (actualProof, packedDeletionIndices, newPreRoot, newPostRoot) ); @@ -142,12 +142,13 @@ contract WorldIDIdentityManagerIdentityDeletion is WorldIDIdentityManagerTest { ); uint256[8] memory actualProof = prepareDeleteIdentitiesTestCase(prf); bytes memory firstCallData = abi.encodeCall( - ManagerImpl.deleteIdentities, + ManagerImplV2.deleteIdentities, (actualProof, packedDeletionIndices, newPreRoot, newPostRoot) ); uint256 secondPostRoot = uint256(newPostRoot) + 1; bytes memory secondCallData = abi.encodeCall( - ManagerImpl.deleteIdentities, (actualProof, secondIndices, newPostRoot, secondPostRoot) + ManagerImplV2.deleteIdentities, + (actualProof, secondIndices, newPostRoot, secondPostRoot) ); vm.expectEmit(true, true, true, true); @@ -191,7 +192,7 @@ contract WorldIDIdentityManagerIdentityDeletion is WorldIDIdentityManagerTest { uint256[8] memory actualProof = prepareDeleteIdentitiesTestCase(prf); bytes memory callData = abi.encodeCall( - ManagerImpl.deleteIdentities, + ManagerImplV2.deleteIdentities, (actualProof, packedDeletionIndices, newPreRoot, newPostRoot) ); bytes memory errorData = abi.encodeWithSelector(VerifierLookupTable.NoSuchVerifier.selector); @@ -227,7 +228,7 @@ contract WorldIDIdentityManagerIdentityDeletion is WorldIDIdentityManagerTest { ); uint256[8] memory actualProof = prepareDeleteIdentitiesTestCase(prf); bytes memory callData = abi.encodeCall( - ManagerImpl.deleteIdentities, + ManagerImplV2.deleteIdentities, (actualProof, packedDeletionIndices, newPreRoot, newPostRoot) ); bytes memory expectedError = @@ -258,7 +259,7 @@ contract WorldIDIdentityManagerIdentityDeletion is WorldIDIdentityManagerTest { ); bytes memory deletionCallData = abi.encodeCall( - ManagerImpl.deleteIdentities, + ManagerImplV2.deleteIdentities, (deletionProof, packedDeletionIndices, deletionPreRoot, newPostRoot) ); bytes memory expectedError = @@ -274,7 +275,7 @@ contract WorldIDIdentityManagerIdentityDeletion is WorldIDIdentityManagerTest { // Setup vm.assume(nonOperator != address(this) && nonOperator != address(0x0)); bytes memory callData = abi.encodeCall( - ManagerImpl.deleteIdentities, + ManagerImplV2.deleteIdentities, (deletionProof, packedDeletionIndices, deletionPreRoot, deletionPostRoot) ); bytes memory errorData = @@ -304,7 +305,7 @@ contract WorldIDIdentityManagerIdentityDeletion is WorldIDIdentityManagerTest { semaphoreVerifier ); bytes memory callData = abi.encodeCall( - ManagerImpl.deleteIdentities, + ManagerImplV2.deleteIdentities, (deletionProof, packedDeletionIndices, actualRoot, deletionPostRoot) ); bytes memory expectedError = abi.encodeWithSelector( @@ -318,12 +319,12 @@ contract WorldIDIdentityManagerIdentityDeletion is WorldIDIdentityManagerTest { /// @notice Tests that identities can only be deleted through the proxy. function testCannotDelteIdentitiesIfNotViaProxy() public { // Setup - address expectedOwner = managerImpl.owner(); + address expectedOwner = managerImplV2.owner(); vm.expectRevert("Function must be called through delegatecall"); vm.prank(expectedOwner); // Test - managerImpl.deleteIdentities( + managerImplV2.deleteIdentities( deletionProof, packedDeletionIndices, initialRoot, deletionPostRoot ); } diff --git a/src/test/identity-manager/WorldIDIdentityManagerIdentityRegistration.t.sol b/src/test/identity-manager/WorldIDIdentityManagerIdentityRegistration.t.sol index 48e2288..afdb7a9 100644 --- a/src/test/identity-manager/WorldIDIdentityManagerIdentityRegistration.t.sol +++ b/src/test/identity-manager/WorldIDIdentityManagerIdentityRegistration.t.sol @@ -12,7 +12,7 @@ import {Verifier as TreeVerifier} from "src/test/InsertionTreeVerifier16.sol"; import {VerifierLookupTable} from "../../data/VerifierLookupTable.sol"; import {WorldIDIdentityManager as IdentityManager} from "../../WorldIDIdentityManager.sol"; -import {WorldIDIdentityManagerImplV2 as ManagerImpl} from "../../WorldIDIdentityManagerImplV2.sol"; +import {WorldIDIdentityManagerImplV2 as ManagerImplV2} from "../../WorldIDIdentityManagerImplV2.sol"; import {WorldIDIdentityManagerImplV1 as ManagerImplV1} from "../../WorldIDIdentityManagerImplV1.sol"; /// @title World ID Identity Manager Identity Registration Tests @@ -27,7 +27,7 @@ contract WorldIDIdentityManagerIdentityRegistration is WorldIDIdentityManagerTes /// Taken from WorldIDIdentityManagerImplV1.sol event TreeChanged( uint256 indexed insertionPreRoot, - ManagerImpl.TreeChange indexed kind, + ManagerImplV2.TreeChange indexed kind, uint256 indexed insertionPostRoot ); @@ -319,8 +319,8 @@ contract WorldIDIdentityManagerIdentityRegistration is WorldIDIdentityManagerTes function testCannotRegisterIdentitiesIfPostRootIncorrect(uint256 newPostRoot) public { // Setup vm.assume(newPostRoot != insertionPostRoot && newPostRoot < SNARK_SCALAR_FIELD); - managerImpl = new ManagerImpl(); - managerImplAddress = address(managerImpl); + managerImplV2 = new ManagerImplV2(); + managerImplV2Address = address(managerImplV2); ITreeVerifier actualVerifier = new TreeVerifier(); ( VerifierLookupTable insertVerifiers, @@ -334,13 +334,13 @@ contract WorldIDIdentityManagerIdentityRegistration is WorldIDIdentityManagerTes (treeDepth, insertionPreRoot, insertVerifiers, updateVerifiers, semaphoreVerifier) ); - identityManager = new IdentityManager(managerImplAddress, callData); + identityManager = new IdentityManager(managerImplV2Address, callData); identityManagerAddress = address(identityManager); // Init V2 - bytes memory initCallV2 = abi.encodeCall(ManagerImpl.initializeV2, (deletionVerifiers)); + bytes memory initCallV2 = abi.encodeCall(managerImplV2.initializeV2, (deletionVerifiers)); bytes memory upgradeCall = abi.encodeCall( - UUPSUpgradeable.upgradeToAndCall, (address(managerImplAddress), initCallV2) + UUPSUpgradeable.upgradeToAndCall, (address(managerImplV2Address), initCallV2) ); assertCallSucceedsOn(identityManagerAddress, upgradeCall, new bytes(0x0)); @@ -451,12 +451,12 @@ contract WorldIDIdentityManagerIdentityRegistration is WorldIDIdentityManagerTes /// @notice Tests that identities can only be registered through the proxy. function testCannotRegisterIdentitiesIfNotViaProxy() public { // Setup - address expectedOwner = managerImpl.owner(); + address expectedOwner = managerImplV2.owner(); vm.expectRevert("Function must be called through delegatecall"); vm.prank(expectedOwner); // Test - managerImpl.registerIdentities( + managerImplV2.registerIdentities( insertionProof, initialRoot, startIndex, identityCommitments, insertionPostRoot ); } diff --git a/src/test/identity-manager/WorldIDIdentityManagerInitialization.t.sol b/src/test/identity-manager/WorldIDIdentityManagerInitialization.t.sol index 675ad10..3c31087 100644 --- a/src/test/identity-manager/WorldIDIdentityManagerInitialization.t.sol +++ b/src/test/identity-manager/WorldIDIdentityManagerInitialization.t.sol @@ -6,7 +6,7 @@ import {UUPSUpgradeable} from "contracts-upgradeable/proxy/utils/UUPSUpgradeable import {WorldIDIdentityManagerTest} from "./WorldIDIdentityManagerTest.sol"; import {WorldIDIdentityManager as IdentityManager} from "../../WorldIDIdentityManager.sol"; -import {WorldIDIdentityManagerImplV2 as ManagerImpl} from "../../WorldIDIdentityManagerImplV2.sol"; +import {WorldIDIdentityManagerImplV2 as ManagerImplV2} from "../../WorldIDIdentityManagerImplV2.sol"; import {WorldIDIdentityManagerImplV1 as ManagerImplV1} from "../../WorldIDIdentityManagerImplV1.sol"; /// @title World ID Identity Manager Initialization Tests @@ -24,7 +24,7 @@ contract WorldIDIdentityManagerInitialization is WorldIDIdentityManagerTest { function testInitialisation() public { // Setup delete identityManager; - delete managerImpl; + delete managerImplV2; delete managerImplV1; bytes memory V1CallData = abi.encodeCall( @@ -39,7 +39,7 @@ contract WorldIDIdentityManagerInitialization is WorldIDIdentityManagerTest { ); managerImplV1 = new ManagerImplV1(); - managerImplAddress = address(managerImpl); + managerImplV1Address = address(managerImplV1); vm.expectEmit(true, true, true, true); emit Initialized(1); @@ -48,13 +48,13 @@ contract WorldIDIdentityManagerInitialization is WorldIDIdentityManagerTest { identityManagerAddress = address(identityManager); // creates Manager Impl V2, which will be used for tests - managerImpl = new ManagerImpl(); - managerImplAddress = address(managerImpl); + managerImplV2 = new ManagerImplV2(); + managerImplV2Address = address(managerImplV2); bytes memory initCallV2 = - abi.encodeCall(ManagerImpl.initializeV2, (defaultDeletionVerifiers)); + abi.encodeCall(ManagerImplV2.initializeV2, (defaultDeletionVerifiers)); bytes memory upgradeCall = abi.encodeCall( - UUPSUpgradeable.upgradeToAndCall, (address(managerImplAddress), initCallV2) + UUPSUpgradeable.upgradeToAndCall, (address(managerImplV2Address), initCallV2) ); vm.expectEmit(true, true, true, true); @@ -70,7 +70,7 @@ contract WorldIDIdentityManagerInitialization is WorldIDIdentityManagerTest { function testInitialisation2() public { // Setup delete identityManager; - delete managerImpl; + delete managerImplV2; delete managerImplV1; bytes memory V1CallData = abi.encodeCall( @@ -85,16 +85,16 @@ contract WorldIDIdentityManagerInitialization is WorldIDIdentityManagerTest { ); // creates Manager Impl V2, which will be used for tests - managerImpl = new ManagerImpl(); - managerImplAddress = address(managerImpl); + managerImplV2 = new ManagerImplV2(); + managerImplV2Address = address(managerImplV2); vm.expectEmit(true, true, true, true); emit Initialized(1); - identityManager = new IdentityManager(managerImplAddress, V1CallData); + identityManager = new IdentityManager(managerImplV2Address, V1CallData); identityManagerAddress = address(identityManager); bytes memory initCallV2 = - abi.encodeCall(ManagerImpl.initializeV2, (defaultDeletionVerifiers)); + abi.encodeCall(ManagerImplV2.initializeV2, (defaultDeletionVerifiers)); // can't expectEmit Initialized 2 due to the low-level call wrapper, but the trace // shows Initialized(2) is emitted @@ -120,7 +120,7 @@ contract WorldIDIdentityManagerInitialization is WorldIDIdentityManagerTest { // Test assertCallFailsOn(identityManagerAddress, callData, expectedReturn); - callData = abi.encodeCall(ManagerImpl.initializeV2, (defaultDeletionVerifiers)); + callData = abi.encodeCall(ManagerImplV2.initializeV2, (defaultDeletionVerifiers)); assertCallFailsOn(identityManagerAddress, callData, expectedReturn); } @@ -128,7 +128,7 @@ contract WorldIDIdentityManagerInitialization is WorldIDIdentityManagerTest { /// @notice Checks that it is impossible to initialize the delegate on its own. function testCannotInitializeTheDelegate() public { // Setup - ManagerImplV1 localImpl = new ManagerImpl(); + ManagerImplV1 localImpl = new ManagerImplV1(); vm.expectRevert("Initializable: contract is already initialized"); // Test @@ -145,10 +145,10 @@ contract WorldIDIdentityManagerInitialization is WorldIDIdentityManagerTest { function testCannotPassUnsupportedTreeDepth() public { // Setup delete identityManager; - delete managerImpl; + delete managerImplV2; - managerImpl = new ManagerImpl(); - managerImplAddress = address(managerImpl); + managerImplV2 = new ManagerImplV2(); + managerImplV2Address = address(managerImplV2); uint8 unsupportedDepth = 15; bytes memory callData = abi.encodeCall( @@ -165,6 +165,6 @@ contract WorldIDIdentityManagerInitialization is WorldIDIdentityManagerTest { vm.expectRevert(abi.encodeWithSelector(ManagerImplV1.UnsupportedTreeDepth.selector, 15)); // Test - identityManager = new IdentityManager(managerImplAddress, callData); + identityManager = new IdentityManager(managerImplV2Address, callData); } } diff --git a/src/test/identity-manager/WorldIDIdentityManagerOwnershipManagement.t.sol b/src/test/identity-manager/WorldIDIdentityManagerOwnershipManagement.t.sol index 630fb93..07d8247 100644 --- a/src/test/identity-manager/WorldIDIdentityManagerOwnershipManagement.t.sol +++ b/src/test/identity-manager/WorldIDIdentityManagerOwnershipManagement.t.sol @@ -16,7 +16,7 @@ import {WorldIDIdentityManagerImplMock} from "../mock/WorldIDIdentityManagerImpl import {WorldIDImpl} from "../../abstract/WorldIDImpl.sol"; import {WorldIDIdentityManager as IdentityManager} from "../../WorldIDIdentityManager.sol"; -import {WorldIDIdentityManagerImplV2 as ManagerImpl} from "../../WorldIDIdentityManagerImplV2.sol"; +import {WorldIDIdentityManagerImplV2 as ManagerImplV2} from "../../WorldIDIdentityManagerImplV2.sol"; import {WorldIDIdentityManagerImplV1 as ManagerImplV1} from "../../WorldIDIdentityManagerImplV1.sol"; /// @title World ID Identity Manager Ownership Management Tests @@ -137,7 +137,7 @@ contract WorldIDIdentityManagerOwnershipManagement is WorldIDIdentityManagerTest vm.expectRevert("Function must be called through delegatecall"); // Test - managerImpl.identityOperator(); + managerImplV2.identityOperator(); } /// @notice Ensures that it is possible for the owner to set the address of the identity @@ -180,6 +180,6 @@ contract WorldIDIdentityManagerOwnershipManagement is WorldIDIdentityManagerTest vm.expectRevert("Function must be called through delegatecall"); // Test - managerImpl.setIdentityOperator(newOperator); + managerImplV2.setIdentityOperator(newOperator); } } diff --git a/src/test/identity-manager/WorldIDIdentityManagerSemaphoreVerification.t.sol b/src/test/identity-manager/WorldIDIdentityManagerSemaphoreVerification.t.sol index 2de8c13..853eedb 100644 --- a/src/test/identity-manager/WorldIDIdentityManagerSemaphoreVerification.t.sol +++ b/src/test/identity-manager/WorldIDIdentityManagerSemaphoreVerification.t.sol @@ -10,6 +10,7 @@ import {SemaphoreVerifier} from "src/test/SemaphoreVerifier16.sol"; import {WorldIDIdentityManager as IdentityManager} from "../../WorldIDIdentityManager.sol"; import {WorldIDIdentityManagerImplV2 as ManagerImpl} from "../../WorldIDIdentityManagerImplV2.sol"; import {WorldIDIdentityManagerImplV1 as ManagerImplV1} from "../../WorldIDIdentityManagerImplV1.sol"; +import {WorldIDIdentityManagerImplV3 as ManagerImplV3} from "../../WorldIDIdentityManagerImplV3.sol"; /// @title World ID Identity Manager Semaphore Proof Verification Tests /// @notice Contains tests for the WorldID identity manager. @@ -17,6 +18,50 @@ import {WorldIDIdentityManagerImplV1 as ManagerImplV1} from "../../WorldIDIdenti /// @dev This test suite tests both the proxy and the functionality of the underlying implementation /// so as to test everything in the context of how it will be deployed. contract WorldIDIdentityManagerSemaphoreVerification is WorldIDIdentityManagerTest { + /// @notice Checks that the proof validates properly with the correct inputs. + function testProofVerificationWithCompressedProof( + uint8 actualTreeDepth, + uint256 nullifierHash, + uint256 signalHash, + uint256 externalNullifierHash, + uint256[4] memory prf // Compressed proof is 4 uints + ) public { + // Setup + ISemaphoreVerifier actualSemaphoreVerifier = new SimpleSemaphoreVerifier(); + vm.assume(SemaphoreTreeDepthValidator.validate(actualTreeDepth)); + vm.assume(prf[0] != 0); + // SimpleVerifier expects that proof[0] % 2 == 0 for compressed proof + // The opposite is true for non-compressed proofs + // This checks that the correct logical path in IdentityManager is taken + vm.assume(prf[0] % 2 == 0); + uint256[8] memory prfExpanded = [prf[0], prf[1], prf[2], prf[3], 0, 0, 0, 0]; + + // Use IdentityManager V3 + makeNewIdentityManagerV3( + actualTreeDepth, + insertionPreRoot, + defaultInsertVerifiers, + defaultDeletionVerifiers, + defaultUpdateVerifiers, + actualSemaphoreVerifier + ); + bytes memory verifyProofCallData = abi.encodeCall( + ManagerImplV1.verifyProof, + (insertionPreRoot, nullifierHash, signalHash, externalNullifierHash, prfExpanded) + ); + + // Test + assertCallSucceedsOn(identityManagerAddress, verifyProofCallData); + + bytes memory verifyCompressedProofCallData = abi.encodeCall( + ManagerImplV3.verifyCompressedProof, + (insertionPreRoot, nullifierHash, signalHash, externalNullifierHash, prf) + ); + + // Test + assertCallSucceedsOn(identityManagerAddress, verifyCompressedProofCallData); + } + /// @notice Checks that the proof validates properly with the correct inputs. function testProofVerificationWithCorrectInputs( uint8 actualTreeDepth, @@ -29,6 +74,8 @@ contract WorldIDIdentityManagerSemaphoreVerification is WorldIDIdentityManagerTe ISemaphoreVerifier actualSemaphoreVerifier = new SimpleSemaphoreVerifier(); vm.assume(SemaphoreTreeDepthValidator.validate(actualTreeDepth)); vm.assume(prf[0] != 0); + // SimpleVerifier expects that proof[0] % 2 != 0 for non-compressed proof + // This checks that the correct logical path in IdentityManager is taken vm.assume(prf[0] % 2 != 0); makeNewIdentityManager( actualTreeDepth, diff --git a/src/test/identity-manager/WorldIDIdentityManagerTest.sol b/src/test/identity-manager/WorldIDIdentityManagerTest.sol index fffcf09..1949928 100644 --- a/src/test/identity-manager/WorldIDIdentityManagerTest.sol +++ b/src/test/identity-manager/WorldIDIdentityManagerTest.sol @@ -17,21 +17,24 @@ import {VerifierLookupTable} from "../../data/VerifierLookupTable.sol"; import {WorldIDIdentityManager as IdentityManager} from "../../WorldIDIdentityManager.sol"; import {WorldIDIdentityManagerImplV1 as ManagerImplV1} from "../../WorldIDIdentityManagerImplV1.sol"; -import {WorldIDIdentityManagerImplV2 as ManagerImpl} from "../../WorldIDIdentityManagerImplV2.sol"; - +import {WorldIDIdentityManagerImplV2 as ManagerImplV2} from "../../WorldIDIdentityManagerImplV2.sol"; +import {WorldIDIdentityManagerImplV3 as ManagerImplV3} from "../../WorldIDIdentityManagerImplV3.sol"; /// @title World ID Identity Manager Test. /// @notice Contains tests for the WorldID identity manager. /// @author Worldcoin /// @dev This test suite tests both the proxy and the functionality of the underlying implementation /// so as to test everything in the context of how it will be deployed. + contract WorldIDIdentityManagerTest is WorldIDTest { /////////////////////////////////////////////////////////////////////////////// /// TEST DATA /// /////////////////////////////////////////////////////////////////////////////// IdentityManager internal identityManager; + // V3 + ManagerImplV3 internal managerImplV3; // V2 - ManagerImpl internal managerImpl; + ManagerImplV2 internal managerImplV2; // V1 ManagerImplV1 internal managerImplV1; @@ -40,8 +43,10 @@ contract WorldIDIdentityManagerTest is WorldIDTest { uint8 internal treeDepth = 16; address internal identityManagerAddress; + // V3 + address internal managerImplV3Address; // V2 - address internal managerImplAddress; + address internal managerImplV2Address; // V1 address internal managerImplV1Address; @@ -198,7 +203,7 @@ contract WorldIDIdentityManagerTest is WorldIDTest { hevm.label(address(this), "Sender"); hevm.label(identityManagerAddress, "IdentityManager"); - hevm.label(managerImplAddress, "ManagerImplementation"); + hevm.label(managerImplV2Address, "ManagerImplementationV2"); hevm.label(managerImplV1Address, "ManagerImplementationV1"); } @@ -239,18 +244,73 @@ contract WorldIDIdentityManagerTest is WorldIDTest { identityManagerAddress = address(identityManager); // creates Manager Impl V2, which will be used for tests - managerImpl = new ManagerImpl(); - managerImplAddress = address(managerImpl); + managerImplV2 = new ManagerImplV2(); + managerImplV2Address = address(managerImplV2); - bytes memory initCallV2 = abi.encodeCall(ManagerImpl.initializeV2, (deletionVerifiers)); + bytes memory initCallV2 = abi.encodeCall(ManagerImplV2.initializeV2, (deletionVerifiers)); bytes memory upgradeCall = abi.encodeCall( - UUPSUpgradeable.upgradeToAndCall, (address(managerImplAddress), initCallV2) + UUPSUpgradeable.upgradeToAndCall, (address(managerImplV2Address), initCallV2) ); // Test assertCallSucceedsOn(identityManagerAddress, upgradeCall, new bytes(0x0)); } + /// @notice Initialises a new identity manager using the provided information. + /// @dev It is initialised in the globals. + /// + /// @param actualPreRoot The pre-root to use. + /// @param insertVerifiers The insertion verifier lookup table. + /// @param updateVerifiers The udpate verifier lookup table. + /// @param actualSemaphoreVerifier The Semaphore verifier instance to use. + function makeNewIdentityManagerV3( + uint8 actualTreeDepth, + uint256 actualPreRoot, + VerifierLookupTable insertVerifiers, + VerifierLookupTable deletionVerifiers, + VerifierLookupTable updateVerifiers, + ISemaphoreVerifier actualSemaphoreVerifier + ) public { + managerImplV1 = new ManagerImplV1(); + managerImplV1Address = address(managerImplV1); + + bytes memory initCallData = abi.encodeCall( + ManagerImplV1.initialize, + ( + actualTreeDepth, + actualPreRoot, + insertVerifiers, + updateVerifiers, + actualSemaphoreVerifier + ) + ); + + identityManager = new IdentityManager(managerImplV1Address, initCallData); + identityManagerAddress = address(identityManager); + + // creates Manager Impl V2, which will be used for tests + managerImplV2 = new ManagerImplV2(); + managerImplV2Address = address(managerImplV2); + + bytes memory initCallV2 = abi.encodeCall(ManagerImplV2.initializeV2, (deletionVerifiers)); + bytes memory upgradeCallV2 = abi.encodeCall( + UUPSUpgradeable.upgradeToAndCall, (address(managerImplV2Address), initCallV2) + ); + + // Test + assertCallSucceedsOn(identityManagerAddress, upgradeCallV2, new bytes(0x0)); + + // creates Manager Impl V3 + managerImplV3 = new ManagerImplV3(); + managerImplV3Address = address(managerImplV3); + + bytes memory upgradeCallV3 = + abi.encodeCall(UUPSUpgradeable.upgradeTo, (address(managerImplV3Address))); + + // Test + assertCallSucceedsOn(identityManagerAddress, upgradeCallV3); + } + /// @notice Initialises a new identity manager using the provided information. /// @dev It is initialised in the globals. /// @@ -327,9 +387,9 @@ contract WorldIDIdentityManagerTest is WorldIDTest { /// @notice Creates a new identity manager without initializing the delegate. /// @dev It is constructed in the globals. function makeUninitIdentityManager() public { - managerImpl = new ManagerImpl(); - managerImplAddress = address(managerImpl); - identityManager = new IdentityManager(managerImplAddress, new bytes(0x0)); + managerImplV2 = new ManagerImplV2(); + managerImplV2Address = address(managerImplV2); + identityManager = new IdentityManager(managerImplV2Address, new bytes(0x0)); identityManagerAddress = address(identityManager); } diff --git a/src/test/identity-manager/WorldIDIdentityManagerUpgrade.t.sol b/src/test/identity-manager/WorldIDIdentityManagerUpgrade.t.sol index 012c4d4..4766efb 100644 --- a/src/test/identity-manager/WorldIDIdentityManagerUpgrade.t.sol +++ b/src/test/identity-manager/WorldIDIdentityManagerUpgrade.t.sol @@ -6,7 +6,7 @@ import {WorldIDIdentityManagerTest} from "./WorldIDIdentityManagerTest.sol"; import {UUPSUpgradeable} from "contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import {WorldIDIdentityManagerImplMock} from "../mock/WorldIDIdentityManagerImplMock.sol"; -import {WorldIDIdentityManagerImplV2 as ManagerImpl} from "../../WorldIDIdentityManagerImplV2.sol"; +import {WorldIDIdentityManagerImplV2 as ManagerImplV2} from "../../WorldIDIdentityManagerImplV2.sol"; import {WorldIDIdentityManagerImplV1 as ManagerImplV1} from "../../WorldIDIdentityManagerImplV1.sol"; /// @title World ID Identity Manager Upgrade Test @@ -74,6 +74,6 @@ contract WorldIDIdentityManagerUpdate is WorldIDIdentityManagerTest { vm.expectRevert("Function must be called through delegatecall"); // Test - managerImpl.upgradeToAndCall(mockUpgradeAddress, initCall); + managerImplV2.upgradeToAndCall(mockUpgradeAddress, initCall); } } diff --git a/src/test/mock/SimpleSemaphoreVerifier.sol b/src/test/mock/SimpleSemaphoreVerifier.sol index 2e80cd7..591313a 100644 --- a/src/test/mock/SimpleSemaphoreVerifier.sol +++ b/src/test/mock/SimpleSemaphoreVerifier.sol @@ -16,4 +16,15 @@ contract SimpleSemaphoreVerifier is ISemaphoreVerifier { revert Semaphore__InvalidProof(); } } + + function verifyCompressedProof(uint256[4] calldata proof, uint256[4] memory input) + external + pure + { + delete input; + + if (proof[0] % 2 != 0) { + revert Semaphore__InvalidProof(); + } + } }