From 8d70399373d757b1a78e85db5f201a7cc942b2a8 Mon Sep 17 00:00:00 2001 From: Wojciech Zmuda Date: Mon, 26 Aug 2024 22:37:10 +0200 Subject: [PATCH 1/8] editorconfig: increase indentation to 4 tabs Every contract seems to use 4 anyway. Signed-off-by: Wojciech Zmuda --- .editorconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.editorconfig b/.editorconfig index af0b7e2..e6e8824 100644 --- a/.editorconfig +++ b/.editorconfig @@ -7,7 +7,7 @@ charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true indent_style = space -indent_size = 2 +indent_size = 4 [*.{yml,yaml}] indent_size = 2 From 64196dcb673c3d81b6d0ce2d7c0bca8a791f212c Mon Sep 17 00:00:00 2001 From: Wojciech Zmuda Date: Mon, 26 Aug 2024 22:34:31 +0200 Subject: [PATCH 2/8] Set SOLC to 0.8.24 and EVM version to Cancun This is groundwork for EIP-4844 support. Cancun introduces the blobhash opcode and SOLC 0.8.24 is the first revision to support this opcode in Solidity. --- foundry.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/foundry.toml b/foundry.toml index 019b255..d598c09 100644 --- a/foundry.toml +++ b/foundry.toml @@ -1,8 +1,8 @@ # === Default Profile ========================================================= [profile.default] -solc = "0.8.21" -evm_version = "paris" +solc = "0.8.24" +evm_version = "cancun" src = 'src' out = 'out' libs = ['lib'] From 1649b556d637883c67ab23a5e82e4270a25dd119 Mon Sep 17 00:00:00 2001 From: Wojciech Zmuda Date: Tue, 10 Sep 2024 22:51:06 +0200 Subject: [PATCH 3/8] Run forge fmt Signed-off-by: Wojciech Zmuda --- lib/forge-std | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/forge-std b/lib/forge-std index 06c5a8f..4d63c97 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit 06c5a8f760f7d2392697cb092eda80c864e4fc06 +Subproject commit 4d63c978718517fa02d4e330fbe7372dbb06c2f1 From a476152b16fe49cd888af8f24506ed509074cd28 Mon Sep 17 00:00:00 2001 From: Wojciech Zmuda Date: Mon, 26 Aug 2024 23:31:55 +0200 Subject: [PATCH 4/8] test: SimpleVerifier: remove unused function It does not seem to be used anywhere. Signed-off-by: Wojciech Zmuda --- src/test/mock/SimpleVerifier.sol | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/test/mock/SimpleVerifier.sol b/src/test/mock/SimpleVerifier.sol index ad4e1d0..c2f2cc1 100644 --- a/src/test/mock/SimpleVerifier.sol +++ b/src/test/mock/SimpleVerifier.sol @@ -29,16 +29,4 @@ library SimpleVerify { function isValidInput(uint256 a) public pure returns (bool) { return a % 2 == 0; } - - function calculateInputHash( - uint32 startIndex, - uint256 preRoot, - uint256 postRoot, - uint256[] calldata identityCommitments - ) public pure returns (bytes32 hash) { - bytes memory bytesToHash = - abi.encodePacked(startIndex, preRoot, postRoot, identityCommitments); - - hash = keccak256(bytesToHash); - } } From 8201cc5e611e7664b62bfffd6d0e2f25e09f15a4 Mon Sep 17 00:00:00 2001 From: Wojciech Zmuda Date: Mon, 26 Aug 2024 22:45:18 +0200 Subject: [PATCH 5/8] lib: forge-std: bump to get vm.blobhashes This is ground work for EIP-4844. Vm.blobhashes allow for mocking value of the next call to the blobhash opcode. This will be useful in EIP-4844 tests to mock this opcode. Update the usage of the one symbol that changed its name. Signed-off-by: Wojciech Zmuda --- src/test/WorldIDTest.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/WorldIDTest.sol b/src/test/WorldIDTest.sol index 0db2fb0..95fc899 100644 --- a/src/test/WorldIDTest.sol +++ b/src/test/WorldIDTest.sol @@ -14,7 +14,7 @@ contract WorldIDTest is Test { /// TEST DATA /// /////////////////////////////////////////////////////////////////////////////// - Vm internal hevm = Vm(HEVM_ADDRESS); + Vm internal hevm = Vm(VM_ADDRESS); address internal nullAddress = address(0x0); address internal thisAddress = address(this); From 00b00d8cb0758778011469ec52dda9eba78afbb4 Mon Sep 17 00:00:00 2001 From: Wojciech Zmuda Date: Mon, 26 Aug 2024 23:15:27 +0200 Subject: [PATCH 6/8] identity-manager: add WorldID Identity Manager for EIP-4844 Implement WorldID Identity Manager in its 3rd implementation. The new implementation contains one main method - registerIdentities. It's the EIP-4844 equivalent of the existing registerIdentities from the V1 implementation. The new registerIdentities calls verifyProof that is also different from its classic counterpart. The new verifyProof comes from the EIP-4844-ready InsertionTreeVerifier164844 contract, that was generated from the semaphore-mtb's InsertionCircuit modified to accommodae EIP-4844 features. Since the new verifyProof function has a different prototype than the previous one, the ITreeVerifier interface has been updated with the new prototype. This change results in lots of boilerplate changes as all contracts implementing this interface must contain a dummy implementation of the new function, even if it does not make practical sense. Add tests for the new feature. Tests are modelled by the existing registration tests. The new contract is basically a copy of the old one, with the main difference of calling the new registerIdentities implementation. Signed-off-by: Wojciech Zmuda --- src/WorldIDIdentityManagerImplV3.sol | 303 +++++++ src/data/VerifierLookupTable4844.sol | 176 ++++ src/interfaces/ITreeVerifier4844.sol | 25 + src/test/InsertionTreeVerifier164844.sol | 757 ++++++++++++++++++ .../WorldIDIdentityManagerCalculation.t.sol | 4 +- .../WorldIDIdentityManagerConstruction.t.sol | 8 +- .../WorldIDIdentityManagerDataQuery.t.sol | 18 +- ...WorldIDIdentityManagerGettersSetters.t.sol | 35 +- ...rldIDIdentityManagerIdentityDeletion.t.sol | 30 +- ...DIdentityManagerIdentityRegistration.t.sol | 49 +- ...ntityManagerIdentityRegistration4844.t.sol | 712 ++++++++++++++++ ...WorldIDIdentityManagerInitialization.t.sol | 26 +- ...IDIdentityManagerOwnershipManagement.t.sol | 4 +- ...IdentityManagerSemaphoreVerification.t.sol | 3 + .../WorldIDIdentityManagerTest.sol | 148 +++- .../WorldIDIdentityManagerUninit.t.sol | 5 +- .../WorldIDIdentityManagerUpgrade.t.sol | 2 +- src/test/mock/SimpleVerifier.sol | 28 + .../mock/WorldIDIdentityManagerImplMock.sol | 3 +- src/utils/UnimplementedTreeVerifier4844.sol | 30 + 20 files changed, 2282 insertions(+), 84 deletions(-) create mode 100644 src/WorldIDIdentityManagerImplV3.sol create mode 100644 src/data/VerifierLookupTable4844.sol create mode 100644 src/interfaces/ITreeVerifier4844.sol create mode 100644 src/test/InsertionTreeVerifier164844.sol create mode 100644 src/test/identity-manager/WorldIDIdentityManagerIdentityRegistration4844.t.sol create mode 100644 src/utils/UnimplementedTreeVerifier4844.sol diff --git a/src/WorldIDIdentityManagerImplV3.sol b/src/WorldIDIdentityManagerImplV3.sol new file mode 100644 index 0000000..0b3b644 --- /dev/null +++ b/src/WorldIDIdentityManagerImplV3.sol @@ -0,0 +1,303 @@ +pragma solidity ^0.8.24; + +import "./WorldIDIdentityManagerImplV2.sol"; +import "./interfaces/ITreeVerifier4844.sol"; +import {VerifierLookupTable4844} from "./data/VerifierLookupTable4844.sol"; + +/// @title WorldID Identity Manager Implementation Version 3 +/// @author Worldcoin +/// @notice An implementation of a batch-based identity manager for the WorldID protocol. +/// @dev The manager is based on the principle of verifying externally-created Zero Knowledge Proofs +/// to perform the insertion using EIP-4844. +/// @dev This is the implementation delegated to by a proxy. +contract WorldIDIdentityManagerImplV3 is WorldIDIdentityManagerImplV2 { + /////////////////////////////////////////////////////////////////////////////// + /// A NOTE ON IMPLEMENTATION CONTRACTS /// + /////////////////////////////////////////////////////////////////////////////// + + // This contract is designed explicitly to operate from behind a proxy contract. As a result, + // there are a few important implementation considerations: + // + // - All updates made after deploying a given version of the implementation should inherit from + // the latest version of the implementation. This contract inherits from its previous implementation + // WorldIDIdentityManagerImplV2. 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 insertions following the EIP-4844 scheme. + VerifierLookupTable4844 internal batchInsertion4844Verifiers; + + /// @notice Thrown when the WorldIDIdentityManagerImplV3 contract is initialized + event WorldIDIdentityManagerImplV3Initialized(); + + /// @notice Initializes the V3 implementation contract. + /// @param _batchInsertion4844Verifiers The table of verifiers for verifying batch identity insertions. + /// @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. + /// + /// + /// @custom:reverts InvalidVerifierLUT if `_batchInsertion4844Verifiers` is set to the zero address + function initializeV3(VerifierLookupTable4844 _batchInsertion4844Verifiers) + public + reinitializer(3) + { + if (address(_batchInsertion4844Verifiers) == address(0)) { + revert InvalidVerifierLUT(); + } + + batchInsertion4844Verifiers = _batchInsertion4844Verifiers; + + emit WorldIDIdentityManagerImplV3Initialized(); + } + + /////////////////////////////////////////////////////////////////////////////// + /// UTILITY FUNCTIONS /// + /////////////////////////////////////////////////////////////////////////////// + + /// @notice Gets the address for the lookup table of merkle tree verifiers used for identity + /// registrations. + /// + /// @return addr The address of the contract being used as the verifier lookup table. + function getRegisterIdentities4844VerifierLookupTableAddress() + public + view + virtual + onlyProxy + onlyInitialized + returns (address) + { + return address(batchInsertion4844Verifiers); + } + + /// @notice Sets the address for the lookup table of merkle tree verifiers used for identity + /// registrations. + /// @dev Only the owner of the contract can call this function. + /// + /// @param newTable The new verifier lookup table to be used for verifying identity + /// registrations. + /// @custom:reverts InvalidVerifierLUT if `newTable` is set to the zero address + function setRegisterIdentities4844VerifierLookupTable(VerifierLookupTable4844 newTable) + public + virtual + onlyProxy + onlyInitialized + onlyOwner + { + if (address(newTable) == address(0)) { + revert InvalidVerifierLUT(); + } + + VerifierLookupTable4844 oldTable = batchInsertion4844Verifiers; + batchInsertion4844Verifiers = newTable; + emit DependencyUpdated( + Dependency.InsertionVerifierLookupTable, address(oldTable), address(newTable) + ); + } + + /////////////////////////////////////////////////////////////////////////////// + /// PUBLIC TYPES /// + /////////////////////////////////////////////////////////////////////////////// + + /// @notice Parameters for registerIdentities4844 function. + /// @dev This struct holds the parameters for registering identities and verifying the insertion proof + /// using KZG proofs as described in EIP-4844. + struct RegisterIdentities4844Params { + /// @notice The proof that given the conditions, insertion 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`. + uint256[8] insertionProof; + /// @notice The Pedersen commitments from the proof. + uint256[2] commitments; + /// @notice The proof of knowledge for the Pedersen commitments. + uint256[2] commitmentPok; + /// @notice KZG commitment for the polynomial extrapolated from the identities. + uint128[3] kzgCommitment; + /// @notice KZG proof associated with the commitment. + uint128[3] kzgProof; + /// @notice Expected evaluation of the polynomial at a certain point equal to KZG challenge. + uint256 expectedEvaluation; + /// @notice The value for the root of the tree before the `identityCommitments` have been inserted. + /// Must be an element of the field `Kr`. (already in reduced form) + uint256 preRoot; + /// @notice The root obtained after inserting all of `identityCommitments` into the tree described + /// by `preRoot`. Must be an element of the field `Kr`. (already in reduced form) + uint256 postRoot; + /// @notice Hash of all inserted identities, constructed by taking a root of the Merkle Tree of minimal + /// depth containing all identities. + bytes32 inputHash; + /// @notice Number of identities being registered in this batch. + uint32 batchSize; + /// @notice The position in the tree at which the insertions were made. + uint32 startIndex; + } + + /////////////////////////////////////////////////////////////////////////////// + /// ERRORS /// + /////////////////////////////////////////////////////////////////////////////// + + /// @notice Thrown when the point evaluation precompile returns failure. + /// This means KZG proof cannot be verified. + error KzgProofVerificationFailed(); + + /////////////////////////////////////////////////////////////////// + /// IDENTITY MANAGEMENT /// + /////////////////////////////////////////////////////////////////// + + /// @notice Registers identities into the WorldID system following the EIP-4844 scheme. + /// @dev Can only be called by the identity operator. + /// @dev Registration is performed off-chain and verified on-chain via insertion proof + /// and KZG proof. This saves gas and time over inserting identities one at a time. + /// + /// @param params parameters for the process defined by the `RegisterIdentities4844Params` structure. + /// @custom:reverts Unauthorized If the message sender is not authorised to add identities. + /// @custom:reverts NotLatestRoot If the provided `params.preRoot` is not the latest root. + /// @custom:reverts ProofValidationFailure If `params.insertionProof` cannot be verified using the + /// provided inputs. + /// @custom:reverts VerifierLookupTable.NoSuchVerifier If the batch sizes doesn't match a known + /// verifier. + /// @custom:reverts KzgProofVerificationFailed If KZG proof verification fails + function registerIdentities4844(RegisterIdentities4844Params calldata params) + public + virtual + onlyProxy + onlyInitialized + onlyIdentityOperator + { + if (params.preRoot != _latestRoot) { + revert NotLatestRoot(params.preRoot, _latestRoot); + } + + bytes32 kzgCommitmentHash = blobhash(0); + bytes32 kzgChallenge = computeKzgChallenge(params.inputHash, kzgCommitmentHash); + bool success = evaluatePoint( + kzgCommitmentHash, + kzgChallenge, + bytes32(params.expectedEvaluation), + params.kzgCommitment, + params.kzgProof + ); + if (!success) { + revert KzgProofVerificationFailed(); + } + + // We need to look up the correct verifier before we can verify. + ITreeVerifier4844 insertionVerifier = + batchInsertion4844Verifiers.getVerifierFor(params.batchSize); + + // With that, we can properly try and verify. + try insertionVerifier.verifyProof( + params.insertionProof, + params.commitments, + params.commitmentPok, + [ + uint256(params.inputHash), + params.expectedEvaluation % SNARK_SCALAR_FIELD, + uint256(kzgCommitmentHash), + uint256(params.startIndex), + params.preRoot, + params.postRoot + ] + ) { + // 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 = params.postRoot; + + // We also need to add the previous root to the history, and set the timestamp at + // which it was expired. + rootHistory[params.preRoot] = uint128(block.timestamp); + + emit TreeChanged(params.preRoot, TreeChange.Insertion, params.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(); + } + } + + /////////////////////////////////////////////////////////////////////////////// + /// UTILITY FUNCTIONS /// + /////////////////////////////////////////////////////////////////////////////// + + address constant PRECOMPILE_POINT_EVALUATION = address(0x0a); + + /// @notice Call the point evaluation precompiled contract. + /// Verify p(z) = y given commitment that corresponds to the polynomial p(x) and a KZG proof. + /// Also verify that the provided commitment matches the provided versioned_hash. + /// @param versioned_hash Reference to a blob in the execution layer (obtained from the data storage or execution environment). + /// @param x x-coordinate at which the blob is being evaluated. + /// @param y y-coordinate at which the blob is being evaluated. + /// @param commitment Commitment to the blob being evaluated (obtained from the KZG commitment scheme). + /// @param kzgProof Proof associated with the commitment (obtained from the KZG proof generation). + /// @return True on success, false otherwise. + function evaluatePoint( + bytes32 versioned_hash, + bytes32 x, + bytes32 y, + uint128[3] calldata commitment, + uint128[3] calldata kzgProof + ) public view returns (bool) { + bytes memory input = abi.encodePacked( + versioned_hash, + x, + y, + commitment[0], + commitment[1], + commitment[2], + kzgProof[0], + kzgProof[1], + kzgProof[2] + ); + (bool success,) = PRECOMPILE_POINT_EVALUATION.staticcall(input); + return success; + } + + /// @notice Converts input values to a KZG challenge. + /// @dev The challenge is defined as a bytes32 value of a keccak256 hash of the concatenated inputs reduced by BN254 modulus. + /// @param inputHash Hash of the input data calculated as described in the comment + /// to `calculateIdentityRegistrationInputHash()`. + /// @param kzgCommitmentVersionedHash versioned hash of the KZG commitment. + /// @return challenge The reduced keccak256 hash. + function computeKzgChallenge(bytes32 inputHash, bytes32 kzgCommitmentVersionedHash) + public + pure + returns (bytes32) + { + bytes memory inputBytes = abi.encodePacked(inputHash, kzgCommitmentVersionedHash); + uint256 reducedHash = uint256(keccak256(inputBytes)) % SNARK_SCALAR_FIELD; + return bytes32(reducedHash); + } +} diff --git a/src/data/VerifierLookupTable4844.sol b/src/data/VerifierLookupTable4844.sol new file mode 100644 index 0000000..61d6e5b --- /dev/null +++ b/src/data/VerifierLookupTable4844.sol @@ -0,0 +1,176 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import {Ownable2Step} from "openzeppelin-contracts/access/Ownable2Step.sol"; + +import {ITreeVerifier4844} from "../interfaces/ITreeVerifier4844.sol"; + +/// @title Batch Lookup Table for EIP-4844 proofs. +/// @author Worldcoin +/// @notice A table that provides the correct tree verifier based on the provided batch size. +/// @dev It should be used to query the correct verifier before using that verifier for verifying a +/// tree modification proof. +contract VerifierLookupTable4844 is Ownable2Step { + //////////////////////////////////////////////////////////////////////////////// + /// DATA /// + //////////////////////////////////////////////////////////////////////////////// + + /// The null address. + address internal constant nullAddress = address(0x0); + + /// The null verifiers. + ITreeVerifier4844 internal constant nullVerifier = ITreeVerifier4844(nullAddress); + + /// The lookup table for routing batches. + /// + /// As we expect to only have a few batch sizes per contract, a mapping is used due to its + /// natively sparse storage. + mapping(uint256 => ITreeVerifier4844) internal verifier_lut; + + //////////////////////////////////////////////////////////////////////////////// + /// ERRORS /// + //////////////////////////////////////////////////////////////////////////////// + + /// @notice Raised if a batch size is requested that the lookup table doesn't know about. + error NoSuchVerifier(); + + /// @notice Raised if an attempt is made to add a verifier for a batch size that already exists. + error VerifierExists(); + + /// @notice Thrown when an attempt is made to renounce ownership. + error CannotRenounceOwnership(); + + //////////////////////////////////////////////////////////////////////////////// + /// EVENTS /// + //////////////////////////////////////////////////////////////////////////////// + + /// @notice Emitted when a verifier is added to the lookup table. + /// + /// @param batchSize The size of the batch that the verifier has been added for. + /// @param verifierAddress The address of the verifier that was associated with `batchSize`. + event VerifierAdded(uint256 indexed batchSize, address indexed verifierAddress); + + /// @notice Emitted when a verifier is updated in the lookup table. + /// + /// @param batchSize The size of the batch that the verifier has been updated for. + /// @param oldVerifierAddress The address of the old verifier for `batchSize`. + /// @param newVerifierAddress The address of the new verifier for `batchSize`. + event VerifierUpdated( + uint256 indexed batchSize, + address indexed oldVerifierAddress, + address indexed newVerifierAddress + ); + + /// @notice Emitted when a verifier is disabled in the lookup table. + /// + /// @param batchSize The batch size that had its verifier disabled. + event VerifierDisabled(uint256 indexed batchSize); + + //////////////////////////////////////////////////////////////////////////////// + /// CONSTRUCTION /// + //////////////////////////////////////////////////////////////////////////////// + + /// @notice Constructs a new batch lookup table. + /// @dev It is initially constructed without any verifiers. + constructor() Ownable2Step() {} + + //////////////////////////////////////////////////////////////////////////////// + /// ACCESSORS /// + //////////////////////////////////////////////////////////////////////////////// + + /// @notice Obtains the verifier for the provided `batchSize`. + /// + /// @param batchSize The batch size to get the associated verifier for. + /// + /// @return verifier The tree verifier for the provided `batchSize`. + /// + /// @custom:reverts NoSuchVerifier If there is no verifier associated with the `batchSize`. + function getVerifierFor(uint256 batchSize) public view returns (ITreeVerifier4844 verifier) { + // Check the preconditions for querying the verifier. + validateVerifier(batchSize); + + // With the preconditions checked, we can return the verifier. + verifier = verifier_lut[batchSize]; + } + + /// @notice Adds a verifier for the provided `batchSize`. + /// + /// @param batchSize The batch size to add the verifier for. + /// @param verifier The verifier for a batch of size `batchSize`. + /// + /// @custom:reverts VerifierExists If `batchSize` already has an associated verifier. + /// @custom:reverts string If the caller is not the owner. + function addVerifier(uint256 batchSize, ITreeVerifier4844 verifier) public onlyOwner { + // Check that there is no entry for that batch size. + if (verifier_lut[batchSize] != nullVerifier) { + revert VerifierExists(); + } + + // Add the verifier. + updateVerifier(batchSize, verifier); + emit VerifierAdded(batchSize, address(verifier)); + } + + /// @notice Updates the verifier for the provided `batchSize`. + /// + /// @param batchSize The batch size to add the verifier for. + /// @param verifier The verifier for a batch of size `batchSize`. + /// + /// @return oldVerifier The old verifier instance associated with this batch size. + /// + /// @custom:reverts string If the caller is not the owner. + function updateVerifier(uint256 batchSize, ITreeVerifier4844 verifier) + public + onlyOwner + returns (ITreeVerifier4844 oldVerifier) + { + oldVerifier = verifier_lut[batchSize]; + verifier_lut[batchSize] = verifier; + emit VerifierUpdated(batchSize, address(oldVerifier), address(verifier)); + } + + /// @notice Disables the verifier for the provided batch size. + /// + /// @param batchSize The batch size to disable the verifier for. + /// + /// @return oldVerifier The old verifier associated with the batch size. + /// + /// @custom:reverts string If the caller is not the owner. + function disableVerifier(uint256 batchSize) + public + onlyOwner + returns (ITreeVerifier4844 oldVerifier) + { + oldVerifier = updateVerifier(batchSize, ITreeVerifier4844(nullAddress)); + emit VerifierDisabled(batchSize); + } + + //////////////////////////////////////////////////////////////////////////////// + /// INTERNAL FUNCTIONALITY /// + //////////////////////////////////////////////////////////////////////////////// + + /// @notice Checks if the entry for the provided `batchSize` is a valid verifier. + /// + /// @param batchSize The batch size to check. + /// + /// @custom:reverts NoSuchVerifier If `batchSize` does not have an associated verifier. + /// @custom:reverts BatchTooLarge If `batchSize` exceeds the maximum batch size. + function validateVerifier(uint256 batchSize) internal view { + if (verifier_lut[batchSize] == nullVerifier) { + revert NoSuchVerifier(); + } + } + + //////////////////////////////////////////////////////////////////////////////// + /// OWNERSHIP MANAGEMENT /// + //////////////////////////////////////////////////////////////////////////////// + + /// @notice Ensures that ownership of the lookup table cannot be renounced. + /// @dev This function is intentionally not `virtual` as we do not want it to be possible to + /// renounce ownership for the lookup table. + /// @dev This function is marked as `onlyOwner` to maintain the access restriction from the base + /// contract. + function renounceOwnership() public view override onlyOwner { + revert CannotRenounceOwnership(); + } +} diff --git a/src/interfaces/ITreeVerifier4844.sol b/src/interfaces/ITreeVerifier4844.sol new file mode 100644 index 0000000..6b6a75c --- /dev/null +++ b/src/interfaces/ITreeVerifier4844.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +/// @title Tree Verifier Interface +/// @author Worldcoin +/// @notice An interface representing a merkle tree verifier for EIP-4844 proofs. +interface ITreeVerifier4844 { + /// Verify an uncompressed Groth16 proof. + /// @notice Reverts with InvalidProof if the proof is invalid or + /// with PublicInputNotInField the public input is not reduced. + /// @notice There is no return value. If the function does not revert, the + /// proof was successfully verified. + /// @param proof the points (A, B, C) in EIP-197 format matching the output + /// of compressProof. + /// @param commitments the Pedersen commitments from the proof. + /// @param commitmentPok the proof of knowledge for the Pedersen commitments. + /// @param input the public input field elements in the scalar field Fr. + /// Elements must be reduced. + function verifyProof( + uint256[8] calldata proof, + uint256[2] calldata commitments, + uint256[2] calldata commitmentPok, + uint256[6] calldata input + ) external; +} diff --git a/src/test/InsertionTreeVerifier164844.sol b/src/test/InsertionTreeVerifier164844.sol new file mode 100644 index 0000000..c7bcf22 --- /dev/null +++ b/src/test/InsertionTreeVerifier164844.sol @@ -0,0 +1,757 @@ +import {ITreeVerifier4844} from "src/interfaces/ITreeVerifier4844.sol"; + +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +/// @title Groth16 verifier template. +/// @author Remco Bloemen +/// @notice Supports verifying Groth16 proofs. Proofs can be in uncompressed +/// (256 bytes) and compressed (128 bytes) format. A view function is provided +/// to compress proofs. +/// @notice See for further explanation. +contract Verifier is ITreeVerifier4844 { + /// Some of the provided public input values are larger than the field modulus. + /// @dev Public input elements are not automatically reduced, as this is can be + /// a dangerous source of bugs. + error PublicInputNotInField(); + + /// The proof is invalid. + /// @dev This can mean that provided Groth16 proof points are not on their + /// curves, that pairing equation fails, or that the proof is not for the + /// provided public input. + error ProofInvalid(); + /// The commitment is invalid + /// @dev This can mean that provided commitment points and/or proof of knowledge are not on their + /// curves, that pairing equation fails, or that the commitment and/or proof of knowledge is not for the + /// commitment key. + error CommitmentInvalid(); + + // Addresses of precompiles + uint256 constant PRECOMPILE_MODEXP = 0x05; + uint256 constant PRECOMPILE_ADD = 0x06; + uint256 constant PRECOMPILE_MUL = 0x07; + uint256 constant PRECOMPILE_VERIFY = 0x08; + + // Base field Fp order P and scalar field Fr order R. + // For BN254 these are computed as follows: + // t = 4965661367192848881 + // P = 36⋅t⁴ + 36⋅t³ + 24⋅t² + 6⋅t + 1 + // R = 36⋅t⁴ + 36⋅t³ + 18⋅t² + 6⋅t + 1 + uint256 constant P = 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47; + uint256 constant R = 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001; + + // Extension field Fp2 = Fp[i] / (i² + 1) + // Note: This is the complex extension field of Fp with i² = -1. + // Values in Fp2 are represented as a pair of Fp elements (a₀, a₁) as a₀ + a₁⋅i. + // Note: The order of Fp2 elements is *opposite* that of the pairing contract, which + // expects Fp2 elements in order (a₁, a₀). This is also the order in which + // Fp2 elements are encoded in the public interface as this became convention. + + // Constants in Fp + uint256 constant FRACTION_1_2_FP = + 0x183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b6c3e7ea4; + uint256 constant FRACTION_27_82_FP = + 0x2b149d40ceb8aaae81be18991be06ac3b5b4c5e559dbefa33267e6dc24a138e5; + uint256 constant FRACTION_3_82_FP = + 0x2fcd3ac2a640a154eb23960892a85a68f031ca0c8344b23a577dcf1052b9e775; + + // Exponents for inversions and square roots mod P + uint256 constant EXP_INVERSE_FP = + 0x30644E72E131A029B85045B68181585D97816A916871CA8D3C208C16D87CFD45; // P - 2 + uint256 constant EXP_SQRT_FP = 0xC19139CB84C680A6E14116DA060561765E05AA45A1C72A34F082305B61F3F52; // (P + 1) / 4; + + // Groth16 alpha point in G1 + uint256 constant ALPHA_X = + 2708969884581619714583557951222918593768772820936646583136789853819173846869; + uint256 constant ALPHA_Y = + 8043584586634431593445517970972074344995663485134139288455040086659571635007; + + // Groth16 beta point in G2 in powers of i + uint256 constant BETA_NEG_X_0 = + 2959634726718323159960582986494250462813569884099052977631775603535594317697; + uint256 constant BETA_NEG_X_1 = + 2567045386415897874314697628037970624972832810910119870003888717933699604901; + uint256 constant BETA_NEG_Y_0 = + 16901523272247727115591169122896696499546950846485806085170956609925505472585; + uint256 constant BETA_NEG_Y_1 = + 9215676527870136774869096108544312861626222401455601740260979586958004850529; + + // Groth16 gamma point in G2 in powers of i + uint256 constant GAMMA_NEG_X_0 = + 8904268816337492794416634636731345512132126205040334900538636341318421538665; + uint256 constant GAMMA_NEG_X_1 = + 19004930723547195287383134699314663710813331368262617951429852355608517342331; + uint256 constant GAMMA_NEG_Y_0 = + 7842710818119939895862574378653362734227090432895633776678943219170947197152; + uint256 constant GAMMA_NEG_Y_1 = + 2119828549076590392190534591515249836229477358991111181013402454206155157118; + + // Groth16 delta point in G2 in powers of i + uint256 constant DELTA_NEG_X_0 = + 16751268373860897104759671698719630006610347517780893954841247694987729581925; + uint256 constant DELTA_NEG_X_1 = + 17074355565731257728214063587600772000822732044793320082675686505906845346452; + uint256 constant DELTA_NEG_Y_0 = + 19276066093240754710159494686794885924197536752080653828868593863903058741434; + uint256 constant DELTA_NEG_Y_1 = + 10391802489342383226451710090356140861208165419529434978751035344608888508609; + // Pedersen G point in G2 in powers of i + uint256 constant PEDERSEN_G_X_0 = + 191329193964481065495212234619899074886870006108961472779204277869671687115; + uint256 constant PEDERSEN_G_X_1 = + 18448119812880134607636953435856909803133499313412999813309765036275639759714; + uint256 constant PEDERSEN_G_Y_0 = + 7320805055268657491420712525633197949310593554647878873210287658169673900549; + uint256 constant PEDERSEN_G_Y_1 = + 3560501050363463728186450810474818536571923785681141032866912257045688789262; + + // Pedersen GRootSigmaNeg point in G2 in powers of i + uint256 constant PEDERSEN_GROOTSIGMANEG_X_0 = + 211898546590335769030549987216927375237199629913882184437950823448881903970; + uint256 constant PEDERSEN_GROOTSIGMANEG_X_1 = + 11880079357380769452360200537618953332011342321508956754029570189743729673109; + uint256 constant PEDERSEN_GROOTSIGMANEG_Y_0 = + 20343036341560553279453586916303753798495271995175822900148534636526224647164; + uint256 constant PEDERSEN_GROOTSIGMANEG_Y_1 = + 18398984926832230363518321101521122731640509584485783610091427005978221352504; + + // Constant and public input points + uint256 constant CONSTANT_X = + 19358550662348637607112384570170513296928047638941946872811625925166768621689; + uint256 constant CONSTANT_Y = + 6626464468086658346890654816494620493177727644183109536624044536458730824315; + uint256 constant PUB_0_X = + 21039846169407040170900369597064405833466068013105470546221436302050929487572; + uint256 constant PUB_0_Y = + 2611968432498880732075319619253202283148240652063087246949510105674797190280; + uint256 constant PUB_1_X = + 17167433712229735771674828746728526346122363071518232159739058701502221378434; + uint256 constant PUB_1_Y = + 1739891145391694203708089066027069569757327500962996441495332028277069755267; + uint256 constant PUB_2_X = + 17798576048770024481159849810854079136640512359488534233899537955087709779447; + uint256 constant PUB_2_Y = + 3109649566724390234731095093811315543854286970944206387661935696332701907948; + uint256 constant PUB_3_X = + 7890320639901919646211933804150885252994659227692929006872667869290059075281; + uint256 constant PUB_3_Y = + 11396290418620464030035551260992220541169365820463159918373688231853778829859; + uint256 constant PUB_4_X = + 16030714242136825467561218858637637879071023746882506204693189470117499845846; + uint256 constant PUB_4_Y = + 14919105472258793272718206026880812470549694675722205962400380939241926000512; + uint256 constant PUB_5_X = + 15864446105389560446153403630146334838539883248526951175596504258425435272439; + uint256 constant PUB_5_Y = + 3764670878071624467628034771504294179451467482124027272932022992371176433497; + uint256 constant PUB_6_X = + 13240007634426972631439790531670899863212947213748180981433638304827247605238; + uint256 constant PUB_6_Y = + 9107574043090153668746153226362613394226970755908178311480271382776655025033; + + /// Negation in Fp. + /// @notice Returns a number x such that a + x = 0 in Fp. + /// @notice The input does not need to be reduced. + /// @param a the base + /// @return x the result + function negate(uint256 a) internal pure returns (uint256 x) { + unchecked { + x = (P - (a % P)) % P; // Modulo is cheaper than branching + } + } + + /// Exponentiation in Fp. + /// @notice Returns a number x such that a ^ e = x in Fp. + /// @notice The input does not need to be reduced. + /// @param a the base + /// @param e the exponent + /// @return x the result + function exp(uint256 a, uint256 e) internal view returns (uint256 x) { + bool success; + assembly ("memory-safe") { + let f := mload(0x40) + mstore(f, 0x20) + mstore(add(f, 0x20), 0x20) + mstore(add(f, 0x40), 0x20) + mstore(add(f, 0x60), a) + mstore(add(f, 0x80), e) + mstore(add(f, 0xa0), P) + success := staticcall(gas(), PRECOMPILE_MODEXP, f, 0xc0, f, 0x20) + x := mload(f) + } + if (!success) { + // Exponentiation failed. + // Should not happen. + revert ProofInvalid(); + } + } + + /// Invertsion in Fp. + /// @notice Returns a number x such that a * x = 1 in Fp. + /// @notice The input does not need to be reduced. + /// @notice Reverts with ProofInvalid() if the inverse does not exist + /// @param a the input + /// @return x the solution + function invert_Fp(uint256 a) internal view returns (uint256 x) { + x = exp(a, EXP_INVERSE_FP); + if (mulmod(a, x, P) != 1) { + // Inverse does not exist. + // Can only happen during G2 point decompression. + revert ProofInvalid(); + } + } + + /// Square root in Fp. + /// @notice Returns a number x such that x * x = a in Fp. + /// @notice Will revert with InvalidProof() if the input is not a square + /// or not reduced. + /// @param a the square + /// @return x the solution + function sqrt_Fp(uint256 a) internal view returns (uint256 x) { + x = exp(a, EXP_SQRT_FP); + if (mulmod(x, x, P) != a) { + // Square root does not exist or a is not reduced. + // Happens when G1 point is not on curve. + revert ProofInvalid(); + } + } + + /// Square test in Fp. + /// @notice Returns wheter a number x exists such that x * x = a in Fp. + /// @notice Will revert with InvalidProof() if the input is not a square + /// or not reduced. + /// @param a the square + /// @return x the solution + function isSquare_Fp(uint256 a) internal view returns (bool) { + uint256 x = exp(a, EXP_SQRT_FP); + return mulmod(x, x, P) == a; + } + + /// Square root in Fp2. + /// @notice Fp2 is the complex extension Fp[i]/(i^2 + 1). The input is + /// a0 + a1 ⋅ i and the result is x0 + x1 ⋅ i. + /// @notice Will revert with InvalidProof() if + /// * the input is not a square, + /// * the hint is incorrect, or + /// * the input coefficents are not reduced. + /// @param a0 The real part of the input. + /// @param a1 The imaginary part of the input. + /// @param hint A hint which of two possible signs to pick in the equation. + /// @return x0 The real part of the square root. + /// @return x1 The imaginary part of the square root. + function sqrt_Fp2(uint256 a0, uint256 a1, bool hint) + internal + view + returns (uint256 x0, uint256 x1) + { + // If this square root reverts there is no solution in Fp2. + uint256 d = sqrt_Fp(addmod(mulmod(a0, a0, P), mulmod(a1, a1, P), P)); + if (hint) { + d = negate(d); + } + // If this square root reverts there is no solution in Fp2. + x0 = sqrt_Fp(mulmod(addmod(a0, d, P), FRACTION_1_2_FP, P)); + x1 = mulmod(a1, invert_Fp(mulmod(x0, 2, P)), P); + + // Check result to make sure we found a root. + // Note: this also fails if a0 or a1 is not reduced. + if ( + a0 != addmod(mulmod(x0, x0, P), negate(mulmod(x1, x1, P)), P) + || a1 != mulmod(2, mulmod(x0, x1, P), P) + ) { + revert ProofInvalid(); + } + } + + /// Compress a G1 point. + /// @notice Reverts with InvalidProof if the coordinates are not reduced + /// or if the point is not on the curve. + /// @notice The point at infinity is encoded as (0,0) and compressed to 0. + /// @param x The X coordinate in Fp. + /// @param y The Y coordinate in Fp. + /// @return c The compresed point (x with one signal bit). + function compress_g1(uint256 x, uint256 y) internal view returns (uint256 c) { + if (x >= P || y >= P) { + // G1 point not in field. + revert ProofInvalid(); + } + if (x == 0 && y == 0) { + // Point at infinity + return 0; + } + + // Note: sqrt_Fp reverts if there is no solution, i.e. the x coordinate is invalid. + uint256 y_pos = sqrt_Fp(addmod(mulmod(mulmod(x, x, P), x, P), 3, P)); + if (y == y_pos) { + return (x << 1) | 0; + } else if (y == negate(y_pos)) { + return (x << 1) | 1; + } else { + // G1 point not on curve. + revert ProofInvalid(); + } + } + + /// Decompress a G1 point. + /// @notice Reverts with InvalidProof if the input does not represent a valid point. + /// @notice The point at infinity is encoded as (0,0) and compressed to 0. + /// @param c The compresed point (x with one signal bit). + /// @return x The X coordinate in Fp. + /// @return y The Y coordinate in Fp. + function decompress_g1(uint256 c) internal view returns (uint256 x, uint256 y) { + // Note that X = 0 is not on the curve since 0³ + 3 = 3 is not a square. + // so we can use it to represent the point at infinity. + if (c == 0) { + // Point at infinity as encoded in EIP196 and EIP197. + return (0, 0); + } + bool negate_point = c & 1 == 1; + x = c >> 1; + if (x >= P) { + // G1 x coordinate not in field. + revert ProofInvalid(); + } + + // Note: (x³ + 3) is irreducible in Fp, so it can not be zero and therefore + // y can not be zero. + // Note: sqrt_Fp reverts if there is no solution, i.e. the point is not on the curve. + y = sqrt_Fp(addmod(mulmod(mulmod(x, x, P), x, P), 3, P)); + if (negate_point) { + y = negate(y); + } + } + + /// Compress a G2 point. + /// @notice Reverts with InvalidProof if the coefficients are not reduced + /// or if the point is not on the curve. + /// @notice The G2 curve is defined over the complex extension Fp[i]/(i^2 + 1) + /// with coordinates (x0 + x1 ⋅ i, y0 + y1 ⋅ i). + /// @notice The point at infinity is encoded as (0,0,0,0) and compressed to (0,0). + /// @param x0 The real part of the X coordinate. + /// @param x1 The imaginary poart of the X coordinate. + /// @param y0 The real part of the Y coordinate. + /// @param y1 The imaginary part of the Y coordinate. + /// @return c0 The first half of the compresed point (x0 with two signal bits). + /// @return c1 The second half of the compressed point (x1 unmodified). + function compress_g2(uint256 x0, uint256 x1, uint256 y0, uint256 y1) + internal + view + returns (uint256 c0, uint256 c1) + { + if (x0 >= P || x1 >= P || y0 >= P || y1 >= P) { + // G2 point not in field. + revert ProofInvalid(); + } + if ((x0 | x1 | y0 | y1) == 0) { + // Point at infinity + return (0, 0); + } + + // Compute y^2 + // Note: shadowing variables and scoping to avoid stack-to-deep. + uint256 y0_pos; + uint256 y1_pos; + { + uint256 n3ab = mulmod(mulmod(x0, x1, P), P - 3, P); + uint256 a_3 = mulmod(mulmod(x0, x0, P), x0, P); + uint256 b_3 = mulmod(mulmod(x1, x1, P), x1, P); + y0_pos = addmod(FRACTION_27_82_FP, addmod(a_3, mulmod(n3ab, x1, P), P), P); + y1_pos = negate(addmod(FRACTION_3_82_FP, addmod(b_3, mulmod(n3ab, x0, P), P), P)); + } + + // Determine hint bit + // If this sqrt fails the x coordinate is not on the curve. + bool hint; + { + uint256 d = sqrt_Fp(addmod(mulmod(y0_pos, y0_pos, P), mulmod(y1_pos, y1_pos, P), P)); + hint = !isSquare_Fp(mulmod(addmod(y0_pos, d, P), FRACTION_1_2_FP, P)); + } + + // Recover y + (y0_pos, y1_pos) = sqrt_Fp2(y0_pos, y1_pos, hint); + if (y0 == y0_pos && y1 == y1_pos) { + c0 = (x0 << 2) | (hint ? 2 : 0) | 0; + c1 = x1; + } else if (y0 == negate(y0_pos) && y1 == negate(y1_pos)) { + c0 = (x0 << 2) | (hint ? 2 : 0) | 1; + c1 = x1; + } else { + // G1 point not on curve. + revert ProofInvalid(); + } + } + + /// Decompress a G2 point. + /// @notice Reverts with InvalidProof if the input does not represent a valid point. + /// @notice The G2 curve is defined over the complex extension Fp[i]/(i^2 + 1) + /// with coordinates (x0 + x1 ⋅ i, y0 + y1 ⋅ i). + /// @notice The point at infinity is encoded as (0,0,0,0) and compressed to (0,0). + /// @param c0 The first half of the compresed point (x0 with two signal bits). + /// @param c1 The second half of the compressed point (x1 unmodified). + /// @return x0 The real part of the X coordinate. + /// @return x1 The imaginary poart of the X coordinate. + /// @return y0 The real part of the Y coordinate. + /// @return y1 The imaginary part of the Y coordinate. + function decompress_g2(uint256 c0, uint256 c1) + internal + view + returns (uint256 x0, uint256 x1, uint256 y0, uint256 y1) + { + // Note that X = (0, 0) is not on the curve since 0³ + 3/(9 + i) is not a square. + // so we can use it to represent the point at infinity. + if (c0 == 0 && c1 == 0) { + // Point at infinity as encoded in EIP197. + return (0, 0, 0, 0); + } + bool negate_point = c0 & 1 == 1; + bool hint = c0 & 2 == 2; + x0 = c0 >> 2; + x1 = c1; + if (x0 >= P || x1 >= P) { + // G2 x0 or x1 coefficient not in field. + revert ProofInvalid(); + } + + uint256 n3ab = mulmod(mulmod(x0, x1, P), P - 3, P); + uint256 a_3 = mulmod(mulmod(x0, x0, P), x0, P); + uint256 b_3 = mulmod(mulmod(x1, x1, P), x1, P); + + y0 = addmod(FRACTION_27_82_FP, addmod(a_3, mulmod(n3ab, x1, P), P), P); + y1 = negate(addmod(FRACTION_3_82_FP, addmod(b_3, mulmod(n3ab, x0, P), P), P)); + + // Note: sqrt_Fp2 reverts if there is no solution, i.e. the point is not on the curve. + // Note: (X³ + 3/(9 + i)) is irreducible in Fp2, so y can not be zero. + // But y0 or y1 may still independently be zero. + (y0, y1) = sqrt_Fp2(y0, y1, hint); + if (negate_point) { + y0 = negate(y0); + y1 = negate(y1); + } + } + + /// Compute the public input linear combination. + /// @notice Reverts with PublicInputNotInField if the input is not in the field. + /// @notice Computes the multi-scalar-multiplication of the public input + /// elements and the verification key including the constant term. + /// @param input The public inputs. These are elements of the scalar field Fr. + /// @param publicCommitments public inputs generated from pedersen commitments. + /// @param commitments The Pedersen commitments from the proof. + /// @return x The X coordinate of the resulting G1 point. + /// @return y The Y coordinate of the resulting G1 point. + function publicInputMSM( + uint256[6] calldata input, + uint256[1] memory publicCommitments, + uint256[2] memory commitments + ) internal view returns (uint256 x, uint256 y) { + // Note: The ECMUL precompile does not reject unreduced values, so we check this. + // Note: Unrolling this loop does not cost much extra in code-size, the bulk of the + // code-size is in the PUB_ constants. + // ECMUL has input (x, y, scalar) and output (x', y'). + // ECADD has input (x1, y1, x2, y2) and output (x', y'). + // We reduce commitments(if any) with constants as the first point argument to ECADD. + // We call them such that ecmul output is already in the second point + // argument to ECADD so we can have a tight loop. + bool success = true; + assembly ("memory-safe") { + let f := mload(0x40) + let g := add(f, 0x40) + let s + mstore(f, CONSTANT_X) + mstore(add(f, 0x20), CONSTANT_Y) + success := and(success, staticcall(gas(), PRECOMPILE_ADD, commitments, 64, g, 0x40)) + success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40)) + mstore(g, PUB_0_X) + mstore(add(g, 0x20), PUB_0_Y) + s := calldataload(input) + mstore(add(g, 0x40), s) + success := and(success, lt(s, R)) + success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40)) + success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40)) + mstore(g, PUB_1_X) + mstore(add(g, 0x20), PUB_1_Y) + s := calldataload(add(input, 32)) + mstore(add(g, 0x40), s) + success := and(success, lt(s, R)) + success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40)) + success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40)) + mstore(g, PUB_2_X) + mstore(add(g, 0x20), PUB_2_Y) + s := calldataload(add(input, 64)) + mstore(add(g, 0x40), s) + success := and(success, lt(s, R)) + success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40)) + success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40)) + mstore(g, PUB_3_X) + mstore(add(g, 0x20), PUB_3_Y) + s := calldataload(add(input, 96)) + mstore(add(g, 0x40), s) + success := and(success, lt(s, R)) + success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40)) + success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40)) + mstore(g, PUB_4_X) + mstore(add(g, 0x20), PUB_4_Y) + s := calldataload(add(input, 128)) + mstore(add(g, 0x40), s) + success := and(success, lt(s, R)) + success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40)) + success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40)) + mstore(g, PUB_5_X) + mstore(add(g, 0x20), PUB_5_Y) + s := calldataload(add(input, 160)) + mstore(add(g, 0x40), s) + success := and(success, lt(s, R)) + success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40)) + success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40)) + mstore(g, PUB_6_X) + mstore(add(g, 0x20), PUB_6_Y) + s := mload(publicCommitments) + mstore(add(g, 0x40), s) + success := and(success, lt(s, R)) + success := and(success, staticcall(gas(), PRECOMPILE_MUL, g, 0x60, g, 0x40)) + success := and(success, staticcall(gas(), PRECOMPILE_ADD, f, 0x80, f, 0x40)) + + x := mload(f) + y := mload(add(f, 0x20)) + } + if (!success) { + // Either Public input not in field, or verification key invalid. + // We assume the contract is correctly generated, so the verification key is valid. + revert PublicInputNotInField(); + } + } + + /// Compress a proof. + /// @notice Will revert with InvalidProof if the curve points are invalid, + /// but does not verify the proof itself. + /// @param proof The uncompressed Groth16 proof. Elements are in the same order as for + /// verifyProof. I.e. Groth16 points (A, B, C) encoded as in EIP-197. + /// @param commitments Pedersen commitments from the proof. + /// @param commitmentPok proof of knowledge for the Pedersen commitments. + /// @return compressed The compressed proof. Elements are in the same order as for + /// verifyCompressedProof. I.e. points (A, B, C) in compressed format. + /// @return compressedCommitments compressed Pedersen commitments from the proof. + /// @return compressedCommitmentPok compressed proof of knowledge for the Pedersen commitments. + function compressProof( + uint256[8] calldata proof, + uint256[2] calldata commitments, + uint256[2] calldata commitmentPok + ) + public + view + returns ( + uint256[4] memory compressed, + uint256[1] memory compressedCommitments, + uint256 compressedCommitmentPok + ) + { + compressed[0] = compress_g1(proof[0], proof[1]); + (compressed[2], compressed[1]) = compress_g2(proof[3], proof[2], proof[5], proof[4]); + compressed[3] = compress_g1(proof[6], proof[7]); + compressedCommitments[0] = compress_g1(commitments[0], commitments[1]); + compressedCommitmentPok = compress_g1(commitmentPok[0], commitmentPok[1]); + } + + /// Verify a Groth16 proof with compressed points. + /// @notice Reverts with InvalidProof if the proof is invalid or + /// with PublicInputNotInField the public input is not reduced. + /// @notice There is no return value. If the function does not revert, the + /// proof was successfully verified. + /// @param compressedProof the points (A, B, C) in compressed format + /// matching the output of compressProof. + /// @param compressedCommitments compressed Pedersen commitments from the proof. + /// @param compressedCommitmentPok compressed proof of knowledge for the Pedersen commitments. + /// @param input the public input field elements in the scalar field Fr. + /// Elements must be reduced. + function verifyCompressedProof( + uint256[4] calldata compressedProof, + uint256[1] calldata compressedCommitments, + uint256 compressedCommitmentPok, + uint256[6] calldata input + ) public view { + uint256[1] memory publicCommitments; + uint256[2] memory commitments; + uint256[24] memory pairings; + { + (commitments[0], commitments[1]) = decompress_g1(compressedCommitments[0]); + (uint256 Px, uint256 Py) = decompress_g1(compressedCommitmentPok); + + uint256[] memory publicAndCommitmentCommitted; + + publicCommitments[0] = uint256( + sha256( + abi.encodePacked(commitments[0], commitments[1], publicAndCommitmentCommitted) + ) + ) % R; + // Commitments + pairings[0] = commitments[0]; + pairings[1] = commitments[1]; + pairings[2] = PEDERSEN_G_X_1; + pairings[3] = PEDERSEN_G_X_0; + pairings[4] = PEDERSEN_G_Y_1; + pairings[5] = PEDERSEN_G_Y_0; + pairings[6] = Px; + pairings[7] = Py; + pairings[8] = PEDERSEN_GROOTSIGMANEG_X_1; + pairings[9] = PEDERSEN_GROOTSIGMANEG_X_0; + pairings[10] = PEDERSEN_GROOTSIGMANEG_Y_1; + pairings[11] = PEDERSEN_GROOTSIGMANEG_Y_0; + + // Verify pedersen commitments + bool success; + assembly ("memory-safe") { + let f := mload(0x40) + + success := staticcall(gas(), PRECOMPILE_VERIFY, pairings, 0x180, f, 0x20) + success := and(success, mload(f)) + } + if (!success) { + revert CommitmentInvalid(); + } + } + + { + (uint256 Ax, uint256 Ay) = decompress_g1(compressedProof[0]); + (uint256 Bx0, uint256 Bx1, uint256 By0, uint256 By1) = + decompress_g2(compressedProof[2], compressedProof[1]); + (uint256 Cx, uint256 Cy) = decompress_g1(compressedProof[3]); + (uint256 Lx, uint256 Ly) = publicInputMSM(input, publicCommitments, commitments); + + // Verify the pairing + // Note: The precompile expects the F2 coefficients in big-endian order. + // Note: The pairing precompile rejects unreduced values, so we won't check that here. + // e(A, B) + pairings[0] = Ax; + pairings[1] = Ay; + pairings[2] = Bx1; + pairings[3] = Bx0; + pairings[4] = By1; + pairings[5] = By0; + // e(C, -δ) + pairings[6] = Cx; + pairings[7] = Cy; + pairings[8] = DELTA_NEG_X_1; + pairings[9] = DELTA_NEG_X_0; + pairings[10] = DELTA_NEG_Y_1; + pairings[11] = DELTA_NEG_Y_0; + // e(α, -β) + pairings[12] = ALPHA_X; + pairings[13] = ALPHA_Y; + pairings[14] = BETA_NEG_X_1; + pairings[15] = BETA_NEG_X_0; + pairings[16] = BETA_NEG_Y_1; + pairings[17] = BETA_NEG_Y_0; + // e(L_pub, -γ) + pairings[18] = Lx; + pairings[19] = Ly; + pairings[20] = GAMMA_NEG_X_1; + pairings[21] = GAMMA_NEG_X_0; + pairings[22] = GAMMA_NEG_Y_1; + pairings[23] = GAMMA_NEG_Y_0; + + // Check pairing equation. + bool success; + uint256[1] memory output; + assembly ("memory-safe") { + success := staticcall(gas(), PRECOMPILE_VERIFY, pairings, 0x300, output, 0x20) + } + if (!success || output[0] != 1) { + // Either proof or verification key invalid. + // We assume the contract is correctly generated, so the verification key is valid. + revert ProofInvalid(); + } + } + } + + /// Verify an uncompressed Groth16 proof. + /// @notice Reverts with InvalidProof if the proof is invalid or + /// with PublicInputNotInField the public input is not reduced. + /// @notice There is no return value. If the function does not revert, the + /// proof was successfully verified. + /// @param proof the points (A, B, C) in EIP-197 format matching the output + /// of compressProof. + /// @param commitments the Pedersen commitments from the proof. + /// @param commitmentPok the proof of knowledge for the Pedersen commitments. + /// @param input the public input field elements in the scalar field Fr. + /// Elements must be reduced. + function verifyProof( + uint256[8] calldata proof, + uint256[2] calldata commitments, + uint256[2] calldata commitmentPok, + uint256[6] calldata input + ) public view { + // HashToField + uint256[1] memory publicCommitments; + uint256[] memory publicAndCommitmentCommitted; + + publicCommitments[0] = uint256( + sha256(abi.encodePacked(commitments[0], commitments[1], publicAndCommitmentCommitted)) + ) % R; + + // Verify pedersen commitments + bool success; + assembly ("memory-safe") { + let f := mload(0x40) + + calldatacopy(f, commitments, 0x40) // Copy Commitments + mstore(add(f, 0x40), PEDERSEN_G_X_1) + mstore(add(f, 0x60), PEDERSEN_G_X_0) + mstore(add(f, 0x80), PEDERSEN_G_Y_1) + mstore(add(f, 0xa0), PEDERSEN_G_Y_0) + calldatacopy(add(f, 0xc0), commitmentPok, 0x40) + mstore(add(f, 0x100), PEDERSEN_GROOTSIGMANEG_X_1) + mstore(add(f, 0x120), PEDERSEN_GROOTSIGMANEG_X_0) + mstore(add(f, 0x140), PEDERSEN_GROOTSIGMANEG_Y_1) + mstore(add(f, 0x160), PEDERSEN_GROOTSIGMANEG_Y_0) + + success := staticcall(gas(), PRECOMPILE_VERIFY, f, 0x180, f, 0x20) + success := and(success, mload(f)) + } + if (!success) { + revert CommitmentInvalid(); + } + + (uint256 x, uint256 y) = publicInputMSM(input, publicCommitments, commitments); + + // Note: The precompile expects the F2 coefficients in big-endian order. + // Note: The pairing precompile rejects unreduced values, so we won't check that here. + assembly ("memory-safe") { + let f := mload(0x40) // Free memory pointer. + + // Copy points (A, B, C) to memory. They are already in correct encoding. + // This is pairing e(A, B) and G1 of e(C, -δ). + calldatacopy(f, proof, 0x100) + + // Complete e(C, -δ) and write e(α, -β), e(L_pub, -γ) to memory. + // OPT: This could be better done using a single codecopy, but + // Solidity (unlike standalone Yul) doesn't provide a way to + // to do this. + mstore(add(f, 0x100), DELTA_NEG_X_1) + mstore(add(f, 0x120), DELTA_NEG_X_0) + mstore(add(f, 0x140), DELTA_NEG_Y_1) + mstore(add(f, 0x160), DELTA_NEG_Y_0) + mstore(add(f, 0x180), ALPHA_X) + mstore(add(f, 0x1a0), ALPHA_Y) + mstore(add(f, 0x1c0), BETA_NEG_X_1) + mstore(add(f, 0x1e0), BETA_NEG_X_0) + mstore(add(f, 0x200), BETA_NEG_Y_1) + mstore(add(f, 0x220), BETA_NEG_Y_0) + mstore(add(f, 0x240), x) + mstore(add(f, 0x260), y) + mstore(add(f, 0x280), GAMMA_NEG_X_1) + mstore(add(f, 0x2a0), GAMMA_NEG_X_0) + mstore(add(f, 0x2c0), GAMMA_NEG_Y_1) + mstore(add(f, 0x2e0), GAMMA_NEG_Y_0) + + // Check pairing equation. + success := staticcall(gas(), PRECOMPILE_VERIFY, f, 0x300, f, 0x20) + // Also check returned value (both are either 1 or 0). + success := and(success, mload(f)) + } + if (!success) { + // Either proof or verification key invalid. + // We assume the contract is correctly generated, so the verification key is valid. + revert ProofInvalid(); + } + } +} diff --git a/src/test/identity-manager/WorldIDIdentityManagerCalculation.t.sol b/src/test/identity-manager/WorldIDIdentityManagerCalculation.t.sol index f9f4c1f..f8637d1 100644 --- a/src/test/identity-manager/WorldIDIdentityManagerCalculation.t.sol +++ b/src/test/identity-manager/WorldIDIdentityManagerCalculation.t.sol @@ -32,7 +32,7 @@ contract WorldIDIdentityManagerCalculation is WorldIDIdentityManagerTest { vm.expectRevert("Function must be called through delegatecall"); // Test - managerImpl.calculateIdentityRegistrationInputHash( + managerImplV2.calculateIdentityRegistrationInputHash( startIndex, insertionPreRoot, insertionPostRoot, identityCommitments ); } @@ -57,7 +57,7 @@ contract WorldIDIdentityManagerCalculation is WorldIDIdentityManagerTest { vm.expectRevert("Function must be called through delegatecall"); // Test - managerImpl.calculateIdentityDeletionInputHash( + managerImplV2.calculateIdentityDeletionInputHash( packedDeletionIndices, deletionPreRoot, deletionPostRoot, deletionBatchSize ); } diff --git a/src/test/identity-manager/WorldIDIdentityManagerConstruction.t.sol b/src/test/identity-manager/WorldIDIdentityManagerConstruction.t.sol index 385a57c..2b20e7a 100644 --- a/src/test/identity-manager/WorldIDIdentityManagerConstruction.t.sol +++ b/src/test/identity-manager/WorldIDIdentityManagerConstruction.t.sol @@ -45,18 +45,18 @@ contract WorldIDIdentityManagerConstruction is WorldIDIdentityManagerTest { ); // Test - identityManager = new IdentityManager(address(managerImpl), callData); + identityManager = new IdentityManager(address(managerImplV2), callData); identityManagerAddress = address(identityManager); // creates Manager Impl V2, which will be used for tests - managerImpl = new ManagerImpl(); - managerImplAddress = address(managerImpl); + managerImplV2 = new ManagerImpl(); + managerImplV2Address = address(managerImplV2); bytes memory initCallV2 = abi.encodeCall(ManagerImpl.initializeV2, (defaultDeletionVerifiers)); bytes memory upgradeCall = abi.encodeCall( - UUPSUpgradeable.upgradeToAndCall, (address(managerImplAddress), initCallV2) + UUPSUpgradeable.upgradeToAndCall, (address(managerImplV2Address), initCallV2) ); // Test diff --git a/src/test/identity-manager/WorldIDIdentityManagerDataQuery.t.sol b/src/test/identity-manager/WorldIDIdentityManagerDataQuery.t.sol index 2df1cb1..fa32ab9 100644 --- a/src/test/identity-manager/WorldIDIdentityManagerDataQuery.t.sol +++ b/src/test/identity-manager/WorldIDIdentityManagerDataQuery.t.sol @@ -7,6 +7,7 @@ import {SemaphoreTreeDepthValidator} from "../../utils/SemaphoreTreeDepthValidat import {SimpleVerify} from "../mock/SimpleVerifier.sol"; import {TypeConverter as TC} from "../utils/TypeConverter.sol"; import {VerifierLookupTable} from "../../data/VerifierLookupTable.sol"; +import {VerifierLookupTable4844} from "../../data/VerifierLookupTable4844.sol"; import {WorldIDIdentityManagerImplV2 as ManagerImpl} from "../../WorldIDIdentityManagerImplV2.sol"; import {WorldIDIdentityManagerImplV1 as ManagerImplV1} from "../../WorldIDIdentityManagerImplV1.sol"; @@ -24,6 +25,7 @@ contract WorldIDIdentityManagerDataQuery is WorldIDIdentityManagerTest { treeDepth, newPreRoot, defaultInsertVerifiers, + defaultInsertVerifiers4844, defaultDeletionVerifiers, defaultUpdateVerifiers, semaphoreVerifier @@ -50,12 +52,14 @@ contract WorldIDIdentityManagerDataQuery is WorldIDIdentityManagerTest { ( VerifierLookupTable insertVerifiers, VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers + VerifierLookupTable updateVerifiers, + VerifierLookupTable4844 insertVerifiers4844 ) = makeVerifierLookupTables(TC.makeDynArray([identities.length])); makeNewIdentityManager( treeDepth, newPreRoot, insertVerifiers, + insertVerifiers4844, deletionVerifiers, updateVerifiers, semaphoreVerifier @@ -91,12 +95,14 @@ contract WorldIDIdentityManagerDataQuery is WorldIDIdentityManagerTest { ( VerifierLookupTable insertVerifiers, VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers + VerifierLookupTable updateVerifiers, + VerifierLookupTable4844 insertVerifiers4844 ) = makeVerifierLookupTables(TC.makeDynArray([identities.length])); makeNewIdentityManager( treeDepth, newPreRoot, insertVerifiers, + insertVerifiers4844, deletionVerifiers, updateVerifiers, semaphoreVerifier @@ -128,7 +134,7 @@ contract WorldIDIdentityManagerDataQuery is WorldIDIdentityManagerTest { // Setup vm.assume(badRoot != initialRoot); bytes memory callData = abi.encodeCall(ManagerImplV1.queryRoot, badRoot); - bytes memory returnData = abi.encode(managerImpl.NO_SUCH_ROOT()); + bytes memory returnData = abi.encode(managerImplV2.NO_SUCH_ROOT()); // Test assertCallSucceedsOn(identityManagerAddress, callData, returnData); @@ -140,7 +146,7 @@ contract WorldIDIdentityManagerDataQuery is WorldIDIdentityManagerTest { vm.expectRevert("Function must be called through delegatecall"); // Test - managerImpl.queryRoot(initialRoot); + managerImplV2.queryRoot(initialRoot); } /// @notice Checks that it is possible to get the latest root from the contract. @@ -150,6 +156,7 @@ contract WorldIDIdentityManagerDataQuery is WorldIDIdentityManagerTest { treeDepth, actualRoot, defaultInsertVerifiers, + defaultInsertVerifiers4844, defaultDeletionVerifiers, defaultUpdateVerifiers, semaphoreVerifier @@ -167,7 +174,7 @@ contract WorldIDIdentityManagerDataQuery is WorldIDIdentityManagerTest { vm.expectRevert("Function must be called through delegatecall"); // Test - managerImpl.latestRoot(); + managerImplV2.latestRoot(); } /// @notice Checks that it is possible to get the tree depth the contract was initialized with. @@ -178,6 +185,7 @@ contract WorldIDIdentityManagerDataQuery is WorldIDIdentityManagerTest { actualTreeDepth, insertionPreRoot, defaultInsertVerifiers, + defaultInsertVerifiers4844, defaultDeletionVerifiers, defaultUpdateVerifiers, semaphoreVerifier diff --git a/src/test/identity-manager/WorldIDIdentityManagerGettersSetters.t.sol b/src/test/identity-manager/WorldIDIdentityManagerGettersSetters.t.sol index 921e387..d3a7d45 100644 --- a/src/test/identity-manager/WorldIDIdentityManagerGettersSetters.t.sol +++ b/src/test/identity-manager/WorldIDIdentityManagerGettersSetters.t.sol @@ -3,7 +3,6 @@ pragma solidity ^0.8.21; import {WorldIDIdentityManagerTest} from "./WorldIDIdentityManagerTest.sol"; -import {ITreeVerifier} from "../../interfaces/ITreeVerifier.sol"; import {SemaphoreVerifier} from "src/SemaphoreVerifier.sol"; import {SimpleVerifier, SimpleVerify} from "../mock/SimpleVerifier.sol"; import {TypeConverter as TC} from "../utils/TypeConverter.sol"; @@ -43,14 +42,15 @@ contract WorldIDIdentityManagerGettersSetters is WorldIDIdentityManagerTest { vm.expectRevert("Function must be called through delegatecall"); // Test - managerImpl.getRegisterIdentitiesVerifierLookupTableAddress(); + managerImplV2.getRegisterIdentitiesVerifierLookupTableAddress(); } /// @notice Checks that it is possible to set the lookup table currently being used to verify /// 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( ManagerImplV1.setRegisterIdentitiesVerifierLookupTable, (insertionVerifiers) @@ -74,7 +74,8 @@ 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( ManagerImplV1.setRegisterIdentitiesVerifierLookupTable, (insertionVerifiers) ); @@ -89,11 +90,12 @@ 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); + managerImplV2.setRegisterIdentitiesVerifierLookupTable(insertionVerifiers); } /// @notice Checks that it is possible to get the address of the contract currently being used @@ -115,14 +117,15 @@ contract WorldIDIdentityManagerGettersSetters is WorldIDIdentityManagerTest { vm.expectRevert("Function must be called through delegatecall"); // Test - managerImpl.getDeleteIdentitiesVerifierLookupTableAddress(); + managerImplV2.getDeleteIdentitiesVerifierLookupTableAddress(); } /// @notice Checks that it is possible to set the lookup table currently being used to verify /// identity deletion proofs. function testCanSetDeleteIdentitiesVerifierLookupTable() public { // Setup - (, VerifierLookupTable deletionVerifiers,) = makeVerifierLookupTables(TC.makeDynArray([40])); + (,, VerifierLookupTable deletionVerifiers,) = + makeVerifierLookupTables(TC.makeDynArray([40])); address newVerifiersAddress = address(deletionVerifiers); bytes memory callData = abi.encodeCall(ManagerImpl.setDeleteIdentitiesVerifierLookupTable, (deletionVerifiers)); @@ -143,7 +146,8 @@ contract WorldIDIdentityManagerGettersSetters is WorldIDIdentityManagerTest { function testCannotSetDeleteIdentitiesVerifierLookupTableUnlessOwner(address notOwner) public { // Setup vm.assume(notOwner != address(this) && notOwner != address(0x0)); - (, VerifierLookupTable deletionVerifiers,) = makeVerifierLookupTables(TC.makeDynArray([40])); + (,, VerifierLookupTable deletionVerifiers,) = + makeVerifierLookupTables(TC.makeDynArray([40])); bytes memory callData = abi.encodeCall( ManagerImplV1.setRegisterIdentitiesVerifierLookupTable, (deletionVerifiers) ); @@ -158,11 +162,12 @@ contract WorldIDIdentityManagerGettersSetters is WorldIDIdentityManagerTest { /// identity deletion unless called via the proxy. function testCannotSetDeleteIdentitiesVerifierLookupTableUnlessViaProxy() public { // Setup - (, VerifierLookupTable deletionVerifiers,) = makeVerifierLookupTables(TC.makeDynArray([40])); + (,, VerifierLookupTable deletionVerifiers,) = + makeVerifierLookupTables(TC.makeDynArray([40])); vm.expectRevert("Function must be called through delegatecall"); // Test - managerImpl.setDeleteIdentitiesVerifierLookupTable(deletionVerifiers); + managerImplV2.setDeleteIdentitiesVerifierLookupTable(deletionVerifiers); } /// @notice Ensures that we can get the address of the semaphore verifier. @@ -181,7 +186,7 @@ contract WorldIDIdentityManagerGettersSetters is WorldIDIdentityManagerTest { vm.expectRevert("Function must be called through delegatecall"); // Test - managerImpl.getSemaphoreVerifierAddress(); + managerImplV2.getSemaphoreVerifierAddress(); } /// @notice Checks that it is possible to set the contract currently being used to verify @@ -224,7 +229,7 @@ contract WorldIDIdentityManagerGettersSetters is WorldIDIdentityManagerTest { vm.expectRevert("Function must be called through delegatecall"); // Test - managerImpl.setSemaphoreVerifier(newVerifier); + managerImplV2.setSemaphoreVerifier(newVerifier); } /// @notice Ensures that it's possible to get the root history expiry time. @@ -243,7 +248,7 @@ contract WorldIDIdentityManagerGettersSetters is WorldIDIdentityManagerTest { vm.expectRevert("Function must be called through delegatecall"); // Test - managerImpl.getRootHistoryExpiry(); + managerImplV2.getRootHistoryExpiry(); } /// @notice Ensures that it is possible to set the root history expiry time. @@ -290,6 +295,6 @@ contract WorldIDIdentityManagerGettersSetters is WorldIDIdentityManagerTest { vm.expectRevert("Function must be called through delegatecall"); // Test - managerImpl.setRootHistoryExpiry(newExpiry); + managerImplV2.setRootHistoryExpiry(newExpiry); } } diff --git a/src/test/identity-manager/WorldIDIdentityManagerIdentityDeletion.t.sol b/src/test/identity-manager/WorldIDIdentityManagerIdentityDeletion.t.sol index fcdd41c..af666f5 100644 --- a/src/test/identity-manager/WorldIDIdentityManagerIdentityDeletion.t.sol +++ b/src/test/identity-manager/WorldIDIdentityManagerIdentityDeletion.t.sol @@ -8,6 +8,7 @@ import {SimpleVerifier, SimpleVerify} from "../mock/SimpleVerifier.sol"; import {TypeConverter as TC} from "../utils/TypeConverter.sol"; import {Verifier as TreeVerifier} from "src/test/DeletionTreeVerifier16.sol"; import {VerifierLookupTable} from "../../data/VerifierLookupTable.sol"; +import {VerifierLookupTable4844} from "../../data/VerifierLookupTable4844.sol"; import {WorldIDIdentityManager as IdentityManager} from "../../WorldIDIdentityManager.sol"; import {WorldIDIdentityManagerImplV2 as ManagerImpl} from "../../WorldIDIdentityManagerImplV2.sol"; @@ -38,13 +39,15 @@ contract WorldIDIdentityManagerIdentityDeletion is WorldIDIdentityManagerTest { ( VerifierLookupTable insertVerifiers, VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers + VerifierLookupTable updateVerifiers, + VerifierLookupTable4844 insertVerifiers4844 ) = makeVerifierLookupTables(TC.makeDynArray([40])); deletionVerifiers.addVerifier(deletionBatchSize, actualVerifier); makeNewIdentityManager( treeDepth, deletionPreRoot, insertVerifiers, + insertVerifiers4844, deletionVerifiers, updateVerifiers, semaphoreVerifier @@ -85,12 +88,14 @@ contract WorldIDIdentityManagerIdentityDeletion is WorldIDIdentityManagerTest { ( VerifierLookupTable insertVerifiers, VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers + VerifierLookupTable updateVerifiers, + VerifierLookupTable4844 insertVerifiers4844 ) = makeVerifierLookupTables(TC.makeDynArray([packedDeletionIndices.length / 4])); makeNewIdentityManager( treeDepth, newPreRoot, insertVerifiers, + insertVerifiers4844, deletionVerifiers, updateVerifiers, semaphoreVerifier @@ -130,12 +135,14 @@ contract WorldIDIdentityManagerIdentityDeletion is WorldIDIdentityManagerTest { ( VerifierLookupTable insertVerifiers, VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers + VerifierLookupTable updateVerifiers, + VerifierLookupTable4844 insertVerifiers4844 ) = makeVerifierLookupTables(TC.makeDynArray([deletionBatchSize, secondIndices.length / 4])); makeNewIdentityManager( treeDepth, newPreRoot, insertVerifiers, + insertVerifiers4844, deletionVerifiers, updateVerifiers, semaphoreVerifier @@ -176,7 +183,8 @@ contract WorldIDIdentityManagerIdentityDeletion is WorldIDIdentityManagerTest { ( VerifierLookupTable insertVerifiers, VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers + VerifierLookupTable updateVerifiers, + VerifierLookupTable4844 insertVerifiers4844 ) = // the -1 offsets the correct batch size by 1 thus causing the error makeVerifierLookupTables(TC.makeDynArray([(packedDeletionIndices.length / 4) - 1])); @@ -184,6 +192,7 @@ contract WorldIDIdentityManagerIdentityDeletion is WorldIDIdentityManagerTest { treeDepth, newPreRoot, insertVerifiers, + insertVerifiers4844, deletionVerifiers, updateVerifiers, semaphoreVerifier @@ -214,13 +223,15 @@ contract WorldIDIdentityManagerIdentityDeletion is WorldIDIdentityManagerTest { ( VerifierLookupTable insertVerifiers, VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers + VerifierLookupTable updateVerifiers, + VerifierLookupTable4844 insertVerifiers4844 ) = makeVerifierLookupTables(TC.makeDynArray([70])); deletionVerifiers.addVerifier(indicesLength, actualVerifier); makeNewIdentityManager( treeDepth, newPreRoot, insertVerifiers, + insertVerifiers4844, deletionVerifiers, updateVerifiers, semaphoreVerifier @@ -245,13 +256,15 @@ contract WorldIDIdentityManagerIdentityDeletion is WorldIDIdentityManagerTest { ( VerifierLookupTable insertVerifiers, VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers + VerifierLookupTable updateVerifiers, + VerifierLookupTable4844 insertVerifiers4844 ) = makeVerifierLookupTables(TC.makeDynArray([70])); deletionVerifiers.addVerifier(deletionBatchSize, actualVerifier); makeNewIdentityManager( treeDepth, deletionPreRoot, insertVerifiers, + insertVerifiers4844, deletionVerifiers, updateVerifiers, semaphoreVerifier @@ -299,6 +312,7 @@ contract WorldIDIdentityManagerIdentityDeletion is WorldIDIdentityManagerTest { treeDepth, uint256(currentPreRoot), defaultInsertVerifiers, + defaultInsertVerifiers4844, defaultDeletionVerifiers, defaultUpdateVerifiers, semaphoreVerifier @@ -318,12 +332,12 @@ contract WorldIDIdentityManagerIdentityDeletion is WorldIDIdentityManagerTest { /// @notice Tests that identities can only be deleted through the proxy. function testCannotDelteIdentitiesIfNotViaProxy() public { // Setup - address expectedOwner = managerImpl.owner(); + address expectedOwner = managerImplV2.owner(); vm.expectRevert("Function must be called through delegatecall"); vm.prank(expectedOwner); // Test - managerImpl.deleteIdentities( + managerImplV2.deleteIdentities( deletionProof, packedDeletionIndices, initialRoot, deletionPostRoot ); } diff --git a/src/test/identity-manager/WorldIDIdentityManagerIdentityRegistration.t.sol b/src/test/identity-manager/WorldIDIdentityManagerIdentityRegistration.t.sol index 48e2288..c08e8e5 100644 --- a/src/test/identity-manager/WorldIDIdentityManagerIdentityRegistration.t.sol +++ b/src/test/identity-manager/WorldIDIdentityManagerIdentityRegistration.t.sol @@ -9,7 +9,9 @@ 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 "src/test/InsertionTreeVerifier16.sol"; +import {Verifier as TreeVerifier4844} from "src/test/InsertionTreeVerifier164844.sol"; import {VerifierLookupTable} from "../../data/VerifierLookupTable.sol"; +import {VerifierLookupTable4844} from "../../data/VerifierLookupTable4844.sol"; import {WorldIDIdentityManager as IdentityManager} from "../../WorldIDIdentityManager.sol"; import {WorldIDIdentityManagerImplV2 as ManagerImpl} from "../../WorldIDIdentityManagerImplV2.sol"; @@ -38,13 +40,15 @@ contract WorldIDIdentityManagerIdentityRegistration is WorldIDIdentityManagerTes ( VerifierLookupTable insertVerifiers, VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers + VerifierLookupTable updateVerifiers, + VerifierLookupTable4844 insertVerifiers4844 ) = makeVerifierLookupTables(TC.makeDynArray([40])); insertVerifiers.addVerifier(identityCommitmentsSize, actualVerifier); makeNewIdentityManager( treeDepth, insertionPreRoot, insertVerifiers, + insertVerifiers4844, deletionVerifiers, updateVerifiers, semaphoreVerifier @@ -86,12 +90,14 @@ contract WorldIDIdentityManagerIdentityRegistration is WorldIDIdentityManagerTes ( VerifierLookupTable insertVerifiers, VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers + VerifierLookupTable updateVerifiers, + VerifierLookupTable4844 insertVerifiers4844 ) = makeVerifierLookupTables(TC.makeDynArray([identities.length])); makeNewIdentityManager( treeDepth, newPreRoot, insertVerifiers, + insertVerifiers4844, deletionVerifiers, updateVerifiers, semaphoreVerifier @@ -133,12 +139,14 @@ contract WorldIDIdentityManagerIdentityRegistration is WorldIDIdentityManagerTes ( VerifierLookupTable insertVerifiers, VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers + VerifierLookupTable updateVerifiers, + VerifierLookupTable4844 insertVerifiers4844 ) = makeVerifierLookupTables(TC.makeDynArray([identities.length, secondIdentsLength])); makeNewIdentityManager( treeDepth, newPreRoot, insertVerifiers, + insertVerifiers4844, deletionVerifiers, updateVerifiers, semaphoreVerifier @@ -185,12 +193,14 @@ contract WorldIDIdentityManagerIdentityRegistration is WorldIDIdentityManagerTes ( VerifierLookupTable insertVerifiers, VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers + VerifierLookupTable updateVerifiers, + VerifierLookupTable4844 insertVerifiers4844 ) = makeVerifierLookupTables(TC.makeDynArray([identities.length - 1])); makeNewIdentityManager( treeDepth, newPreRoot, insertVerifiers, + insertVerifiers4844, deletionVerifiers, updateVerifiers, semaphoreVerifier @@ -221,13 +231,15 @@ contract WorldIDIdentityManagerIdentityRegistration is WorldIDIdentityManagerTes ( VerifierLookupTable insertVerifiers, VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers + VerifierLookupTable updateVerifiers, + VerifierLookupTable4844 insertVerifiers4844 ) = makeVerifierLookupTables(TC.makeDynArray([70])); insertVerifiers.addVerifier(identityCommitments.length, actualVerifier); makeNewIdentityManager( treeDepth, newPreRoot, insertVerifiers, + insertVerifiers4844, deletionVerifiers, updateVerifiers, semaphoreVerifier @@ -251,13 +263,15 @@ contract WorldIDIdentityManagerIdentityRegistration is WorldIDIdentityManagerTes ( VerifierLookupTable insertVerifiers, VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers + VerifierLookupTable updateVerifiers, + VerifierLookupTable4844 insertVerifiers4844 ) = makeVerifierLookupTables(TC.makeDynArray([70])); insertVerifiers.addVerifier(identityCommitmentsSize, actualVerifier); makeNewIdentityManager( treeDepth, insertionPreRoot, insertVerifiers, + insertVerifiers4844, deletionVerifiers, updateVerifiers, semaphoreVerifier @@ -293,13 +307,15 @@ contract WorldIDIdentityManagerIdentityRegistration is WorldIDIdentityManagerTes ( VerifierLookupTable insertVerifiers, VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers + VerifierLookupTable updateVerifiers, + VerifierLookupTable4844 insertVerifiers4844 ) = makeVerifierLookupTables(TC.makeDynArray([70])); insertVerifiers.addVerifier(identityCommitmentsSize, actualVerifier); makeNewIdentityManager( treeDepth, insertionPreRoot, insertVerifiers, + insertVerifiers4844, deletionVerifiers, updateVerifiers, semaphoreVerifier @@ -319,13 +335,13 @@ contract WorldIDIdentityManagerIdentityRegistration is WorldIDIdentityManagerTes function testCannotRegisterIdentitiesIfPostRootIncorrect(uint256 newPostRoot) public { // Setup vm.assume(newPostRoot != insertionPostRoot && newPostRoot < SNARK_SCALAR_FIELD); - managerImpl = new ManagerImpl(); - managerImplAddress = address(managerImpl); + managerImplV2 = new ManagerImpl(); + managerImplV2Address = address(managerImplV2); ITreeVerifier actualVerifier = new TreeVerifier(); ( VerifierLookupTable insertVerifiers, VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers + VerifierLookupTable updateVerifiers, ) = makeVerifierLookupTables(TC.makeDynArray([70])); insertVerifiers.addVerifier(identityCommitmentsSize, actualVerifier); @@ -334,13 +350,13 @@ contract WorldIDIdentityManagerIdentityRegistration is WorldIDIdentityManagerTes (treeDepth, insertionPreRoot, insertVerifiers, updateVerifiers, semaphoreVerifier) ); - identityManager = new IdentityManager(managerImplAddress, callData); + identityManager = new IdentityManager(managerImplV2Address, callData); identityManagerAddress = address(identityManager); // Init V2 bytes memory initCallV2 = abi.encodeCall(ManagerImpl.initializeV2, (deletionVerifiers)); bytes memory upgradeCall = abi.encodeCall( - UUPSUpgradeable.upgradeToAndCall, (address(managerImplAddress), initCallV2) + UUPSUpgradeable.upgradeToAndCall, (address(managerImplV2Address), initCallV2) ); assertCallSucceedsOn(identityManagerAddress, upgradeCall, new bytes(0x0)); @@ -387,6 +403,7 @@ contract WorldIDIdentityManagerIdentityRegistration is WorldIDIdentityManagerTes treeDepth, uint256(currentPreRoot), defaultInsertVerifiers, + defaultInsertVerifiers4844, defaultDeletionVerifiers, defaultUpdateVerifiers, semaphoreVerifier @@ -415,12 +432,14 @@ contract WorldIDIdentityManagerIdentityRegistration is WorldIDIdentityManagerTes ( VerifierLookupTable insertVerifiers, VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers + VerifierLookupTable updateVerifiers, + VerifierLookupTable4844 insertVerifiers4844 ) = makeVerifierLookupTables(TC.makeDynArray([identitiesLength])); makeNewIdentityManager( treeDepth, initialRoot, insertVerifiers, + insertVerifiers4844, deletionVerifiers, updateVerifiers, semaphoreVerifier @@ -451,12 +470,12 @@ contract WorldIDIdentityManagerIdentityRegistration is WorldIDIdentityManagerTes /// @notice Tests that identities can only be registered through the proxy. function testCannotRegisterIdentitiesIfNotViaProxy() public { // Setup - address expectedOwner = managerImpl.owner(); + address expectedOwner = managerImplV2.owner(); vm.expectRevert("Function must be called through delegatecall"); vm.prank(expectedOwner); // Test - managerImpl.registerIdentities( + managerImplV2.registerIdentities( insertionProof, initialRoot, startIndex, identityCommitments, insertionPostRoot ); } diff --git a/src/test/identity-manager/WorldIDIdentityManagerIdentityRegistration4844.t.sol b/src/test/identity-manager/WorldIDIdentityManagerIdentityRegistration4844.t.sol new file mode 100644 index 0000000..e42522f --- /dev/null +++ b/src/test/identity-manager/WorldIDIdentityManagerIdentityRegistration4844.t.sol @@ -0,0 +1,712 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import {UUPSUpgradeable} from "contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; + +import {WorldIDIdentityManagerTest} from "./WorldIDIdentityManagerTest.sol"; + +import {ITreeVerifier4844 as ITreeVerifier} from "../../interfaces/ITreeVerifier4844.sol"; +import {SimpleVerify} from "../mock/SimpleVerifier.sol"; +import {TypeConverter as TC} from "../utils/TypeConverter.sol"; +import {Verifier as TreeVerifier} from "src/test/InsertionTreeVerifier164844.sol"; +import {VerifierLookupTable} from "../../data/VerifierLookupTable.sol"; +import {VerifierLookupTable4844} from "../../data/VerifierLookupTable4844.sol"; + +import {WorldIDIdentityManager as IdentityManager} from "../../WorldIDIdentityManager.sol"; +import {WorldIDIdentityManagerImplV2 as ManagerImpl} from "../../WorldIDIdentityManagerImplV2.sol"; +import {WorldIDIdentityManagerImplV1 as ManagerImplV1} from "../../WorldIDIdentityManagerImplV1.sol"; +import {WorldIDIdentityManagerImplV3 as ManagerImplV3} from "../../WorldIDIdentityManagerImplV3.sol"; + +/// @title World ID Identity Manager Identity Registration Tests for EIP-4844 protocol. +/// @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 WorldIDIdentityManagerIdentityRegistration4844 is WorldIDIdentityManagerTest { + /// Taken from SimpleVerifier.sol + event VerifiedProof(uint256 batchSize); + + /// Taken from WorldIDIdentityManagerImplV1.sol + event TreeChanged( + uint256 indexed insertionPreRoot, + ManagerImplV1.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 deletionVerifiers, + VerifierLookupTable updateVerifiers, + VerifierLookupTable4844 insertVerifiers4844 + ) = makeVerifierLookupTables(TC.makeDynArray([40])); + insertVerifiers4844.addVerifier(identityCommitmentsSize, actualVerifier); + makeNewIdentityManager( + treeDepth, + insertionPreRoot, + insertVerifiers, + insertVerifiers4844, + deletionVerifiers, + updateVerifiers, + semaphoreVerifier + ); + + ManagerImplV3.RegisterIdentities4844Params memory params = ManagerImplV3 + .RegisterIdentities4844Params({ + insertionProof: insertionProof4844, + commitments: commitments, + commitmentPok: commitmentsPok, + kzgCommitment: kzgCommitment, + kzgProof: kzgProof, + expectedEvaluation: insertionExpectedEvaluation, + preRoot: insertionPreRoot, + postRoot: insertionPostRoot4844, + inputHash: insertionInputHash4844, + batchSize: uint32(identityCommitmentsSize), + startIndex: startIndex + }); + + // Mock blobhash. This is valid for the next call only. + prepareBlobhash(kzgToVersionedHash(kzgCommitment)); + + bytes memory registerCallData = abi.encodeCall(ManagerImplV3.registerIdentities4844, params); + + bytes memory latestRootCallData = abi.encodeCall(ManagerImplV1.latestRoot, ()); + bytes memory queryRootCallData = + abi.encodeCall(ManagerImplV1.queryRoot, (insertionPostRoot4844)); + + // Test + assertCallSucceedsOn(identityManagerAddress, registerCallData); + assertCallSucceedsOn( + identityManagerAddress, latestRootCallData, abi.encode(insertionPostRoot4844) + ); + assertCallSucceedsOn( + identityManagerAddress, + queryRootCallData, + abi.encode(ManagerImplV1.RootInfo(insertionPostRoot4844, 0, true)) + ); + } + + /// @notice Checks that the proof validates properly with correct inputs. + function testRegisterIdentitiesWithCorrectInputs( + uint128[8] memory prf, + uint32 newStartIndex, + uint128 newPreRoot, + uint128 newPostRoot, + uint128[] memory identities, + address identityOperator + ) public { + // Setup + vm.assume(SimpleVerify.isValidInput(uint256(prf[0]))); + vm.assume(newPreRoot != newPostRoot); + vm.assume(identities.length <= 1000); + vm.assume(identityOperator != nullAddress && identityOperator != thisAddress); + ( + VerifierLookupTable insertVerifiers, + VerifierLookupTable deletionVerifiers, + VerifierLookupTable updateVerifiers, + VerifierLookupTable4844 insertVerifiers4844 + ) = makeVerifierLookupTables(TC.makeDynArray([identities.length])); + makeNewIdentityManager( + treeDepth, + newPreRoot, + insertVerifiers, + insertVerifiers4844, + deletionVerifiers, + updateVerifiers, + semaphoreVerifier + ); + + (uint256[] memory preparedIdents, uint256[8] memory actualProof) = + prepareInsertIdentitiesTestCase(identities, prf); + + ManagerImplV3.RegisterIdentities4844Params memory params = ManagerImplV3 + .RegisterIdentities4844Params({ + insertionProof: actualProof, + commitments: commitments, + commitmentPok: commitmentsPok, + kzgCommitment: kzgCommitment, + kzgProof: kzgProof, + expectedEvaluation: insertionExpectedEvaluation, + preRoot: newPreRoot, + postRoot: newPostRoot, + inputHash: insertionInputHash4844, + batchSize: uint32(preparedIdents.length), + startIndex: newStartIndex + }); + + // Mock blobhash. This is valid for the next call only. + prepareBlobhash(kzgToVersionedHash(kzgCommitment)); + bytes memory callData = abi.encodeCall(ManagerImplV3.registerIdentities4844, params); + + 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.Insertion, newPostRoot); + vm.prank(identityOperator); + + // Test + assertCallSucceedsOn(identityManagerAddress, callData); + } + + /// @notice Ensures that identity registration selects the correct verifier when registering + /// identities. + function testRegisterIdentitiesSelectsCorrectVerifier( + uint128[8] memory prf, + uint32 newStartIndex, + uint128 newPreRoot, + uint128 newPostRoot, + uint128[] memory identities + ) public { + vm.assume(SimpleVerify.isValidInput(uint256(prf[0]))); + vm.assume(newPreRoot != newPostRoot); + vm.assume(identities.length <= 1000 && identities.length > 0); + uint256 secondIdentsLength = identities.length / 2; + ( + VerifierLookupTable insertVerifiers, + VerifierLookupTable deletionVerifiers, + VerifierLookupTable updateVerifiers, + VerifierLookupTable4844 insertVerifiers4844 + ) = makeVerifierLookupTables(TC.makeDynArray([identities.length, secondIdentsLength])); + makeNewIdentityManager( + treeDepth, + newPreRoot, + insertVerifiers, + insertVerifiers4844, + deletionVerifiers, + updateVerifiers, + semaphoreVerifier + ); + (uint256[] memory preparedIdents, uint256[8] memory actualProof) = + prepareInsertIdentitiesTestCase(identities, prf); + uint256[] memory secondIdents = new uint256[](secondIdentsLength); + for (uint256 i = 0; i < secondIdentsLength; ++i) { + secondIdents[i] = preparedIdents[i]; + } + + ManagerImplV3.RegisterIdentities4844Params memory params = ManagerImplV3 + .RegisterIdentities4844Params({ + insertionProof: actualProof, + commitments: commitments, + commitmentPok: commitmentsPok, + kzgCommitment: kzgCommitment, + kzgProof: kzgProof, + expectedEvaluation: insertionExpectedEvaluation, + preRoot: newPreRoot, + postRoot: newPostRoot, + inputHash: insertionInputHash4844, + batchSize: uint32(preparedIdents.length), + startIndex: newStartIndex + }); + + // Mock blobhash. This is valid for the next call only. + prepareBlobhash(kzgToVersionedHash(kzgCommitment)); + bytes memory firstCallData = abi.encodeCall(ManagerImplV3.registerIdentities4844, params); + + uint256 secondPostRoot = uint256(newPostRoot) + 1; + params.preRoot = newPostRoot; + params.batchSize = uint32(secondIdentsLength); + params.postRoot = secondPostRoot; + + // Mock blobhash. This is valid for the next call only. + prepareBlobhash(kzgToVersionedHash(kzgCommitment)); + bytes memory secondCallData = abi.encodeCall(ManagerImplV3.registerIdentities4844, params); + + vm.expectEmit(true, true, true, true); + emit VerifiedProof(identities.length); + + // Test + assertCallSucceedsOn(identityManagerAddress, firstCallData); + + vm.expectEmit(true, true, true, true); + emit VerifiedProof(identities.length / 2); + + assertCallSucceedsOn(identityManagerAddress, secondCallData); + } + + /// @notice Ensures that the contract reverts if passed a batch size it doesn't know about. + function testCannotRegisterIdentitiesWithInvalidBatchSize( + uint128[8] memory prf, + uint32 newStartIndex, + uint128 newPreRoot, + uint128 newPostRoot, + uint128[] memory identities + ) public { + vm.assume(SimpleVerify.isValidInput(uint256(prf[0]))); + vm.assume(newPreRoot != newPostRoot); + vm.assume(identities.length > 0); + ( + VerifierLookupTable insertVerifiers, + VerifierLookupTable deletionVerifiers, + VerifierLookupTable updateVerifiers, + VerifierLookupTable4844 insertVerifiers4844 + ) = makeVerifierLookupTables(TC.makeDynArray([identities.length - 1])); + makeNewIdentityManager( + treeDepth, + newPreRoot, + insertVerifiers, + insertVerifiers4844, + deletionVerifiers, + updateVerifiers, + semaphoreVerifier + ); + (uint256[] memory preparedIdents, uint256[8] memory actualProof) = + prepareInsertIdentitiesTestCase(identities, prf); + + ManagerImplV3.RegisterIdentities4844Params memory params = ManagerImplV3 + .RegisterIdentities4844Params({ + insertionProof: actualProof, + commitments: commitments, + commitmentPok: commitmentsPok, + kzgCommitment: kzgCommitment, + kzgProof: kzgProof, + expectedEvaluation: insertionExpectedEvaluation, + preRoot: newPreRoot, + postRoot: newPostRoot, + inputHash: insertionInputHash4844, + batchSize: uint32(preparedIdents.length), + startIndex: newStartIndex + }); + + // Mock blobhash. This is valid for the next call only. + prepareBlobhash(kzgToVersionedHash(kzgCommitment)); + bytes memory callData = abi.encodeCall(ManagerImplV3.registerIdentities4844, params); + + bytes memory errorData = + abi.encodeWithSelector(VerifierLookupTable4844.NoSuchVerifier.selector); + + // Test + assertCallFailsOn(identityManagerAddress, callData, errorData); + } + + /// @notice Checks that it reverts if the provided proof is incorrect for the public inputs. + function testCannotRegisterIdentitiesWithIncorrectInputs( + uint128[8] memory prf, + uint32 newStartIndex, + uint128 newPreRoot, + uint128 newPostRoot + ) public { + // Setup + vm.assume(!SimpleVerify.isValidInput(uint256(prf[0]))); + ITreeVerifier actualVerifier = new TreeVerifier(); + ( + VerifierLookupTable insertVerifiers, + VerifierLookupTable deletionVerifiers, + VerifierLookupTable updateVerifiers, + VerifierLookupTable4844 insertVerifiers4844 + ) = makeVerifierLookupTables(TC.makeDynArray([70])); + insertVerifiers4844.addVerifier(identityCommitments.length, actualVerifier); + makeNewIdentityManager( + treeDepth, + newPreRoot, + insertVerifiers, + insertVerifiers4844, + deletionVerifiers, + updateVerifiers, + semaphoreVerifier + ); + + ManagerImplV3.RegisterIdentities4844Params memory params = ManagerImplV3 + .RegisterIdentities4844Params({ + insertionProof: insertionProof, + commitments: commitments, + commitmentPok: commitmentsPok, + kzgCommitment: kzgCommitment, + kzgProof: kzgProof, + expectedEvaluation: insertionExpectedEvaluation, + preRoot: newPreRoot, + postRoot: newPostRoot, + inputHash: insertionInputHash4844, + batchSize: uint32(identityCommitments.length), + startIndex: newStartIndex + }); + + // Mock blobhash. This is valid for the next call only. + prepareBlobhash(kzgToVersionedHash(kzgCommitment)); + bytes memory callData = abi.encodeCall(ManagerImplV3.registerIdentities4844, params); + + bytes memory expectedError = + abi.encodeWithSelector(ManagerImplV1.ProofValidationFailure.selector); + + // Test + assertCallFailsOn(identityManagerAddress, callData, expectedError); + } + + /// @notice Checks that it reverts if the provided start index is incorrect. + function testCannotRegisterIdentitiesIfStartIndexIncorrect(uint32 newStartIndex) public { + // Setup + vm.assume(newStartIndex != startIndex); + ITreeVerifier actualVerifier = new TreeVerifier(); + ( + VerifierLookupTable insertVerifiers, + VerifierLookupTable deletionVerifiers, + VerifierLookupTable updateVerifiers, + VerifierLookupTable4844 insertVerifiers4844 + ) = makeVerifierLookupTables(TC.makeDynArray([70])); + insertVerifiers4844.addVerifier(identityCommitmentsSize, actualVerifier); + makeNewIdentityManager( + treeDepth, + insertionPreRoot, + insertVerifiers, + insertVerifiers4844, + deletionVerifiers, + updateVerifiers, + semaphoreVerifier + ); + + ManagerImplV3.RegisterIdentities4844Params memory params = ManagerImplV3 + .RegisterIdentities4844Params({ + insertionProof: insertionProof, + commitments: commitments, + commitmentPok: commitmentsPok, + kzgCommitment: kzgCommitment, + kzgProof: kzgProof, + expectedEvaluation: insertionExpectedEvaluation, + preRoot: insertionPreRoot, + postRoot: insertionPostRoot, + inputHash: insertionInputHash4844, + batchSize: uint32(identityCommitments.length), + startIndex: newStartIndex + }); + + // Mock blobhash. This is valid for the next call only. + prepareBlobhash(kzgToVersionedHash(kzgCommitment)); + bytes memory registerCallData = abi.encodeCall(ManagerImplV3.registerIdentities4844, params); + + bytes memory expectedError = + abi.encodeWithSelector(ManagerImplV1.ProofValidationFailure.selector); + + // Test + assertCallFailsOn(identityManagerAddress, registerCallData, expectedError); + } + + /// @notice Checks that it reverts if the provided post root is incorrect. + function testCannotRegisterIdentitiesIfPostRootIncorrect(uint256 newPostRoot) public { + // Setup + vm.assume(newPostRoot != insertionPostRoot && newPostRoot < SNARK_SCALAR_FIELD); + managerImplV2 = new ManagerImpl(); + managerImplV2Address = address(managerImplV2); + ITreeVerifier actualVerifier = new TreeVerifier(); + ( + VerifierLookupTable insertVerifiers, + VerifierLookupTable deletionVerifiers, + VerifierLookupTable updateVerifiers, + VerifierLookupTable4844 insertVerifiers4844 + ) = makeVerifierLookupTables(TC.makeDynArray([70])); + insertVerifiers4844.addVerifier(identityCommitmentsSize, actualVerifier); + + bytes memory callData = abi.encodeCall( + ManagerImplV1.initialize, + (treeDepth, insertionPreRoot, insertVerifiers, updateVerifiers, semaphoreVerifier) + ); + + identityManager = new IdentityManager(managerImplV2Address, callData); + identityManagerAddress = address(identityManager); + + // Init V2 + bytes memory initCallV2 = abi.encodeCall(ManagerImpl.initializeV2, (deletionVerifiers)); + bytes memory upgradeCall = abi.encodeCall( + UUPSUpgradeable.upgradeToAndCall, (address(managerImplV2Address), initCallV2) + ); + assertCallSucceedsOn(identityManagerAddress, upgradeCall, new bytes(0x0)); + + // Init V3 + managerImplV3 = new ManagerImplV3(); + managerImplV3Address = address(managerImplV3); + bytes memory initCallV3 = abi.encodeCall(managerImplV3.initializeV3, (insertVerifiers4844)); + bytes memory upgradeCallV3 = abi.encodeCall( + UUPSUpgradeable.upgradeToAndCall, (address(managerImplV3Address), initCallV3) + ); + assertCallSucceedsOn(identityManagerAddress, upgradeCallV3, new bytes(0x0)); + + ManagerImplV3.RegisterIdentities4844Params memory params = ManagerImplV3 + .RegisterIdentities4844Params({ + insertionProof: insertionProof, + commitments: commitments, + commitmentPok: commitmentsPok, + kzgCommitment: kzgCommitment, + kzgProof: kzgProof, + expectedEvaluation: insertionExpectedEvaluation, + preRoot: insertionPreRoot, + postRoot: newPostRoot, + inputHash: insertionInputHash4844, + batchSize: uint32(identityCommitments.length), + startIndex: startIndex + }); + + // Mock blobhash. This is valid for the next call only. + prepareBlobhash(kzgToVersionedHash(kzgCommitment)); + bytes memory registerCallData = abi.encodeCall(ManagerImplV3.registerIdentities4844, params); + + bytes memory expectedError = + abi.encodeWithSelector(ManagerImplV1.ProofValidationFailure.selector); + + // Test + assertCallFailsOn(identityManagerAddress, registerCallData, expectedError); + } + + /// @notice Tests that it reverts if an attempt is made to register identities as an address + /// that is not the identity operator address. + function testCannotRegisterIdentitiesAsNonIdentityOperator(address nonOperator) public { + // Setup + vm.assume(nonOperator != address(this) && nonOperator != address(0x0)); + + ManagerImplV3.RegisterIdentities4844Params memory params = ManagerImplV3 + .RegisterIdentities4844Params({ + insertionProof: insertionProof, + commitments: commitments, + commitmentPok: commitmentsPok, + kzgCommitment: kzgCommitment, + kzgProof: kzgProof, + expectedEvaluation: insertionExpectedEvaluation, + preRoot: insertionPreRoot, + postRoot: insertionPostRoot, + inputHash: insertionInputHash4844, + batchSize: uint32(identityCommitments.length), + startIndex: startIndex + }); + + // Mock blobhash. This is valid for the next call only. + prepareBlobhash(kzgToVersionedHash(kzgCommitment)); + bytes memory callData = abi.encodeCall(ManagerImplV3.registerIdentities4844, params); + + 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 register identities with an outdated + /// root. + function testCannotRegisterIdentitiesWithOutdatedRoot( + uint256 currentPreRoot, + uint256 actualRoot + ) public { + // Setup + vm.assume( + currentPreRoot != actualRoot && currentPreRoot < SNARK_SCALAR_FIELD + && actualRoot < SNARK_SCALAR_FIELD + ); + makeNewIdentityManager( + treeDepth, + uint256(currentPreRoot), + defaultInsertVerifiers, + defaultInsertVerifiers4844, + defaultDeletionVerifiers, + defaultUpdateVerifiers, + semaphoreVerifier + ); + + ManagerImplV3.RegisterIdentities4844Params memory params = ManagerImplV3 + .RegisterIdentities4844Params({ + insertionProof: insertionProof, + commitments: commitments, + commitmentPok: commitmentsPok, + kzgCommitment: kzgCommitment, + kzgProof: kzgProof, + expectedEvaluation: insertionExpectedEvaluation, + preRoot: actualRoot, + postRoot: insertionPostRoot, + inputHash: insertionInputHash4844, + batchSize: uint32(identityCommitments.length), + startIndex: startIndex + }); + + // Mock blobhash. This is valid for the next call only. + prepareBlobhash(kzgToVersionedHash(kzgCommitment)); + bytes memory callData = abi.encodeCall(ManagerImplV3.registerIdentities4844, params); + + bytes memory expectedError = abi.encodeWithSelector( + ManagerImplV1.NotLatestRoot.selector, actualRoot, uint256(currentPreRoot) + ); + + // Test + assertCallFailsOn(identityManagerAddress, callData, expectedError); + } + + /// @notice Tests that runs of zeroes are accepted by the `registerIdentities4844` function as valid + /// arrays of identity commitments. + function testRegisterIdentitiesWithRunsOfZeroes(uint8 identitiesLength, uint8 zeroPosition) + public + { + // Setup + vm.assume(identitiesLength != 0 && identitiesLength <= 1000); + vm.assume(zeroPosition < identitiesLength && zeroPosition > 0); + uint256[] memory identities = new uint256[](identitiesLength); + ( + VerifierLookupTable insertVerifiers, + VerifierLookupTable deletionVerifiers, + VerifierLookupTable updateVerifiers, + VerifierLookupTable4844 insertVerifiers4844 + ) = makeVerifierLookupTables(TC.makeDynArray([identitiesLength])); + makeNewIdentityManager( + treeDepth, + initialRoot, + insertVerifiers, + insertVerifiers4844, + deletionVerifiers, + updateVerifiers, + semaphoreVerifier + ); + + for (uint256 i = 0; i < zeroPosition; ++i) { + identities[i] = i + 1; + } + for (uint256 i = zeroPosition; i < identitiesLength; ++i) { + identities[i] = 0x0; + } + + ManagerImplV3.RegisterIdentities4844Params memory params = ManagerImplV3 + .RegisterIdentities4844Params({ + insertionProof: [uint256(2), 1, 3, 4, 5, 6, 7, 9], + commitments: commitments, + commitmentPok: commitmentsPok, + kzgCommitment: kzgCommitment, + kzgProof: kzgProof, + expectedEvaluation: insertionExpectedEvaluation, + preRoot: initialRoot, + postRoot: insertionPostRoot, + inputHash: insertionInputHash4844, + batchSize: uint32(identities.length), + startIndex: startIndex + }); + + // Mock blobhash. This is valid for the next call only. + prepareBlobhash(kzgToVersionedHash(kzgCommitment)); + bytes memory callData = abi.encodeCall(ManagerImplV3.registerIdentities4844, params); + + // Test + assertCallSucceedsOn(identityManagerAddress, callData, new bytes(0)); + } + + /// @notice Tests that identities can only be registered through the proxy. + function testCannotRegisterIdentitiesIfNotViaProxy() public { + // Setup + address expectedOwner = managerImplV3.owner(); + vm.expectRevert("Function must be called through delegatecall"); + vm.prank(expectedOwner); + + // Test + ManagerImplV3.RegisterIdentities4844Params memory params = ManagerImplV3 + .RegisterIdentities4844Params({ + insertionProof: insertionProof, + commitments: commitments, + commitmentPok: commitmentsPok, + kzgCommitment: kzgCommitment, + kzgProof: kzgProof, + expectedEvaluation: insertionExpectedEvaluation, + preRoot: initialRoot, + postRoot: insertionPostRoot, + inputHash: insertionInputHash4844, + batchSize: uint32(identityCommitments.length), + startIndex: startIndex + }); + + managerImplV3.registerIdentities4844(params); + } + + /// @notice Checks that the transaction fails if KZG proof cannot be verified. + function testRegisterIdentitiesWithBadKzgProof() public { + // Setup + ITreeVerifier actualVerifier = new TreeVerifier(); + ( + VerifierLookupTable insertVerifiers, + VerifierLookupTable deletionVerifiers, + VerifierLookupTable updateVerifiers, + VerifierLookupTable4844 insertionVerifier4844 + ) = makeVerifierLookupTables(TC.makeDynArray([40])); + insertionVerifier4844.addVerifier(identityCommitmentsSize, actualVerifier); + makeNewIdentityManager( + treeDepth, + insertionPreRoot, + insertVerifiers, + insertionVerifier4844, + deletionVerifiers, + updateVerifiers, + semaphoreVerifier + ); + + ManagerImplV3.RegisterIdentities4844Params memory params = ManagerImplV3 + .RegisterIdentities4844Params({ + insertionProof: insertionProof4844, + commitments: commitments, + commitmentPok: commitmentsPok, + kzgCommitment: kzgCommitment, + kzgProof: kzgCommitment, // Intentionally pass something that's not the KZG proof + expectedEvaluation: insertionExpectedEvaluation, + preRoot: insertionPreRoot, + postRoot: insertionPostRoot4844, + inputHash: insertionInputHash4844, + batchSize: uint32(identityCommitmentsSize), + startIndex: startIndex + }); + + // Mock blobhash. This is valid for the next call only. + prepareBlobhash(kzgToVersionedHash(kzgCommitment)); + + bytes memory registerCallData = abi.encodeCall(ManagerImplV3.registerIdentities4844, params); + + bytes memory expectedError = + abi.encodeWithSelector(ManagerImplV3.KzgProofVerificationFailed.selector); + + // Test + assertCallFailsOn(identityManagerAddress, registerCallData, expectedError); + } + + /// @notice Checks that the transaction fails if KZG commitment does not match the rest of KZG-related input. + function testRegisterIdentitiesWithBadKzgCommitment() public { + // Setup + ITreeVerifier actualVerifier = new TreeVerifier(); + ( + VerifierLookupTable insertVerifiers, + VerifierLookupTable deletionVerifiers, + VerifierLookupTable updateVerifiers, + VerifierLookupTable4844 insertionVerifier4844 + ) = makeVerifierLookupTables(TC.makeDynArray([40])); + insertionVerifier4844.addVerifier(identityCommitmentsSize, actualVerifier); + makeNewIdentityManager( + treeDepth, + insertionPreRoot, + insertVerifiers, + insertionVerifier4844, + deletionVerifiers, + updateVerifiers, + semaphoreVerifier + ); + + ManagerImplV3.RegisterIdentities4844Params memory params = ManagerImplV3 + .RegisterIdentities4844Params({ + insertionProof: insertionProof4844, + commitments: commitments, + commitmentPok: commitmentsPok, + kzgCommitment: kzgProof, // Intentionally pass something that's not the KZG commitment + kzgProof: kzgProof, + expectedEvaluation: insertionExpectedEvaluation, + preRoot: insertionPreRoot, + postRoot: insertionPostRoot4844, + inputHash: insertionInputHash4844, + batchSize: uint32(identityCommitmentsSize), + startIndex: startIndex + }); + + // Mock blobhash. This is valid for the next call only. + prepareBlobhash(kzgToVersionedHash(kzgCommitment)); + + bytes memory registerCallData = abi.encodeCall(ManagerImplV3.registerIdentities4844, params); + + bytes memory expectedError = + abi.encodeWithSelector(ManagerImplV3.KzgProofVerificationFailed.selector); + + // Test + assertCallFailsOn(identityManagerAddress, registerCallData, expectedError); + } +} diff --git a/src/test/identity-manager/WorldIDIdentityManagerInitialization.t.sol b/src/test/identity-manager/WorldIDIdentityManagerInitialization.t.sol index 675ad10..684016e 100644 --- a/src/test/identity-manager/WorldIDIdentityManagerInitialization.t.sol +++ b/src/test/identity-manager/WorldIDIdentityManagerInitialization.t.sol @@ -24,7 +24,7 @@ contract WorldIDIdentityManagerInitialization is WorldIDIdentityManagerTest { function testInitialisation() public { // Setup delete identityManager; - delete managerImpl; + delete managerImplV2; delete managerImplV1; bytes memory V1CallData = abi.encodeCall( @@ -39,7 +39,7 @@ contract WorldIDIdentityManagerInitialization is WorldIDIdentityManagerTest { ); managerImplV1 = new ManagerImplV1(); - managerImplAddress = address(managerImpl); + managerImplV2Address = address(managerImplV2); vm.expectEmit(true, true, true, true); emit Initialized(1); @@ -48,13 +48,13 @@ contract WorldIDIdentityManagerInitialization is WorldIDIdentityManagerTest { identityManagerAddress = address(identityManager); // creates Manager Impl V2, which will be used for tests - managerImpl = new ManagerImpl(); - managerImplAddress = address(managerImpl); + managerImplV2 = new ManagerImpl(); + managerImplV2Address = address(managerImplV2); bytes memory initCallV2 = abi.encodeCall(ManagerImpl.initializeV2, (defaultDeletionVerifiers)); bytes memory upgradeCall = abi.encodeCall( - UUPSUpgradeable.upgradeToAndCall, (address(managerImplAddress), initCallV2) + UUPSUpgradeable.upgradeToAndCall, (address(managerImplV2Address), initCallV2) ); vm.expectEmit(true, true, true, true); @@ -70,7 +70,7 @@ contract WorldIDIdentityManagerInitialization is WorldIDIdentityManagerTest { function testInitialisation2() public { // Setup delete identityManager; - delete managerImpl; + delete managerImplV2; delete managerImplV1; bytes memory V1CallData = abi.encodeCall( @@ -85,12 +85,12 @@ contract WorldIDIdentityManagerInitialization is WorldIDIdentityManagerTest { ); // creates Manager Impl V2, which will be used for tests - managerImpl = new ManagerImpl(); - managerImplAddress = address(managerImpl); + managerImplV2 = new ManagerImpl(); + managerImplV2Address = address(managerImplV2); vm.expectEmit(true, true, true, true); emit Initialized(1); - identityManager = new IdentityManager(managerImplAddress, V1CallData); + identityManager = new IdentityManager(managerImplV2Address, V1CallData); identityManagerAddress = address(identityManager); bytes memory initCallV2 = @@ -145,10 +145,10 @@ contract WorldIDIdentityManagerInitialization is WorldIDIdentityManagerTest { function testCannotPassUnsupportedTreeDepth() public { // Setup delete identityManager; - delete managerImpl; + delete managerImplV2; - managerImpl = new ManagerImpl(); - managerImplAddress = address(managerImpl); + managerImplV2 = new ManagerImpl(); + managerImplV2Address = address(managerImplV2); uint8 unsupportedDepth = 15; bytes memory callData = abi.encodeCall( @@ -165,6 +165,6 @@ contract WorldIDIdentityManagerInitialization is WorldIDIdentityManagerTest { vm.expectRevert(abi.encodeWithSelector(ManagerImplV1.UnsupportedTreeDepth.selector, 15)); // Test - identityManager = new IdentityManager(managerImplAddress, callData); + identityManager = new IdentityManager(managerImplV2Address, callData); } } diff --git a/src/test/identity-manager/WorldIDIdentityManagerOwnershipManagement.t.sol b/src/test/identity-manager/WorldIDIdentityManagerOwnershipManagement.t.sol index 630fb93..79d9982 100644 --- a/src/test/identity-manager/WorldIDIdentityManagerOwnershipManagement.t.sol +++ b/src/test/identity-manager/WorldIDIdentityManagerOwnershipManagement.t.sol @@ -137,7 +137,7 @@ contract WorldIDIdentityManagerOwnershipManagement is WorldIDIdentityManagerTest vm.expectRevert("Function must be called through delegatecall"); // Test - managerImpl.identityOperator(); + managerImplV2.identityOperator(); } /// @notice Ensures that it is possible for the owner to set the address of the identity @@ -180,6 +180,6 @@ contract WorldIDIdentityManagerOwnershipManagement is WorldIDIdentityManagerTest vm.expectRevert("Function must be called through delegatecall"); // Test - managerImpl.setIdentityOperator(newOperator); + managerImplV2.setIdentityOperator(newOperator); } } diff --git a/src/test/identity-manager/WorldIDIdentityManagerSemaphoreVerification.t.sol b/src/test/identity-manager/WorldIDIdentityManagerSemaphoreVerification.t.sol index 2de8c13..3860c3b 100644 --- a/src/test/identity-manager/WorldIDIdentityManagerSemaphoreVerification.t.sol +++ b/src/test/identity-manager/WorldIDIdentityManagerSemaphoreVerification.t.sol @@ -34,6 +34,7 @@ contract WorldIDIdentityManagerSemaphoreVerification is WorldIDIdentityManagerTe actualTreeDepth, insertionPreRoot, defaultInsertVerifiers, + defaultInsertVerifiers4844, defaultDeletionVerifiers, defaultUpdateVerifiers, actualSemaphoreVerifier @@ -63,6 +64,7 @@ contract WorldIDIdentityManagerSemaphoreVerification is WorldIDIdentityManagerTe actualTreeDepth, inclusionRoot, defaultInsertVerifiers, + defaultInsertVerifiers4844, defaultDeletionVerifiers, defaultUpdateVerifiers, actualSemaphoreVerifier @@ -86,6 +88,7 @@ contract WorldIDIdentityManagerSemaphoreVerification is WorldIDIdentityManagerTe treeDepth, inclusionRoot, defaultInsertVerifiers, + defaultInsertVerifiers4844, defaultDeletionVerifiers, defaultUpdateVerifiers, actualSemaphoreVerifier diff --git a/src/test/identity-manager/WorldIDIdentityManagerTest.sol b/src/test/identity-manager/WorldIDIdentityManagerTest.sol index fffcf09..1739bbe 100644 --- a/src/test/identity-manager/WorldIDIdentityManagerTest.sol +++ b/src/test/identity-manager/WorldIDIdentityManagerTest.sol @@ -1,23 +1,26 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.21; +pragma solidity ^0.8.24; import {UUPSUpgradeable} from "contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import {WorldIDTest} from "../WorldIDTest.sol"; import {ITreeVerifier} from "../../interfaces/ITreeVerifier.sol"; +import {ITreeVerifier4844} from "../../interfaces/ITreeVerifier4844.sol"; import {ISemaphoreVerifier} from "src/interfaces/ISemaphoreVerifier.sol"; import {IBridge} from "../../interfaces/IBridge.sol"; import {SimpleStateBridge} from "../mock/SimpleStateBridge.sol"; -import {SimpleVerifier, SimpleVerify} from "../mock/SimpleVerifier.sol"; +import {SimpleVerifier, SimpleVerifier4844, SimpleVerify} from "../mock/SimpleVerifier.sol"; import {UnimplementedTreeVerifier} from "../../utils/UnimplementedTreeVerifier.sol"; import {SemaphoreVerifier} from "src/SemaphoreVerifier.sol"; import {VerifierLookupTable} from "../../data/VerifierLookupTable.sol"; +import {VerifierLookupTable4844} from "../../data/VerifierLookupTable4844.sol"; import {WorldIDIdentityManager as IdentityManager} from "../../WorldIDIdentityManager.sol"; import {WorldIDIdentityManagerImplV1 as ManagerImplV1} from "../../WorldIDIdentityManagerImplV1.sol"; import {WorldIDIdentityManagerImplV2 as ManagerImpl} from "../../WorldIDIdentityManagerImplV2.sol"; +import {WorldIDIdentityManagerImplV3 as ManagerImplV3} from "../../WorldIDIdentityManagerImplV3.sol"; /// @title World ID Identity Manager Test. /// @notice Contains tests for the WorldID identity manager. @@ -30,18 +33,23 @@ contract WorldIDIdentityManagerTest is WorldIDTest { /////////////////////////////////////////////////////////////////////////////// IdentityManager internal identityManager; + // V3 + ManagerImplV3 internal managerImplV3; // V2 - ManagerImpl internal managerImpl; + ManagerImpl internal managerImplV2; // V1 ManagerImplV1 internal managerImplV1; ITreeVerifier internal treeVerifier; + ITreeVerifier4844 internal treeVerifier4844; uint256 internal initialRoot = 0x0; uint8 internal treeDepth = 16; address internal identityManagerAddress; + // V3 + address internal managerImplV3Address; // V2 - address internal managerImplAddress; + address internal managerImplV2Address; // V1 address internal managerImplV1Address; @@ -65,6 +73,23 @@ contract WorldIDIdentityManagerTest is WorldIDTest { uint256 identityCommitmentsSize = 3; uint256[8] insertionProof; + /////////////////////////////////////////////////////////////////// + /// 4844 INSERTION /// + /////////////////////////////////////////////////////////////////// + /// @dev generated using `./gnark-mbu gen-test-params --mode insertion --tree-depth 16 --batch-size 3 | ./gnark-mbu prove --mode insertion --keys-file test_insertion.ps` + bytes32 internal constant insertionInputHash4844 = + 0x14a24bedc17b5596c60da74552640bd130d41d96b8c587dcadcf23217399e17b; + uint256 internal constant insertionExpectedEvaluation = + 0x3d5d4a7d6098f2147ed77be69d93179e6179479b8771c2554e5404c06f836408; + uint256 internal constant insertionPostRoot4844 = + 0x0c3f30b0604dae9a378e2bf62826bf5a772e9ad745df6f8c8256dff351fecee8; + + uint256[8] insertionProof4844; + uint256[2] commitments; + uint256[2] commitmentsPok; + uint128[3] kzgCommitment; + uint128[3] kzgProof; + /////////////////////////////////////////////////////////////////// /// DELETION /// /////////////////////////////////////////////////////////////////// @@ -113,6 +138,7 @@ contract WorldIDIdentityManagerTest is WorldIDTest { // Verifiers uint256 initialBatchSize = 30; VerifierLookupTable internal defaultInsertVerifiers; + VerifierLookupTable4844 internal defaultInsertVerifiers4844; VerifierLookupTable internal defaultDeletionVerifiers; VerifierLookupTable internal defaultUpdateVerifiers; @@ -143,6 +169,39 @@ contract WorldIDIdentityManagerTest is WorldIDTest { 0x24830332559eada283d4473b17091b239443e75e9e09f0ebce8e72c235ee665d ]; + insertionProof4844 = [ + 0x18dba02648df62914fe9c6dba182c73480253ed22b383c4ae7ead51152e73300, + 0x0660cf8023a5785e930e0333864ed17c8641a559e7bad817af736f6648a76447, + 0x2662090884185d3f910ce62dadf005a278226d877f41d3c52bd1d6b4a91aa2be, + 0x18a279bd46da024aa71cc7f64c396b3c64a6f13a1cf5fc443ad916ac93478b4d, + 0x0fdd92b46d74766433d3a501da207f8dfb16e4d74fe2a6dcad008f2e656f8842, + 0x2212ff3545056108d1162467172c368a89614ad29469f4d71f02e4ebcb6eb3ac, + 0x041dbe374440a1a1acdef5bc7d204ce3b20e4d6fbd41b41b787896e51ed023e9, + 0x047eddec1fc18e112fe15ce861484e8309f0605260e063c9591a6e0450934c80 + ]; + + commitments = [ + 0x04dd4ea218ac1d6b85f5d8ffb3007ad0c029302d1af96f0830ade252ccba5b98, + 0x18702f80829840758f18e3a9e624a8d049944dcc494bf260f8e0f9047cebf027 + ]; + + commitmentsPok = [ + 0x0ab698df05861ae9048ba5c388857fc32a0db801ab3b8bfc4c9b298819da6a66, + 0x085221b73b2a59518c04f1f6f41a7879637cf9f984ed4e05bd28e6507fb67614 + ]; + + kzgProof = [ + 0x925d42714da54a935f209022d256986a, + 0x2b545ab39f127832297a492ed5875be9, + 0x1a983b57c0639403a38ad7d24e0095bc + ]; + + kzgCommitment = [ + 0xb422b2e3bf75a087b84d8086fd35b8a2, + 0x299a559c92ef938fd63d6e6009b74bb9, + 0x47670537338c47c4472f9be9886b65ac + ]; + // Create the deletion proof term. // output from semaphore-mtb prove in src/test/data/DeletionProof.json /// @dev test_deletion.ps is generated using semaphore-mtb: `./gnark-mbu setup --mode deletion --batch-size 8 --tree-depth 16 --output test_deletion.ps` @@ -187,10 +246,14 @@ contract WorldIDIdentityManagerTest is WorldIDTest { defaultUpdateVerifiers.addVerifier(initialBatchSize, treeVerifier); defaultDeletionVerifiers = new VerifierLookupTable(); defaultDeletionVerifiers.addVerifier(initialBatchSize, treeVerifier); + treeVerifier4844 = new SimpleVerifier4844(initialBatchSize); + defaultInsertVerifiers4844 = new VerifierLookupTable4844(); + defaultInsertVerifiers4844.addVerifier(initialBatchSize, treeVerifier4844); makeNewIdentityManager( treeDepth, initialRoot, defaultInsertVerifiers, + defaultInsertVerifiers4844, defaultDeletionVerifiers, defaultUpdateVerifiers, semaphoreVerifier @@ -198,8 +261,9 @@ contract WorldIDIdentityManagerTest is WorldIDTest { hevm.label(address(this), "Sender"); hevm.label(identityManagerAddress, "IdentityManager"); - hevm.label(managerImplAddress, "ManagerImplementation"); hevm.label(managerImplV1Address, "ManagerImplementationV1"); + hevm.label(managerImplV1Address, "ManagerImplementationV2"); + hevm.label(managerImplV3Address, "ManagerImplementationV3"); } /////////////////////////////////////////////////////////////////////////////// @@ -211,12 +275,14 @@ contract WorldIDIdentityManagerTest is WorldIDTest { /// /// @param actualPreRoot The pre-root to use. /// @param insertVerifiers The insertion verifier lookup table. + /// @param insertVerifiers4844 The insertion verifier lookup table for EIP-4844 proofs. /// @param updateVerifiers The udpate verifier lookup table. /// @param actualSemaphoreVerifier The Semaphore verifier instance to use. function makeNewIdentityManager( uint8 actualTreeDepth, uint256 actualPreRoot, VerifierLookupTable insertVerifiers, + VerifierLookupTable4844 insertVerifiers4844, VerifierLookupTable deletionVerifiers, VerifierLookupTable updateVerifiers, ISemaphoreVerifier actualSemaphoreVerifier @@ -239,16 +305,28 @@ contract WorldIDIdentityManagerTest is WorldIDTest { identityManagerAddress = address(identityManager); // creates Manager Impl V2, which will be used for tests - managerImpl = new ManagerImpl(); - managerImplAddress = address(managerImpl); + managerImplV2 = new ManagerImpl(); + managerImplV2Address = address(managerImplV2); - bytes memory initCallV2 = abi.encodeCall(ManagerImpl.initializeV2, (deletionVerifiers)); - bytes memory upgradeCall = abi.encodeCall( - UUPSUpgradeable.upgradeToAndCall, (address(managerImplAddress), initCallV2) + bytes memory initCallV2 = abi.encodeCall(managerImplV2.initializeV2, (deletionVerifiers)); + bytes memory upgradeCallV2 = abi.encodeCall( + UUPSUpgradeable.upgradeToAndCall, (address(managerImplV2Address), initCallV2) ); // Test - assertCallSucceedsOn(identityManagerAddress, upgradeCall, new bytes(0x0)); + assertCallSucceedsOn(identityManagerAddress, upgradeCallV2, new bytes(0x0)); + + // creates Manager Impl V3, which will be used for tests + managerImplV3 = new ManagerImplV3(); + managerImplV3Address = address(managerImplV3); + + bytes memory initCallV3 = abi.encodeCall(managerImplV3.initializeV3, (insertVerifiers4844)); + bytes memory upgradeCallV3 = abi.encodeCall( + UUPSUpgradeable.upgradeToAndCall, (address(managerImplV3Address), initCallV3) + ); + + // Test + assertCallSucceedsOn(identityManagerAddress, upgradeCallV3, new bytes(0x0)); } /// @notice Initialises a new identity manager using the provided information. @@ -264,7 +342,8 @@ contract WorldIDIdentityManagerTest is WorldIDTest { ( VerifierLookupTable insertVerifiers, VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers + VerifierLookupTable updateVerifiers, + VerifierLookupTable4844 insertVerifiers4844 ) = makeVerifierLookupTables(batchSizes); defaultInsertVerifiers = insertVerifiers; defaultDeletionVerifiers = deletionVerifiers; @@ -275,6 +354,7 @@ contract WorldIDIdentityManagerTest is WorldIDTest { treeDepth, actualPreRoot, insertVerifiers, + insertVerifiers4844, deletionVerifiers, updateVerifiers, semaphoreVerifier @@ -289,6 +369,7 @@ contract WorldIDIdentityManagerTest is WorldIDTest { /// @return insertVerifiers The insertion verifier lookup table. /// @return deletionVerifiers The deletion verifier lookup table. /// @return updateVerifiers The update verifier lookup table. + /// @return insertVerifiers4844 The insertion verifier lookup table for EIP-4844 proofs. /// /// @custom:reverts VerifierExists If `batchSizes` contains a duplicate. /// @custom:reverts string If any batch size exceeds 1000. @@ -298,7 +379,8 @@ contract WorldIDIdentityManagerTest is WorldIDTest { returns ( VerifierLookupTable insertVerifiers, VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers + VerifierLookupTable updateVerifiers, + VerifierLookupTable4844 insertVerifiers4844 ) { // Construct the verifier LUTs from the provided `batchSizes` info. @@ -311,6 +393,7 @@ contract WorldIDIdentityManagerTest is WorldIDTest { insertVerifiers = new VerifierLookupTable(); deletionVerifiers = new VerifierLookupTable(); updateVerifiers = new VerifierLookupTable(); + insertVerifiers4844 = new VerifierLookupTable4844(); for (uint256 i = 0; i < batchSizes.length; ++i) { uint256 batchSize = batchSizes[i]; if (batchSize > 1000) { @@ -321,15 +404,18 @@ contract WorldIDIdentityManagerTest is WorldIDTest { insertVerifiers.addVerifier(batchSize, batchVerifier); deletionVerifiers.addVerifier(batchSize, batchVerifier); updateVerifiers.addVerifier(batchSize, batchVerifier); + + ITreeVerifier4844 batchVerifier4844 = new SimpleVerifier4844(batchSize); + insertVerifiers4844.addVerifier(batchSize, batchVerifier4844); } } /// @notice Creates a new identity manager without initializing the delegate. /// @dev It is constructed in the globals. function makeUninitIdentityManager() public { - managerImpl = new ManagerImpl(); - managerImplAddress = address(managerImpl); - identityManager = new IdentityManager(managerImplAddress, new bytes(0x0)); + managerImplV2 = new ManagerImpl(); + managerImplV2Address = address(managerImplV2); + identityManager = new IdentityManager(managerImplV2Address, new bytes(0x0)); identityManagerAddress = address(identityManager); } @@ -379,6 +465,36 @@ contract WorldIDIdentityManagerTest is WorldIDTest { actualProof = [uint256(prf[0]), prf[1], prf[2], prf[3], prf[4], prf[5], prf[6], prf[7]]; } + bytes1 constant VERSIONED_HASH_VERSION_KZG = 0x01; + + /// @notice Convert a KZG commitment to a versioned hash as per EIP-4844. + /// Implementation as per https://github.com/ethereum/EIPs/blob/master/EIPS/eip-4844.md#helpers + /// @param commitment KZG commitment split to 3 128-bit words. + /// @return versioned hash in the form of a 32-byte word. + function kzgToVersionedHash(uint128[3] memory commitment) public pure returns (bytes32) { + bytes memory commitmentBytes = abi.encodePacked(commitment[0], commitment[1], commitment[2]); + bytes32 hash = sha256(commitmentBytes); + + bytes memory truncatedHash = new bytes(31); + for (uint256 i = 0; i < 31; i++) { + truncatedHash[i] = hash[i + 1]; + } + + return bytes32(abi.encodePacked(VERSIONED_HASH_VERSION_KZG, truncatedHash)); + } + + /// @notice Store the given value as a blobhash to be used in tests. + /// The given value will be stored in the 0th blobhash slot and can be retrieved with `blobhash(0)` + /// convenience wrapper of with the `BLOBHASH` opcode. + /// @dev This function is effective only for the next function call, so prepare blobhash as the very last + /// step before the intended usage. + /// @param value Value to be set as contents of the 0th blobhash slot. + function prepareBlobhash(bytes32 value) public { + bytes32[] memory blobhashes = new bytes32[](1); + blobhashes[0] = value; + vm.blobhashes(blobhashes); + } + /// @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 dcb7efe..bb580ce 100644 --- a/src/test/identity-manager/WorldIDIdentityManagerUninit.t.sol +++ b/src/test/identity-manager/WorldIDIdentityManagerUninit.t.sol @@ -123,7 +123,7 @@ 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) ); @@ -153,7 +153,8 @@ contract WorldIDIdentityManagerUninit is WorldIDIdentityManagerTest { function testShouldNotCallSetDeleteIdentitiesVerifierLookupTableWhileUninit() public { // Setup makeUninitIdentityManager(); - (, VerifierLookupTable deletionVerifiers,) = makeVerifierLookupTables(TC.makeDynArray([75])); + (,, VerifierLookupTable deletionVerifiers,) = + makeVerifierLookupTables(TC.makeDynArray([75])); bytes memory callData = abi.encodeCall(ManagerImpl.setDeleteIdentitiesVerifierLookupTable, (deletionVerifiers)); bytes memory expectedError = diff --git a/src/test/identity-manager/WorldIDIdentityManagerUpgrade.t.sol b/src/test/identity-manager/WorldIDIdentityManagerUpgrade.t.sol index 012c4d4..fbcf4a8 100644 --- a/src/test/identity-manager/WorldIDIdentityManagerUpgrade.t.sol +++ b/src/test/identity-manager/WorldIDIdentityManagerUpgrade.t.sol @@ -74,6 +74,6 @@ contract WorldIDIdentityManagerUpdate is WorldIDIdentityManagerTest { vm.expectRevert("Function must be called through delegatecall"); // Test - managerImpl.upgradeToAndCall(mockUpgradeAddress, initCall); + managerImplV2.upgradeToAndCall(mockUpgradeAddress, initCall); } } diff --git a/src/test/mock/SimpleVerifier.sol b/src/test/mock/SimpleVerifier.sol index c2f2cc1..02adfe3 100644 --- a/src/test/mock/SimpleVerifier.sol +++ b/src/test/mock/SimpleVerifier.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.21; import {ITreeVerifier} from "../../interfaces/ITreeVerifier.sol"; +import {ITreeVerifier4844} from "../../interfaces/ITreeVerifier4844.sol"; /// @title Simple Verifier /// @author Worldcoin @@ -25,6 +26,33 @@ contract SimpleVerifier is ITreeVerifier { } } +/// @title Simple Verifier for EIP-4844 proofs. +/// @author Worldcoin +/// @notice A dumb verifier to make it easy to fuzz test successes and failures. +contract SimpleVerifier4844 is ITreeVerifier4844 { + uint256 batchSize; + + event VerifiedProof(uint256 batchSize); + + constructor(uint256 _batchSize) { + batchSize = _batchSize; + } + + function verifyProof( + uint256[8] memory proof, + uint256[2] memory, + uint256[2] memory, + uint256[6] memory input + ) external { + bool result = proof[0] % 2 == 0; + + input[0] = 0; + if (result) { + emit VerifiedProof(batchSize); + } + } +} + library SimpleVerify { function isValidInput(uint256 a) public pure returns (bool) { return a % 2 == 0; diff --git a/src/test/mock/WorldIDIdentityManagerImplMock.sol b/src/test/mock/WorldIDIdentityManagerImplMock.sol index f4a2cd0..af99121 100644 --- a/src/test/mock/WorldIDIdentityManagerImplMock.sol +++ b/src/test/mock/WorldIDIdentityManagerImplMock.sol @@ -13,7 +13,8 @@ contract WorldIDIdentityManagerImplMock is WorldIDIdentityManagerImplV1 { } /// @notice Used to initialize the new things in the upgraded contract. - function initialize(uint32 data) public virtual reinitializer(3) { + /// The reinitializer value is high not to conflict with subsequent implementations. + function initialize(uint32 data) public virtual reinitializer(255) { _someMoreData = data; } diff --git a/src/utils/UnimplementedTreeVerifier4844.sol b/src/utils/UnimplementedTreeVerifier4844.sol new file mode 100644 index 0000000..b105348 --- /dev/null +++ b/src/utils/UnimplementedTreeVerifier4844.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import {ITreeVerifier4844 as ITreeVerifier} from "../interfaces/ITreeVerifier4844.sol"; + +/// @title Unimplemented Tree Verifier +/// @author Worldcoin +/// @notice A tree verifier instance that will always revert. +/// @dev This verifier is used as the default implementation for the update and remove endpoints in +/// the WorldID identity manager. We do not currently have ZK circuit designs for these +/// endpoints, but having the contract portion already implemented makes it easier for the +/// future where those will work. +contract UnimplementedTreeVerifier is ITreeVerifier { + /// @notice Thrown when an operation is not supported. + error UnsupportedOperation(); + + /// Verify an uncompressed Groth16 proof. + /// @notice Reverts with InvalidProof if the proof is invalid or + /// with PublicInputNotInField the public input is not reduced. + /// @notice There is no return value. If the function does not revert, the + /// proof was successfully verified. + function verifyProof( + uint256[8] calldata, + uint256[2] calldata, + uint256[2] calldata, + uint256[6] calldata + ) public pure { + revert UnsupportedOperation(); + } +} From 07e43ac55e856ccdfe9e971234d6228fe5d4e815 Mon Sep 17 00:00:00 2001 From: Wojciech Zmuda Date: Tue, 10 Sep 2024 21:24:04 +0200 Subject: [PATCH 7/8] identity-manager: move verifier lookup tables to the base test contract Making these variables global shrunks stack significantly, allowing to build contracts without the --via-ir flag. Signed-off-by: Wojciech Zmuda --- .../WorldIDIdentityManagerDataQuery.t.sol | 16 +--- ...WorldIDIdentityManagerGettersSetters.t.sol | 26 +++--- ...rldIDIdentityManagerIdentityDeletion.t.sol | 47 +++------- ...DIdentityManagerIdentityRegistration.t.sol | 71 ++++----------- ...ntityManagerIdentityRegistration4844.t.sol | 88 +++++-------------- .../WorldIDIdentityManagerTest.sol | 74 +++++++--------- .../WorldIDIdentityManagerUninit.t.sol | 5 +- 7 files changed, 103 insertions(+), 224 deletions(-) diff --git a/src/test/identity-manager/WorldIDIdentityManagerDataQuery.t.sol b/src/test/identity-manager/WorldIDIdentityManagerDataQuery.t.sol index fa32ab9..74a107f 100644 --- a/src/test/identity-manager/WorldIDIdentityManagerDataQuery.t.sol +++ b/src/test/identity-manager/WorldIDIdentityManagerDataQuery.t.sol @@ -49,12 +49,8 @@ contract WorldIDIdentityManagerDataQuery is WorldIDIdentityManagerTest { vm.assume(SimpleVerify.isValidInput(uint256(prf[0]))); vm.assume(newPreRoot != newPostRoot); vm.assume(identities.length <= 1000); - ( - VerifierLookupTable insertVerifiers, - VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers, - VerifierLookupTable4844 insertVerifiers4844 - ) = makeVerifierLookupTables(TC.makeDynArray([identities.length])); + (insertVerifiers, deletionVerifiers, updateVerifiers, insertVerifiers4844) = + makeVerifierLookupTables(TC.makeDynArray([identities.length])); makeNewIdentityManager( treeDepth, newPreRoot, @@ -92,12 +88,8 @@ contract WorldIDIdentityManagerDataQuery is WorldIDIdentityManagerTest { vm.assume(newPreRoot != newPostRoot); vm.assume(SimpleVerify.isValidInput(uint256(prf[0]))); vm.assume(identities.length <= 1000); - ( - VerifierLookupTable insertVerifiers, - VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers, - VerifierLookupTable4844 insertVerifiers4844 - ) = makeVerifierLookupTables(TC.makeDynArray([identities.length])); + (insertVerifiers, deletionVerifiers, updateVerifiers, insertVerifiers4844) = + makeVerifierLookupTables(TC.makeDynArray([identities.length])); makeNewIdentityManager( treeDepth, newPreRoot, diff --git a/src/test/identity-manager/WorldIDIdentityManagerGettersSetters.t.sol b/src/test/identity-manager/WorldIDIdentityManagerGettersSetters.t.sol index d3a7d45..f131d8f 100644 --- a/src/test/identity-manager/WorldIDIdentityManagerGettersSetters.t.sol +++ b/src/test/identity-manager/WorldIDIdentityManagerGettersSetters.t.sol @@ -49,11 +49,10 @@ contract WorldIDIdentityManagerGettersSetters is WorldIDIdentityManagerTest { /// identity registration proofs. function testCanSetRegisterIdentitiesVerifierLookupTable() public { // Setup - (VerifierLookupTable insertionVerifiers,,,) = - makeVerifierLookupTables(TC.makeDynArray([40])); - address newVerifiersAddress = address(insertionVerifiers); + (insertVerifiers,,,) = makeVerifierLookupTables(TC.makeDynArray([40])); + address newVerifiersAddress = address(insertVerifiers); bytes memory callData = abi.encodeCall( - ManagerImplV1.setRegisterIdentitiesVerifierLookupTable, (insertionVerifiers) + ManagerImplV1.setRegisterIdentitiesVerifierLookupTable, (insertVerifiers) ); bytes memory checkCallData = abi.encodeCall(ManagerImplV1.getRegisterIdentitiesVerifierLookupTableAddress, ()); @@ -74,10 +73,9 @@ contract WorldIDIdentityManagerGettersSetters is WorldIDIdentityManagerTest { { // Setup vm.assume(notOwner != address(this) && notOwner != address(0x0)); - (VerifierLookupTable insertionVerifiers,,,) = - makeVerifierLookupTables(TC.makeDynArray([40])); + (insertVerifiers,,,) = makeVerifierLookupTables(TC.makeDynArray([40])); bytes memory callData = abi.encodeCall( - ManagerImplV1.setRegisterIdentitiesVerifierLookupTable, (insertionVerifiers) + ManagerImplV1.setRegisterIdentitiesVerifierLookupTable, (insertVerifiers) ); bytes memory errorData = encodeStringRevert("Ownable: caller is not the owner"); vm.prank(notOwner); @@ -90,12 +88,11 @@ contract WorldIDIdentityManagerGettersSetters is WorldIDIdentityManagerTest { /// identity registration unless called via the proxy. function testCannotSetRegisterIdentitiesVerifierLookupTableUnlessViaProxy() public { // Setup - (VerifierLookupTable insertionVerifiers,,,) = - makeVerifierLookupTables(TC.makeDynArray([40])); + (insertVerifiers,,,) = makeVerifierLookupTables(TC.makeDynArray([40])); vm.expectRevert("Function must be called through delegatecall"); // Test - managerImplV2.setRegisterIdentitiesVerifierLookupTable(insertionVerifiers); + managerImplV2.setRegisterIdentitiesVerifierLookupTable(insertVerifiers); } /// @notice Checks that it is possible to get the address of the contract currently being used @@ -124,8 +121,7 @@ contract WorldIDIdentityManagerGettersSetters is WorldIDIdentityManagerTest { /// identity deletion proofs. function testCanSetDeleteIdentitiesVerifierLookupTable() public { // Setup - (,, VerifierLookupTable deletionVerifiers,) = - makeVerifierLookupTables(TC.makeDynArray([40])); + (,, deletionVerifiers,) = makeVerifierLookupTables(TC.makeDynArray([40])); address newVerifiersAddress = address(deletionVerifiers); bytes memory callData = abi.encodeCall(ManagerImpl.setDeleteIdentitiesVerifierLookupTable, (deletionVerifiers)); @@ -146,8 +142,7 @@ contract WorldIDIdentityManagerGettersSetters is WorldIDIdentityManagerTest { function testCannotSetDeleteIdentitiesVerifierLookupTableUnlessOwner(address notOwner) public { // Setup vm.assume(notOwner != address(this) && notOwner != address(0x0)); - (,, VerifierLookupTable deletionVerifiers,) = - makeVerifierLookupTables(TC.makeDynArray([40])); + (,, deletionVerifiers,) = makeVerifierLookupTables(TC.makeDynArray([40])); bytes memory callData = abi.encodeCall( ManagerImplV1.setRegisterIdentitiesVerifierLookupTable, (deletionVerifiers) ); @@ -162,8 +157,7 @@ contract WorldIDIdentityManagerGettersSetters is WorldIDIdentityManagerTest { /// identity deletion unless called via the proxy. function testCannotSetDeleteIdentitiesVerifierLookupTableUnlessViaProxy() public { // Setup - (,, VerifierLookupTable deletionVerifiers,) = - makeVerifierLookupTables(TC.makeDynArray([40])); + (,, deletionVerifiers,) = makeVerifierLookupTables(TC.makeDynArray([40])); vm.expectRevert("Function must be called through delegatecall"); // Test diff --git a/src/test/identity-manager/WorldIDIdentityManagerIdentityDeletion.t.sol b/src/test/identity-manager/WorldIDIdentityManagerIdentityDeletion.t.sol index af666f5..e055393 100644 --- a/src/test/identity-manager/WorldIDIdentityManagerIdentityDeletion.t.sol +++ b/src/test/identity-manager/WorldIDIdentityManagerIdentityDeletion.t.sol @@ -36,12 +36,8 @@ contract WorldIDIdentityManagerIdentityDeletion is WorldIDIdentityManagerTest { function testDeleteIdentitiesWithCorrectInputsFromKnown() public { // Setup ITreeVerifier actualVerifier = new TreeVerifier(); - ( - VerifierLookupTable insertVerifiers, - VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers, - VerifierLookupTable4844 insertVerifiers4844 - ) = makeVerifierLookupTables(TC.makeDynArray([40])); + (insertVerifiers, deletionVerifiers, updateVerifiers, insertVerifiers4844) = + makeVerifierLookupTables(TC.makeDynArray([40])); deletionVerifiers.addVerifier(deletionBatchSize, actualVerifier); makeNewIdentityManager( treeDepth, @@ -85,12 +81,8 @@ contract WorldIDIdentityManagerIdentityDeletion is WorldIDIdentityManagerTest { vm.assume(packedDeletionIndices.length <= 125); vm.assume(packedDeletionIndices.length % 4 == 0); vm.assume(identityOperator != nullAddress && identityOperator != thisAddress); - ( - VerifierLookupTable insertVerifiers, - VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers, - VerifierLookupTable4844 insertVerifiers4844 - ) = makeVerifierLookupTables(TC.makeDynArray([packedDeletionIndices.length / 4])); + (insertVerifiers, deletionVerifiers, updateVerifiers, insertVerifiers4844) = + makeVerifierLookupTables(TC.makeDynArray([packedDeletionIndices.length / 4])); makeNewIdentityManager( treeDepth, newPreRoot, @@ -132,12 +124,8 @@ contract WorldIDIdentityManagerIdentityDeletion is WorldIDIdentityManagerTest { bytes memory secondIndices = abi.encodePacked(uint32(0), uint32(2), uint32(4), uint32(6)); - ( - VerifierLookupTable insertVerifiers, - VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers, - VerifierLookupTable4844 insertVerifiers4844 - ) = makeVerifierLookupTables(TC.makeDynArray([deletionBatchSize, secondIndices.length / 4])); + (insertVerifiers, deletionVerifiers, updateVerifiers, insertVerifiers4844) = + makeVerifierLookupTables(TC.makeDynArray([deletionBatchSize, secondIndices.length / 4])); makeNewIdentityManager( treeDepth, newPreRoot, @@ -180,12 +168,7 @@ contract WorldIDIdentityManagerIdentityDeletion is WorldIDIdentityManagerTest { vm.assume(newPreRoot != newPostRoot); vm.assume(packedDeletionIndices.length > 4); vm.assume(packedDeletionIndices.length % 4 == 0); - ( - VerifierLookupTable insertVerifiers, - VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers, - VerifierLookupTable4844 insertVerifiers4844 - ) = + (insertVerifiers, deletionVerifiers, updateVerifiers, insertVerifiers4844) = // the -1 offsets the correct batch size by 1 thus causing the error makeVerifierLookupTables(TC.makeDynArray([(packedDeletionIndices.length / 4) - 1])); makeNewIdentityManager( @@ -220,12 +203,8 @@ contract WorldIDIdentityManagerIdentityDeletion is WorldIDIdentityManagerTest { vm.assume(newPreRoot != newPostRoot); ITreeVerifier actualVerifier = new TreeVerifier(); uint32 indicesLength = uint32(packedDeletionIndices.length / 4); - ( - VerifierLookupTable insertVerifiers, - VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers, - VerifierLookupTable4844 insertVerifiers4844 - ) = makeVerifierLookupTables(TC.makeDynArray([70])); + (insertVerifiers, deletionVerifiers, updateVerifiers, insertVerifiers4844) = + makeVerifierLookupTables(TC.makeDynArray([70])); deletionVerifiers.addVerifier(indicesLength, actualVerifier); makeNewIdentityManager( treeDepth, @@ -253,12 +232,8 @@ contract WorldIDIdentityManagerIdentityDeletion is WorldIDIdentityManagerTest { // Setup vm.assume(newPostRoot != deletionPostRoot && newPostRoot < SNARK_SCALAR_FIELD); ITreeVerifier actualVerifier = new TreeVerifier(); - ( - VerifierLookupTable insertVerifiers, - VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers, - VerifierLookupTable4844 insertVerifiers4844 - ) = makeVerifierLookupTables(TC.makeDynArray([70])); + (insertVerifiers, deletionVerifiers, updateVerifiers, insertVerifiers4844) = + makeVerifierLookupTables(TC.makeDynArray([70])); deletionVerifiers.addVerifier(deletionBatchSize, actualVerifier); makeNewIdentityManager( treeDepth, diff --git a/src/test/identity-manager/WorldIDIdentityManagerIdentityRegistration.t.sol b/src/test/identity-manager/WorldIDIdentityManagerIdentityRegistration.t.sol index c08e8e5..03c8e78 100644 --- a/src/test/identity-manager/WorldIDIdentityManagerIdentityRegistration.t.sol +++ b/src/test/identity-manager/WorldIDIdentityManagerIdentityRegistration.t.sol @@ -37,12 +37,8 @@ contract WorldIDIdentityManagerIdentityRegistration is WorldIDIdentityManagerTes function testRegisterIdentitiesWithCorrectInputsFromKnown() public { // Setup ITreeVerifier actualVerifier = new TreeVerifier(); - ( - VerifierLookupTable insertVerifiers, - VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers, - VerifierLookupTable4844 insertVerifiers4844 - ) = makeVerifierLookupTables(TC.makeDynArray([40])); + (insertVerifiers, deletionVerifiers, updateVerifiers, insertVerifiers4844) = + makeVerifierLookupTables(TC.makeDynArray([40])); insertVerifiers.addVerifier(identityCommitmentsSize, actualVerifier); makeNewIdentityManager( treeDepth, @@ -87,12 +83,8 @@ contract WorldIDIdentityManagerIdentityRegistration is WorldIDIdentityManagerTes vm.assume(newPreRoot != newPostRoot); vm.assume(identities.length <= 1000); vm.assume(identityOperator != nullAddress && identityOperator != thisAddress); - ( - VerifierLookupTable insertVerifiers, - VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers, - VerifierLookupTable4844 insertVerifiers4844 - ) = makeVerifierLookupTables(TC.makeDynArray([identities.length])); + (insertVerifiers, deletionVerifiers, updateVerifiers, insertVerifiers4844) = + makeVerifierLookupTables(TC.makeDynArray([identities.length])); makeNewIdentityManager( treeDepth, newPreRoot, @@ -136,12 +128,8 @@ contract WorldIDIdentityManagerIdentityRegistration is WorldIDIdentityManagerTes vm.assume(newPreRoot != newPostRoot); vm.assume(identities.length <= 1000 && identities.length > 0); uint256 secondIdentsLength = identities.length / 2; - ( - VerifierLookupTable insertVerifiers, - VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers, - VerifierLookupTable4844 insertVerifiers4844 - ) = makeVerifierLookupTables(TC.makeDynArray([identities.length, secondIdentsLength])); + (insertVerifiers, deletionVerifiers, updateVerifiers, insertVerifiers4844) = + makeVerifierLookupTables(TC.makeDynArray([identities.length, secondIdentsLength])); makeNewIdentityManager( treeDepth, newPreRoot, @@ -190,12 +178,8 @@ contract WorldIDIdentityManagerIdentityRegistration is WorldIDIdentityManagerTes vm.assume(SimpleVerify.isValidInput(uint256(prf[0]))); vm.assume(newPreRoot != newPostRoot); vm.assume(identities.length > 0); - ( - VerifierLookupTable insertVerifiers, - VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers, - VerifierLookupTable4844 insertVerifiers4844 - ) = makeVerifierLookupTables(TC.makeDynArray([identities.length - 1])); + (insertVerifiers, deletionVerifiers, updateVerifiers, insertVerifiers4844) = + makeVerifierLookupTables(TC.makeDynArray([identities.length - 1])); makeNewIdentityManager( treeDepth, newPreRoot, @@ -228,12 +212,8 @@ contract WorldIDIdentityManagerIdentityRegistration is WorldIDIdentityManagerTes // Setup vm.assume(!SimpleVerify.isValidInput(uint256(prf[0]))); ITreeVerifier actualVerifier = new TreeVerifier(); - ( - VerifierLookupTable insertVerifiers, - VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers, - VerifierLookupTable4844 insertVerifiers4844 - ) = makeVerifierLookupTables(TC.makeDynArray([70])); + (insertVerifiers, deletionVerifiers, updateVerifiers, insertVerifiers4844) = + makeVerifierLookupTables(TC.makeDynArray([70])); insertVerifiers.addVerifier(identityCommitments.length, actualVerifier); makeNewIdentityManager( treeDepth, @@ -260,12 +240,8 @@ contract WorldIDIdentityManagerIdentityRegistration is WorldIDIdentityManagerTes // Setup vm.assume(newStartIndex != startIndex); ITreeVerifier actualVerifier = new TreeVerifier(); - ( - VerifierLookupTable insertVerifiers, - VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers, - VerifierLookupTable4844 insertVerifiers4844 - ) = makeVerifierLookupTables(TC.makeDynArray([70])); + (insertVerifiers, deletionVerifiers, updateVerifiers, insertVerifiers4844) = + makeVerifierLookupTables(TC.makeDynArray([70])); insertVerifiers.addVerifier(identityCommitmentsSize, actualVerifier); makeNewIdentityManager( treeDepth, @@ -304,12 +280,8 @@ contract WorldIDIdentityManagerIdentityRegistration is WorldIDIdentityManagerTes uint256[] memory identities = cloneArray(identityCommitments); identities[invalidSlot] = identity; ITreeVerifier actualVerifier = new TreeVerifier(); - ( - VerifierLookupTable insertVerifiers, - VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers, - VerifierLookupTable4844 insertVerifiers4844 - ) = makeVerifierLookupTables(TC.makeDynArray([70])); + (insertVerifiers, deletionVerifiers, updateVerifiers, insertVerifiers4844) = + makeVerifierLookupTables(TC.makeDynArray([70])); insertVerifiers.addVerifier(identityCommitmentsSize, actualVerifier); makeNewIdentityManager( treeDepth, @@ -338,11 +310,8 @@ contract WorldIDIdentityManagerIdentityRegistration is WorldIDIdentityManagerTes managerImplV2 = new ManagerImpl(); managerImplV2Address = address(managerImplV2); ITreeVerifier actualVerifier = new TreeVerifier(); - ( - VerifierLookupTable insertVerifiers, - VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers, - ) = makeVerifierLookupTables(TC.makeDynArray([70])); + (insertVerifiers, deletionVerifiers, updateVerifiers,) = + makeVerifierLookupTables(TC.makeDynArray([70])); insertVerifiers.addVerifier(identityCommitmentsSize, actualVerifier); bytes memory callData = abi.encodeCall( @@ -429,12 +398,8 @@ 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 deletionVerifiers, - VerifierLookupTable updateVerifiers, - VerifierLookupTable4844 insertVerifiers4844 - ) = makeVerifierLookupTables(TC.makeDynArray([identitiesLength])); + (insertVerifiers, deletionVerifiers, updateVerifiers, insertVerifiers4844) = + makeVerifierLookupTables(TC.makeDynArray([identitiesLength])); makeNewIdentityManager( treeDepth, initialRoot, diff --git a/src/test/identity-manager/WorldIDIdentityManagerIdentityRegistration4844.t.sol b/src/test/identity-manager/WorldIDIdentityManagerIdentityRegistration4844.t.sol index e42522f..a866f5b 100644 --- a/src/test/identity-manager/WorldIDIdentityManagerIdentityRegistration4844.t.sol +++ b/src/test/identity-manager/WorldIDIdentityManagerIdentityRegistration4844.t.sol @@ -37,12 +37,8 @@ contract WorldIDIdentityManagerIdentityRegistration4844 is WorldIDIdentityManage function testRegisterIdentitiesWithCorrectInputsFromKnown() public { // Setup ITreeVerifier actualVerifier = new TreeVerifier(); - ( - VerifierLookupTable insertVerifiers, - VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers, - VerifierLookupTable4844 insertVerifiers4844 - ) = makeVerifierLookupTables(TC.makeDynArray([40])); + (insertVerifiers, deletionVerifiers, updateVerifiers, insertVerifiers4844) = + makeVerifierLookupTables(TC.makeDynArray([40])); insertVerifiers4844.addVerifier(identityCommitmentsSize, actualVerifier); makeNewIdentityManager( treeDepth, @@ -104,12 +100,8 @@ contract WorldIDIdentityManagerIdentityRegistration4844 is WorldIDIdentityManage vm.assume(newPreRoot != newPostRoot); vm.assume(identities.length <= 1000); vm.assume(identityOperator != nullAddress && identityOperator != thisAddress); - ( - VerifierLookupTable insertVerifiers, - VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers, - VerifierLookupTable4844 insertVerifiers4844 - ) = makeVerifierLookupTables(TC.makeDynArray([identities.length])); + (insertVerifiers, deletionVerifiers, updateVerifiers, insertVerifiers4844) = + makeVerifierLookupTables(TC.makeDynArray([identities.length])); makeNewIdentityManager( treeDepth, newPreRoot, @@ -169,12 +161,8 @@ contract WorldIDIdentityManagerIdentityRegistration4844 is WorldIDIdentityManage vm.assume(newPreRoot != newPostRoot); vm.assume(identities.length <= 1000 && identities.length > 0); uint256 secondIdentsLength = identities.length / 2; - ( - VerifierLookupTable insertVerifiers, - VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers, - VerifierLookupTable4844 insertVerifiers4844 - ) = makeVerifierLookupTables(TC.makeDynArray([identities.length, secondIdentsLength])); + (insertVerifiers, deletionVerifiers, updateVerifiers, insertVerifiers4844) = + makeVerifierLookupTables(TC.makeDynArray([identities.length, secondIdentsLength])); makeNewIdentityManager( treeDepth, newPreRoot, @@ -242,12 +230,8 @@ contract WorldIDIdentityManagerIdentityRegistration4844 is WorldIDIdentityManage vm.assume(SimpleVerify.isValidInput(uint256(prf[0]))); vm.assume(newPreRoot != newPostRoot); vm.assume(identities.length > 0); - ( - VerifierLookupTable insertVerifiers, - VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers, - VerifierLookupTable4844 insertVerifiers4844 - ) = makeVerifierLookupTables(TC.makeDynArray([identities.length - 1])); + (insertVerifiers, deletionVerifiers, updateVerifiers, insertVerifiers4844) = + makeVerifierLookupTables(TC.makeDynArray([identities.length - 1])); makeNewIdentityManager( treeDepth, newPreRoot, @@ -296,12 +280,8 @@ contract WorldIDIdentityManagerIdentityRegistration4844 is WorldIDIdentityManage // Setup vm.assume(!SimpleVerify.isValidInput(uint256(prf[0]))); ITreeVerifier actualVerifier = new TreeVerifier(); - ( - VerifierLookupTable insertVerifiers, - VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers, - VerifierLookupTable4844 insertVerifiers4844 - ) = makeVerifierLookupTables(TC.makeDynArray([70])); + (insertVerifiers, deletionVerifiers, updateVerifiers, insertVerifiers4844) = + makeVerifierLookupTables(TC.makeDynArray([70])); insertVerifiers4844.addVerifier(identityCommitments.length, actualVerifier); makeNewIdentityManager( treeDepth, @@ -344,12 +324,8 @@ contract WorldIDIdentityManagerIdentityRegistration4844 is WorldIDIdentityManage // Setup vm.assume(newStartIndex != startIndex); ITreeVerifier actualVerifier = new TreeVerifier(); - ( - VerifierLookupTable insertVerifiers, - VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers, - VerifierLookupTable4844 insertVerifiers4844 - ) = makeVerifierLookupTables(TC.makeDynArray([70])); + (insertVerifiers, deletionVerifiers, updateVerifiers, insertVerifiers4844) = + makeVerifierLookupTables(TC.makeDynArray([70])); insertVerifiers4844.addVerifier(identityCommitmentsSize, actualVerifier); makeNewIdentityManager( treeDepth, @@ -394,12 +370,8 @@ contract WorldIDIdentityManagerIdentityRegistration4844 is WorldIDIdentityManage managerImplV2 = new ManagerImpl(); managerImplV2Address = address(managerImplV2); ITreeVerifier actualVerifier = new TreeVerifier(); - ( - VerifierLookupTable insertVerifiers, - VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers, - VerifierLookupTable4844 insertVerifiers4844 - ) = makeVerifierLookupTables(TC.makeDynArray([70])); + (insertVerifiers, deletionVerifiers, updateVerifiers, insertVerifiers4844) = + makeVerifierLookupTables(TC.makeDynArray([70])); insertVerifiers4844.addVerifier(identityCommitmentsSize, actualVerifier); bytes memory callData = abi.encodeCall( @@ -542,12 +514,8 @@ contract WorldIDIdentityManagerIdentityRegistration4844 is WorldIDIdentityManage vm.assume(identitiesLength != 0 && identitiesLength <= 1000); vm.assume(zeroPosition < identitiesLength && zeroPosition > 0); uint256[] memory identities = new uint256[](identitiesLength); - ( - VerifierLookupTable insertVerifiers, - VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers, - VerifierLookupTable4844 insertVerifiers4844 - ) = makeVerifierLookupTables(TC.makeDynArray([identitiesLength])); + (insertVerifiers, deletionVerifiers, updateVerifiers, insertVerifiers4844) = + makeVerifierLookupTables(TC.makeDynArray([identitiesLength])); makeNewIdentityManager( treeDepth, initialRoot, @@ -618,18 +586,14 @@ contract WorldIDIdentityManagerIdentityRegistration4844 is WorldIDIdentityManage function testRegisterIdentitiesWithBadKzgProof() public { // Setup ITreeVerifier actualVerifier = new TreeVerifier(); - ( - VerifierLookupTable insertVerifiers, - VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers, - VerifierLookupTable4844 insertionVerifier4844 - ) = makeVerifierLookupTables(TC.makeDynArray([40])); - insertionVerifier4844.addVerifier(identityCommitmentsSize, actualVerifier); + (insertVerifiers, deletionVerifiers, updateVerifiers, insertVerifiers4844) = + makeVerifierLookupTables(TC.makeDynArray([40])); + insertVerifiers4844.addVerifier(identityCommitmentsSize, actualVerifier); makeNewIdentityManager( treeDepth, insertionPreRoot, insertVerifiers, - insertionVerifier4844, + insertVerifiers4844, deletionVerifiers, updateVerifiers, semaphoreVerifier @@ -666,18 +630,14 @@ contract WorldIDIdentityManagerIdentityRegistration4844 is WorldIDIdentityManage function testRegisterIdentitiesWithBadKzgCommitment() public { // Setup ITreeVerifier actualVerifier = new TreeVerifier(); - ( - VerifierLookupTable insertVerifiers, - VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers, - VerifierLookupTable4844 insertionVerifier4844 - ) = makeVerifierLookupTables(TC.makeDynArray([40])); - insertionVerifier4844.addVerifier(identityCommitmentsSize, actualVerifier); + (insertVerifiers, deletionVerifiers, updateVerifiers, insertVerifiers4844) = + makeVerifierLookupTables(TC.makeDynArray([40])); + insertVerifiers4844.addVerifier(identityCommitmentsSize, actualVerifier); makeNewIdentityManager( treeDepth, insertionPreRoot, insertVerifiers, - insertionVerifier4844, + insertVerifiers4844, deletionVerifiers, updateVerifiers, semaphoreVerifier diff --git a/src/test/identity-manager/WorldIDIdentityManagerTest.sol b/src/test/identity-manager/WorldIDIdentityManagerTest.sol index 1739bbe..f1e2be5 100644 --- a/src/test/identity-manager/WorldIDIdentityManagerTest.sol +++ b/src/test/identity-manager/WorldIDIdentityManagerTest.sol @@ -141,6 +141,10 @@ contract WorldIDIdentityManagerTest is WorldIDTest { VerifierLookupTable4844 internal defaultInsertVerifiers4844; VerifierLookupTable internal defaultDeletionVerifiers; VerifierLookupTable internal defaultUpdateVerifiers; + VerifierLookupTable insertVerifiers; + VerifierLookupTable deletionVerifiers; + VerifierLookupTable updateVerifiers; + VerifierLookupTable4844 insertVerifiers4844; /////////////////////////////////////////////////////////////////////////////// /// TEST ORCHESTRATION /// @@ -274,31 +278,25 @@ contract WorldIDIdentityManagerTest is WorldIDTest { /// @dev It is initialised in the globals. /// /// @param actualPreRoot The pre-root to use. - /// @param insertVerifiers The insertion verifier lookup table. - /// @param insertVerifiers4844 The insertion verifier lookup table for EIP-4844 proofs. - /// @param updateVerifiers The udpate verifier lookup table. - /// @param actualSemaphoreVerifier The Semaphore verifier instance to use. + /// @param insertVer The insertion verifier lookup table. + /// @param insertVer4844 The insertion verifier lookup table for EIP-4844 proofs. + /// @param updateVer The update verifier lookup table. + /// @param actualSemaphoreVer The Semaphore verifier instance to use. function makeNewIdentityManager( uint8 actualTreeDepth, uint256 actualPreRoot, - VerifierLookupTable insertVerifiers, - VerifierLookupTable4844 insertVerifiers4844, - VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers, - ISemaphoreVerifier actualSemaphoreVerifier + VerifierLookupTable insertVer, + VerifierLookupTable4844 insertVer4844, + VerifierLookupTable deletionVer, + VerifierLookupTable updateVer, + ISemaphoreVerifier actualSemaphoreVer ) public { managerImplV1 = new ManagerImplV1(); managerImplV1Address = address(managerImplV1); bytes memory initCallData = abi.encodeCall( ManagerImplV1.initialize, - ( - actualTreeDepth, - actualPreRoot, - insertVerifiers, - updateVerifiers, - actualSemaphoreVerifier - ) + (actualTreeDepth, actualPreRoot, insertVer, updateVer, actualSemaphoreVer) ); identityManager = new IdentityManager(managerImplV1Address, initCallData); @@ -308,7 +306,7 @@ contract WorldIDIdentityManagerTest is WorldIDTest { managerImplV2 = new ManagerImpl(); managerImplV2Address = address(managerImplV2); - bytes memory initCallV2 = abi.encodeCall(managerImplV2.initializeV2, (deletionVerifiers)); + bytes memory initCallV2 = abi.encodeCall(managerImplV2.initializeV2, (deletionVer)); bytes memory upgradeCallV2 = abi.encodeCall( UUPSUpgradeable.upgradeToAndCall, (address(managerImplV2Address), initCallV2) ); @@ -320,7 +318,7 @@ contract WorldIDIdentityManagerTest is WorldIDTest { managerImplV3 = new ManagerImplV3(); managerImplV3Address = address(managerImplV3); - bytes memory initCallV3 = abi.encodeCall(managerImplV3.initializeV3, (insertVerifiers4844)); + bytes memory initCallV3 = abi.encodeCall(managerImplV3.initializeV3, (insertVer4844)); bytes memory upgradeCallV3 = abi.encodeCall( UUPSUpgradeable.upgradeToAndCall, (address(managerImplV3Address), initCallV3) ); @@ -339,12 +337,8 @@ contract WorldIDIdentityManagerTest is WorldIDTest { /// @custom:reverts string If any batch size exceeds 1000. /// @custom:reverts string If `batchSizes` is empty. function makeNewIdentityManager(uint256 actualPreRoot, uint256[] calldata batchSizes) public { - ( - VerifierLookupTable insertVerifiers, - VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers, - VerifierLookupTable4844 insertVerifiers4844 - ) = makeVerifierLookupTables(batchSizes); + (insertVerifiers, deletionVerifiers, updateVerifiers, insertVerifiers4844) = + makeVerifierLookupTables(batchSizes); defaultInsertVerifiers = insertVerifiers; defaultDeletionVerifiers = deletionVerifiers; defaultUpdateVerifiers = updateVerifiers; @@ -366,10 +360,10 @@ contract WorldIDIdentityManagerTest is WorldIDTest { /// @param batchSizes The batch sizes to create verifiers for. Verifiers will be created for /// 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. - /// @return insertVerifiers4844 The insertion verifier lookup table for EIP-4844 proofs. + /// @return insertVer The insertion verifier lookup table. + /// @return deletionVer The deletion verifier lookup table. + /// @return updateVer The update verifier lookup table. + /// @return insertVer4844 The insertion verifier lookup table for EIP-4844 proofs. /// /// @custom:reverts VerifierExists If `batchSizes` contains a duplicate. /// @custom:reverts string If any batch size exceeds 1000. @@ -377,10 +371,10 @@ contract WorldIDIdentityManagerTest is WorldIDTest { function makeVerifierLookupTables(uint256[] memory batchSizes) public returns ( - VerifierLookupTable insertVerifiers, - VerifierLookupTable deletionVerifiers, - VerifierLookupTable updateVerifiers, - VerifierLookupTable4844 insertVerifiers4844 + VerifierLookupTable insertVer, + VerifierLookupTable deletionVer, + VerifierLookupTable updateVer, + VerifierLookupTable4844 insertVer4844 ) { // Construct the verifier LUTs from the provided `batchSizes` info. @@ -390,10 +384,10 @@ contract WorldIDIdentityManagerTest is WorldIDTest { if (batchSizes[0] > 1000) { revert("batch size greater than 1000."); } - insertVerifiers = new VerifierLookupTable(); - deletionVerifiers = new VerifierLookupTable(); - updateVerifiers = new VerifierLookupTable(); - insertVerifiers4844 = new VerifierLookupTable4844(); + insertVer = new VerifierLookupTable(); + deletionVer = new VerifierLookupTable(); + updateVer = new VerifierLookupTable(); + insertVer4844 = new VerifierLookupTable4844(); for (uint256 i = 0; i < batchSizes.length; ++i) { uint256 batchSize = batchSizes[i]; if (batchSize > 1000) { @@ -401,12 +395,12 @@ contract WorldIDIdentityManagerTest is WorldIDTest { } ITreeVerifier batchVerifier = new SimpleVerifier(batchSize); - insertVerifiers.addVerifier(batchSize, batchVerifier); - deletionVerifiers.addVerifier(batchSize, batchVerifier); - updateVerifiers.addVerifier(batchSize, batchVerifier); + insertVer.addVerifier(batchSize, batchVerifier); + deletionVer.addVerifier(batchSize, batchVerifier); + updateVer.addVerifier(batchSize, batchVerifier); ITreeVerifier4844 batchVerifier4844 = new SimpleVerifier4844(batchSize); - insertVerifiers4844.addVerifier(batchSize, batchVerifier4844); + insertVer4844.addVerifier(batchSize, batchVerifier4844); } } diff --git a/src/test/identity-manager/WorldIDIdentityManagerUninit.t.sol b/src/test/identity-manager/WorldIDIdentityManagerUninit.t.sol index bb580ce..e64927a 100644 --- a/src/test/identity-manager/WorldIDIdentityManagerUninit.t.sol +++ b/src/test/identity-manager/WorldIDIdentityManagerUninit.t.sol @@ -123,7 +123,7 @@ contract WorldIDIdentityManagerUninit is WorldIDIdentityManagerTest { function testShouldNotCallSetRegisterIdentitiesVerifierLookupTableWhileUninit() public { // Setup makeUninitIdentityManager(); - (VerifierLookupTable insertVerifiers,,,) = makeVerifierLookupTables(TC.makeDynArray([75])); + (insertVerifiers,,,) = makeVerifierLookupTables(TC.makeDynArray([75])); bytes memory callData = abi.encodeCall( ManagerImplV1.setRegisterIdentitiesVerifierLookupTable, (insertVerifiers) ); @@ -153,8 +153,7 @@ contract WorldIDIdentityManagerUninit is WorldIDIdentityManagerTest { function testShouldNotCallSetDeleteIdentitiesVerifierLookupTableWhileUninit() public { // Setup makeUninitIdentityManager(); - (,, VerifierLookupTable deletionVerifiers,) = - makeVerifierLookupTables(TC.makeDynArray([75])); + (,, deletionVerifiers,) = makeVerifierLookupTables(TC.makeDynArray([75])); bytes memory callData = abi.encodeCall(ManagerImpl.setDeleteIdentitiesVerifierLookupTable, (deletionVerifiers)); bytes memory expectedError = From 01b5472891c8183d28817346b915fc08252f971d Mon Sep 17 00:00:00 2001 From: Wojciech Zmuda Date: Thu, 12 Sep 2024 00:02:55 +0200 Subject: [PATCH 8/8] github: remove benchmark step The --gas-report flag of the forge test command seems to conflict with blobhash mocks in tests, making the new EIP-4844 tests fail, despite they pass without this flag. Turn off benchmarks temporarily, to unblock EIP-4844 development. This should be debugged and fixed later. Signed-off-by: Wojciech Zmuda --- .github/workflows/tests.yml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 90a74ec..5e02e5e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -27,12 +27,9 @@ jobs: run: make build - name: Run Tests - run: make test + run: | + make test + make snapshot - name: Check formatting run: make format-check - - - name: Run Benchmarks - run: | - make bench - make snapshot