From 68c956968fb9901f042f1262c1e8684bff6aacdd Mon Sep 17 00:00:00 2001 From: "dcbuilder.eth" Date: Thu, 7 Sep 2023 19:33:41 +0100 Subject: [PATCH] create WorldIDIdentityManagerImplV2 to add support for deleteIdentities (#126) * init * add logic and fix breaking tests missing new tests and removing all state bridge logic * make ci ignore draft PRs, use latest foundry version * remove state bridge logic * update solidity version * intermediary push * fix tests tests compile, however missing ImplV2 tests for init, uninit and deletionVerifier logic * fix init tests * fix uninit tests * add deletion tests missing two tests which require the real gnark circuit, pending * add deletion circuit tests failing atm * add getters setters tests * add asm keccak * compilation fix tests are still broken * fix test * fix deletion tests * trigger CI * add scoping to avoid breaking test into multiple parts * fix to use bytes calldata instead of uint256[] * update nastpec docs * update nastpec docs --------- Co-authored-by: Marcin Kostrzewa --- .github/workflows/tests.yml | 3 +- README.md | 5 +- src/WorldIDIdentityManager.sol | 2 +- src/WorldIDIdentityManagerImplV1.sol | 146 ++----- src/WorldIDIdentityManagerImplV2.sol | 236 +++++++++++ src/WorldIDRouter.sol | 2 +- src/WorldIDRouterImplV1.sol | 2 +- src/abstract/WorldIDImpl.sol | 2 +- src/abstract/WorldIDProxy.sol | 2 +- src/data/VerifierLookupTable.sol | 2 +- src/interfaces/IBaseWorldID.sol | 2 +- src/interfaces/IBridge.sol | 2 +- src/interfaces/ITreeVerifier.sol | 2 +- src/interfaces/IWorldID.sol | 2 +- src/interfaces/IWorldIDGroups.sol | 2 +- src/test/WorldIDTest.sol | 2 +- src/test/data/TestDeletionParams.json | 153 +++++++ ...stParams.json => TestInsertionParams.json} | 0 .../WorldIDIdentityManagerCalculation.t.sol | 56 ++- .../WorldIDIdentityManagerConstruction.t.sol | 29 +- .../WorldIDIdentityManagerDataQuery.t.sol | 78 ++-- ...WorldIDIdentityManagerGettersSetters.t.sol | 126 ++++-- ...rldIDIdentityManagerIdentityDeletion.t.sol | 387 ++++++++++++++++++ ...DIdentityManagerIdentityRegistration.t.sol | 279 +++++++------ ...WorldIDIdentityManagerIdentityUpdate.t.sol | 115 +++--- ...WorldIDIdentityManagerInitialization.t.sol | 57 ++- ...IDIdentityManagerOwnershipManagement.t.sol | 15 +- ...IdentityManagerSemaphoreVerification.t.sol | 27 +- .../WorldIDIdentityManagerStateBridge.t.sol | 155 ------- .../WorldIDIdentityManagerTest.sol | 157 ++++--- .../WorldIDIdentityManagerUninit.t.sol | 103 +++-- .../WorldIDIdentityManagerUpgrade.t.sol | 11 +- src/test/mock/DeletionTreeVerifier.sol | 335 +++++++++++++++ ...Verifier.sol => InsertionTreeVerifier.sol} | 2 +- src/test/mock/SequencerVerifier.sol | 2 +- src/test/mock/SimpleSemaphoreVerifier.sol | 2 +- src/test/mock/SimpleStateBridge.sol | 2 +- src/test/mock/SimpleVerifier.sol | 2 +- .../mock/WorldIDIdentityManagerImplMock.sol | 4 +- src/test/mock/WorldIDRouterImplMock.sol | 2 +- .../router/WorldIDRouterConstruction.t.sol | 2 +- src/test/router/WorldIDRouterDataQuery.t.sol | 2 +- .../WorldIDRouterOwnershipManagement.t.sol | 2 +- src/test/router/WorldIDRouterRouting.t.sol | 2 +- .../router/WorldIDRouterStateBridge.t.sol | 2 +- src/test/router/WorldIDRouterTest.sol | 2 +- src/test/router/WorldIDRouterUninit.t.sol | 2 +- src/test/router/WorldIDRouterUpgrade.t.sol | 2 +- src/test/utils/TypeConverter.sol | 2 +- .../VerifierLookupTableConstruction.t.sol | 2 +- ...rifierLookupTableOwnershipManagement.t.sol | 2 +- .../VerifierLookupTableQuery.t.sol.sol | 2 +- .../VerifierLookupTableTest.sol | 2 +- src/utils/CheckInitialized.sol | 2 +- src/utils/SemaphoreTreeDepthValidator.sol | 2 +- src/utils/UnimplementedTreeVerifier.sol | 2 +- 56 files changed, 1838 insertions(+), 707 deletions(-) create mode 100644 src/WorldIDIdentityManagerImplV2.sol create mode 100644 src/test/data/TestDeletionParams.json rename src/test/data/{TestParams.json => TestInsertionParams.json} (100%) create mode 100644 src/test/identity-manager/WorldIDIdentityManagerIdentityDeletion.t.sol delete mode 100644 src/test/identity-manager/WorldIDIdentityManagerStateBridge.t.sol create mode 100644 src/test/mock/DeletionTreeVerifier.sol rename src/test/mock/{TreeVerifier.sol => InsertionTreeVerifier.sol} (99%) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f21a3e2..5dd6da4 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -8,6 +8,7 @@ name: Unit Tests jobs: tests: + if: github.event.pull_request.draft == false runs-on: ubuntu-latest steps: @@ -17,8 +18,6 @@ jobs: - name: Install Foundry uses: onbjerg/foundry-toolchain@v1 - with: - version: nightly-e15e33a07c0920189fc336391f538c3dad53da73 - uses: actions/setup-node@v2 with: diff --git a/README.md b/README.md index 5b56dbb..df78c6c 100644 --- a/README.md +++ b/README.md @@ -24,8 +24,9 @@ any personal data**. Stop bots, stop abuse. World ID uses a device called the [Orb](https://worldcoin.org/how-the-launch-works) which takes a picture of a person's iris to verify they are a unique and alive human. The protocol only requires a hash-equivalent (i.e. irreversible) of the iris to be stored (which happens on a blockchain). The -protocol uses [Zero-knowledge proofs](https://docs.worldcoin.org/further-reading/zero-knowledge-proofs) so no traceable information is -ever public. +protocol uses +[Zero-knowledge proofs](https://docs.worldcoin.org/further-reading/zero-knowledge-proofs) so no +traceable information is ever public. World ID is meant for on-chain web3 apps, traditional Cloud applications, and even IRL verifications. Go to the [World ID app](https://worldcoin.org/download-app) to get started. diff --git a/src/WorldIDIdentityManager.sol b/src/WorldIDIdentityManager.sol index f7a3784..7d0b3d6 100644 --- a/src/WorldIDIdentityManager.sol +++ b/src/WorldIDIdentityManager.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; import {WorldIDProxy} from "./abstract/WorldIDProxy.sol"; diff --git a/src/WorldIDIdentityManagerImplV1.sol b/src/WorldIDIdentityManagerImplV1.sol index 4bd1e3b..280a058 100644 --- a/src/WorldIDIdentityManagerImplV1.sol +++ b/src/WorldIDIdentityManagerImplV1.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; import {WorldIDImpl} from "./abstract/WorldIDImpl.sol"; @@ -81,7 +81,7 @@ contract WorldIDIdentityManagerImplV1 is WorldIDImpl, IWorldID { /// @notice The table of verifiers for verifying batch identity insertions. VerifierLookupTable internal batchInsertionVerifiers; - /// @notice The table of verifiers for verifying batch identity insertions. + /// @notice The table of verifiers for verifying identity updates. VerifierLookupTable internal identityUpdateVerifiers; /// @notice The verifier instance needed for operating within the semaphore protocol. @@ -123,6 +123,7 @@ contract WorldIDIdentityManagerImplV1 is WorldIDImpl, IWorldID { /// @notice Represents the kind of change that is made to the root of the tree. enum TreeChange { Insertion, + Deletion, Update } @@ -130,6 +131,7 @@ contract WorldIDIdentityManagerImplV1 is WorldIDImpl, IWorldID { enum Dependency { StateBridge, InsertionVerifierLookupTable, + DeletionVerifierLookupTable, UpdateVerifierLookupTable, SemaphoreVerifier } @@ -239,12 +241,7 @@ contract WorldIDIdentityManagerImplV1 is WorldIDImpl, IWorldID { /// @param _treeDepth The depth of the MerkeTree /// @param initialRoot The initial value for the `latestRoot` in the contract. When deploying /// this should be set to the root of the empty tree. - /// @param _enableStateBridge Whether or not the state bridge should be enabled when - /// initialising the identity manager. - /// @param __stateBridge The initial state bridge contract to use. - event WorldIDIdentityManagerImplInitialized( - uint8 _treeDepth, uint256 initialRoot, bool _enableStateBridge, IBridge __stateBridge - ); + event WorldIDIdentityManagerImplInitialized(uint8 _treeDepth, uint256 initialRoot); /////////////////////////////////////////////////////////////////////////////// /// INITIALIZATION /// @@ -273,9 +270,6 @@ contract WorldIDIdentityManagerImplV1 is WorldIDImpl, IWorldID { /// @param _batchInsertionVerifiers The verifier lookup table for batch insertions. /// @param _batchUpdateVerifiers The verifier lookup table for batch updates. /// @param _semaphoreVerifier The verifier to use for semaphore protocol proofs. - /// @param _enableStateBridge Whether or not the state bridge should be enabled when - /// initialising the identity manager. - /// @param __stateBridge The initial state bridge contract to use. /// /// @custom:reverts string If called more than once at the same initialisation number. /// @custom:reverts UnsupportedTreeDepth If passed tree depth is not among defined values. @@ -284,9 +278,7 @@ contract WorldIDIdentityManagerImplV1 is WorldIDImpl, IWorldID { uint256 initialRoot, VerifierLookupTable _batchInsertionVerifiers, VerifierLookupTable _batchUpdateVerifiers, - ISemaphoreVerifier _semaphoreVerifier, - bool _enableStateBridge, - IBridge __stateBridge + ISemaphoreVerifier _semaphoreVerifier ) public reinitializer(1) { // First, ensure that all of the parent contracts are initialised. __delegateInit(); @@ -302,16 +294,12 @@ contract WorldIDIdentityManagerImplV1 is WorldIDImpl, IWorldID { batchInsertionVerifiers = _batchInsertionVerifiers; identityUpdateVerifiers = _batchUpdateVerifiers; semaphoreVerifier = _semaphoreVerifier; - _stateBridge = __stateBridge; - _isStateBridgeEnabled = _enableStateBridge; _identityOperator = owner(); // Say that the contract is initialized. __setInitialized(); - emit WorldIDIdentityManagerImplInitialized( - _treeDepth, initialRoot, _enableStateBridge, __stateBridge - ); + emit WorldIDIdentityManagerImplInitialized(_treeDepth, initialRoot); } /// @notice Responsible for initialising all of the supertypes of this contract. @@ -421,9 +409,6 @@ contract WorldIDIdentityManagerImplV1 is WorldIDImpl, IWorldID { // which it was expired. rootHistory[preRoot] = uint128(block.timestamp); - // With the update confirmed, we send the root across multiple chains to ensure sync. - sendRootToStateBridge(); - emit TreeChanged(preRoot, TreeChange.Insertion, postRoot); } catch Error(string memory errString) { /// This is not the revert we're looking for. @@ -571,9 +556,6 @@ contract WorldIDIdentityManagerImplV1 is WorldIDImpl, IWorldID { // it was expired. rootHistory[preRoot] = uint128(block.timestamp); - // With the update confirmed, we send the root across multiple chains to ensure sync. - sendRootToStateBridge(); - emit TreeChanged(preRoot, TreeChange.Update, postRoot); } catch Error(string memory errString) { /// This is not the revert we're looking for. @@ -663,78 +645,6 @@ contract WorldIDIdentityManagerImplV1 is WorldIDImpl, IWorldID { return _latestRoot; } - /// @notice Sends the latest root to the state bridge. - /// @dev Only sends if the state bridge address is not the zero address. - /// - function sendRootToStateBridge() internal virtual onlyProxy onlyInitialized { - if (_isStateBridgeEnabled && address(_stateBridge) != address(0)) { - _stateBridge.sendRootMultichain(_latestRoot); - } - } - - /// @notice Allows a caller to query the address of the current stateBridge. - /// - /// @return stateBridgeContract The address of the currently used stateBridge - function stateBridge() - public - view - virtual - onlyProxy - onlyInitialized - returns (IBridge stateBridgeContract) - { - return _stateBridge; - } - - /// @notice Allows a caller to upgrade the stateBridge. - /// @dev Only the owner of the contract can call this function. - /// - /// @param newStateBridge The new stateBridge contract - function setStateBridge(IBridge newStateBridge) - public - virtual - onlyProxy - onlyInitialized - onlyOwner - { - if (address(newStateBridge) == address(0)) { - revert InvalidStateBridgeAddress(); - } - - if (!_isStateBridgeEnabled) { - enableStateBridge(); - } - - IBridge oldStateBridge = _stateBridge; - _stateBridge = newStateBridge; - - emit DependencyUpdated( - Dependency.StateBridge, address(oldStateBridge), address(newStateBridge) - ); - } - - /// @notice Enables the state bridge. - /// @dev Only the owner of the contract can call this function. - function enableStateBridge() public virtual onlyProxy onlyInitialized onlyOwner { - if (!_isStateBridgeEnabled) { - _isStateBridgeEnabled = true; - emit StateBridgeStateChange(true); - } else { - revert StateBridgeAlreadyEnabled(); - } - } - - /// @notice Disables the state bridge. - /// @dev Only the owner of the contract can call this function. - function disableStateBridge() public virtual onlyProxy onlyInitialized onlyOwner { - if (_isStateBridgeEnabled) { - _isStateBridgeEnabled = false; - emit StateBridgeStateChange(false); - } else { - revert StateBridgeAlreadyDisabled(); - } - } - /// @notice Allows a caller to query the root history for information about a given root. /// @dev Should be used sparingly as the query can be quite expensive. /// @@ -797,11 +707,9 @@ contract WorldIDIdentityManagerImplV1 is WorldIDImpl, IWorldID { // if `element` is zero, it will underflow and be greater than SNARK_SCALAR_FIELD - 1. // If `element` is non-zero, we can cancel the -1 terms and the comparison becomes what // we want: `element < SNARK_SCALAR_FIELD`. - for - { } - and(lt(offset, max), lt(sub(calldataload(offset), 1), SNARK_SCALAR_FIELD_MIN_ONE)) - { offset := add(offset, 32) } - { } + for {} and( + lt(offset, max), lt(sub(calldataload(offset), 1), SNARK_SCALAR_FIELD_MIN_ONE) + ) { offset := add(offset, 32) } {} } // check if the loop terminated before end of array and handle the remaining elements @@ -813,17 +721,15 @@ contract WorldIDIdentityManagerImplV1 is WorldIDImpl, IWorldID { // this means we need to finish looping and make sure all remaining elements are zero. if (element == 0) { assembly ("memory-safe") { - // we just confirmed this element is zero - offset := add(offset, 32) - - // increment offset until either of the following happens: - // - offset is equal to max, meaning we've reached the end of the array - // - the element at offset is non-zero - for - { } - and(lt(offset, max), iszero(calldataload(offset))) - { offset := add(offset, 32) } - { } + // we just confirmed this element is zero + offset := add(offset, 32) + + // increment offset until either of the following happens: + // - offset is equal to max, meaning we've reached the end of the array + // - the element at offset is non-zero + for {} and(lt(offset, max), iszero(calldataload(offset))) { + offset := add(offset, 32) + } {} } // check if the loop terminated because it found a non-zero element @@ -836,7 +742,7 @@ contract WorldIDIdentityManagerImplV1 is WorldIDImpl, IWorldID { // otherwise check if the loop terminated because it found an unreduced element // if so, revert else if (element >= SNARK_SCALAR_FIELD) { - revert UnreducedElement(UnreducedElementType.IdentityCommitment, element); + revert UnreducedElement(UnreducedElementType.IdentityCommitment, element); } } } @@ -859,13 +765,11 @@ contract WorldIDIdentityManagerImplV1 is WorldIDImpl, IWorldID { // increment offset until either of the following happens: // - offset is equal to max, meaning we've reached the end of the array // - the element at offset is greater than or equal to SNARK_SCALAR_FIELD - for - { } - and(lt(offset, max), lt(calldataload(offset), SNARK_SCALAR_FIELD)) - { offset := add(offset, 32) } - { } + for {} and(lt(offset, max), lt(calldataload(offset), SNARK_SCALAR_FIELD)) { + offset := add(offset, 32) + } {} } - // check if the loop terminated before end of array and revert if so + // check if the loop terminated before end of array and revert if so if (offset < max) { uint256 index = identityCommitments.length - ((max - offset) >> 5); uint256 element = identityCommitments[index]; @@ -1053,8 +957,6 @@ contract WorldIDIdentityManagerImplV1 is WorldIDImpl, IWorldID { uint256 oldExpiry = rootHistoryExpiry; rootHistoryExpiry = newExpiryTime; - _stateBridge.setRootHistoryExpiry(newExpiryTime); - emit RootHistoryExpirySet(oldExpiry, newExpiryTime); } diff --git a/src/WorldIDIdentityManagerImplV2.sol b/src/WorldIDIdentityManagerImplV2.sol new file mode 100644 index 0000000..e39ac10 --- /dev/null +++ b/src/WorldIDIdentityManagerImplV2.sol @@ -0,0 +1,236 @@ +pragma solidity ^0.8.21; + +import "./WorldIDIdentityManagerImplV1.sol"; + +/// @title WorldID Identity Manager Implementation Version 2 +/// @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 insertions. +/// @dev This is the implementation delegated to by a proxy. +contract WorldIDIdentityManagerImplV2 is WorldIDIdentityManagerImplV1 { + /////////////////////////////////////////////////////////////////////////////// + /// 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). + + /// @notice The table of verifiers for verifying batch identity deletions. + VerifierLookupTable internal batchDeletionVerifiers; + + /// @notice Initializes the V2 implementation contract. + /// @dev Must be called exactly once + /// @dev This is marked `reinitializer()` to allow for updated initialisation steps when working + /// with upgrades based upon this contract. Be aware that there are only 256 (zero-indexed) + /// initialisations allowed, so decide carefully when to use them. Many cases can safely be + /// replaced by use of setters. + /// @dev This function is explicitly not virtual as it does not make sense to override even when + /// upgrading. Create a separate initializer function instead. + /// + /// + function initializeV2(VerifierLookupTable _batchUpdateVerifiers) public reinitializer(2) { + batchDeletionVerifiers = _batchUpdateVerifiers; + } + + /////////////////////////////////////////////////////////////////// + /// IDENTITY MANAGEMENT /// + /////////////////////////////////////////////////////////////////// + + /// @notice Deletes identities from the WorldID system. + /// @dev Can only be called by the owner. + /// @dev Deletion is performed off-chain and verified on-chain via the `deletionProof`. + /// This saves gas and time over deleting identities one at a time. + /// + /// @param deletionProof The proof that given the conditions (`preRoot` and `packedDeletionIndices`), + /// deletion into the tree results in `postRoot`. Elements 0 and 1 are the `x` and `y` + /// coordinates for `ar` respectively. Elements 2 and 3 are the `x` coordinate for `bs`, + /// and elements 4 and 5 are the `y` coordinate for `bs`. Elements 6 and 7 are the `x` + /// and `y` coordinates for `krs`. + /// @param batchSize The number of identities that are to be deleted in the current batch. + /// @param packedDeletionIndices The indices of the identities that were deleted from the tree. + /// @param preRoot The value for the root of the tree before the `identityCommitments` have been + /// inserted. Must be an element of the field `Kr`. + /// @param postRoot The root obtained after deleting all of `identityCommitments` into the tree + /// described by `preRoot`. Must be an element of the field `Kr`. + /// + /// @custom:reverts Unauthorized If the message sender is not authorised to add identities. + /// @custom:reverts InvalidCommitment If one or more of the provided commitments is invalid. + /// @custom:reverts NotLatestRoot If the provided `preRoot` is not the latest root. + /// @custom:reverts ProofValidationFailure If `deletionProof` cannot be verified using the + /// provided inputs. + /// @custom:reverts UnreducedElement If any of the `preRoot`, `postRoot` and + /// `identityCommitments` is not an element of the field `Kr`. It describes the + /// type and value of the unreduced element. + /// @custom:reverts VerifierLookupTable.NoSuchVerifier If the batch sizes doesn't match a known + /// verifier. + /// @custom:reverts VerifierLookupTable.BatchTooLarge If the batch size exceeds the maximum + /// batch size. + function deleteIdentities( + uint256[8] calldata deletionProof, + uint32 batchSize, + bytes calldata packedDeletionIndices, + uint256 preRoot, + uint256 postRoot + ) public virtual onlyProxy onlyInitialized onlyIdentityOperator { + // We can only operate on the latest root in reduced form. + if (preRoot >= SNARK_SCALAR_FIELD) { + revert UnreducedElement(UnreducedElementType.PreRoot, preRoot); + } + if (preRoot != _latestRoot) { + revert NotLatestRoot(preRoot, _latestRoot); + } + + // We need the post root to be in reduced form. + if (postRoot >= SNARK_SCALAR_FIELD) { + revert UnreducedElement(UnreducedElementType.PostRoot, postRoot); + } + + // Having validated the preconditions we can now check the proof itself. + bytes32 inputHash = + calculateIdentityDeletionInputHash(packedDeletionIndices, preRoot, postRoot, batchSize); + + // No matter what, the inputs can result in a hash that is not an element of the scalar + // field in which we're operating. We reduce it into the field before handing it to the + // verifier. + uint256 reducedElement = uint256(inputHash) % SNARK_SCALAR_FIELD; + + // We need to look up the correct verifier before we can verify. + ITreeVerifier deletionVerifier = batchDeletionVerifiers.getVerifierFor(batchSize); + + // With that, we can properly try and verify. + try deletionVerifier.verifyProof( + [deletionProof[0], deletionProof[1]], + [[deletionProof[2], deletionProof[3]], [deletionProof[4], deletionProof[5]]], + [deletionProof[6], deletionProof[7]], + [reducedElement] + ) returns (bool verifierResult) { + // If the proof did not verify, we revert with a failure. + if (!verifierResult) { + revert ProofValidationFailure(); + } + + // If it did verify, we need to update the contract's state. We set the currently valid + // root to the root after the insertions. + _latestRoot = postRoot; + + // We also need to add the previous root to the history, and set the timestamp at + // which it was expired. + rootHistory[preRoot] = uint128(block.timestamp); + + emit TreeChanged(preRoot, TreeChange.Deletion, postRoot); + } catch Error(string memory errString) { + /// This is not the revert we're looking for. + revert(errString); + } catch { + // If we reach here we know it's the internal error, as the tree verifier only uses + // `require`s otherwise, which will be re-thrown above. + revert ProofValidationFailure(); + } + } + + /// @notice Gets the address for the lookup table of merkle tree verifiers used for batch identity + /// deletions. + /// @dev The deletion verifier supports batch deletions of size 10, 100 and 1000 members per batch. + /// + /// @return addr The address of the contract being used as the verifier lookup table. + function getDeleteIdentitiesVerifierLookupTableAddress() + public + view + virtual + onlyProxy + onlyInitialized + returns (address) + { + return address(batchDeletionVerifiers); + } + + /// @notice Sets the address for the lookup table of merkle tree verifiers used for identity + /// deletions. + /// @dev Only the owner of the contract can call this function. + /// + /// @param newTable The new verifier lookup table to be used for verifying identity + /// deletions. + function setDeleteIdentitiesVerifierLookupTable(VerifierLookupTable newTable) + public + virtual + onlyProxy + onlyInitialized + onlyOwner + { + VerifierLookupTable oldTable = batchDeletionVerifiers; + batchDeletionVerifiers = newTable; + emit DependencyUpdated( + Dependency.DeletionVerifierLookupTable, address(oldTable), address(newTable) + ); + } + + /////////////////////////////////////////////////////////////////////////////// + /// UTILITY FUNCTIONS /// + /////////////////////////////////////////////////////////////////////////////// + + /// @notice Calculates the input hash for the identity deletion verifier. + /// @dev Implements the computation described below. + /// + /// @param packedDeletionIndices The indices of the identities that were deleted from the tree. + /// @param preRoot The root value of the tree before these insertions were made. + /// @param postRoot The root value of the tree after these insertions were made. + /// @param batchSize The number of identities that were deleted in this batch + /// + /// @return hash The input hash calculated as described below. + /// + /// @dev the deletion indices are packed into bytes calldata where each deletion index is 32 bits + /// wide. The indices are encoded using abi.encodePacked for testing. + /// + /// We keccak hash all input to save verification gas. Inputs for the hash are arranged as follows: + /// + /// packedDeletionIndices || PreRoot || PostRoot + /// 32 bits * batchSize || 256 || 256 + function calculateIdentityDeletionInputHash( + bytes calldata packedDeletionIndices, + uint256 preRoot, + uint256 postRoot, + uint32 batchSize + ) public view virtual onlyProxy onlyInitialized returns (bytes32 hash) { + assembly { + let startOffset := mload(0x40) + let indicesByteSize := mul(batchSize, 4) + calldatacopy(startOffset, packedDeletionIndices.offset, indicesByteSize) + let rootsOffset := add(startOffset, indicesByteSize) + mstore(rootsOffset, preRoot) + mstore(add(rootsOffset, 32), postRoot) + hash := keccak256(startOffset, add(64, indicesByteSize)) + } + } +} diff --git a/src/WorldIDRouter.sol b/src/WorldIDRouter.sol index 6afb105..fd01001 100644 --- a/src/WorldIDRouter.sol +++ b/src/WorldIDRouter.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; import {WorldIDProxy} from "./abstract/WorldIDProxy.sol"; diff --git a/src/WorldIDRouterImplV1.sol b/src/WorldIDRouterImplV1.sol index 0dd3433..2b4aa35 100644 --- a/src/WorldIDRouterImplV1.sol +++ b/src/WorldIDRouterImplV1.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; import {WorldIDImpl} from "./abstract/WorldIDImpl.sol"; diff --git a/src/abstract/WorldIDImpl.sol b/src/abstract/WorldIDImpl.sol index e3f308d..cba8652 100644 --- a/src/abstract/WorldIDImpl.sol +++ b/src/abstract/WorldIDImpl.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; import {CheckInitialized} from "../utils/CheckInitialized.sol"; diff --git a/src/abstract/WorldIDProxy.sol b/src/abstract/WorldIDProxy.sol index ab04ce5..4083864 100644 --- a/src/abstract/WorldIDProxy.sol +++ b/src/abstract/WorldIDProxy.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; import {ERC1967Proxy} from "openzeppelin-contracts/proxy/ERC1967/ERC1967Proxy.sol"; diff --git a/src/data/VerifierLookupTable.sol b/src/data/VerifierLookupTable.sol index defcbd3..333674d 100644 --- a/src/data/VerifierLookupTable.sol +++ b/src/data/VerifierLookupTable.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; import {Ownable2Step} from "openzeppelin-contracts/access/Ownable2Step.sol"; diff --git a/src/interfaces/IBaseWorldID.sol b/src/interfaces/IBaseWorldID.sol index b8bcaa7..3edd659 100644 --- a/src/interfaces/IBaseWorldID.sol +++ b/src/interfaces/IBaseWorldID.sol @@ -1,5 +1,5 @@ //SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; /// @title Base WorldID interface /// @author Worldcoin diff --git a/src/interfaces/IBridge.sol b/src/interfaces/IBridge.sol index b1cce5d..30fb905 100644 --- a/src/interfaces/IBridge.sol +++ b/src/interfaces/IBridge.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; interface IBridge { /// @notice Sends the latest Semaphore root to Optimism. diff --git a/src/interfaces/ITreeVerifier.sol b/src/interfaces/ITreeVerifier.sol index a4b6ade..7fa2001 100644 --- a/src/interfaces/ITreeVerifier.sol +++ b/src/interfaces/ITreeVerifier.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; /// @title Tree Verifier Interface /// @author Worldcoin diff --git a/src/interfaces/IWorldID.sol b/src/interfaces/IWorldID.sol index fe81c15..9266d15 100644 --- a/src/interfaces/IWorldID.sol +++ b/src/interfaces/IWorldID.sol @@ -1,5 +1,5 @@ //SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; import {IBaseWorldID} from "./IBaseWorldID.sol"; diff --git a/src/interfaces/IWorldIDGroups.sol b/src/interfaces/IWorldIDGroups.sol index a9a547d..beed417 100644 --- a/src/interfaces/IWorldIDGroups.sol +++ b/src/interfaces/IWorldIDGroups.sol @@ -1,5 +1,5 @@ //SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; import {IBaseWorldID} from "./IBaseWorldID.sol"; diff --git a/src/test/WorldIDTest.sol b/src/test/WorldIDTest.sol index 3bd90f0..0db2fb0 100644 --- a/src/test/WorldIDTest.sol +++ b/src/test/WorldIDTest.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; import {Vm} from "forge-std/Vm.sol"; import {Test} from "forge-std/Test.sol"; diff --git a/src/test/data/TestDeletionParams.json b/src/test/data/TestDeletionParams.json new file mode 100644 index 0000000..36e66e8 --- /dev/null +++ b/src/test/data/TestDeletionParams.json @@ -0,0 +1,153 @@ +{ + "inputHash": "0x227590f99431e20f2f95fdfb1b7dfb648c04242c950c31263ba165647c96501a", + "deletionIndices": [0, 2, 4, 6, 8, 10, 12, 14], + "preRoot": "0x18cb13df3e79b9f847a1494d0a2e6f3cc0041d9cae7e5ccb8cd1852ecdc4af58", + "postRoot": "0x82fcf94594d7363636338e2c29242cc77e3d04f36c8ad64d294d2ab4d251708", + "identityCommitments": ["0x1", "0x3", "0x5", "0x7", "0x9", "0xb", "0xd", "0xf"], + "merkleProofs": [ + [ + "0x2", + "0x20a3af0435914ccd84b806164531b0cd36e37d4efb93efab76913a93e1f30996", + "0x207c74956e87b3f9e6d31ca140770d3b0921e96ff4131e83c93404c448aace11", + "0x20eadd2578c984addd652554c2e2e104406bb3156de001b765b40bd2178c9b8d", + "0x7f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a", + "0x2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55", + "0x2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78", + "0x78295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d", + "0x2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61", + "0xe884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747", + "0x1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2", + "0x1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636", + "0x2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a", + "0x14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0", + "0x190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c", + "0x22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92" + ], + [ + "0x4", + "0x65e2c6cc08a36c4a943286bc91c216054a1981eb4f7570f67394ef8937a21b8", + "0x207c74956e87b3f9e6d31ca140770d3b0921e96ff4131e83c93404c448aace11", + "0x20eadd2578c984addd652554c2e2e104406bb3156de001b765b40bd2178c9b8d", + "0x7f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a", + "0x2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55", + "0x2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78", + "0x78295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d", + "0x2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61", + "0xe884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747", + "0x1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2", + "0x1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636", + "0x2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a", + "0x14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0", + "0x190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c", + "0x22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92" + ], + [ + "0x6", + "0x2aef487272d385cd5eba40e25144e80641fef93ff5b25a0133b0d1bd50077920", + "0x1ebadf14eecbfe79f7ac75d3b6c688b8630fb675fcf24ab20e2a08381998266f", + "0x20eadd2578c984addd652554c2e2e104406bb3156de001b765b40bd2178c9b8d", + "0x7f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a", + "0x2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55", + "0x2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78", + "0x78295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d", + "0x2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61", + "0xe884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747", + "0x1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2", + "0x1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636", + "0x2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a", + "0x14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0", + "0x190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c", + "0x22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92" + ], + [ + "0x8", + "0x20dc7044757b1f24c7f832b9667c4556714019a71a066a22b92bdd77723f6b04", + "0x1ebadf14eecbfe79f7ac75d3b6c688b8630fb675fcf24ab20e2a08381998266f", + "0x20eadd2578c984addd652554c2e2e104406bb3156de001b765b40bd2178c9b8d", + "0x7f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a", + "0x2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55", + "0x2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78", + "0x78295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d", + "0x2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61", + "0xe884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747", + "0x1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2", + "0x1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636", + "0x2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a", + "0x14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0", + "0x190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c", + "0x22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92" + ], + [ + "0xa", + "0x1340c981e5112e73251c5f2e80a7d95c02a2d6086f8cd28c201e09f38d054cf8", + "0x2285bd343aa98c6c45b027216e1cb201894a8c3cf1a0b3765abca88a0ed3a644", + "0x1c430bfac3ed5c853cd68c414d64defb441b07efdd649912885a43f0c39a6f6e", + "0x7f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a", + "0x2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55", + "0x2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78", + "0x78295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d", + "0x2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61", + "0xe884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747", + "0x1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2", + "0x1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636", + "0x2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a", + "0x14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0", + "0x190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c", + "0x22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92" + ], + [ + "0xc", + "0x275543cd9fbcdee56ac8c9a937ad062f198be71714077c99e50ee6b5e2328a2c", + "0x2285bd343aa98c6c45b027216e1cb201894a8c3cf1a0b3765abca88a0ed3a644", + "0x1c430bfac3ed5c853cd68c414d64defb441b07efdd649912885a43f0c39a6f6e", + "0x7f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a", + "0x2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55", + "0x2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78", + "0x78295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d", + "0x2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61", + "0xe884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747", + "0x1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2", + "0x1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636", + "0x2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a", + "0x14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0", + "0x190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c", + "0x22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92" + ], + [ + "0xe", + "0x158743daa8227296c7b3b03fca21e8c7e7e535b1791091d38182cd29795e6001", + "0xa89786c05a24b044fcac8caa1e998c39d9975c454860e5442d85ff42be75e85", + "0x1c430bfac3ed5c853cd68c414d64defb441b07efdd649912885a43f0c39a6f6e", + "0x7f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a", + "0x2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55", + "0x2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78", + "0x78295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d", + "0x2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61", + "0xe884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747", + "0x1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2", + "0x1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636", + "0x2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a", + "0x14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0", + "0x190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c", + "0x22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92" + ], + [ + "0x10", + "0x158cdf90c232e1624129ff7a0994c9acb5b45340ad851036088946991e724354", + "0xa89786c05a24b044fcac8caa1e998c39d9975c454860e5442d85ff42be75e85", + "0x1c430bfac3ed5c853cd68c414d64defb441b07efdd649912885a43f0c39a6f6e", + "0x7f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a", + "0x2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55", + "0x2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78", + "0x78295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d", + "0x2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61", + "0xe884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747", + "0x1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2", + "0x1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636", + "0x2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a", + "0x14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0", + "0x190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c", + "0x22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92" + ] + ] +} diff --git a/src/test/data/TestParams.json b/src/test/data/TestInsertionParams.json similarity index 100% rename from src/test/data/TestParams.json rename to src/test/data/TestInsertionParams.json diff --git a/src/test/identity-manager/WorldIDIdentityManagerCalculation.t.sol b/src/test/identity-manager/WorldIDIdentityManagerCalculation.t.sol index d6f0fbd..e89000c 100644 --- a/src/test/identity-manager/WorldIDIdentityManagerCalculation.t.sol +++ b/src/test/identity-manager/WorldIDIdentityManagerCalculation.t.sol @@ -1,9 +1,10 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; import {WorldIDIdentityManagerTest} from "./WorldIDIdentityManagerTest.sol"; -import {WorldIDIdentityManagerImplV1 as ManagerImpl} from "../../WorldIDIdentityManagerImplV1.sol"; +import {WorldIDIdentityManagerImplV2 as ManagerImpl} from "../../WorldIDIdentityManagerImplV2.sol"; +import {WorldIDIdentityManagerImplV1 as ManagerImplV1} from "../../WorldIDIdentityManagerImplV1.sol"; /// @title World ID Identity Manager Calculation Tests /// @notice Contains tests for the WorldID identity manager. @@ -16,10 +17,10 @@ contract WorldIDIdentityManagerCalculation is WorldIDIdentityManagerTest { function testCalculateIdentityRegistrationInputHashFromParametersOnKnownInput() public { // Setup bytes memory callData = abi.encodeCall( - ManagerImpl.calculateIdentityRegistrationInputHash, - (startIndex, preRoot, postRoot, identityCommitments) + ManagerImplV1.calculateIdentityRegistrationInputHash, + (startIndex, insertionPreRoot, insertionPostRoot, identityCommitments) ); - bytes memory returnData = abi.encode(inputHash); + bytes memory returnData = abi.encode(insertionInputHash); // Test assertCallSucceedsOn(identityManagerAddress, callData, returnData); @@ -32,14 +33,39 @@ contract WorldIDIdentityManagerCalculation is WorldIDIdentityManagerTest { // Test managerImpl.calculateIdentityRegistrationInputHash( - startIndex, preRoot, postRoot, identityCommitments + startIndex, insertionPreRoot, insertionPostRoot, identityCommitments + ); + } + + /// @notice Tests whether it is possible to correctly calculate the `inputHash` to the merkle + /// tree verifier. + function testCalculateIdentityDeletionInputHashFromParametersOnKnownInput() public { + // Setup + bytes memory callData = abi.encodeCall( + ManagerImpl.calculateIdentityDeletionInputHash, + (packedDeletionIndices, deletionPreRoot, deletionPostRoot, deletionBatchSize) + ); + bytes memory returnData = abi.encode(deletionInputHash); + + // Test + assertCallSucceedsOn(identityManagerAddress, callData, returnData); + } + + /// @notice Checks that the input hash can only be calculated if behind the proxy. + function testCannotCalculateIdentityDeletionInputHashIfNotViaProxy() public { + // Setup + vm.expectRevert("Function must be called through delegatecall"); + + // Test + managerImpl.calculateIdentityDeletionInputHash( + packedDeletionIndices, deletionPreRoot, deletionPostRoot, deletionBatchSize ); } /// @notice Check whether it's possible to caculate the identity update input hash. function testCanCalculateIdentityUpdateInputHash( - uint256 preRoot, - uint256 postRoot, + uint256 insertionPreRoot, + uint256 insertionPostRoot, uint32 startIndex1, uint32 startIndex2, uint256 oldIdent1, @@ -62,8 +88,8 @@ contract WorldIDIdentityManagerCalculation is WorldIDIdentityManagerTest { bytes32 expectedResult = keccak256( abi.encodePacked( - preRoot, - postRoot, + insertionPreRoot, + insertionPostRoot, uint256(startIndex1), uint256(startIndex2), oldIdent1, @@ -73,8 +99,8 @@ contract WorldIDIdentityManagerCalculation is WorldIDIdentityManagerTest { ) ); bytes memory callData = abi.encodeCall( - ManagerImpl.calculateIdentityUpdateInputHash, - (preRoot, postRoot, leafIndices, oldIdents, newIdents) + ManagerImplV1.calculateIdentityUpdateInputHash, + (insertionPreRoot, insertionPostRoot, leafIndices, oldIdents, newIdents) ); bytes memory expectedReturn = abi.encode(expectedResult); @@ -85,8 +111,8 @@ contract WorldIDIdentityManagerCalculation is WorldIDIdentityManagerTest { /// @notice Ensures that the identity update hash can only be calculated when called via the /// proxy. function testCannotCalculateIdentityUpdateHashIfNotViaProxy( - uint256 preRoot, - uint256 postRoot, + uint256 insertionPreRoot, + uint256 insertionPostRoot, uint32[] memory leafIndices, uint256[] memory oldIdents, uint256[] memory newIdents @@ -96,7 +122,7 @@ contract WorldIDIdentityManagerCalculation is WorldIDIdentityManagerTest { // Test managerImpl.calculateIdentityUpdateInputHash( - preRoot, postRoot, leafIndices, oldIdents, newIdents + insertionPreRoot, insertionPostRoot, leafIndices, oldIdents, newIdents ); } } diff --git a/src/test/identity-manager/WorldIDIdentityManagerConstruction.t.sol b/src/test/identity-manager/WorldIDIdentityManagerConstruction.t.sol index 0fa8472..385a57c 100644 --- a/src/test/identity-manager/WorldIDIdentityManagerConstruction.t.sol +++ b/src/test/identity-manager/WorldIDIdentityManagerConstruction.t.sol @@ -1,10 +1,12 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; import {WorldIDIdentityManagerTest} from "./WorldIDIdentityManagerTest.sol"; +import {UUPSUpgradeable} from "contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import {WorldIDIdentityManager as IdentityManager} from "../../WorldIDIdentityManager.sol"; -import {WorldIDIdentityManagerImplV1 as ManagerImpl} from "../../WorldIDIdentityManagerImplV1.sol"; +import {WorldIDIdentityManagerImplV2 as ManagerImpl} from "../../WorldIDIdentityManagerImplV2.sol"; +import {WorldIDIdentityManagerImplV1 as ManagerImplV1} from "../../WorldIDIdentityManagerImplV1.sol"; /// @title World ID Identity Manager Construction Tests /// @notice Contains tests for the WorldID identity manager @@ -30,21 +32,34 @@ contract WorldIDIdentityManagerConstruction is WorldIDIdentityManagerTest { // Setup vm.expectEmit(true, true, true, true); emit Initialized(1); - managerImpl = new ManagerImpl(); + managerImplV1 = new ManagerImplV1(); bytes memory callData = abi.encodeCall( - ManagerImpl.initialize, + ManagerImplV1.initialize, ( treeDepth, initialRoot, defaultInsertVerifiers, defaultUpdateVerifiers, - semaphoreVerifier, - isStateBridgeEnabled, - stateBridge + semaphoreVerifier ) ); // Test identityManager = new IdentityManager(address(managerImpl), callData); + + identityManagerAddress = address(identityManager); + + // creates Manager Impl V2, which will be used for tests + managerImpl = new ManagerImpl(); + managerImplAddress = address(managerImpl); + + bytes memory initCallV2 = + abi.encodeCall(ManagerImpl.initializeV2, (defaultDeletionVerifiers)); + bytes memory upgradeCall = abi.encodeCall( + UUPSUpgradeable.upgradeToAndCall, (address(managerImplAddress), initCallV2) + ); + + // Test + assertCallSucceedsOn(identityManagerAddress, upgradeCall, new bytes(0x0)); } } diff --git a/src/test/identity-manager/WorldIDIdentityManagerDataQuery.t.sol b/src/test/identity-manager/WorldIDIdentityManagerDataQuery.t.sol index 2778ebb..2df1cb1 100644 --- a/src/test/identity-manager/WorldIDIdentityManagerDataQuery.t.sol +++ b/src/test/identity-manager/WorldIDIdentityManagerDataQuery.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; import {WorldIDIdentityManagerTest} from "./WorldIDIdentityManagerTest.sol"; @@ -8,7 +8,8 @@ import {SimpleVerify} from "../mock/SimpleVerifier.sol"; import {TypeConverter as TC} from "../utils/TypeConverter.sol"; import {VerifierLookupTable} from "../../data/VerifierLookupTable.sol"; -import {WorldIDIdentityManagerImplV1 as ManagerImpl} from "../../WorldIDIdentityManagerImplV1.sol"; +import {WorldIDIdentityManagerImplV2 as ManagerImpl} from "../../WorldIDIdentityManagerImplV2.sol"; +import {WorldIDIdentityManagerImplV1 as ManagerImplV1} from "../../WorldIDIdentityManagerImplV1.sol"; /// @title World ID Identity Manager Data Querying Tests /// @notice Contains tests for the WorldID identity manager. @@ -23,13 +24,12 @@ contract WorldIDIdentityManagerDataQuery is WorldIDIdentityManagerTest { treeDepth, newPreRoot, defaultInsertVerifiers, + defaultDeletionVerifiers, defaultUpdateVerifiers, - semaphoreVerifier, - isStateBridgeEnabled, - stateBridge + semaphoreVerifier ); - bytes memory callData = abi.encodeCall(ManagerImpl.queryRoot, newPreRoot); - bytes memory returnData = abi.encode(ManagerImpl.RootInfo(newPreRoot, 0, true)); + bytes memory callData = abi.encodeCall(ManagerImplV1.queryRoot, newPreRoot); + bytes memory returnData = abi.encode(ManagerImplV1.RootInfo(newPreRoot, 0, true)); // Test assertCallSucceedsOn(identityManagerAddress, callData, returnData); @@ -47,32 +47,30 @@ contract WorldIDIdentityManagerDataQuery is WorldIDIdentityManagerTest { vm.assume(SimpleVerify.isValidInput(uint256(prf[0]))); vm.assume(newPreRoot != newPostRoot); vm.assume(identities.length <= 1000); - (VerifierLookupTable insertVerifiers, VerifierLookupTable updateVerifiers) = - makeVerifierLookupTables(TC.makeDynArray([identities.length])); + ( + VerifierLookupTable insertVerifiers, + VerifierLookupTable deletionVerifiers, + VerifierLookupTable updateVerifiers + ) = makeVerifierLookupTables(TC.makeDynArray([identities.length])); makeNewIdentityManager( treeDepth, newPreRoot, insertVerifiers, + deletionVerifiers, updateVerifiers, - semaphoreVerifier, - isStateBridgeEnabled, - stateBridge + semaphoreVerifier ); (uint256[] memory preparedIdents, uint256[8] memory actualProof) = prepareInsertIdentitiesTestCase(identities, prf); bytes memory registerCallData = abi.encodeCall( - ManagerImpl.registerIdentities, + ManagerImplV1.registerIdentities, (actualProof, newPreRoot, newStartIndex, preparedIdents, newPostRoot) ); - // expect event that state root was sent to state bridge - vm.expectEmit(true, true, true, true); - emit StateRootSentMultichain(newPostRoot); - assertCallSucceedsOn(identityManagerAddress, registerCallData); - bytes memory queryCallData = abi.encodeCall(ManagerImpl.queryRoot, (newPreRoot)); + bytes memory queryCallData = abi.encodeCall(ManagerImplV1.queryRoot, (newPreRoot)); bytes memory returnData = - abi.encode(ManagerImpl.RootInfo(newPreRoot, uint128(block.timestamp), true)); + abi.encode(ManagerImplV1.RootInfo(newPreRoot, uint128(block.timestamp), true)); // Test assertCallSucceedsOn(identityManagerAddress, queryCallData, returnData); @@ -90,34 +88,32 @@ contract WorldIDIdentityManagerDataQuery is WorldIDIdentityManagerTest { vm.assume(newPreRoot != newPostRoot); vm.assume(SimpleVerify.isValidInput(uint256(prf[0]))); vm.assume(identities.length <= 1000); - (VerifierLookupTable insertVerifiers, VerifierLookupTable updateVerifiers) = - makeVerifierLookupTables(TC.makeDynArray([identities.length])); + ( + VerifierLookupTable insertVerifiers, + VerifierLookupTable deletionVerifiers, + VerifierLookupTable updateVerifiers + ) = makeVerifierLookupTables(TC.makeDynArray([identities.length])); makeNewIdentityManager( treeDepth, newPreRoot, insertVerifiers, + deletionVerifiers, updateVerifiers, - semaphoreVerifier, - isStateBridgeEnabled, - stateBridge + semaphoreVerifier ); (uint256[] memory preparedIdents, uint256[8] memory actualProof) = prepareInsertIdentitiesTestCase(identities, prf); uint256 originalTimestamp = block.timestamp; bytes memory registerCallData = abi.encodeCall( - ManagerImpl.registerIdentities, + ManagerImplV1.registerIdentities, (actualProof, newPreRoot, newStartIndex, preparedIdents, newPostRoot) ); - // expect event that state root was sent to state bridge - vm.expectEmit(true, true, true, true); - emit StateRootSentMultichain(newPostRoot); - assertCallSucceedsOn(identityManagerAddress, registerCallData); - bytes memory queryCallData = abi.encodeCall(ManagerImpl.queryRoot, (newPreRoot)); + bytes memory queryCallData = abi.encodeCall(ManagerImplV1.queryRoot, (newPreRoot)); bytes memory returnData = - abi.encode(ManagerImpl.RootInfo(newPreRoot, uint128(originalTimestamp), false)); - vm.warp(originalTimestamp + 2 hours); // Force preRoot to expire + abi.encode(ManagerImplV1.RootInfo(newPreRoot, uint128(originalTimestamp), false)); + vm.warp(originalTimestamp + 2 hours); // Force insertionPreRoot to expire // Test assertCallSucceedsOn(identityManagerAddress, queryCallData, returnData); @@ -131,7 +127,7 @@ contract WorldIDIdentityManagerDataQuery is WorldIDIdentityManagerTest { function testQueryInvalidRoot(uint256 badRoot) public { // Setup vm.assume(badRoot != initialRoot); - bytes memory callData = abi.encodeCall(ManagerImpl.queryRoot, badRoot); + bytes memory callData = abi.encodeCall(ManagerImplV1.queryRoot, badRoot); bytes memory returnData = abi.encode(managerImpl.NO_SUCH_ROOT()); // Test @@ -154,12 +150,11 @@ contract WorldIDIdentityManagerDataQuery is WorldIDIdentityManagerTest { treeDepth, actualRoot, defaultInsertVerifiers, + defaultDeletionVerifiers, defaultUpdateVerifiers, - semaphoreVerifier, - isStateBridgeEnabled, - stateBridge + semaphoreVerifier ); - bytes memory callData = abi.encodeCall(ManagerImpl.latestRoot, ()); + bytes memory callData = abi.encodeCall(ManagerImplV1.latestRoot, ()); bytes memory returnData = abi.encode(actualRoot); // Test @@ -181,14 +176,13 @@ contract WorldIDIdentityManagerDataQuery is WorldIDIdentityManagerTest { vm.assume(SemaphoreTreeDepthValidator.validate(actualTreeDepth)); makeNewIdentityManager( actualTreeDepth, - preRoot, + insertionPreRoot, defaultInsertVerifiers, + defaultDeletionVerifiers, defaultUpdateVerifiers, - semaphoreVerifier, - isStateBridgeEnabled, - stateBridge + semaphoreVerifier ); - bytes memory callData = abi.encodeCall(ManagerImpl.getTreeDepth, ()); + bytes memory callData = abi.encodeCall(ManagerImplV1.getTreeDepth, ()); bytes memory returnData = abi.encode(actualTreeDepth); // Test diff --git a/src/test/identity-manager/WorldIDIdentityManagerGettersSetters.t.sol b/src/test/identity-manager/WorldIDIdentityManagerGettersSetters.t.sol index 41302c4..852137b 100644 --- a/src/test/identity-manager/WorldIDIdentityManagerGettersSetters.t.sol +++ b/src/test/identity-manager/WorldIDIdentityManagerGettersSetters.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; import {WorldIDIdentityManagerTest} from "./WorldIDIdentityManagerTest.sol"; @@ -9,7 +9,8 @@ import {SimpleVerifier, SimpleVerify} from "../mock/SimpleVerifier.sol"; import {TypeConverter as TC} from "../utils/TypeConverter.sol"; import {VerifierLookupTable} from "../../data/VerifierLookupTable.sol"; -import {WorldIDIdentityManagerImplV1 as ManagerImpl} from "../../WorldIDIdentityManagerImplV1.sol"; +import {WorldIDIdentityManagerImplV2 as ManagerImpl} from "../../WorldIDIdentityManagerImplV2.sol"; +import {WorldIDIdentityManagerImplV1 as ManagerImplV1} from "../../WorldIDIdentityManagerImplV1.sol"; /// @title World ID Identity Manager Getter and Setter Tests /// @notice Contains tests for the WorldID identity manager. @@ -28,7 +29,7 @@ contract WorldIDIdentityManagerGettersSetters is WorldIDIdentityManagerTest { function testCanGetRegisterIdentitiesVerifierLookupTableAddress() public { // Setup bytes memory callData = - abi.encodeCall(ManagerImpl.getRegisterIdentitiesVerifierLookupTableAddress, ()); + abi.encodeCall(ManagerImplV1.getRegisterIdentitiesVerifierLookupTableAddress, ()); bytes memory expectedReturn = abi.encode(address(defaultInsertVerifiers)); // Test @@ -49,17 +50,17 @@ contract WorldIDIdentityManagerGettersSetters is WorldIDIdentityManagerTest { /// identity registration proofs. function testCanSetRegisterIdentitiesVerifierLookupTable() public { // Setup - (VerifierLookupTable insertionVerifiers,) = makeVerifierLookupTables(TC.makeDynArray([40])); + (VerifierLookupTable insertionVerifiers,,) = makeVerifierLookupTables(TC.makeDynArray([40])); address newVerifiersAddress = address(insertionVerifiers); bytes memory callData = abi.encodeCall( - ManagerImpl.setRegisterIdentitiesVerifierLookupTable, (insertionVerifiers) + ManagerImplV1.setRegisterIdentitiesVerifierLookupTable, (insertionVerifiers) ); bytes memory checkCallData = - abi.encodeCall(ManagerImpl.getRegisterIdentitiesVerifierLookupTableAddress, ()); + abi.encodeCall(ManagerImplV1.getRegisterIdentitiesVerifierLookupTableAddress, ()); bytes memory expectedReturn = abi.encode(newVerifiersAddress); vm.expectEmit(true, false, true, true); emit DependencyUpdated( - ManagerImpl.Dependency.InsertionVerifierLookupTable, nullAddress, newVerifiersAddress + ManagerImplV1.Dependency.InsertionVerifierLookupTable, nullAddress, newVerifiersAddress ); // Test @@ -73,9 +74,9 @@ contract WorldIDIdentityManagerGettersSetters is WorldIDIdentityManagerTest { { // Setup vm.assume(notOwner != address(this) && notOwner != address(0x0)); - (VerifierLookupTable insertionVerifiers,) = makeVerifierLookupTables(TC.makeDynArray([40])); + (VerifierLookupTable insertionVerifiers,,) = makeVerifierLookupTables(TC.makeDynArray([40])); bytes memory callData = abi.encodeCall( - ManagerImpl.setRegisterIdentitiesVerifierLookupTable, (insertionVerifiers) + ManagerImplV1.setRegisterIdentitiesVerifierLookupTable, (insertionVerifiers) ); bytes memory errorData = encodeStringRevert("Ownable: caller is not the owner"); vm.prank(notOwner); @@ -88,19 +89,88 @@ contract WorldIDIdentityManagerGettersSetters is WorldIDIdentityManagerTest { /// identity registration unless called via the proxy. function testCannotSetRegisterIdentitiesVerifierLookupTableUnlessViaProxy() public { // Setup - (VerifierLookupTable insertionVerifiers,) = makeVerifierLookupTables(TC.makeDynArray([40])); + (VerifierLookupTable insertionVerifiers,,) = makeVerifierLookupTables(TC.makeDynArray([40])); vm.expectRevert("Function must be called through delegatecall"); // Test managerImpl.setRegisterIdentitiesVerifierLookupTable(insertionVerifiers); } + /// @notice Checks that it is possible to get the address of the contract currently being used + /// to verify identity deletion proofs. + function testCanGetDeleteIdentitiesVerifierLookupTableAddress() public { + // Setup + bytes memory callData = + abi.encodeCall(ManagerImpl.getDeleteIdentitiesVerifierLookupTableAddress, ()); + bytes memory expectedReturn = abi.encode(address(defaultDeletionVerifiers)); + + // Test + assertCallSucceedsOn(identityManagerAddress, callData, expectedReturn); + } + + /// @notice Ensures that it is not possible to get the address of the verifier lookup table for + /// identity deletion unless called via the proxy. + function testCannotGetDeleteIdentitiesVerifierLookupTableAddressUnlessViaProxy() public { + // Setup + vm.expectRevert("Function must be called through delegatecall"); + + // Test + managerImpl.getDeleteIdentitiesVerifierLookupTableAddress(); + } + + /// @notice Checks that it is possible to set the lookup table currently being used to verify + /// identity deletion proofs. + function testCanSetDeleteIdentitiesVerifierLookupTable() public { + // Setup + (, VerifierLookupTable deletionVerifiers,) = makeVerifierLookupTables(TC.makeDynArray([40])); + address newVerifiersAddress = address(deletionVerifiers); + bytes memory callData = + abi.encodeCall(ManagerImpl.setDeleteIdentitiesVerifierLookupTable, (deletionVerifiers)); + bytes memory checkCallData = + abi.encodeCall(ManagerImpl.getDeleteIdentitiesVerifierLookupTableAddress, ()); + bytes memory expectedReturn = abi.encode(newVerifiersAddress); + vm.expectEmit(true, false, true, true); + emit DependencyUpdated( + ManagerImplV1.Dependency.DeletionVerifierLookupTable, nullAddress, newVerifiersAddress + ); + + // Test + assertCallSucceedsOn(identityManagerAddress, callData); + assertCallSucceedsOn(identityManagerAddress, checkCallData, expectedReturn); + } + + /// @notice Checks that the delete identities lookup table cannot be set except by the owner. + function testCannotSetDeleteIdentitiesVerifierLookupTableUnlessOwner(address notOwner) public { + // Setup + vm.assume(notOwner != address(this) && notOwner != address(0x0)); + (, VerifierLookupTable deletionVerifiers,) = makeVerifierLookupTables(TC.makeDynArray([40])); + bytes memory callData = abi.encodeCall( + ManagerImplV1.setRegisterIdentitiesVerifierLookupTable, (deletionVerifiers) + ); + bytes memory errorData = encodeStringRevert("Ownable: caller is not the owner"); + vm.prank(notOwner); + + // Test + assertCallFailsOn(identityManagerAddress, callData, errorData); + } + + /// @notice Ensures that it is not possible to set the address of the verifier lookup table for + /// identity deletion unless called via the proxy. + function testCannotSetDeleteIdentitiesVerifierLookupTableUnlessViaProxy() public { + // Setup + (, VerifierLookupTable deletionVerifiers,) = makeVerifierLookupTables(TC.makeDynArray([40])); + vm.expectRevert("Function must be called through delegatecall"); + + // Test + managerImpl.setDeleteIdentitiesVerifierLookupTable(deletionVerifiers); + } + /// @notice Checks that it is possible to get the address of the lookup table currently being /// used to verify identity update proofs. function testCanGetIdentityUpdateVerifierLookupTableAddress() public { // Setup bytes memory callData = - abi.encodeCall(ManagerImpl.getIdentityUpdateVerifierLookupTableAddress, ()); + abi.encodeCall(ManagerImplV1.getIdentityUpdateVerifierLookupTableAddress, ()); bytes memory expectedReturn = abi.encode(defaultUpdateVerifiers); // Test @@ -121,16 +191,16 @@ contract WorldIDIdentityManagerGettersSetters is WorldIDIdentityManagerTest { /// identity update proofs. function testCanSetIdentityUpdateVerifierLookupTable() public { // Setup - (, VerifierLookupTable updateVerifiers) = makeVerifierLookupTables(TC.makeDynArray([40])); + (,, VerifierLookupTable updateVerifiers) = makeVerifierLookupTables(TC.makeDynArray([40])); address newVerifierAddress = address(updateVerifiers); bytes memory callData = - abi.encodeCall(ManagerImpl.setIdentityUpdateVerifierLookupTable, (updateVerifiers)); + abi.encodeCall(ManagerImplV1.setIdentityUpdateVerifierLookupTable, (updateVerifiers)); bytes memory checkCallData = - abi.encodeCall(ManagerImpl.getIdentityUpdateVerifierLookupTableAddress, ()); + abi.encodeCall(ManagerImplV1.getIdentityUpdateVerifierLookupTableAddress, ()); bytes memory expectedReturn = abi.encode(newVerifierAddress); vm.expectEmit(true, false, true, true); emit DependencyUpdated( - ManagerImpl.Dependency.UpdateVerifierLookupTable, nullAddress, newVerifierAddress + ManagerImplV1.Dependency.UpdateVerifierLookupTable, nullAddress, newVerifierAddress ); // Test @@ -143,9 +213,9 @@ contract WorldIDIdentityManagerGettersSetters is WorldIDIdentityManagerTest { function testCannotSetIdentityUpdateVerifierLookupTableUnlessOwner(address notOwner) public { // Setup vm.assume(notOwner != address(this) && notOwner != address(0x0)); - (, VerifierLookupTable updateVerifiers) = makeVerifierLookupTables(TC.makeDynArray([40])); + (,, VerifierLookupTable updateVerifiers) = makeVerifierLookupTables(TC.makeDynArray([40])); bytes memory callData = - abi.encodeCall(ManagerImpl.setIdentityUpdateVerifierLookupTable, (updateVerifiers)); + abi.encodeCall(ManagerImplV1.setIdentityUpdateVerifierLookupTable, (updateVerifiers)); bytes memory errorData = encodeStringRevert("Ownable: caller is not the owner"); vm.prank(notOwner); @@ -157,7 +227,7 @@ contract WorldIDIdentityManagerGettersSetters is WorldIDIdentityManagerTest { /// identity removal unless called via the proxy. function testCannotSetIdentityUpdateVerifierLookupTableUnlessViaProxy() public { // Setup - (, VerifierLookupTable updateVerifiers) = makeVerifierLookupTables(TC.makeDynArray([40])); + (,, VerifierLookupTable updateVerifiers) = makeVerifierLookupTables(TC.makeDynArray([40])); vm.expectRevert("Function must be called through delegatecall"); // Test @@ -167,7 +237,7 @@ contract WorldIDIdentityManagerGettersSetters is WorldIDIdentityManagerTest { /// @notice Ensures that we can get the address of the semaphore verifier. function testCanGetSemaphoreVerifierAddress() public { // Setup - bytes memory callData = abi.encodeCall(ManagerImpl.getSemaphoreVerifierAddress, ()); + bytes memory callData = abi.encodeCall(ManagerImplV1.getSemaphoreVerifierAddress, ()); // Test assertCallSucceedsOn(identityManagerAddress, callData); @@ -189,12 +259,12 @@ contract WorldIDIdentityManagerGettersSetters is WorldIDIdentityManagerTest { // Setup SemaphoreVerifier newVerifier = new SemaphoreVerifier(); address newVerifierAddress = address(newVerifier); - bytes memory callData = abi.encodeCall(ManagerImpl.setSemaphoreVerifier, (newVerifier)); - bytes memory checkCallData = abi.encodeCall(ManagerImpl.getSemaphoreVerifierAddress, ()); + bytes memory callData = abi.encodeCall(ManagerImplV1.setSemaphoreVerifier, (newVerifier)); + bytes memory checkCallData = abi.encodeCall(ManagerImplV1.getSemaphoreVerifierAddress, ()); bytes memory expectedReturn = abi.encode(newVerifierAddress); vm.expectEmit(true, false, true, true); emit DependencyUpdated( - ManagerImpl.Dependency.SemaphoreVerifier, nullAddress, newVerifierAddress + ManagerImplV1.Dependency.SemaphoreVerifier, nullAddress, newVerifierAddress ); // Test @@ -207,7 +277,7 @@ contract WorldIDIdentityManagerGettersSetters is WorldIDIdentityManagerTest { // Setup vm.assume(notOwner != address(this) && notOwner != address(0x0)); SemaphoreVerifier newVerifier = new SemaphoreVerifier(); - bytes memory callData = abi.encodeCall(ManagerImpl.setSemaphoreVerifier, (newVerifier)); + bytes memory callData = abi.encodeCall(ManagerImplV1.setSemaphoreVerifier, (newVerifier)); bytes memory errorData = encodeStringRevert("Ownable: caller is not the owner"); vm.prank(notOwner); @@ -229,7 +299,7 @@ contract WorldIDIdentityManagerGettersSetters is WorldIDIdentityManagerTest { /// @notice Ensures that it's possible to get the root history expiry time. function testCanGetRootHistoryExpiry() public { // Setup - bytes memory callData = abi.encodeCall(ManagerImpl.getRootHistoryExpiry, ()); + bytes memory callData = abi.encodeCall(ManagerImplV1.getRootHistoryExpiry, ()); bytes memory result = abi.encode(uint256(1 hours)); // Test @@ -249,8 +319,8 @@ contract WorldIDIdentityManagerGettersSetters is WorldIDIdentityManagerTest { function testCanSetRootHistoryExpiry(uint256 newExpiry) public { // Setup vm.assume(newExpiry != 0 && newExpiry != 1 hours); - bytes memory callData = abi.encodeCall(ManagerImpl.setRootHistoryExpiry, (newExpiry)); - bytes memory checkCallData = abi.encodeCall(ManagerImpl.getRootHistoryExpiry, ()); + bytes memory callData = abi.encodeCall(ManagerImplV1.setRootHistoryExpiry, (newExpiry)); + bytes memory checkCallData = abi.encodeCall(ManagerImplV1.getRootHistoryExpiry, ()); bytes memory expectedReturn = abi.encode(newExpiry); vm.expectEmit(true, true, true, true); emit RootHistoryExpirySet(1 hours, newExpiry); @@ -263,7 +333,7 @@ contract WorldIDIdentityManagerGettersSetters is WorldIDIdentityManagerTest { /// @notice Ensures that the root history expiry time can't be set to zero. function testCannotSetRootHistoryExpiryToZero() public { // Setup - bytes memory callData = abi.encodeCall(ManagerImpl.setRootHistoryExpiry, (0)); + bytes memory callData = abi.encodeCall(ManagerImplV1.setRootHistoryExpiry, (0)); bytes memory expectedError = encodeStringRevert("Expiry time cannot be zero."); // Test @@ -275,7 +345,7 @@ contract WorldIDIdentityManagerGettersSetters is WorldIDIdentityManagerTest { // Setup vm.assume(notOwner != address(this) && notOwner != address(0x0)); SemaphoreVerifier newVerifier = new SemaphoreVerifier(); - bytes memory callData = abi.encodeCall(ManagerImpl.setSemaphoreVerifier, (newVerifier)); + bytes memory callData = abi.encodeCall(ManagerImplV1.setSemaphoreVerifier, (newVerifier)); bytes memory errorData = encodeStringRevert("Ownable: caller is not the owner"); vm.prank(notOwner); diff --git a/src/test/identity-manager/WorldIDIdentityManagerIdentityDeletion.t.sol b/src/test/identity-manager/WorldIDIdentityManagerIdentityDeletion.t.sol new file mode 100644 index 0000000..984695d --- /dev/null +++ b/src/test/identity-manager/WorldIDIdentityManagerIdentityDeletion.t.sol @@ -0,0 +1,387 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.21; + +import {WorldIDIdentityManagerTest} from "./WorldIDIdentityManagerTest.sol"; + +import {ITreeVerifier} from "../../interfaces/ITreeVerifier.sol"; +import {SimpleVerifier, SimpleVerify} from "../mock/SimpleVerifier.sol"; +import {TypeConverter as TC} from "../utils/TypeConverter.sol"; +import {Verifier as TreeVerifier} from "../mock/DeletionTreeVerifier.sol"; +import {VerifierLookupTable} from "../../data/VerifierLookupTable.sol"; + +import {WorldIDIdentityManager as IdentityManager} from "../../WorldIDIdentityManager.sol"; +import {WorldIDIdentityManagerImplV2 as ManagerImpl} from "../../WorldIDIdentityManagerImplV2.sol"; +import {WorldIDIdentityManagerImplV1 as ManagerImplV1} from "../../WorldIDIdentityManagerImplV1.sol"; + +import {console} from "forge-std/console.sol"; + +/// @title World ID Identity Manager Identity Deletion Tests +/// @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 WorldIDIdentityManagerIdentityDeletion is WorldIDIdentityManagerTest { + /// Taken from SimpleVerifier.sol + event VerifiedProof(uint256 batchSize); + + /// Taken from WorldIDIdentityManagerImplV1.sol + event TreeChanged( + uint256 indexed deletionPreRoot, + ManagerImpl.TreeChange indexed kind, + uint256 indexed deletionPostRoot + ); + + /// @notice Checks that the deletionProof validates properly with the correct inputs. + function testDeleteIdentitiesWithCorrectInputsFromKnown() public { + // Setup + ITreeVerifier actualVerifier = new TreeVerifier(); + ( + VerifierLookupTable insertVerifiers, + VerifierLookupTable deletionVerifiers, + VerifierLookupTable updateVerifiers + ) = makeVerifierLookupTables(TC.makeDynArray([40])); + deletionVerifiers.addVerifier(deletionBatchSize, actualVerifier); + makeNewIdentityManager( + treeDepth, + deletionPreRoot, + insertVerifiers, + deletionVerifiers, + updateVerifiers, + semaphoreVerifier + ); + bytes memory deleteCallData = abi.encodeCall( + ManagerImpl.deleteIdentities, + ( + deletionProof, + deletionBatchSize, + packedDeletionIndices, + deletionPreRoot, + deletionPostRoot + ) + ); + bytes memory latestRootCallData = abi.encodeCall(ManagerImplV1.latestRoot, ()); + bytes memory queryRootCallData = abi.encodeCall(ManagerImplV1.queryRoot, (deletionPostRoot)); + + // Test + assertCallSucceedsOn(identityManagerAddress, deleteCallData); + assertCallSucceedsOn( + identityManagerAddress, latestRootCallData, abi.encode(deletionPostRoot) + ); + assertCallSucceedsOn( + identityManagerAddress, + queryRootCallData, + abi.encode(ManagerImplV1.RootInfo(deletionPostRoot, 0, true)) + ); + } + + /// @notice Checks that the deletionProof validates properly with correct inputs. + function testDeleteIdentitiesWithCorrectInputs( + uint128[8] memory prf, + uint128 newPreRoot, + bytes calldata packedDeletionIndices, + uint128 newPostRoot, + address identityOperator + ) public { + // Setup + vm.assume(SimpleVerify.isValidInput(uint256(prf[0]))); + vm.assume(newPreRoot != newPostRoot); + vm.assume(packedDeletionIndices.length <= 125); + vm.assume(identityOperator != nullAddress && identityOperator != thisAddress); + ( + VerifierLookupTable insertVerifiers, + VerifierLookupTable deletionVerifiers, + VerifierLookupTable updateVerifiers + ) = makeVerifierLookupTables(TC.makeDynArray([packedDeletionIndices.length * 8])); + makeNewIdentityManager( + treeDepth, + newPreRoot, + insertVerifiers, + deletionVerifiers, + updateVerifiers, + semaphoreVerifier + ); + uint256[8] memory actualProof = prepareDeleteIdentitiesTestCase(prf); + bytes memory callData = abi.encodeCall( + ManagerImpl.deleteIdentities, + ( + actualProof, + uint32(packedDeletionIndices.length * 8), + packedDeletionIndices, + newPreRoot, + newPostRoot + ) + ); + + bytes memory setupCallData = + abi.encodeCall(ManagerImplV1.setIdentityOperator, identityOperator); + (bool success,) = identityManagerAddress.call(setupCallData); + assert(success); + + // Expect the root to have been sent to the state bridge. + vm.expectEmit(true, true, true, true); + emit TreeChanged(newPreRoot, ManagerImplV1.TreeChange.Deletion, newPostRoot); + vm.prank(identityOperator); + + // Test + assertCallSucceedsOn(identityManagerAddress, callData); + } + + /// @notice Ensures that identity deletion selects the correct verifier when deleting + /// identities. + function testDeleteIdentitiesSelectsCorrectVerifier( + uint128[8] memory prf, + uint128 newPreRoot, + bytes calldata packedDeletionIndices, + uint128 newPostRoot + ) public { + vm.assume(SimpleVerify.isValidInput(uint256(prf[0]))); + vm.assume(newPreRoot != newPostRoot); + vm.assume(packedDeletionIndices.length <= 1000 && packedDeletionIndices.length > 0); + + bytes memory secondIndices = abi.encodePacked(uint32(0), uint32(2), uint32(4), uint32(6)); + uint32 secondIndicesLength = uint32(secondIndices.length); + + ( + VerifierLookupTable insertVerifiers, + VerifierLookupTable deletionVerifiers, + VerifierLookupTable updateVerifiers + ) = makeVerifierLookupTables(TC.makeDynArray([deletionBatchSize, secondIndicesLength])); + makeNewIdentityManager( + treeDepth, + newPreRoot, + insertVerifiers, + deletionVerifiers, + updateVerifiers, + semaphoreVerifier + ); + uint256[8] memory actualProof = prepareDeleteIdentitiesTestCase(prf); + bytes memory firstCallData = abi.encodeCall( + ManagerImpl.deleteIdentities, + (actualProof, deletionBatchSize, packedDeletionIndices, newPreRoot, newPostRoot) + ); + uint256 secondPostRoot = uint256(newPostRoot) + 1; + bytes memory secondCallData = abi.encodeCall( + ManagerImpl.deleteIdentities, + (actualProof, secondIndicesLength, secondIndices, newPostRoot, secondPostRoot) + ); + + vm.expectEmit(true, true, true, true); + emit VerifiedProof(deletionBatchSize); + + // Test + assertCallSucceedsOn(identityManagerAddress, firstCallData); + + vm.expectEmit(true, true, true, true); + emit VerifiedProof(uint256(secondIndicesLength)); + + assertCallSucceedsOn(identityManagerAddress, secondCallData); + } + + /// @notice Ensures that the contract reverts if passed a batch size it doesn't know about. + function testCannotDeleteIdentitiesWithInvalidBatchSize( + uint128[8] memory prf, + uint128 newPreRoot, + bytes calldata packedDeletionIndices, + uint128 newPostRoot + ) public { + vm.assume(SimpleVerify.isValidInput(uint256(prf[0]))); + vm.assume(newPreRoot != newPostRoot); + vm.assume(packedDeletionIndices.length > 0); + ( + VerifierLookupTable insertVerifiers, + VerifierLookupTable deletionVerifiers, + VerifierLookupTable updateVerifiers + ) = makeVerifierLookupTables(TC.makeDynArray([deletionBatchSize - 1])); + makeNewIdentityManager( + treeDepth, + newPreRoot, + insertVerifiers, + deletionVerifiers, + updateVerifiers, + semaphoreVerifier + ); + uint256[8] memory actualProof = prepareDeleteIdentitiesTestCase(prf); + + bytes memory callData = abi.encodeCall( + ManagerImpl.deleteIdentities, + (actualProof, deletionBatchSize, packedDeletionIndices, newPreRoot, newPostRoot) + ); + bytes memory errorData = abi.encodeWithSelector(VerifierLookupTable.NoSuchVerifier.selector); + + // Test + assertCallFailsOn(identityManagerAddress, callData, errorData); + } + + /// @notice Checks that it reverts if the provided deletionProof is incorrect for the public inputs. + function testCannotDeleteIdentitiesWithIncorrectInputs( + uint128[8] memory prf, + uint128 newPreRoot, + bytes calldata packedDeletionIndices, + uint128 newPostRoot + ) public { + // Setup + vm.assume(!SimpleVerify.isValidInput(uint256(prf[0]))); + vm.assume(newPreRoot != newPostRoot); + vm.assume(packedDeletionIndices.length <= 1000); + uint32 indicesLength = uint32(packedDeletionIndices.length * 8); + + ( + VerifierLookupTable insertVerifiers, + VerifierLookupTable deletionVerifiers, + VerifierLookupTable updateVerifiers + ) = makeVerifierLookupTables(TC.makeDynArray([indicesLength])); + makeNewIdentityManager( + treeDepth, + newPreRoot, + insertVerifiers, + deletionVerifiers, + updateVerifiers, + semaphoreVerifier + ); + uint256[8] memory actualProof = prepareDeleteIdentitiesTestCase(prf); + bytes memory callData = abi.encodeCall( + ManagerImpl.deleteIdentities, + (actualProof, indicesLength, packedDeletionIndices, newPreRoot, newPostRoot) + ); + bytes memory expectedError = + abi.encodeWithSelector(ManagerImplV1.ProofValidationFailure.selector); + + // Test + assertCallFailsOn(identityManagerAddress, callData, expectedError); + } + + /// @notice Checks that it reverts if the provided post root is incorrect. + function testCannotDeleteIdentitiesIfPostRootIncorrect(uint256 newPostRoot) public { + // Setup + vm.assume(newPostRoot != deletionPostRoot && newPostRoot < SNARK_SCALAR_FIELD); + ITreeVerifier actualVerifier = new TreeVerifier(); + ( + VerifierLookupTable insertVerifiers, + VerifierLookupTable deletionVerifiers, + VerifierLookupTable updateVerifiers + ) = makeVerifierLookupTables(TC.makeDynArray([deletionBatchSize])); + deletionVerifiers.addVerifier(identityCommitmentsSize, actualVerifier); + makeNewIdentityManager( + treeDepth, + deletionPreRoot, + insertVerifiers, + deletionVerifiers, + updateVerifiers, + semaphoreVerifier + ); + + bytes memory deletionCallData = abi.encodeCall( + ManagerImpl.deleteIdentities, + (deletionProof, deletionBatchSize, packedDeletionIndices, deletionPreRoot, newPostRoot) + ); + bytes memory expectedError = + abi.encodeWithSelector(ManagerImplV1.ProofValidationFailure.selector); + + // Test + assertCallFailsOn(identityManagerAddress, deletionCallData, expectedError); + } + + /// @notice Tests that it reverts if an attempt is made to delete identities as an address + /// that is not the identity operator address. + function testCannotDeleteIdentitiesAsNonIdentityOperator(address nonOperator) public { + // Setup + vm.assume(nonOperator != address(this) && nonOperator != address(0x0)); + bytes memory callData = abi.encodeCall( + ManagerImpl.deleteIdentities, + ( + deletionProof, + deletionBatchSize, + packedDeletionIndices, + deletionPreRoot, + deletionPostRoot + ) + ); + bytes memory errorData = + abi.encodeWithSelector(ManagerImplV1.Unauthorized.selector, nonOperator); + vm.prank(nonOperator); + + // Test + assertCallFailsOn(identityManagerAddress, callData, errorData); + } + + /// @notice Tests that it reverts if an attempt is made to delete identities with an outdated + /// root. + function testCannotDeleteIdentitiesWithOutdatedRoot(uint256 currentPreRoot, uint256 actualRoot) + public + { + // Setup + vm.assume( + currentPreRoot != actualRoot && currentPreRoot < SNARK_SCALAR_FIELD + && actualRoot < SNARK_SCALAR_FIELD + ); + makeNewIdentityManager( + treeDepth, + uint256(currentPreRoot), + defaultInsertVerifiers, + defaultDeletionVerifiers, + defaultUpdateVerifiers, + semaphoreVerifier + ); + bytes memory callData = abi.encodeCall( + ManagerImpl.deleteIdentities, + (deletionProof, deletionBatchSize, packedDeletionIndices, actualRoot, deletionPostRoot) + ); + bytes memory expectedError = abi.encodeWithSelector( + ManagerImplV1.NotLatestRoot.selector, actualRoot, uint256(currentPreRoot) + ); + + // Test + assertCallFailsOn(identityManagerAddress, callData, expectedError); + } + + /// @notice Tests that it reverts if an attempt is made to delete identities with a pre + /// root that is not in reduced form. + function testCannotDeleteIdentitiesWithUnreducedPreRoot(uint128 i) public { + // Setup + uint256 newPreRoot = SNARK_SCALAR_FIELD + i; + bytes memory callData = abi.encodeCall( + ManagerImpl.deleteIdentities, + (deletionProof, deletionBatchSize, packedDeletionIndices, newPreRoot, deletionPostRoot) + ); + bytes memory expectedError = abi.encodeWithSelector( + ManagerImplV1.UnreducedElement.selector, + ManagerImplV1.UnreducedElementType.PreRoot, + newPreRoot + ); + + // Test + assertCallFailsOn(identityManagerAddress, callData, expectedError); + } + + /// @notice Tests that it reverts if an attempt is made to delete identities with a deletionPostRoot + /// that is not in reduced form. + function testCannotDeleteIdentitiesWithUnreducedPostRoot(uint128 i) public { + // Setup + uint256 newPostRoot = SNARK_SCALAR_FIELD + i; + bytes memory callData = abi.encodeCall( + ManagerImpl.deleteIdentities, + (deletionProof, deletionBatchSize, packedDeletionIndices, initialRoot, newPostRoot) + ); + bytes memory expectedError = abi.encodeWithSelector( + ManagerImplV1.UnreducedElement.selector, + ManagerImplV1.UnreducedElementType.PostRoot, + newPostRoot + ); + + // Test + assertCallFailsOn(identityManagerAddress, callData, expectedError); + } + + /// @notice Tests that identities can only be deleted through the proxy. + function testCannotDelteIdentitiesIfNotViaProxy() public { + // Setup + address expectedOwner = managerImpl.owner(); + vm.expectRevert("Function must be called through delegatecall"); + vm.prank(expectedOwner); + + // Test + managerImpl.deleteIdentities( + deletionProof, deletionBatchSize, packedDeletionIndices, initialRoot, deletionPostRoot + ); + } +} diff --git a/src/test/identity-manager/WorldIDIdentityManagerIdentityRegistration.t.sol b/src/test/identity-manager/WorldIDIdentityManagerIdentityRegistration.t.sol index 8396473..679d769 100644 --- a/src/test/identity-manager/WorldIDIdentityManagerIdentityRegistration.t.sol +++ b/src/test/identity-manager/WorldIDIdentityManagerIdentityRegistration.t.sol @@ -1,16 +1,19 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; + +import {UUPSUpgradeable} from "contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import {WorldIDIdentityManagerTest} from "./WorldIDIdentityManagerTest.sol"; import {ITreeVerifier} from "../../interfaces/ITreeVerifier.sol"; import {SimpleVerifier, SimpleVerify} from "../mock/SimpleVerifier.sol"; import {TypeConverter as TC} from "../utils/TypeConverter.sol"; -import {Verifier as TreeVerifier} from "../mock/TreeVerifier.sol"; +import {Verifier as TreeVerifier} from "../mock/InsertionTreeVerifier.sol"; import {VerifierLookupTable} from "../../data/VerifierLookupTable.sol"; import {WorldIDIdentityManager as IdentityManager} from "../../WorldIDIdentityManager.sol"; -import {WorldIDIdentityManagerImplV1 as ManagerImpl} from "../../WorldIDIdentityManagerImplV1.sol"; +import {WorldIDIdentityManagerImplV2 as ManagerImpl} from "../../WorldIDIdentityManagerImplV2.sol"; +import {WorldIDIdentityManagerImplV1 as ManagerImplV1} from "../../WorldIDIdentityManagerImplV1.sol"; /// @title World ID Identity Manager Identity Registration Tests /// @notice Contains tests for the WorldID identity manager. @@ -23,43 +26,46 @@ contract WorldIDIdentityManagerIdentityRegistration is WorldIDIdentityManagerTes /// Taken from WorldIDIdentityManagerImplV1.sol event TreeChanged( - uint256 indexed preRoot, ManagerImpl.TreeChange indexed kind, uint256 indexed postRoot + uint256 indexed insertionPreRoot, + ManagerImpl.TreeChange indexed kind, + uint256 indexed insertionPostRoot ); /// @notice Checks that the proof validates properly with the correct inputs. function testRegisterIdentitiesWithCorrectInputsFromKnown() public { // Setup ITreeVerifier actualVerifier = new TreeVerifier(); - (VerifierLookupTable insertVerifiers, VerifierLookupTable updateVerifiers) = - makeVerifierLookupTables(TC.makeDynArray([40])); + ( + VerifierLookupTable insertVerifiers, + VerifierLookupTable deletionVerifiers, + VerifierLookupTable updateVerifiers + ) = makeVerifierLookupTables(TC.makeDynArray([40])); insertVerifiers.addVerifier(identityCommitmentsSize, actualVerifier); makeNewIdentityManager( treeDepth, - preRoot, + insertionPreRoot, insertVerifiers, + deletionVerifiers, updateVerifiers, - semaphoreVerifier, - isStateBridgeEnabled, - stateBridge + semaphoreVerifier ); bytes memory registerCallData = abi.encodeCall( - ManagerImpl.registerIdentities, - (proof, preRoot, startIndex, identityCommitments, postRoot) + ManagerImplV1.registerIdentities, + (insertionProof, insertionPreRoot, startIndex, identityCommitments, insertionPostRoot) ); - bytes memory latestRootCallData = abi.encodeCall(ManagerImpl.latestRoot, ()); - bytes memory queryRootCallData = abi.encodeCall(ManagerImpl.queryRoot, (postRoot)); - - // Expect the root to have been sent to the state bridge. - vm.expectEmit(true, true, true, true); - emit StateRootSentMultichain(postRoot); + bytes memory latestRootCallData = abi.encodeCall(ManagerImplV1.latestRoot, ()); + bytes memory queryRootCallData = + abi.encodeCall(ManagerImplV1.queryRoot, (insertionPostRoot)); // Test assertCallSucceedsOn(identityManagerAddress, registerCallData); - assertCallSucceedsOn(identityManagerAddress, latestRootCallData, abi.encode(postRoot)); + assertCallSucceedsOn( + identityManagerAddress, latestRootCallData, abi.encode(insertionPostRoot) + ); assertCallSucceedsOn( identityManagerAddress, queryRootCallData, - abi.encode(ManagerImpl.RootInfo(postRoot, 0, true)) + abi.encode(ManagerImplV1.RootInfo(insertionPostRoot, 0, true)) ); } @@ -77,34 +83,34 @@ contract WorldIDIdentityManagerIdentityRegistration is WorldIDIdentityManagerTes vm.assume(newPreRoot != newPostRoot); vm.assume(identities.length <= 1000); vm.assume(identityOperator != nullAddress && identityOperator != thisAddress); - (VerifierLookupTable insertVerifiers, VerifierLookupTable updateVerifiers) = - makeVerifierLookupTables(TC.makeDynArray([identities.length])); + ( + VerifierLookupTable insertVerifiers, + VerifierLookupTable deletionVerifiers, + VerifierLookupTable updateVerifiers + ) = makeVerifierLookupTables(TC.makeDynArray([identities.length])); makeNewIdentityManager( treeDepth, newPreRoot, insertVerifiers, + deletionVerifiers, updateVerifiers, - semaphoreVerifier, - isStateBridgeEnabled, - stateBridge + semaphoreVerifier ); (uint256[] memory preparedIdents, uint256[8] memory actualProof) = prepareInsertIdentitiesTestCase(identities, prf); bytes memory callData = abi.encodeCall( - ManagerImpl.registerIdentities, + ManagerImplV1.registerIdentities, (actualProof, newPreRoot, newStartIndex, preparedIdents, newPostRoot) ); bytes memory setupCallData = - abi.encodeCall(ManagerImpl.setIdentityOperator, identityOperator); + abi.encodeCall(ManagerImplV1.setIdentityOperator, identityOperator); (bool success,) = identityManagerAddress.call(setupCallData); assert(success); // Expect the root to have been sent to the state bridge. vm.expectEmit(true, true, true, true); - emit StateRootSentMultichain(newPostRoot); - vm.expectEmit(true, true, true, true); - emit TreeChanged(newPreRoot, ManagerImpl.TreeChange.Insertion, newPostRoot); + emit TreeChanged(newPreRoot, ManagerImplV1.TreeChange.Insertion, newPostRoot); vm.prank(identityOperator); // Test @@ -124,16 +130,18 @@ contract WorldIDIdentityManagerIdentityRegistration is WorldIDIdentityManagerTes vm.assume(newPreRoot != newPostRoot); vm.assume(identities.length <= 1000 && identities.length > 0); uint256 secondIdentsLength = identities.length / 2; - (VerifierLookupTable insertVerifiers, VerifierLookupTable updateVerifiers) = - makeVerifierLookupTables(TC.makeDynArray([identities.length, secondIdentsLength])); + ( + VerifierLookupTable insertVerifiers, + VerifierLookupTable deletionVerifiers, + VerifierLookupTable updateVerifiers + ) = makeVerifierLookupTables(TC.makeDynArray([identities.length, secondIdentsLength])); makeNewIdentityManager( treeDepth, newPreRoot, insertVerifiers, + deletionVerifiers, updateVerifiers, - semaphoreVerifier, - isStateBridgeEnabled, - stateBridge + semaphoreVerifier ); (uint256[] memory preparedIdents, uint256[8] memory actualProof) = prepareInsertIdentitiesTestCase(identities, prf); @@ -142,27 +150,23 @@ contract WorldIDIdentityManagerIdentityRegistration is WorldIDIdentityManagerTes secondIdents[i] = preparedIdents[i]; } bytes memory firstCallData = abi.encodeCall( - ManagerImpl.registerIdentities, + ManagerImplV1.registerIdentities, (actualProof, newPreRoot, newStartIndex, preparedIdents, newPostRoot) ); uint256 secondPostRoot = uint256(newPostRoot) + 1; bytes memory secondCallData = abi.encodeCall( - ManagerImpl.registerIdentities, + ManagerImplV1.registerIdentities, (actualProof, newPostRoot, newStartIndex, secondIdents, secondPostRoot) ); vm.expectEmit(true, true, true, true); emit VerifiedProof(identities.length); - vm.expectEmit(true, true, true, true); - emit StateRootSentMultichain(newPostRoot); // Test assertCallSucceedsOn(identityManagerAddress, firstCallData); vm.expectEmit(true, true, true, true); emit VerifiedProof(identities.length / 2); - vm.expectEmit(true, true, true, true); - emit StateRootSentMultichain(secondPostRoot); assertCallSucceedsOn(identityManagerAddress, secondCallData); } @@ -178,22 +182,24 @@ contract WorldIDIdentityManagerIdentityRegistration is WorldIDIdentityManagerTes vm.assume(SimpleVerify.isValidInput(uint256(prf[0]))); vm.assume(newPreRoot != newPostRoot); vm.assume(identities.length > 0); - (VerifierLookupTable insertVerifiers, VerifierLookupTable updateVerifiers) = - makeVerifierLookupTables(TC.makeDynArray([identities.length - 1])); + ( + VerifierLookupTable insertVerifiers, + VerifierLookupTable deletionVerifiers, + VerifierLookupTable updateVerifiers + ) = makeVerifierLookupTables(TC.makeDynArray([identities.length - 1])); makeNewIdentityManager( treeDepth, newPreRoot, insertVerifiers, + deletionVerifiers, updateVerifiers, - semaphoreVerifier, - isStateBridgeEnabled, - stateBridge + semaphoreVerifier ); (uint256[] memory preparedIdents, uint256[8] memory actualProof) = prepareInsertIdentitiesTestCase(identities, prf); bytes memory callData = abi.encodeCall( - ManagerImpl.registerIdentities, + ManagerImplV1.registerIdentities, (actualProof, newPreRoot, newStartIndex, preparedIdents, newPostRoot) ); bytes memory errorData = abi.encodeWithSelector(VerifierLookupTable.NoSuchVerifier.selector); @@ -213,26 +219,27 @@ contract WorldIDIdentityManagerIdentityRegistration is WorldIDIdentityManagerTes // Setup vm.assume(!SimpleVerify.isValidInput(uint256(prf[0]))); vm.assume(newPreRoot != newPostRoot); - vm.assume(identities.length <= 1000); - (VerifierLookupTable insertVerifiers, VerifierLookupTable updateVerifiers) = - makeVerifierLookupTables(TC.makeDynArray([identities.length])); + ( + VerifierLookupTable insertVerifiers, + VerifierLookupTable deletionVerifiers, + VerifierLookupTable updateVerifiers + ) = makeVerifierLookupTables(TC.makeDynArray([identities.length])); makeNewIdentityManager( treeDepth, newPreRoot, insertVerifiers, + deletionVerifiers, updateVerifiers, - semaphoreVerifier, - isStateBridgeEnabled, - stateBridge + semaphoreVerifier ); (uint256[] memory preparedIdents, uint256[8] memory actualProof) = prepareInsertIdentitiesTestCase(identities, prf); bytes memory callData = abi.encodeCall( - ManagerImpl.registerIdentities, + ManagerImplV1.registerIdentities, (actualProof, newPreRoot, newStartIndex, preparedIdents, newPostRoot) ); bytes memory expectedError = - abi.encodeWithSelector(ManagerImpl.ProofValidationFailure.selector); + abi.encodeWithSelector(ManagerImplV1.ProofValidationFailure.selector); // Test assertCallFailsOn(identityManagerAddress, callData, expectedError); @@ -243,24 +250,32 @@ contract WorldIDIdentityManagerIdentityRegistration is WorldIDIdentityManagerTes // Setup vm.assume(newStartIndex != startIndex); ITreeVerifier actualVerifier = new TreeVerifier(); - (VerifierLookupTable insertVerifiers, VerifierLookupTable updateVerifiers) = - makeVerifierLookupTables(TC.makeDynArray([70])); + ( + VerifierLookupTable insertVerifiers, + VerifierLookupTable deletionVerifiers, + VerifierLookupTable updateVerifiers + ) = makeVerifierLookupTables(TC.makeDynArray([70])); insertVerifiers.addVerifier(identityCommitmentsSize, actualVerifier); makeNewIdentityManager( treeDepth, - preRoot, + insertionPreRoot, insertVerifiers, + deletionVerifiers, updateVerifiers, - semaphoreVerifier, - isStateBridgeEnabled, - stateBridge + semaphoreVerifier ); bytes memory registerCallData = abi.encodeCall( - ManagerImpl.registerIdentities, - (proof, preRoot, newStartIndex, identityCommitments, postRoot) + ManagerImplV1.registerIdentities, + ( + insertionProof, + insertionPreRoot, + newStartIndex, + identityCommitments, + insertionPostRoot + ) ); bytes memory expectedError = - abi.encodeWithSelector(ManagerImpl.ProofValidationFailure.selector); + abi.encodeWithSelector(ManagerImplV1.ProofValidationFailure.selector); // Test assertCallFailsOn(identityManagerAddress, registerCallData, expectedError); @@ -277,23 +292,26 @@ contract WorldIDIdentityManagerIdentityRegistration is WorldIDIdentityManagerTes uint256[] memory identities = cloneArray(identityCommitments); identities[invalidSlot] = identity; ITreeVerifier actualVerifier = new TreeVerifier(); - (VerifierLookupTable insertVerifiers, VerifierLookupTable updateVerifiers) = - makeVerifierLookupTables(TC.makeDynArray([70])); + ( + VerifierLookupTable insertVerifiers, + VerifierLookupTable deletionVerifiers, + VerifierLookupTable updateVerifiers + ) = makeVerifierLookupTables(TC.makeDynArray([70])); insertVerifiers.addVerifier(identityCommitmentsSize, actualVerifier); makeNewIdentityManager( treeDepth, - preRoot, + insertionPreRoot, insertVerifiers, + deletionVerifiers, updateVerifiers, - semaphoreVerifier, - isStateBridgeEnabled, - stateBridge + semaphoreVerifier ); bytes memory registerCallData = abi.encodeCall( - ManagerImpl.registerIdentities, (proof, preRoot, startIndex, identities, postRoot) + ManagerImplV1.registerIdentities, + (insertionProof, insertionPreRoot, startIndex, identities, insertionPostRoot) ); bytes memory expectedError = - abi.encodeWithSelector(ManagerImpl.ProofValidationFailure.selector); + abi.encodeWithSelector(ManagerImplV1.ProofValidationFailure.selector); // Test assertCallFailsOn(identityManagerAddress, registerCallData, expectedError); @@ -302,35 +320,38 @@ contract WorldIDIdentityManagerIdentityRegistration is WorldIDIdentityManagerTes /// @notice Checks that it reverts if the provided post root is incorrect. function testCannotRegisterIdentitiesIfPostRootIncorrect(uint256 newPostRoot) public { // Setup - vm.assume(newPostRoot != postRoot && newPostRoot < SNARK_SCALAR_FIELD); + vm.assume(newPostRoot != insertionPostRoot && newPostRoot < SNARK_SCALAR_FIELD); managerImpl = new ManagerImpl(); managerImplAddress = address(managerImpl); ITreeVerifier actualVerifier = new TreeVerifier(); - (VerifierLookupTable insertVerifiers, VerifierLookupTable updateVerifiers) = - makeVerifierLookupTables(TC.makeDynArray([70])); + ( + VerifierLookupTable insertVerifiers, + VerifierLookupTable deletionVerifiers, + VerifierLookupTable updateVerifiers + ) = makeVerifierLookupTables(TC.makeDynArray([70])); insertVerifiers.addVerifier(identityCommitmentsSize, actualVerifier); bytes memory callData = abi.encodeCall( - ManagerImpl.initialize, - ( - treeDepth, - preRoot, - insertVerifiers, - updateVerifiers, - semaphoreVerifier, - isStateBridgeEnabled, - stateBridge - ) + ManagerImplV1.initialize, + (treeDepth, insertionPreRoot, insertVerifiers, updateVerifiers, semaphoreVerifier) ); identityManager = new IdentityManager(managerImplAddress, callData); identityManagerAddress = address(identityManager); + + // Init V2 + bytes memory initCallV2 = abi.encodeCall(ManagerImpl.initializeV2, (deletionVerifiers)); + bytes memory upgradeCall = abi.encodeCall( + UUPSUpgradeable.upgradeToAndCall, (address(managerImplAddress), initCallV2) + ); + assertCallSucceedsOn(identityManagerAddress, upgradeCall, new bytes(0x0)); + bytes memory registerCallData = abi.encodeCall( - ManagerImpl.registerIdentities, - (proof, preRoot, startIndex, identityCommitments, newPostRoot) + ManagerImplV1.registerIdentities, + (insertionProof, insertionPreRoot, startIndex, identityCommitments, newPostRoot) ); bytes memory expectedError = - abi.encodeWithSelector(ManagerImpl.ProofValidationFailure.selector); + abi.encodeWithSelector(ManagerImplV1.ProofValidationFailure.selector); // Test assertCallFailsOn(identityManagerAddress, registerCallData, expectedError); @@ -342,11 +363,11 @@ contract WorldIDIdentityManagerIdentityRegistration is WorldIDIdentityManagerTes // Setup vm.assume(nonOperator != address(this) && nonOperator != address(0x0)); bytes memory callData = abi.encodeCall( - ManagerImpl.registerIdentities, - (proof, preRoot, startIndex, identityCommitments, postRoot) + ManagerImplV1.registerIdentities, + (insertionProof, insertionPreRoot, startIndex, identityCommitments, insertionPostRoot) ); bytes memory errorData = - abi.encodeWithSelector(ManagerImpl.Unauthorized.selector, nonOperator); + abi.encodeWithSelector(ManagerImplV1.Unauthorized.selector, nonOperator); vm.prank(nonOperator); // Test @@ -368,17 +389,16 @@ contract WorldIDIdentityManagerIdentityRegistration is WorldIDIdentityManagerTes treeDepth, uint256(currentPreRoot), defaultInsertVerifiers, + defaultDeletionVerifiers, defaultUpdateVerifiers, - semaphoreVerifier, - isStateBridgeEnabled, - stateBridge + semaphoreVerifier ); bytes memory callData = abi.encodeCall( - ManagerImpl.registerIdentities, - (proof, actualRoot, startIndex, identityCommitments, postRoot) + ManagerImplV1.registerIdentities, + (insertionProof, actualRoot, startIndex, identityCommitments, insertionPostRoot) ); bytes memory expectedError = abi.encodeWithSelector( - ManagerImpl.NotLatestRoot.selector, actualRoot, uint256(currentPreRoot) + ManagerImplV1.NotLatestRoot.selector, actualRoot, uint256(currentPreRoot) ); // Test @@ -402,11 +422,11 @@ contract WorldIDIdentityManagerIdentityRegistration is WorldIDIdentityManagerTes invalidCommitments[invalidPosition] = 0x0; bytes memory callData = abi.encodeCall( - ManagerImpl.registerIdentities, - (proof, initialRoot, startIndex, invalidCommitments, postRoot) + ManagerImplV1.registerIdentities, + (insertionProof, initialRoot, startIndex, invalidCommitments, insertionPostRoot) ); bytes memory expectedError = abi.encodeWithSelector( - ManagerImpl.InvalidCommitment.selector, uint256(invalidPosition + 1) + ManagerImplV1.InvalidCommitment.selector, uint256(invalidPosition + 1) ); // Test @@ -422,16 +442,18 @@ contract WorldIDIdentityManagerIdentityRegistration is WorldIDIdentityManagerTes vm.assume(identitiesLength != 0 && identitiesLength <= 1000); vm.assume(zeroPosition < identitiesLength && zeroPosition > 0); uint256[] memory identities = new uint256[](identitiesLength); - (VerifierLookupTable insertVerifiers, VerifierLookupTable updateVerifiers) = - makeVerifierLookupTables(TC.makeDynArray([identitiesLength])); + ( + VerifierLookupTable insertVerifiers, + VerifierLookupTable deletionVerifiers, + VerifierLookupTable updateVerifiers + ) = makeVerifierLookupTables(TC.makeDynArray([identitiesLength])); makeNewIdentityManager( treeDepth, initialRoot, insertVerifiers, + deletionVerifiers, updateVerifiers, - semaphoreVerifier, - isStateBridgeEnabled, - stateBridge + semaphoreVerifier ); for (uint256 i = 0; i < zeroPosition; ++i) { @@ -442,8 +464,14 @@ contract WorldIDIdentityManagerIdentityRegistration is WorldIDIdentityManagerTes } bytes memory callData = abi.encodeCall( - ManagerImpl.registerIdentities, - ([uint256(2), 1, 3, 4, 5, 6, 7, 9], initialRoot, startIndex, identities, postRoot) + ManagerImplV1.registerIdentities, + ( + [uint256(2), 1, 3, 4, 5, 6, 7, 9], + initialRoot, + startIndex, + identities, + insertionPostRoot + ) ); // Test @@ -458,12 +486,12 @@ contract WorldIDIdentityManagerIdentityRegistration is WorldIDIdentityManagerTes uint256[] memory unreducedCommitments = new uint256[](identityCommitments.length); unreducedCommitments[position] = SNARK_SCALAR_FIELD + i; bytes memory callData = abi.encodeCall( - ManagerImpl.registerIdentities, - (proof, initialRoot, startIndex, unreducedCommitments, postRoot) + ManagerImplV1.registerIdentities, + (insertionProof, initialRoot, startIndex, unreducedCommitments, insertionPostRoot) ); bytes memory expectedError = abi.encodeWithSelector( - ManagerImpl.UnreducedElement.selector, - ManagerImpl.UnreducedElementType.IdentityCommitment, + ManagerImplV1.UnreducedElement.selector, + ManagerImplV1.UnreducedElementType.IdentityCommitment, SNARK_SCALAR_FIELD + i ); @@ -477,12 +505,12 @@ contract WorldIDIdentityManagerIdentityRegistration is WorldIDIdentityManagerTes // Setup uint256 newPreRoot = SNARK_SCALAR_FIELD + i; bytes memory callData = abi.encodeCall( - ManagerImpl.registerIdentities, - (proof, newPreRoot, startIndex, identityCommitments, postRoot) + ManagerImplV1.registerIdentities, + (insertionProof, newPreRoot, startIndex, identityCommitments, insertionPostRoot) ); bytes memory expectedError = abi.encodeWithSelector( - ManagerImpl.UnreducedElement.selector, - ManagerImpl.UnreducedElementType.PreRoot, + ManagerImplV1.UnreducedElement.selector, + ManagerImplV1.UnreducedElementType.PreRoot, newPreRoot ); @@ -490,18 +518,18 @@ contract WorldIDIdentityManagerIdentityRegistration is WorldIDIdentityManagerTes assertCallFailsOn(identityManagerAddress, callData, expectedError); } - /// @notice Tests that it reverts if an attempt is made to register identities with a postRoot + /// @notice Tests that it reverts if an attempt is made to register identities with a insertionPostRoot /// that is not in reduced form. function testCannotRegisterIdentitiesWithUnreducedPostRoot(uint128 i) public { // Setup uint256 newPostRoot = SNARK_SCALAR_FIELD + i; bytes memory callData = abi.encodeCall( - ManagerImpl.registerIdentities, - (proof, initialRoot, startIndex, identityCommitments, newPostRoot) + ManagerImplV1.registerIdentities, + (insertionProof, initialRoot, startIndex, identityCommitments, newPostRoot) ); bytes memory expectedError = abi.encodeWithSelector( - ManagerImpl.UnreducedElement.selector, - ManagerImpl.UnreducedElementType.PostRoot, + ManagerImplV1.UnreducedElement.selector, + ManagerImplV1.UnreducedElementType.PostRoot, newPostRoot ); @@ -515,10 +543,15 @@ contract WorldIDIdentityManagerIdentityRegistration is WorldIDIdentityManagerTes function testCannotRegisterIdentitiesWithUnreducedStartIndex(uint256 i) public { // Setup vm.assume(i > type(uint32).max); - bytes4 functionSelector = ManagerImpl.registerIdentities.selector; + bytes4 functionSelector = ManagerImplV1.registerIdentities.selector; // Have to encode with selector as otherwise it's typechecked. bytes memory callData = abi.encodeWithSelector( - functionSelector, proof, preRoot, i, identityCommitments, postRoot + functionSelector, + insertionProof, + insertionPreRoot, + i, + identityCommitments, + insertionPostRoot ); // Test @@ -534,7 +567,7 @@ contract WorldIDIdentityManagerIdentityRegistration is WorldIDIdentityManagerTes // Test managerImpl.registerIdentities( - proof, initialRoot, startIndex, identityCommitments, postRoot + insertionProof, initialRoot, startIndex, identityCommitments, insertionPostRoot ); } } diff --git a/src/test/identity-manager/WorldIDIdentityManagerIdentityUpdate.t.sol b/src/test/identity-manager/WorldIDIdentityManagerIdentityUpdate.t.sol index 6877f21..2a7c9ca 100644 --- a/src/test/identity-manager/WorldIDIdentityManagerIdentityUpdate.t.sol +++ b/src/test/identity-manager/WorldIDIdentityManagerIdentityUpdate.t.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; import {WorldIDIdentityManagerTest} from "./WorldIDIdentityManagerTest.sol"; import {ITreeVerifier} from "../../interfaces/ITreeVerifier.sol"; import {SimpleVerifier, SimpleVerify} from "../mock/SimpleVerifier.sol"; import {TypeConverter as TC} from "../utils/TypeConverter.sol"; -import {Verifier as TreeVerifier} from "../mock/TreeVerifier.sol"; +import {Verifier as TreeVerifier} from "../mock/InsertionTreeVerifier.sol"; import {VerifierLookupTable} from "../../data/VerifierLookupTable.sol"; import {WorldIDIdentityManager as IdentityManager} from "../../WorldIDIdentityManager.sol"; @@ -23,7 +23,9 @@ contract WorldIDIdentityManagerIdentityUpdate is WorldIDIdentityManagerTest { /// Taken from WorldIDIdentityManagerImplV1.sol event TreeChanged( - uint256 indexed preRoot, ManagerImpl.TreeChange indexed kind, uint256 indexed postRoot + uint256 indexed insertionPreRoot, + ManagerImpl.TreeChange indexed kind, + uint256 indexed insertionPostRoot ); /// @notice Checks that the proof validates properly with correct inputs. @@ -39,16 +41,18 @@ contract WorldIDIdentityManagerIdentityUpdate is WorldIDIdentityManagerTest { vm.assume(newPreRoot != newPostRoot); vm.assume(identities.length <= 1000); // Keeps the test time sane-ish. vm.assume(identityOperator != nullAddress && identityOperator != thisAddress); - (VerifierLookupTable insertVerifiers, VerifierLookupTable updateVerifiers) = - makeVerifierLookupTables(TC.makeDynArray([identities.length])); + ( + VerifierLookupTable insertVerifiers, + VerifierLookupTable deletionVerifiers, + VerifierLookupTable updateVerifiers + ) = makeVerifierLookupTables(TC.makeDynArray([identities.length])); makeNewIdentityManager( treeDepth, newPreRoot, insertVerifiers, + deletionVerifiers, updateVerifiers, - semaphoreVerifier, - isStateBridgeEnabled, - stateBridge + semaphoreVerifier ); ( uint32[] memory leafIndices, @@ -68,8 +72,6 @@ contract WorldIDIdentityManagerIdentityUpdate is WorldIDIdentityManagerTest { // Expect that the state root was sent to the state bridge vm.expectEmit(true, true, true, true); - emit StateRootSentMultichain(newPostRoot); - vm.expectEmit(true, true, true, true); emit TreeChanged(newPreRoot, ManagerImpl.TreeChange.Update, newPostRoot); vm.prank(identityOperator); @@ -88,16 +90,18 @@ contract WorldIDIdentityManagerIdentityUpdate is WorldIDIdentityManagerTest { vm.assume(newPreRoot != newPostRoot); vm.assume(identities.length <= 1000 && identities.length > 0); uint256 secondIdentsLength = identities.length / 2; - (VerifierLookupTable insertVerifiers, VerifierLookupTable updateVerifiers) = - makeVerifierLookupTables(TC.makeDynArray([identities.length, secondIdentsLength])); + ( + VerifierLookupTable insertVerifiers, + VerifierLookupTable deletionVerifiers, + VerifierLookupTable updateVerifiers + ) = makeVerifierLookupTables(TC.makeDynArray([identities.length, secondIdentsLength])); makeNewIdentityManager( treeDepth, newPreRoot, insertVerifiers, + deletionVerifiers, updateVerifiers, - semaphoreVerifier, - isStateBridgeEnabled, - stateBridge + semaphoreVerifier ); ( uint32[] memory leafIndices, @@ -174,16 +178,12 @@ contract WorldIDIdentityManagerIdentityUpdate is WorldIDIdentityManagerTest { vm.expectEmit(true, true, true, true); emit VerifiedProof(oldIdents.length); - vm.expectEmit(true, true, true, true); - emit StateRootSentMultichain(newPostRoot); // Test assertCallSucceedsOn(identityManagerAddress, firstCallData); vm.expectEmit(true, true, true, true); emit VerifiedProof(secondOldIdents.length); - vm.expectEmit(true, true, true, true); - emit StateRootSentMultichain(secondPostRoot); assertCallSucceedsOn(identityManagerAddress, secondCallData); } @@ -198,16 +198,18 @@ contract WorldIDIdentityManagerIdentityUpdate is WorldIDIdentityManagerTest { vm.assume(SimpleVerify.isValidInput(uint256(prf[0]))); vm.assume(newPreRoot != newPostRoot); vm.assume(identities.length > 0); - (VerifierLookupTable insertVerifiers, VerifierLookupTable updateVerifiers) = - makeVerifierLookupTables(TC.makeDynArray([identities.length - 1])); + ( + VerifierLookupTable insertVerifiers, + VerifierLookupTable deletionVerifiers, + VerifierLookupTable updateVerifiers + ) = makeVerifierLookupTables(TC.makeDynArray([identities.length - 1])); makeNewIdentityManager( treeDepth, newPreRoot, insertVerifiers, + deletionVerifiers, updateVerifiers, - semaphoreVerifier, - isStateBridgeEnabled, - stateBridge + semaphoreVerifier ); ( uint32[] memory leafIndices, @@ -236,16 +238,18 @@ contract WorldIDIdentityManagerIdentityUpdate is WorldIDIdentityManagerTest { vm.assume(!SimpleVerify.isValidInput(uint256(prf[0]))); vm.assume(newPreRoot != newPostRoot); vm.assume(identities.length <= 1000); - (VerifierLookupTable insertVerifiers, VerifierLookupTable updateVerifiers) = - makeVerifierLookupTables(TC.makeDynArray([identities.length])); + ( + VerifierLookupTable insertVerifiers, + VerifierLookupTable deletionVerifiers, + VerifierLookupTable updateVerifiers + ) = makeVerifierLookupTables(TC.makeDynArray([identities.length])); makeNewIdentityManager( treeDepth, newPreRoot, insertVerifiers, + deletionVerifiers, updateVerifiers, - semaphoreVerifier, - isStateBridgeEnabled, - stateBridge + semaphoreVerifier ); ( uint32[] memory leafIndices, @@ -281,7 +285,7 @@ contract WorldIDIdentityManagerIdentityUpdate is WorldIDIdentityManagerTest { ) = prepareUpdateIdentitiesTestCase(identities, prf); bytes memory callData = abi.encodeCall( ManagerImpl.updateIdentities, - (actualProof, preRoot, leafIndices, oldIdents, newIdents, postRoot) + (actualProof, insertionPreRoot, leafIndices, oldIdents, newIdents, insertionPostRoot) ); bytes memory errorData = abi.encodeWithSelector(ManagerImpl.Unauthorized.selector, nonOperator); @@ -311,20 +315,22 @@ contract WorldIDIdentityManagerIdentityUpdate is WorldIDIdentityManagerTest { uint256[] memory newIdents, uint256[8] memory actualProof ) = prepareUpdateIdentitiesTestCase(identities, prf); - (VerifierLookupTable insertVerifiers, VerifierLookupTable updateVerifiers) = - makeVerifierLookupTables(TC.makeDynArray([identities.length])); + ( + VerifierLookupTable insertVerifiers, + VerifierLookupTable deletionVerifiers, + VerifierLookupTable updateVerifiers + ) = makeVerifierLookupTables(TC.makeDynArray([identities.length])); makeNewIdentityManager( treeDepth, uint256(currentPreRoot), insertVerifiers, + deletionVerifiers, updateVerifiers, - semaphoreVerifier, - isStateBridgeEnabled, - stateBridge + semaphoreVerifier ); bytes memory callData = abi.encodeCall( ManagerImpl.updateIdentities, - (actualProof, actualRoot, leafIndices, oldIdents, newIdents, postRoot) + (actualProof, actualRoot, leafIndices, oldIdents, newIdents, insertionPostRoot) ); bytes memory expectedError = abi.encodeWithSelector( ManagerImpl.NotLatestRoot.selector, actualRoot, uint256(currentPreRoot) @@ -353,17 +359,23 @@ contract WorldIDIdentityManagerIdentityUpdate is WorldIDIdentityManagerTest { uint256[] memory newIdents, uint256[8] memory actualProof ) = prepareUpdateIdentitiesTestCase(identities, prf); - (VerifierLookupTable insertVerifiers, VerifierLookupTable updateVerifiers) = - makeVerifierLookupTables(TC.makeDynArray([identities.length])); - makeNewIdentityManager( - treeDepth, - newPreRoot, - insertVerifiers, - updateVerifiers, - semaphoreVerifier, - isStateBridgeEnabled, - stateBridge - ); + // scoping to avoid stack too deep errors + { + ( + VerifierLookupTable insertVerifiers, + VerifierLookupTable deletionVerifiers, + VerifierLookupTable updateVerifiers + ) = makeVerifierLookupTables(TC.makeDynArray([identities.length])); + makeNewIdentityManager( + treeDepth, + newPreRoot, + insertVerifiers, + deletionVerifiers, + updateVerifiers, + semaphoreVerifier + ); + } + if (changeOld) { oldIdents[position] = SNARK_SCALAR_FIELD + i; } else { @@ -371,8 +383,9 @@ contract WorldIDIdentityManagerIdentityUpdate is WorldIDIdentityManagerTest { } bytes memory callData = abi.encodeCall( ManagerImpl.updateIdentities, - (actualProof, newPreRoot, leafIndices, oldIdents, newIdents, postRoot) + (actualProof, newPreRoot, leafIndices, oldIdents, newIdents, insertionPostRoot) ); + bytes memory expectedError = abi.encodeWithSelector( ManagerImpl.UnreducedElement.selector, ManagerImpl.UnreducedElementType.IdentityCommitment, @@ -400,7 +413,7 @@ contract WorldIDIdentityManagerIdentityUpdate is WorldIDIdentityManagerTest { ) = prepareUpdateIdentitiesTestCase(identities, prf); bytes memory callData = abi.encodeCall( ManagerImpl.updateIdentities, - (actualProof, newPreRoot, leafIndices, oldIdents, newIdents, postRoot) + (actualProof, newPreRoot, leafIndices, oldIdents, newIdents, insertionPostRoot) ); bytes memory expectedError = abi.encodeWithSelector( ManagerImpl.UnreducedElement.selector, @@ -412,7 +425,7 @@ contract WorldIDIdentityManagerIdentityUpdate is WorldIDIdentityManagerTest { assertCallFailsOn(identityManagerAddress, callData, expectedError); } - /// @notice Tests that it reverts if an attempt is made to update identities with a postRoot + /// @notice Tests that it reverts if an attempt is made to update identities with a insertionPostRoot /// that is not in reduced form. function testCannotUpdateIdentitiesWithUnreducedPostRoot( uint128 i, @@ -459,7 +472,7 @@ contract WorldIDIdentityManagerIdentityUpdate is WorldIDIdentityManagerTest { // Test managerImpl.updateIdentities( - actualProof, initialRoot, leafIndices, oldIdents, newIdents, postRoot + actualProof, initialRoot, leafIndices, oldIdents, newIdents, insertionPostRoot ); } } diff --git a/src/test/identity-manager/WorldIDIdentityManagerInitialization.t.sol b/src/test/identity-manager/WorldIDIdentityManagerInitialization.t.sol index c376aa8..37b7ef0 100644 --- a/src/test/identity-manager/WorldIDIdentityManagerInitialization.t.sol +++ b/src/test/identity-manager/WorldIDIdentityManagerInitialization.t.sol @@ -1,10 +1,13 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; + +import {UUPSUpgradeable} from "contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import {WorldIDIdentityManagerTest} from "./WorldIDIdentityManagerTest.sol"; import {WorldIDIdentityManager as IdentityManager} from "../../WorldIDIdentityManager.sol"; -import {WorldIDIdentityManagerImplV1 as ManagerImpl} from "../../WorldIDIdentityManagerImplV1.sol"; +import {WorldIDIdentityManagerImplV2 as ManagerImpl} from "../../WorldIDIdentityManagerImplV2.sol"; +import {WorldIDIdentityManagerImplV1 as ManagerImplV1} from "../../WorldIDIdentityManagerImplV1.sol"; /// @title World ID Identity Manager Initialization Tests /// @notice Contains tests for the WorldID identity manager. @@ -20,42 +23,54 @@ contract WorldIDIdentityManagerInitialization is WorldIDIdentityManagerTest { // Setup delete identityManager; delete managerImpl; + delete managerImplV1; - managerImpl = new ManagerImpl(); + managerImplV1 = new ManagerImplV1(); managerImplAddress = address(managerImpl); bytes memory callData = abi.encodeCall( - ManagerImpl.initialize, + ManagerImplV1.initialize, ( treeDepth, initialRoot, defaultInsertVerifiers, defaultUpdateVerifiers, - semaphoreVerifier, - isStateBridgeEnabled, - stateBridge + semaphoreVerifier ) ); vm.expectEmit(true, true, true, true); emit Initialized(1); + identityManager = new IdentityManager(managerImplV1Address, callData); + identityManagerAddress = address(identityManager); + + // creates Manager Impl V2, which will be used for tests + managerImpl = new ManagerImpl(); + managerImplAddress = address(managerImpl); + + bytes memory initCallV2 = + abi.encodeCall(ManagerImpl.initializeV2, (defaultDeletionVerifiers)); + bytes memory upgradeCall = abi.encodeCall( + UUPSUpgradeable.upgradeToAndCall, (address(managerImplAddress), initCallV2) + ); + + vm.expectEmit(true, true, true, true); + emit Initialized(2); // Test - identityManager = new IdentityManager(managerImplAddress, callData); + assertCallSucceedsOn(identityManagerAddress, upgradeCall, new bytes(0x0)); } /// @notice Checks that it is not possible to initialise the contract more than once. function testInitializationOnlyOnce() public { // Setup bytes memory callData = abi.encodeCall( - ManagerImpl.initialize, + ManagerImplV1.initialize, ( treeDepth, initialRoot, defaultInsertVerifiers, defaultUpdateVerifiers, - semaphoreVerifier, - isStateBridgeEnabled, - stateBridge + semaphoreVerifier ) ); bytes memory expectedReturn = @@ -63,12 +78,16 @@ contract WorldIDIdentityManagerInitialization is WorldIDIdentityManagerTest { // Test assertCallFailsOn(identityManagerAddress, callData, expectedReturn); + + callData = abi.encodeCall(ManagerImpl.initializeV2, (defaultDeletionVerifiers)); + + assertCallFailsOn(identityManagerAddress, callData, expectedReturn); } /// @notice Checks that it is impossible to initialize the delegate on its own. function testCannotInitializeTheDelegate() public { // Setup - ManagerImpl localImpl = new ManagerImpl(); + ManagerImplV1 localImpl = new ManagerImpl(); vm.expectRevert("Initializable: contract is already initialized"); // Test @@ -77,9 +96,7 @@ contract WorldIDIdentityManagerInitialization is WorldIDIdentityManagerTest { initialRoot, defaultInsertVerifiers, defaultUpdateVerifiers, - semaphoreVerifier, - isStateBridgeEnabled, - stateBridge + semaphoreVerifier ); } @@ -94,19 +111,17 @@ contract WorldIDIdentityManagerInitialization is WorldIDIdentityManagerTest { uint8 unsupportedDepth = 15; bytes memory callData = abi.encodeCall( - ManagerImpl.initialize, + ManagerImplV1.initialize, ( unsupportedDepth, initialRoot, defaultInsertVerifiers, defaultUpdateVerifiers, - semaphoreVerifier, - isStateBridgeEnabled, - stateBridge + semaphoreVerifier ) ); - vm.expectRevert(abi.encodeWithSelector(ManagerImpl.UnsupportedTreeDepth.selector, 15)); + vm.expectRevert(abi.encodeWithSelector(ManagerImplV1.UnsupportedTreeDepth.selector, 15)); // Test identityManager = new IdentityManager(managerImplAddress, callData); diff --git a/src/test/identity-manager/WorldIDIdentityManagerOwnershipManagement.t.sol b/src/test/identity-manager/WorldIDIdentityManagerOwnershipManagement.t.sol index 26a5fd3..04e1557 100644 --- a/src/test/identity-manager/WorldIDIdentityManagerOwnershipManagement.t.sol +++ b/src/test/identity-manager/WorldIDIdentityManagerOwnershipManagement.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; import {WorldIDIdentityManagerTest} from "./WorldIDIdentityManagerTest.sol"; @@ -11,12 +11,13 @@ import {Ownable2StepUpgradeable} from "contracts-upgradeable/access/Ownable2Step import {SemaphoreVerifier} from "semaphore/base/SemaphoreVerifier.sol"; import {SimpleVerifier, SimpleVerify} from "../mock/SimpleVerifier.sol"; import {UUPSUpgradeable} from "contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; -import {Verifier as TreeVerifier} from "../mock/TreeVerifier.sol"; +import {Verifier as TreeVerifier} from "../mock/InsertionTreeVerifier.sol"; import {WorldIDIdentityManagerImplMock} from "../mock/WorldIDIdentityManagerImplMock.sol"; import {WorldIDImpl} from "../../abstract/WorldIDImpl.sol"; import {WorldIDIdentityManager as IdentityManager} from "../../WorldIDIdentityManager.sol"; -import {WorldIDIdentityManagerImplV1 as ManagerImpl} from "../../WorldIDIdentityManagerImplV1.sol"; +import {WorldIDIdentityManagerImplV2 as ManagerImpl} from "../../WorldIDIdentityManagerImplV2.sol"; +import {WorldIDIdentityManagerImplV1 as ManagerImplV1} from "../../WorldIDIdentityManagerImplV1.sol"; /// @title World ID Identity Manager Ownership Management Tests /// @notice Contains tests for the WorldID identity manager. @@ -122,7 +123,7 @@ contract WorldIDIdentityManagerOwnershipManagement is WorldIDIdentityManagerTest /// @notice Enures that the contract has a notion of identity operator. function testHasIdentityOperator() public { // Setup - bytes memory callData = abi.encodeCall(ManagerImpl.identityOperator, ()); + bytes memory callData = abi.encodeCall(ManagerImplV1.identityOperator, ()); bytes memory returnData = abi.encode(thisAddress); // Test @@ -144,9 +145,9 @@ contract WorldIDIdentityManagerOwnershipManagement is WorldIDIdentityManagerTest function testCanSetIdentityOperatorAsOwner(address newOperator) public { // Setup vm.assume(newOperator != thisAddress); - bytes memory callData = abi.encodeCall(ManagerImpl.setIdentityOperator, (newOperator)); + bytes memory callData = abi.encodeCall(ManagerImplV1.setIdentityOperator, (newOperator)); bytes memory returnData = abi.encode(thisAddress); - bytes memory checkCallData1 = abi.encodeCall(ManagerImpl.identityOperator, ()); + bytes memory checkCallData1 = abi.encodeCall(ManagerImplV1.identityOperator, ()); bytes memory checkCallReturn1 = abi.encode(newOperator); bytes memory checkCallData2 = abi.encodeCall(OwnableUpgradeable.owner, ()); bytes memory checkCallReturn2 = abi.encode(thisAddress); @@ -164,7 +165,7 @@ contract WorldIDIdentityManagerOwnershipManagement is WorldIDIdentityManagerTest function testCannotSetIdentityOperatorAsNonOwner(address newOperator, address naughty) public { // Setup vm.assume(naughty != nullAddress && naughty != thisAddress); - bytes memory callData = abi.encodeCall(ManagerImpl.setIdentityOperator, (newOperator)); + bytes memory callData = abi.encodeCall(ManagerImplV1.setIdentityOperator, (newOperator)); bytes memory errorData = encodeStringRevert("Ownable: caller is not the owner"); vm.prank(naughty); diff --git a/src/test/identity-manager/WorldIDIdentityManagerSemaphoreVerification.t.sol b/src/test/identity-manager/WorldIDIdentityManagerSemaphoreVerification.t.sol index aba6c90..83977ea 100644 --- a/src/test/identity-manager/WorldIDIdentityManagerSemaphoreVerification.t.sol +++ b/src/test/identity-manager/WorldIDIdentityManagerSemaphoreVerification.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; import {WorldIDIdentityManagerTest} from "./WorldIDIdentityManagerTest.sol"; @@ -8,7 +8,8 @@ import {SemaphoreTreeDepthValidator} from "../../utils/SemaphoreTreeDepthValidat import {SimpleSemaphoreVerifier} from "../mock/SimpleSemaphoreVerifier.sol"; import {WorldIDIdentityManager as IdentityManager} from "../../WorldIDIdentityManager.sol"; -import {WorldIDIdentityManagerImplV1 as ManagerImpl} from "../../WorldIDIdentityManagerImplV1.sol"; +import {WorldIDIdentityManagerImplV2 as ManagerImpl} from "../../WorldIDIdentityManagerImplV2.sol"; +import {WorldIDIdentityManagerImplV1 as ManagerImplV1} from "../../WorldIDIdentityManagerImplV1.sol"; /// @title World ID Identity Manager Semaphore Proof Verification Tests /// @notice Contains tests for the WorldID identity manager. @@ -30,16 +31,15 @@ contract WorldIDIdentityManagerSemaphoreValidation is WorldIDIdentityManagerTest vm.assume(prf[0] != 0); makeNewIdentityManager( actualTreeDepth, - preRoot, + insertionPreRoot, defaultInsertVerifiers, + defaultDeletionVerifiers, defaultUpdateVerifiers, - actualSemaphoreVerifier, - isStateBridgeEnabled, - stateBridge + actualSemaphoreVerifier ); bytes memory verifyProofCallData = abi.encodeCall( - ManagerImpl.verifyProof, - (preRoot, nullifierHash, signalHash, externalNullifierHash, proof) + ManagerImplV1.verifyProof, + (insertionPreRoot, nullifierHash, signalHash, externalNullifierHash, insertionProof) ); // Test @@ -60,16 +60,15 @@ contract WorldIDIdentityManagerSemaphoreValidation is WorldIDIdentityManagerTest vm.assume(prf[0] % 2 == 0); makeNewIdentityManager( actualTreeDepth, - preRoot, + insertionPreRoot, defaultInsertVerifiers, + defaultDeletionVerifiers, defaultUpdateVerifiers, - actualSemaphoreVerifier, - isStateBridgeEnabled, - stateBridge + actualSemaphoreVerifier ); bytes memory verifyProofCallData = abi.encodeCall( - ManagerImpl.verifyProof, - (preRoot, nullifierHash, signalHash, externalNullifierHash, prf) + ManagerImplV1.verifyProof, + (insertionPreRoot, nullifierHash, signalHash, externalNullifierHash, prf) ); vm.expectRevert("Semaphore__InvalidProof()"); diff --git a/src/test/identity-manager/WorldIDIdentityManagerStateBridge.t.sol b/src/test/identity-manager/WorldIDIdentityManagerStateBridge.t.sol deleted file mode 100644 index ee64f2e..0000000 --- a/src/test/identity-manager/WorldIDIdentityManagerStateBridge.t.sol +++ /dev/null @@ -1,155 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -import {WorldIDIdentityManagerTest} from "./WorldIDIdentityManagerTest.sol"; - -import {IBridge} from "../../interfaces/IBridge.sol"; -import {ITreeVerifier} from "../../interfaces/ITreeVerifier.sol"; -import {TypeConverter as TC} from "../utils/TypeConverter.sol"; -import {Verifier as TreeVerifier} from "../mock/TreeVerifier.sol"; -import {VerifierLookupTable} from "../../data/VerifierLookupTable.sol"; - -import {WorldIDIdentityManagerImplV1 as ManagerImpl} from "../../WorldIDIdentityManagerImplV1.sol"; - -/// @title World ID Identity Manager State Bridge Tests -/// @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 WorldIDIdentityManagerStateBridge is WorldIDIdentityManagerTest { - /// @notice Taken from WorldIDIdentityManagerImplV1.sol - event DependencyUpdated( - ManagerImpl.Dependency indexed kind, address indexed oldAddress, address indexed newAddress - ); - event StateBridgeStateChange(bool indexed isEnabled); - - /// @notice Tests that it is possible to upgrade `stateBridge` to a new implementation. - function testCanUpgradeStateBridge(IBridge newStateBridge) public { - // Setup - address stateBridgeAddress = address(newStateBridge); - vm.assume(stateBridgeAddress != nullAddress && stateBridgeAddress != thisAddress); - bytes memory callData = abi.encodeCall(ManagerImpl.setStateBridge, (newStateBridge)); - vm.expectEmit(true, false, true, true); - emit DependencyUpdated(ManagerImpl.Dependency.StateBridge, nullAddress, stateBridgeAddress); - - // Test - assertCallSucceedsOn(identityManagerAddress, callData, new bytes(0x0)); - } - - /// @notice Tests that it is possible to disable the `stateBridge`. - function testCanDisableStateBridgeFunctionality() public { - // Setup - bytes memory callData = abi.encodeCall(ManagerImpl.disableStateBridge, ()); - (VerifierLookupTable insertVerifiers, VerifierLookupTable updateVerifiers) = - makeVerifierLookupTables(TC.makeDynArray([40])); - insertVerifiers.addVerifier(identityCommitmentsSize, new TreeVerifier()); - makeNewIdentityManager( - treeDepth, - preRoot, - insertVerifiers, - updateVerifiers, - semaphoreVerifier, - isStateBridgeEnabled, - stateBridge - ); - bytes memory registerCallData = abi.encodeCall( - ManagerImpl.registerIdentities, - (proof, preRoot, startIndex, identityCommitments, postRoot) - ); - bytes memory latestRootCallData = abi.encodeCall(ManagerImpl.latestRoot, ()); - bytes memory queryRootCallData = abi.encodeCall(ManagerImpl.queryRoot, (postRoot)); - - // expect event that state root was sent to state bridge - vm.expectEmit(true, true, true, true); - emit StateRootSentMultichain(postRoot); - - // Test - assertCallSucceedsOn(identityManagerAddress, registerCallData); - assertCallSucceedsOn(identityManagerAddress, latestRootCallData, abi.encode(postRoot)); - assertCallSucceedsOn( - identityManagerAddress, - queryRootCallData, - abi.encode(ManagerImpl.RootInfo(postRoot, 0, true)) - ); - - vm.expectEmit(true, true, true, true); - emit StateBridgeStateChange(false); - - assertCallSucceedsOn(identityManagerAddress, callData, new bytes(0x0)); - } - - /// @notice Tests that it is not possible to upgrade `stateBridge` to the 0x0 address. - function testCannotUpgradeStateBridgeToZeroAddress() public { - // address used to disable the state bridge functionality - address zeroAddress = address(0x0); - // Setup - bytes memory callData = abi.encodeCall(ManagerImpl.setStateBridge, (IBridge(zeroAddress))); - - bytes memory expectedError = - abi.encodeWithSelector(ManagerImpl.InvalidStateBridgeAddress.selector); - - // Test - assertCallFailsOn(identityManagerAddress, callData, expectedError); - } - - /// @notice Checks that it is impossible to call `setStateBridge` as a non-owner. - function testCannotUpdateStateBridgeAsNonOwner(address nonManager) public { - // Setup - vm.assume(nonManager != address(this) && nonManager != address(0x0)); - - bytes memory callData = abi.encodeCall(ManagerImpl.setStateBridge, (IBridge(address(0x1)))); - - bytes memory errorData = encodeStringRevert("Ownable: caller is not the owner"); - vm.prank(nonManager); - - // Test - assertCallFailsOn(identityManagerAddress, callData, errorData); - } - - /// @notice Checks that the state bridge can be enabled if it is disabled. - function testCanEnableStateBridgeIfDisabled() public { - // Setup - makeNewIdentityManager( - treeDepth, - preRoot, - defaultInsertVerifiers, - defaultUpdateVerifiers, - semaphoreVerifier, - false, - stateBridge - ); - bytes memory callData = abi.encodeCall(ManagerImpl.enableStateBridge, ()); - vm.expectEmit(true, true, true, true); - emit StateBridgeStateChange(true); - - // Test - assertCallSucceedsOn(identityManagerAddress, callData, new bytes(0x0)); - } - - /// @notice Tests that it is impossible to enable the `stateBridge` if it is already - /// enabled. - function testCannotEnableStateBridgeIfAlreadyEnabled() public { - // Setup - bytes memory callData = abi.encodeCall(ManagerImpl.enableStateBridge, ()); - - bytes memory expectedError = - abi.encodeWithSelector(ManagerImpl.StateBridgeAlreadyEnabled.selector); - // Test - assertCallFailsOn(identityManagerAddress, callData, expectedError); - } - - /// @notice Tests that it is impossible to disabled the `stateBridge` if it is already - /// disabled. - function testCannotDisableStateBridgeIfAlreadyDisabled() public { - // Setup - bytes memory callData = abi.encodeCall(ManagerImpl.disableStateBridge, ()); - - // disable state bridge - assertCallSucceedsOn(identityManagerAddress, callData, new bytes(0x0)); - - bytes memory expectedError = - abi.encodeWithSelector(ManagerImpl.StateBridgeAlreadyDisabled.selector); - // Test - assertCallFailsOn(identityManagerAddress, callData, expectedError); - } -} diff --git a/src/test/identity-manager/WorldIDIdentityManagerTest.sol b/src/test/identity-manager/WorldIDIdentityManagerTest.sol index c72fd7a..f1ca0a8 100644 --- a/src/test/identity-manager/WorldIDIdentityManagerTest.sol +++ b/src/test/identity-manager/WorldIDIdentityManagerTest.sol @@ -1,5 +1,7 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; + +import {UUPSUpgradeable} from "contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import {WorldIDTest} from "../WorldIDTest.sol"; @@ -14,7 +16,8 @@ import {SemaphoreVerifier} from "semaphore/base/SemaphoreVerifier.sol"; import {VerifierLookupTable} from "../../data/VerifierLookupTable.sol"; import {WorldIDIdentityManager as IdentityManager} from "../../WorldIDIdentityManager.sol"; -import {WorldIDIdentityManagerImplV1 as ManagerImpl} from "../../WorldIDIdentityManagerImplV1.sol"; +import {WorldIDIdentityManagerImplV1 as ManagerImplV1} from "../../WorldIDIdentityManagerImplV1.sol"; +import {WorldIDIdentityManagerImplV2 as ManagerImpl} from "../../WorldIDIdentityManagerImplV2.sol"; /// @title World ID Identity Manager Test. /// @notice Contains tests for the WorldID identity manager. @@ -27,40 +30,61 @@ contract WorldIDIdentityManagerTest is WorldIDTest { /////////////////////////////////////////////////////////////////////////////// IdentityManager internal identityManager; + // V2 ManagerImpl internal managerImpl; + // V1 + ManagerImplV1 internal managerImplV1; ITreeVerifier internal treeVerifier; uint256 internal initialRoot = 0x0; uint8 internal treeDepth = 16; address internal identityManagerAddress; + // V2 address internal managerImplAddress; + // V1 + address internal managerImplV1Address; uint256 internal slotCounter = 0; - // All hardcoded test data taken from `src/test/data/TestParams.json`. This will be dynamically + /////////////////////////////////////////////////////////////////// + /// INSERTION /// + /////////////////////////////////////////////////////////////////// + // All hardcoded test data taken from `src/test/data/TestInsertionParams.json`. This will be dynamically // generated at some point in the future. - bytes32 internal constant inputHash = + bytes32 internal constant insertionInputHash = 0x7d7f77c56064e1f8577de14bba99eff85599ab0e76d0caeadd1ad61674b8a9c3; uint32 internal constant startIndex = 0; - uint256 internal constant preRoot = + uint256 internal constant insertionPreRoot = 0x18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238; - uint256 internal constant postRoot = + uint256 internal constant insertionPostRoot = 0x5c1e52b41a571293b30efacd2afdb7173b20cfaf1f646c4ac9f96eb75848270; + uint256[] identityCommitments; uint256 identityCommitmentsSize = 3; - uint256[8] proof; + uint256[8] insertionProof; + + /////////////////////////////////////////////////////////////////// + /// DELETION /// + /////////////////////////////////////////////////////////////////// + // All hardcoded test data taken from `src/test/data/TestDeletionParams.json`. This will be dynamically + // generated at some point in the future. + bytes32 internal constant deletionInputHash = + 0x227590f99431e20f2f95fdfb1b7dfb648c04242c950c31263ba165647c96501a; + uint256 internal constant deletionPreRoot = + 0x18cb13df3e79b9f847a1494d0a2e6f3cc0041d9cae7e5ccb8cd1852ecdc4af58; + uint256 internal constant deletionPostRoot = + 0x82fcf94594d7363636338e2c29242cc77e3d04f36c8ad64d294d2ab4d251708; + bytes packedDeletionIndices = abi.encodePacked( + uint32(0), uint32(2), uint32(4), uint32(6), uint32(8), uint32(10), uint32(12), uint32(14) + ); + uint32 deletionBatchSize = 8; + uint256[8] deletionProof; // Needed for testing things. uint256 internal constant SNARK_SCALAR_FIELD = 21888242871839275222246405745257275088548364400416034343698204186575808495617; - // State bridge mock - IBridge internal stateBridge; - bool internal isStateBridgeEnabled = true; - - event StateRootSentMultichain(uint256 indexed root); - // Mock Verifiers ITreeVerifier unimplementedVerifier = new UnimplementedTreeVerifier(); SemaphoreVerifier semaphoreVerifier = new SemaphoreVerifier(); @@ -68,6 +92,7 @@ contract WorldIDIdentityManagerTest is WorldIDTest { // Verifiers uint256 initialBatchSize = 30; VerifierLookupTable internal defaultInsertVerifiers; + VerifierLookupTable internal defaultDeletionVerifiers; VerifierLookupTable internal defaultUpdateVerifiers; /////////////////////////////////////////////////////////////////////////////// @@ -81,8 +106,8 @@ contract WorldIDIdentityManagerTest is WorldIDTest { identityCommitments[1] = 0x2; identityCommitments[2] = 0x3; - // Create the proof term. - proof = [ + // Create the insertion proof term. + insertionProof = [ 0x2a45bf326884bbf13c821a5e4f30690a391156cccf80a2922fb24250111dd7eb, 0x23a7376a159513e6d0e22d43fcdca9d0c8a5c54a73b59fce6962a41e71355894, 0x21b9fc7c2d1f76c2e1a972b00f18728a57a34d7e4ae040811bf1626132ff3658, @@ -92,6 +117,18 @@ contract WorldIDIdentityManagerTest is WorldIDTest { 0x23115ff1573808639f19724479b195b7894a45c9868242ad2a416767359c6c78, 0x23f3fa30273c7f38e360496e7f9790450096d4a9592e1fe6e0a996cb05b8fb28 ]; + + // Create the deletion proof term. + deletionProof = [ + 0x226cb5c88ce8ccf2774dc13847c3b579c4e4bc8d47bfc2a9ac1454e1a9a42ee3, + 0x1e69ced73a40a88c9f68df4a1bf34c3ff67efca6e2682bd8d1bb96a7a3e4bf50, + 0x1029d8179a82355f902562af0f0e719e31ac12f63effb0c9006ee3332c280e01, + 0x24ae80c2f18161206a0eacc736ddef2c518e0dec37e13c6f0cf17034d04508cc, + 0x29213a2cb6582178edd743f8e8b5541175855b55d0c90f1894a1e415b625af70, + 0x2c9119a368d137dd2409dee4796eb96059296f87009e3d47b0a858cfd05d6954, + 0x2cdd77b17d2270a8fe1385ec60fdd6c644f83549331ae116a538c555f56a9540, + 0x1f7214627223f7839a538052d96ad480a9565a6ec8a9e1fcecf54a7d73a55495 + ]; } /// @notice This function runs before every single test. @@ -100,24 +137,19 @@ contract WorldIDIdentityManagerTest is WorldIDTest { treeVerifier = new SimpleVerifier(initialBatchSize); defaultInsertVerifiers = new VerifierLookupTable(); defaultInsertVerifiers.addVerifier(initialBatchSize, treeVerifier); - defaultUpdateVerifiers = new VerifierLookupTable(); - defaultUpdateVerifiers.addVerifier(initialBatchSize, treeVerifier); - stateBridge = new SimpleStateBridge(); - stateBridge = IBridge(stateBridge); makeNewIdentityManager( treeDepth, initialRoot, defaultInsertVerifiers, + defaultDeletionVerifiers, defaultUpdateVerifiers, - semaphoreVerifier, - isStateBridgeEnabled, - stateBridge + semaphoreVerifier ); hevm.label(address(this), "Sender"); hevm.label(identityManagerAddress, "IdentityManager"); hevm.label(managerImplAddress, "ManagerImplementation"); - hevm.label(address(stateBridge), "StateBridge"); + hevm.label(managerImplV1Address, "ManagerImplementationV1"); } /////////////////////////////////////////////////////////////////////////////// @@ -131,59 +163,61 @@ contract WorldIDIdentityManagerTest is WorldIDTest { /// @param insertVerifiers The insertion verifier lookup table. /// @param updateVerifiers The udpate verifier lookup table. /// @param actualSemaphoreVerifier The Semaphore verifier instance to use. - /// @param enableStateBridge Whether or not the new identity manager should have the state - /// bridge enabled. - /// @param actualStateBridge The current state bridge. function makeNewIdentityManager( uint8 actualTreeDepth, uint256 actualPreRoot, VerifierLookupTable insertVerifiers, + VerifierLookupTable deletionVerifiers, VerifierLookupTable updateVerifiers, - ISemaphoreVerifier actualSemaphoreVerifier, - bool enableStateBridge, - IBridge actualStateBridge + ISemaphoreVerifier actualSemaphoreVerifier ) public { - managerImpl = new ManagerImpl(); - managerImplAddress = address(managerImpl); + managerImplV1 = new ManagerImplV1(); + managerImplV1Address = address(managerImplV1); bytes memory initCallData = abi.encodeCall( - ManagerImpl.initialize, + ManagerImplV1.initialize, ( actualTreeDepth, actualPreRoot, insertVerifiers, updateVerifiers, - actualSemaphoreVerifier, - enableStateBridge, - actualStateBridge + actualSemaphoreVerifier ) ); - identityManager = new IdentityManager(managerImplAddress, initCallData); + identityManager = new IdentityManager(managerImplV1Address, initCallData); identityManagerAddress = address(identityManager); + + // creates Manager Impl V2, which will be used for tests + managerImpl = new ManagerImpl(); + managerImplAddress = address(managerImpl); + + bytes memory initCallV2 = abi.encodeCall(ManagerImpl.initializeV2, (deletionVerifiers)); + bytes memory upgradeCall = abi.encodeCall( + UUPSUpgradeable.upgradeToAndCall, (address(managerImplAddress), 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 enableStateBridge Whether or not the new identity manager should have the state - /// bridge enabled. - /// @param actualStateBridge The current state bridge. /// @param batchSizes The batch sizes to create verifiers for. Verifiers will be created for /// both insertions and updates. Must be non-empty. /// /// @custom:reverts string If any batch size exceeds 1000. /// @custom:reverts string If `batchSizes` is empty. - function makeNewIdentityManager( - uint256 actualPreRoot, - bool enableStateBridge, - IBridge actualStateBridge, - uint256[] calldata batchSizes - ) public { - (VerifierLookupTable insertVerifiers, VerifierLookupTable updateVerifiers) = - makeVerifierLookupTables(batchSizes); + function makeNewIdentityManager(uint256 actualPreRoot, uint256[] calldata batchSizes) public { + ( + VerifierLookupTable insertVerifiers, + VerifierLookupTable deletionVerifiers, + VerifierLookupTable updateVerifiers + ) = makeVerifierLookupTables(batchSizes); defaultInsertVerifiers = insertVerifiers; + defaultDeletionVerifiers = deletionVerifiers; defaultUpdateVerifiers = updateVerifiers; // Now we can build the identity manager as usual. @@ -191,10 +225,9 @@ contract WorldIDIdentityManagerTest is WorldIDTest { treeDepth, actualPreRoot, insertVerifiers, + deletionVerifiers, updateVerifiers, - semaphoreVerifier, - enableStateBridge, - actualStateBridge + semaphoreVerifier ); } @@ -204,6 +237,7 @@ contract WorldIDIdentityManagerTest is WorldIDTest { /// both insertions and updates. Must be non-empty and contain no duplicates. /// /// @return insertVerifiers The insertion verifier lookup table. + /// @return deletionVerifiers The deletion verifier lookup table. /// @return updateVerifiers The update verifier lookup table. /// /// @custom:reverts VerifierExists If `batchSizes` contains a duplicate. @@ -211,7 +245,11 @@ contract WorldIDIdentityManagerTest is WorldIDTest { /// @custom:reverts string If `batchSizes` is empty. function makeVerifierLookupTables(uint256[] memory batchSizes) public - returns (VerifierLookupTable insertVerifiers, VerifierLookupTable updateVerifiers) + returns ( + VerifierLookupTable insertVerifiers, + VerifierLookupTable deletionVerifiers, + VerifierLookupTable updateVerifiers + ) { // Construct the verifier LUTs from the provided `batchSizes` info. if (batchSizes.length == 0) { @@ -221,6 +259,7 @@ contract WorldIDIdentityManagerTest is WorldIDTest { revert("batch size greater than 1000."); } insertVerifiers = new VerifierLookupTable(); + deletionVerifiers = new VerifierLookupTable(); updateVerifiers = new VerifierLookupTable(); for (uint256 i = 0; i < batchSizes.length; ++i) { uint256 batchSize = batchSizes[i]; @@ -230,6 +269,7 @@ contract WorldIDIdentityManagerTest is WorldIDTest { ITreeVerifier batchVerifier = new SimpleVerifier(batchSize); insertVerifiers.addVerifier(batchSize, batchVerifier); + deletionVerifiers.addVerifier(batchSize, batchVerifier); updateVerifiers.addVerifier(batchSize, batchVerifier); } } @@ -289,6 +329,21 @@ contract WorldIDIdentityManagerTest is WorldIDTest { actualProof = [uint256(prf[0]), prf[1], prf[2], prf[3], prf[4], prf[5], prf[6], prf[7]]; } + /// @notice Prepares a verifier test case. + /// @dev This is useful to make property-based fuzz testing work better by requiring less + /// constraints on the generated input. + /// + /// @param prf The generated proof terms to convert. + /// + /// @return actualProof The conversion of `prf` to the proper type. + function prepareDeleteIdentitiesTestCase(uint128[8] memory prf) + public + pure + returns (uint256[8] memory actualProof) + { + actualProof = [uint256(prf[0]), prf[1], prf[2], prf[3], prf[4], prf[5], prf[6], prf[7]]; + } + /// @notice Prepares a verifier test case. /// @dev This is useful to make property-based fuzz testing work better by requiring less /// constraints on the generated input. diff --git a/src/test/identity-manager/WorldIDIdentityManagerUninit.t.sol b/src/test/identity-manager/WorldIDIdentityManagerUninit.t.sol index 541365f..594f70c 100644 --- a/src/test/identity-manager/WorldIDIdentityManagerUninit.t.sol +++ b/src/test/identity-manager/WorldIDIdentityManagerUninit.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; import {WorldIDIdentityManagerTest} from "./WorldIDIdentityManagerTest.sol"; @@ -8,10 +8,11 @@ import {ITreeVerifier} from "../../interfaces/ITreeVerifier.sol"; import {CheckInitialized} from "../../utils/CheckInitialized.sol"; import {SemaphoreVerifier} from "semaphore/base/SemaphoreVerifier.sol"; import {TypeConverter as TC} from "../utils/TypeConverter.sol"; -import {Verifier as TreeVerifier} from "../mock/TreeVerifier.sol"; +import {Verifier as TreeVerifier} from "../mock/InsertionTreeVerifier.sol"; import {VerifierLookupTable} from "../../data/VerifierLookupTable.sol"; -import {WorldIDIdentityManagerImplV1 as ManagerImpl} from "../../WorldIDIdentityManagerImplV1.sol"; +import {WorldIDIdentityManagerImplV2 as ManagerImpl} from "../../WorldIDIdentityManagerImplV2.sol"; +import {WorldIDIdentityManagerImplV1 as ManagerImplV1} from "../../WorldIDIdentityManagerImplV1.sol"; /// @title World ID Identity Manager Uninit Tests /// @notice Contains tests for the WorldID identity manager. @@ -25,8 +26,30 @@ contract WorldIDIdentityManagerUninit is WorldIDIdentityManagerTest { // Setup makeUninitIdentityManager(); bytes memory callData = abi.encodeCall( - ManagerImpl.registerIdentities, - (proof, preRoot, startIndex, identityCommitments, postRoot) + ManagerImplV1.registerIdentities, + (insertionProof, insertionPreRoot, startIndex, identityCommitments, insertionPostRoot) + ); + bytes memory expectedError = + abi.encodeWithSelector(CheckInitialized.ImplementationNotInitialized.selector); + + // Test + assertCallFailsOn(identityManagerAddress, callData, expectedError); + } + + /// @notice Checks that it is impossible to call `deleteIdentities` while the contract is not + /// initialised. + function testShouldNotCallDeleteIdentitiesWhileUninit() public { + // Setup + makeUninitIdentityManager(); + bytes memory callData = abi.encodeCall( + ManagerImpl.deleteIdentities, + ( + deletionProof, + deletionBatchSize, + packedDeletionIndices, + deletionPreRoot, + deletionPostRoot + ) ); bytes memory expectedError = abi.encodeWithSelector(CheckInitialized.ImplementationNotInitialized.selector); @@ -50,8 +73,8 @@ contract WorldIDIdentityManagerUninit is WorldIDIdentityManagerTest { uint256[8] memory actualProof ) = prepareUpdateIdentitiesTestCase(identities, prf); bytes memory callData = abi.encodeCall( - ManagerImpl.updateIdentities, - (actualProof, initialRoot, leafIndices, oldIdents, newIdents, postRoot) + ManagerImplV1.updateIdentities, + (actualProof, initialRoot, leafIndices, oldIdents, newIdents, insertionPostRoot) ); bytes memory expectedError = abi.encodeWithSelector(CheckInitialized.ImplementationNotInitialized.selector); @@ -66,8 +89,8 @@ contract WorldIDIdentityManagerUninit is WorldIDIdentityManagerTest { // Setup makeUninitIdentityManager(); bytes memory callData = abi.encodeCall( - ManagerImpl.calculateIdentityRegistrationInputHash, - (startIndex, preRoot, postRoot, identityCommitments) + ManagerImplV1.calculateIdentityRegistrationInputHash, + (startIndex, insertionPreRoot, insertionPostRoot, identityCommitments) ); bytes memory expectedError = abi.encodeWithSelector(CheckInitialized.ImplementationNotInitialized.selector); @@ -81,7 +104,7 @@ contract WorldIDIdentityManagerUninit is WorldIDIdentityManagerTest { function testShouldNotCallLatestRootWhileUninit() public { // Setup makeUninitIdentityManager(); - bytes memory callData = abi.encodeCall(ManagerImpl.latestRoot, ()); + bytes memory callData = abi.encodeCall(ManagerImplV1.latestRoot, ()); bytes memory expectedError = abi.encodeWithSelector(CheckInitialized.ImplementationNotInitialized.selector); @@ -94,7 +117,7 @@ contract WorldIDIdentityManagerUninit is WorldIDIdentityManagerTest { function testShouldNotCallQueryRootWhileUninit() public { // Setup makeUninitIdentityManager(); - bytes memory callData = abi.encodeCall(ManagerImpl.queryRoot, (preRoot)); + bytes memory callData = abi.encodeCall(ManagerImplV1.queryRoot, (insertionPreRoot)); bytes memory expectedError = abi.encodeWithSelector(CheckInitialized.ImplementationNotInitialized.selector); @@ -107,7 +130,7 @@ contract WorldIDIdentityManagerUninit is WorldIDIdentityManagerTest { function testShouldNotCallRequireValidRootWhileUninit() public { // Setup makeUninitIdentityManager(); - bytes memory callData = abi.encodeCall(ManagerImpl.requireValidRoot, (preRoot)); + bytes memory callData = abi.encodeCall(ManagerImplV1.requireValidRoot, (insertionPreRoot)); bytes memory expectedError = abi.encodeWithSelector(CheckInitialized.ImplementationNotInitialized.selector); @@ -117,11 +140,11 @@ contract WorldIDIdentityManagerUninit is WorldIDIdentityManagerTest { /// @notice Checks that it is impossible to call `getRegisterIdentitiesVerifierLookupTableAddress` /// while the contract is not initialized. - function testShouldNotCallgetRegisterIdentitiesVerifierLookupTableAddressWhileUninit() public { + function testShouldNotCallGetRegisterIdentitiesVerifierLookupTableAddressWhileUninit() public { // Setup makeUninitIdentityManager(); bytes memory callData = - abi.encodeCall(ManagerImpl.getRegisterIdentitiesVerifierLookupTableAddress, ()); + abi.encodeCall(ManagerImplV1.getRegisterIdentitiesVerifierLookupTableAddress, ()); bytes memory expectedError = abi.encodeWithSelector(CheckInitialized.ImplementationNotInitialized.selector); @@ -134,9 +157,39 @@ contract WorldIDIdentityManagerUninit is WorldIDIdentityManagerTest { function testShouldNotCallSetRegisterIdentitiesVerifierLookupTableWhileUninit() public { // Setup makeUninitIdentityManager(); - (VerifierLookupTable insertVerifiers,) = makeVerifierLookupTables(TC.makeDynArray([75])); + (VerifierLookupTable insertVerifiers,,) = makeVerifierLookupTables(TC.makeDynArray([75])); + bytes memory callData = abi.encodeCall( + ManagerImplV1.setRegisterIdentitiesVerifierLookupTable, (insertVerifiers) + ); + bytes memory expectedError = + abi.encodeWithSelector(CheckInitialized.ImplementationNotInitialized.selector); + + // Test + assertCallFailsOn(identityManagerAddress, callData, expectedError); + } + + /// @notice Checks that it is impossible to call `getDeleteIdentitiesVerifierLookupTableAddress` + /// while the contract is not initialized. + function testShouldNotCallGetDeleteIdentitiesVerifierLookupTableAddressWhileUninit() public { + // Setup + makeUninitIdentityManager(); + bytes memory callData = + abi.encodeCall(ManagerImpl.getDeleteIdentitiesVerifierLookupTableAddress, ()); + bytes memory expectedError = + abi.encodeWithSelector(CheckInitialized.ImplementationNotInitialized.selector); + + // Test + assertCallFailsOn(identityManagerAddress, callData, expectedError); + } + + /// @notice Checks that it is impossible to call `setDeleteIdentitiesVerifierLookupTable` + /// while the contract is not initialized. + function testShouldNotCallSetDeleteIdentitiesVerifierLookupTableWhileUninit() public { + // Setup + makeUninitIdentityManager(); + (, VerifierLookupTable deletionVerifiers,) = makeVerifierLookupTables(TC.makeDynArray([75])); bytes memory callData = - abi.encodeCall(ManagerImpl.setRegisterIdentitiesVerifierLookupTable, (insertVerifiers)); + abi.encodeCall(ManagerImpl.setDeleteIdentitiesVerifierLookupTable, (deletionVerifiers)); bytes memory expectedError = abi.encodeWithSelector(CheckInitialized.ImplementationNotInitialized.selector); @@ -150,7 +203,7 @@ contract WorldIDIdentityManagerUninit is WorldIDIdentityManagerTest { // Setup makeUninitIdentityManager(); bytes memory callData = - abi.encodeCall(ManagerImpl.getIdentityUpdateVerifierLookupTableAddress, ()); + abi.encodeCall(ManagerImplV1.getIdentityUpdateVerifierLookupTableAddress, ()); bytes memory expectedError = abi.encodeWithSelector(CheckInitialized.ImplementationNotInitialized.selector); @@ -163,9 +216,9 @@ contract WorldIDIdentityManagerUninit is WorldIDIdentityManagerTest { function testShouldNotCallSetIdentityUpdateVerifierLookupTableWhileUninit() public { // Setup makeUninitIdentityManager(); - (, VerifierLookupTable updateVerifiers) = makeVerifierLookupTables(TC.makeDynArray([75])); + (,, VerifierLookupTable updateVerifiers) = makeVerifierLookupTables(TC.makeDynArray([75])); bytes memory callData = - abi.encodeCall(ManagerImpl.setIdentityUpdateVerifierLookupTable, (updateVerifiers)); + abi.encodeCall(ManagerImplV1.setIdentityUpdateVerifierLookupTable, (updateVerifiers)); bytes memory expectedError = abi.encodeWithSelector(CheckInitialized.ImplementationNotInitialized.selector); @@ -178,7 +231,7 @@ contract WorldIDIdentityManagerUninit is WorldIDIdentityManagerTest { function testShouldNotCallGetSemaphoreVerifierAddressWhileUninit() public { // Setup makeUninitIdentityManager(); - bytes memory callData = abi.encodeCall(ManagerImpl.getSemaphoreVerifierAddress, ()); + bytes memory callData = abi.encodeCall(ManagerImplV1.getSemaphoreVerifierAddress, ()); bytes memory expectedError = abi.encodeWithSelector(CheckInitialized.ImplementationNotInitialized.selector); @@ -192,7 +245,7 @@ contract WorldIDIdentityManagerUninit is WorldIDIdentityManagerTest { // Setup makeUninitIdentityManager(); SemaphoreVerifier newVerifier = new SemaphoreVerifier(); - bytes memory callData = abi.encodeCall(ManagerImpl.setSemaphoreVerifier, (newVerifier)); + bytes memory callData = abi.encodeCall(ManagerImplV1.setSemaphoreVerifier, (newVerifier)); bytes memory expectedError = abi.encodeWithSelector(CheckInitialized.ImplementationNotInitialized.selector); @@ -205,7 +258,7 @@ contract WorldIDIdentityManagerUninit is WorldIDIdentityManagerTest { function testShouldNotCallGetRootHistoryExpiryWhileUninit() public { // Setup makeUninitIdentityManager(); - bytes memory callData = abi.encodeCall(ManagerImpl.getRootHistoryExpiry, ()); + bytes memory callData = abi.encodeCall(ManagerImplV1.getRootHistoryExpiry, ()); bytes memory expectedError = abi.encodeWithSelector(CheckInitialized.ImplementationNotInitialized.selector); @@ -218,7 +271,7 @@ contract WorldIDIdentityManagerUninit is WorldIDIdentityManagerTest { function testShouldNotCallSetRootHistoryExpiryWhileUninit() public { // Setup makeUninitIdentityManager(); - bytes memory callData = abi.encodeCall(ManagerImpl.setRootHistoryExpiry, (2 hours)); + bytes memory callData = abi.encodeCall(ManagerImplV1.setRootHistoryExpiry, (2 hours)); bytes memory expectedError = abi.encodeWithSelector(CheckInitialized.ImplementationNotInitialized.selector); @@ -231,7 +284,7 @@ contract WorldIDIdentityManagerUninit is WorldIDIdentityManagerTest { function testShouldNotCallIdentityOperatorWhileUninit() public { // Setup makeUninitIdentityManager(); - bytes memory callData = abi.encodeCall(ManagerImpl.identityOperator, ()); + bytes memory callData = abi.encodeCall(ManagerImplV1.identityOperator, ()); bytes memory expectedError = abi.encodeWithSelector(CheckInitialized.ImplementationNotInitialized.selector); @@ -244,7 +297,7 @@ contract WorldIDIdentityManagerUninit is WorldIDIdentityManagerTest { function testShouldNotCallSetIdentityOperatorWhileUninit(address newOperator) public { // Setup makeUninitIdentityManager(); - bytes memory callData = abi.encodeCall(ManagerImpl.setIdentityOperator, (newOperator)); + bytes memory callData = abi.encodeCall(ManagerImplV1.setIdentityOperator, (newOperator)); bytes memory expectedError = abi.encodeWithSelector(CheckInitialized.ImplementationNotInitialized.selector); diff --git a/src/test/identity-manager/WorldIDIdentityManagerUpgrade.t.sol b/src/test/identity-manager/WorldIDIdentityManagerUpgrade.t.sol index d3a1b21..012c4d4 100644 --- a/src/test/identity-manager/WorldIDIdentityManagerUpgrade.t.sol +++ b/src/test/identity-manager/WorldIDIdentityManagerUpgrade.t.sol @@ -1,12 +1,13 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; import {WorldIDIdentityManagerTest} from "./WorldIDIdentityManagerTest.sol"; import {UUPSUpgradeable} from "contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import {WorldIDIdentityManagerImplMock} from "../mock/WorldIDIdentityManagerImplMock.sol"; -import {WorldIDIdentityManagerImplV1 as ManagerImpl} from "../../WorldIDIdentityManagerImplV1.sol"; +import {WorldIDIdentityManagerImplV2 as ManagerImpl} from "../../WorldIDIdentityManagerImplV2.sol"; +import {WorldIDIdentityManagerImplV1 as ManagerImplV1} from "../../WorldIDIdentityManagerImplV1.sol"; /// @title World ID Identity Manager Upgrade Test /// @notice Contains tests for the WorldID identity manager. @@ -61,15 +62,13 @@ contract WorldIDIdentityManagerUpdate is WorldIDIdentityManagerTest { WorldIDIdentityManagerImplMock mockUpgrade = new WorldIDIdentityManagerImplMock(); address mockUpgradeAddress = address(mockUpgrade); bytes memory initCall = abi.encodeCall( - ManagerImpl.initialize, + ManagerImplV1.initialize, ( treeDepth, initialRoot, defaultInsertVerifiers, defaultUpdateVerifiers, - semaphoreVerifier, - isStateBridgeEnabled, - stateBridge + semaphoreVerifier ) ); vm.expectRevert("Function must be called through delegatecall"); diff --git a/src/test/mock/DeletionTreeVerifier.sol b/src/test/mock/DeletionTreeVerifier.sol new file mode 100644 index 0000000..fa8c091 --- /dev/null +++ b/src/test/mock/DeletionTreeVerifier.sol @@ -0,0 +1,335 @@ +// SPDX-License-Identifier: AML +// +// Copyright 2017 Christian Reitwiessner +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +// 2019 OKIMS + +pragma solidity ^0.8.21; + +// Worldcoin Modification Begin +import {ITreeVerifier} from "../../interfaces/ITreeVerifier.sol"; +// Worldcoin Modification End + +library Pairing { + uint256 constant PRIME_Q = + 21888242871839275222246405745257275088696311157297823662689037894645226208583; + + struct G1Point { + uint256 X; + uint256 Y; + } + + // Encoding of field elements is: X[0] * z + X[1] + struct G2Point { + uint256[2] X; + uint256[2] Y; + } + + /* + * @return The negation of p, i.e. p.plus(p.negate()) should be zero. + */ + function negate(G1Point memory p) internal pure returns (G1Point memory) { + // The prime q in the base field F_q for G1 + if (p.X == 0 && p.Y == 0) { + return G1Point(0, 0); + } else { + return G1Point(p.X, PRIME_Q - (p.Y % PRIME_Q)); + } + } + + /* + * @return The sum of two points of G1 + */ + function plus(G1Point memory p1, G1Point memory p2) internal view returns (G1Point memory r) { + uint256[4] memory input; + input[0] = p1.X; + input[1] = p1.Y; + input[2] = p2.X; + input[3] = p2.Y; + bool success; + + // solium-disable-next-line security/no-inline-assembly + assembly { + success := staticcall(sub(gas(), 2000), 6, input, 0xc0, r, 0x60) + // Use "invalid" to make gas estimation work + switch success + case 0 { invalid() } + } + + require(success, "pairing-add-failed"); + } + + /* + * Same as plus but accepts raw input instead of struct + * @return The sum of two points of G1, one is represented as array + */ + function plus_raw(uint256[4] memory input, G1Point memory r) internal view { + bool success; + + // solium-disable-next-line security/no-inline-assembly + assembly { + success := staticcall(sub(gas(), 2000), 6, input, 0xc0, r, 0x60) + // Use "invalid" to make gas estimation work + switch success + case 0 { invalid() } + } + + require(success, "pairing-add-failed"); + } + + /* + * @return The product of a point on G1 and a scalar, i.e. + * p == p.scalar_mul(1) and p.plus(p) == p.scalar_mul(2) for all + * points p. + */ + function scalar_mul(G1Point memory p, uint256 s) internal view returns (G1Point memory r) { + uint256[3] memory input; + input[0] = p.X; + input[1] = p.Y; + input[2] = s; + bool success; + // solium-disable-next-line security/no-inline-assembly + assembly { + success := staticcall(sub(gas(), 2000), 7, input, 0x80, r, 0x60) + // Use "invalid" to make gas estimation work + switch success + case 0 { invalid() } + } + require(success, "pairing-mul-failed"); + } + + /* + * Same as scalar_mul but accepts raw input instead of struct, + * Which avoid extra allocation. provided input can be allocated outside and re-used multiple times + */ + function scalar_mul_raw(uint256[3] memory input, G1Point memory r) internal view { + bool success; + + // solium-disable-next-line security/no-inline-assembly + assembly { + success := staticcall(sub(gas(), 2000), 7, input, 0x80, r, 0x60) + // Use "invalid" to make gas estimation work + switch success + case 0 { invalid() } + } + require(success, "pairing-mul-failed"); + } + + /* @return The result of computing the pairing check + * e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1 + * For example, + * pairing([P1(), P1().negate()], [P2(), P2()]) should return true. + */ + function pairing( + G1Point memory a1, + G2Point memory a2, + G1Point memory b1, + G2Point memory b2, + G1Point memory c1, + G2Point memory c2, + G1Point memory d1, + G2Point memory d2 + ) internal view returns (bool) { + G1Point[4] memory p1 = [a1, b1, c1, d1]; + G2Point[4] memory p2 = [a2, b2, c2, d2]; + uint256 inputSize = 24; + uint256[] memory input = new uint256[](inputSize); + + for (uint256 i = 0; i < 4; i++) { + uint256 j = i * 6; + input[j + 0] = p1[i].X; + input[j + 1] = p1[i].Y; + input[j + 2] = p2[i].X[0]; + input[j + 3] = p2[i].X[1]; + input[j + 4] = p2[i].Y[0]; + input[j + 5] = p2[i].Y[1]; + } + + uint256[1] memory out; + bool success; + + // solium-disable-next-line security/no-inline-assembly + assembly { + success := + staticcall(sub(gas(), 2000), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20) + // Use "invalid" to make gas estimation work + switch success + case 0 { invalid() } + } + + require(success, "pairing-opcode-failed"); + + return out[0] != 0; + } +} + +// Worldcoin Modification Begin +contract Verifier is ITreeVerifier { + // Worldcoin Modification End + + using Pairing for *; + + uint256 constant SNARK_SCALAR_FIELD = + 21888242871839275222246405745257275088548364400416034343698204186575808495617; + uint256 constant PRIME_Q = + 21888242871839275222246405745257275088696311157297823662689037894645226208583; + + struct VerifyingKey { + Pairing.G1Point alfa1; + Pairing.G2Point beta2; + Pairing.G2Point gamma2; + Pairing.G2Point delta2; + } + // []G1Point IC (K in gnark) appears directly in verifyProof + + struct Proof { + Pairing.G1Point A; + Pairing.G2Point B; + Pairing.G1Point C; + } + + function verifyingKey() internal pure returns (VerifyingKey memory vk) { + vk.alfa1 = Pairing.G1Point( + uint256(2184882805247985271871938329809546770618201109555895111244149305081310910327), + uint256(17955645170177143982732722322466892227921193317441707980265350454293781592165) + ); + vk.beta2 = Pairing.G2Point( + [ + uint256(19855677946098676054074545153598313691709048620187466153071818854499304267600), + uint256(18657555492226973921129443722983243120369700949936204046844056195692112150201) + ], + [ + uint256(19899331546556644910451623385834342033151091485086175875975296474884352013698), + uint256(10995968579664432264929184488637152820315692844617253571090316769579558136929) + ] + ); + vk.gamma2 = Pairing.G2Point( + [ + uint256(12712468093034557682736638829278859597903355905402452222082044636405719748293), + uint256(1917182124610107899409672606570417748821880411015039808590532928588300807548) + ], + [ + uint256(1438003574655150752592998959824941589353720796470233245442162954585047876922), + uint256(2676092741872789850724416751461362857645326402402773002025647629553435066621) + ] + ); + vk.delta2 = Pairing.G2Point( + [ + uint256(2056132247358553984450248077313152314306136758650729761826455114846826227966), + uint256(15515789660346665091954320736611020446193505887038665208802484819014444710907) + ], + [ + uint256(9291393946046405373282932088596154501511046549222190749151984429927112121942), + uint256(584737725353760575079302581443060760656177906229108303797581436421293000715) + ] + ); + } + + // accumulate scalarMul(mul_input) into q + // that is computes sets q = (mul_input[0:2] * mul_input[3]) + q + function accumulate( + uint256[3] memory mul_input, + Pairing.G1Point memory p, + uint256[4] memory buffer, + Pairing.G1Point memory q + ) internal view { + // computes p = mul_input[0:2] * mul_input[3] + Pairing.scalar_mul_raw(mul_input, p); + + // point addition inputs + buffer[0] = q.X; + buffer[1] = q.Y; + buffer[2] = p.X; + buffer[3] = p.Y; + + // q = p + q + Pairing.plus_raw(buffer, q); + } + + /* + * @returns Whether the proof is valid given the hardcoded verifying key + * above and the public inputs + */ + function verifyProof( + uint256[2] memory a, + uint256[2][2] memory b, + uint256[2] memory c, + uint256[1] calldata input + ) public view returns (bool r) { + Proof memory proof; + proof.A = Pairing.G1Point(a[0], a[1]); + proof.B = Pairing.G2Point([b[0][0], b[0][1]], [b[1][0], b[1][1]]); + proof.C = Pairing.G1Point(c[0], c[1]); + + // Make sure that proof.A, B, and C are each less than the prime q + require(proof.A.X < PRIME_Q, "verifier-aX-gte-prime-q"); + require(proof.A.Y < PRIME_Q, "verifier-aY-gte-prime-q"); + + require(proof.B.X[0] < PRIME_Q, "verifier-bX0-gte-prime-q"); + require(proof.B.Y[0] < PRIME_Q, "verifier-bY0-gte-prime-q"); + + require(proof.B.X[1] < PRIME_Q, "verifier-bX1-gte-prime-q"); + require(proof.B.Y[1] < PRIME_Q, "verifier-bY1-gte-prime-q"); + + require(proof.C.X < PRIME_Q, "verifier-cX-gte-prime-q"); + require(proof.C.Y < PRIME_Q, "verifier-cY-gte-prime-q"); + + // Make sure that every input is less than the snark scalar field + for (uint256 i = 0; i < input.length; i++) { + require(input[i] < SNARK_SCALAR_FIELD, "verifier-gte-snark-scalar-field"); + } + + VerifyingKey memory vk = verifyingKey(); + + // Compute the linear combination vk_x + Pairing.G1Point memory vk_x = Pairing.G1Point(0, 0); + + // Buffer reused for addition p1 + p2 to avoid memory allocations + // [0:2] -> p1.X, p1.Y ; [2:4] -> p2.X, p2.Y + uint256[4] memory add_input; + + // Buffer reused for multiplication p1 * s + // [0:2] -> p1.X, p1.Y ; [3] -> s + uint256[3] memory mul_input; + + // temporary point to avoid extra allocations in accumulate + Pairing.G1Point memory q = Pairing.G1Point(0, 0); + + vk_x.X = + uint256(3315258076715751967157201087019286537737159716424336725966632081613888671155); // vk.K[0].X + vk_x.Y = + uint256(2535036613140843436293083215368108378007620479265526787320589574026047767984); // vk.K[0].Y + mul_input[0] = + uint256(9542466376132327359478346341646067084942113202844591123106912654453266858527); // vk.K[1].X + mul_input[1] = + uint256(7493970464425500800073970400008818080866272618230969924226053545012177030554); // vk.K[1].Y + mul_input[2] = input[0]; + accumulate(mul_input, q, add_input, vk_x); // vk_x += vk.K[1] * input[0] + + return Pairing.pairing( + Pairing.negate(proof.A), + proof.B, + vk.alfa1, + vk.beta2, + vk_x, + vk.gamma2, + proof.C, + vk.delta2 + ); + } +} diff --git a/src/test/mock/TreeVerifier.sol b/src/test/mock/InsertionTreeVerifier.sol similarity index 99% rename from src/test/mock/TreeVerifier.sol rename to src/test/mock/InsertionTreeVerifier.sol index ed364d0..9d38b14 100644 --- a/src/test/mock/TreeVerifier.sol +++ b/src/test/mock/InsertionTreeVerifier.sol @@ -19,7 +19,7 @@ // 2019 OKIMS -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; // Worldcoin Modification Begin import {ITreeVerifier} from "../../interfaces/ITreeVerifier.sol"; diff --git a/src/test/mock/SequencerVerifier.sol b/src/test/mock/SequencerVerifier.sol index 07f9f29..aa8b30e 100644 --- a/src/test/mock/SequencerVerifier.sol +++ b/src/test/mock/SequencerVerifier.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; import {ITreeVerifier} from "../../interfaces/ITreeVerifier.sol"; diff --git a/src/test/mock/SimpleSemaphoreVerifier.sol b/src/test/mock/SimpleSemaphoreVerifier.sol index e5fa55a..17b9229 100644 --- a/src/test/mock/SimpleSemaphoreVerifier.sol +++ b/src/test/mock/SimpleSemaphoreVerifier.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; import {ISemaphoreVerifier} from "semaphore/interfaces/ISemaphoreVerifier.sol"; diff --git a/src/test/mock/SimpleStateBridge.sol b/src/test/mock/SimpleStateBridge.sol index 42da916..f1485bc 100644 --- a/src/test/mock/SimpleStateBridge.sol +++ b/src/test/mock/SimpleStateBridge.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; import {IBridge} from "../../interfaces/IBridge.sol"; import {IWorldID} from "../../interfaces/IWorldID.sol"; diff --git a/src/test/mock/SimpleVerifier.sol b/src/test/mock/SimpleVerifier.sol index e6ed85a..e639688 100644 --- a/src/test/mock/SimpleVerifier.sol +++ b/src/test/mock/SimpleVerifier.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; import {ITreeVerifier} from "../../interfaces/ITreeVerifier.sol"; diff --git a/src/test/mock/WorldIDIdentityManagerImplMock.sol b/src/test/mock/WorldIDIdentityManagerImplMock.sol index 6212a6c..f4a2cd0 100644 --- a/src/test/mock/WorldIDIdentityManagerImplMock.sol +++ b/src/test/mock/WorldIDIdentityManagerImplMock.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; import {WorldIDIdentityManagerImplV1} from "../../WorldIDIdentityManagerImplV1.sol"; @@ -13,7 +13,7 @@ contract WorldIDIdentityManagerImplMock is WorldIDIdentityManagerImplV1 { } /// @notice Used to initialize the new things in the upgraded contract. - function initialize(uint32 data) public virtual reinitializer(2) { + function initialize(uint32 data) public virtual reinitializer(3) { _someMoreData = data; } diff --git a/src/test/mock/WorldIDRouterImplMock.sol b/src/test/mock/WorldIDRouterImplMock.sol index 279622d..2916cf2 100644 --- a/src/test/mock/WorldIDRouterImplMock.sol +++ b/src/test/mock/WorldIDRouterImplMock.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; import {WorldIDRouterImplV1} from "../../WorldIDRouterImplV1.sol"; diff --git a/src/test/router/WorldIDRouterConstruction.t.sol b/src/test/router/WorldIDRouterConstruction.t.sol index ee86c06..392ee2b 100644 --- a/src/test/router/WorldIDRouterConstruction.t.sol +++ b/src/test/router/WorldIDRouterConstruction.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; import {WorldIDRouterTest} from "./WorldIDRouterTest.sol"; diff --git a/src/test/router/WorldIDRouterDataQuery.t.sol b/src/test/router/WorldIDRouterDataQuery.t.sol index e86cc4a..16a62eb 100644 --- a/src/test/router/WorldIDRouterDataQuery.t.sol +++ b/src/test/router/WorldIDRouterDataQuery.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; import {WorldIDRouterTest} from "./WorldIDRouterTest.sol"; diff --git a/src/test/router/WorldIDRouterOwnershipManagement.t.sol b/src/test/router/WorldIDRouterOwnershipManagement.t.sol index 42da343..f8e12ed 100644 --- a/src/test/router/WorldIDRouterOwnershipManagement.t.sol +++ b/src/test/router/WorldIDRouterOwnershipManagement.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; import {WorldIDRouterTest} from "./WorldIDRouterTest.sol"; diff --git a/src/test/router/WorldIDRouterRouting.t.sol b/src/test/router/WorldIDRouterRouting.t.sol index 8487ff8..e4156c6 100644 --- a/src/test/router/WorldIDRouterRouting.t.sol +++ b/src/test/router/WorldIDRouterRouting.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; import {WorldIDRouterTest} from "./WorldIDRouterTest.sol"; diff --git a/src/test/router/WorldIDRouterStateBridge.t.sol b/src/test/router/WorldIDRouterStateBridge.t.sol index b92434a..1c7621f 100644 --- a/src/test/router/WorldIDRouterStateBridge.t.sol +++ b/src/test/router/WorldIDRouterStateBridge.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; import {WorldIDRouterTest} from "./WorldIDRouterTest.sol"; diff --git a/src/test/router/WorldIDRouterTest.sol b/src/test/router/WorldIDRouterTest.sol index 8c83b76..4eb5db3 100644 --- a/src/test/router/WorldIDRouterTest.sol +++ b/src/test/router/WorldIDRouterTest.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; import {WorldIDTest} from "../WorldIDTest.sol"; diff --git a/src/test/router/WorldIDRouterUninit.t.sol b/src/test/router/WorldIDRouterUninit.t.sol index f3e242c..e8b17bb 100644 --- a/src/test/router/WorldIDRouterUninit.t.sol +++ b/src/test/router/WorldIDRouterUninit.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; import {WorldIDRouterTest} from "./WorldIDRouterTest.sol"; diff --git a/src/test/router/WorldIDRouterUpgrade.t.sol b/src/test/router/WorldIDRouterUpgrade.t.sol index 9cf2260..1215564 100644 --- a/src/test/router/WorldIDRouterUpgrade.t.sol +++ b/src/test/router/WorldIDRouterUpgrade.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; import {WorldIDRouterTest} from "./WorldIDRouterTest.sol"; diff --git a/src/test/utils/TypeConverter.sol b/src/test/utils/TypeConverter.sol index 9958e9f..0c5166e 100644 --- a/src/test/utils/TypeConverter.sol +++ b/src/test/utils/TypeConverter.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; library TypeConverter { function toString(address x) public pure returns (string memory) { diff --git a/src/test/verifier-lookup-table/VerifierLookupTableConstruction.t.sol b/src/test/verifier-lookup-table/VerifierLookupTableConstruction.t.sol index 1a4237a..3eec627 100644 --- a/src/test/verifier-lookup-table/VerifierLookupTableConstruction.t.sol +++ b/src/test/verifier-lookup-table/VerifierLookupTableConstruction.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; import {VerifierLookupTableTest} from "./VerifierLookupTableTest.sol"; diff --git a/src/test/verifier-lookup-table/VerifierLookupTableOwnershipManagement.t.sol b/src/test/verifier-lookup-table/VerifierLookupTableOwnershipManagement.t.sol index 2aa9df6..5437ac8 100644 --- a/src/test/verifier-lookup-table/VerifierLookupTableOwnershipManagement.t.sol +++ b/src/test/verifier-lookup-table/VerifierLookupTableOwnershipManagement.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; import {VerifierLookupTableTest} from "./VerifierLookupTableTest.sol"; diff --git a/src/test/verifier-lookup-table/VerifierLookupTableQuery.t.sol.sol b/src/test/verifier-lookup-table/VerifierLookupTableQuery.t.sol.sol index 87271ab..63e327d 100644 --- a/src/test/verifier-lookup-table/VerifierLookupTableQuery.t.sol.sol +++ b/src/test/verifier-lookup-table/VerifierLookupTableQuery.t.sol.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; import {VerifierLookupTableTest} from "./VerifierLookupTableTest.sol"; diff --git a/src/test/verifier-lookup-table/VerifierLookupTableTest.sol b/src/test/verifier-lookup-table/VerifierLookupTableTest.sol index 4ec3092..436f1a0 100644 --- a/src/test/verifier-lookup-table/VerifierLookupTableTest.sol +++ b/src/test/verifier-lookup-table/VerifierLookupTableTest.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; import {WorldIDTest} from "../WorldIDTest.sol"; diff --git a/src/utils/CheckInitialized.sol b/src/utils/CheckInitialized.sol index adff4e2..1e4e745 100644 --- a/src/utils/CheckInitialized.sol +++ b/src/utils/CheckInitialized.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; import {Initializable} from "contracts-upgradeable/proxy/utils/Initializable.sol"; diff --git a/src/utils/SemaphoreTreeDepthValidator.sol b/src/utils/SemaphoreTreeDepthValidator.sol index 6317c34..6dd8add 100644 --- a/src/utils/SemaphoreTreeDepthValidator.sol +++ b/src/utils/SemaphoreTreeDepthValidator.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; /// @title Semaphore tree depth validator /// @author Worldcoin diff --git a/src/utils/UnimplementedTreeVerifier.sol b/src/utils/UnimplementedTreeVerifier.sol index ef35389..706f935 100644 --- a/src/utils/UnimplementedTreeVerifier.sol +++ b/src/utils/UnimplementedTreeVerifier.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; import {ITreeVerifier} from "../interfaces/ITreeVerifier.sol";