From bf716994123a718885f70309c8b9eaf9e3b3781d Mon Sep 17 00:00:00 2001 From: daveroga Date: Tue, 26 Nov 2024 11:18:33 +0100 Subject: [PATCH 01/69] new universal verifier multi-query --- contracts/interfaces/IRequestValidator.sol | 46 ++++++++ .../verifiers/UniversalVerifierMultiQuery.sol | 111 ++++++++++++++++++ 2 files changed, 157 insertions(+) create mode 100644 contracts/interfaces/IRequestValidator.sol create mode 100644 contracts/verifiers/UniversalVerifierMultiQuery.sol diff --git a/contracts/interfaces/IRequestValidator.sol b/contracts/interfaces/IRequestValidator.sol new file mode 100644 index 00000000..72a3fb7b --- /dev/null +++ b/contracts/interfaces/IRequestValidator.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.27; + +import {IState} from "./IState.sol"; + +/** + * @dev IRequestValidator. Interface for verification of request query data. + */ +interface IRequestValidator { + /** + * @dev OutputParam. Information about output params from verification. Used in verify function. + * @param name Name of the output param + * @param value Value of the output param + */ + struct OutputParam { + string name; + uint256 value; + } + + /** + * @dev Get version of the contract + */ + function version() external view returns (string memory); + + /** + * @dev Verify the proof with the supported method informed in the request query data + * packed as bytes and that the proof was generated by the sender. + * @param proof Proof packed as bytes to verify. + * @param data Request query data of the credential to verify. + * @param sender Sender of the proof. + * @param state State contract to get identities and gist states to check. + * @return Array of output params as result. + */ + function verify( + bytes calldata proof, + bytes calldata data, + address sender, + IState state + ) external returns (IRequestValidator.OutputParam[] memory); + + /** + * @dev Get supported methods ids. + * @return ids Array of methods ids supported. + */ + function getSupportedMethodsIds() external view returns (string[] memory ids); +} diff --git a/contracts/verifiers/UniversalVerifierMultiQuery.sol b/contracts/verifiers/UniversalVerifierMultiQuery.sol new file mode 100644 index 00000000..88714237 --- /dev/null +++ b/contracts/verifiers/UniversalVerifierMultiQuery.sol @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.27; + +import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; +import {IRequestValidator} from "../interfaces/IRequestValidator.sol"; +import {VerifierLib} from "../lib/VerifierLib.sol"; +import {IState} from "../interfaces/IState.sol"; + +contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { + /** + * @dev Version of the contract + */ + string public constant VERSION = "1.0.0"; + + /** + * @dev Request. Structure for request. + * @param metadata Metadata of the request. + * @param validator Validator circuit. + * @param data Data of the request. Proof parameters could be ZK groth16, plonk, ESDSA, EIP712, etc. + */ + struct Request { + string metadata; + IRequestValidator validator; + bytes data; + } + + /** + * @dev MultiQuery. Structure for request. + * @param multiQueryId Multi query id. + * @param requestIds Request ids for this multi query. + * @param linkedOutputParamsNames Output params to link from every request proof. + */ + struct MultiQuery { + uint256 multiQueryId; + uint256[] requestIds; + string[] linkedOutputParamsNames; // is Output params names linked between requests + } + + /// @custom:storage-location erc7201:iden3.storage.UniversalVerifierMultiQuery + struct UniversalVerifierMultiQueryStorage { + mapping(address user => mapping(uint256 requestId => VerifierLib.Proof)) _proofs; + mapping(uint64 requestId => Request) _requests; + uint64[] _requestIds; + IState _state; + } + + // keccak256(abi.encode(uint256(keccak256("iden3.storage.UniversalVerifierMultiQuery")) - 1)) & ~bytes32(uint256(0xff)); + bytes32 internal constant UniversalVerifierMultiQueryStorageLocation = + 0x4235c64ddf027641dd9aa586d246e4cc3acfcb3f7016a8aa68f7ac21d38b3b00; + + /** + * @dev Event emitted upon submitting a request + */ + event ResponseSubmitted(uint256 indexed requestId, address indexed caller); + + /** + * @dev Event emitted upon adding a request + */ + event RequestSet( + uint256 indexed requestId, + address indexed requestOwner, + string metadata, + address validator, + bytes data + ); + + /** + * @dev Event emitted upon updating a request + */ + event RequestUpdate( + uint256 indexed requestId, + address indexed requestOwner, + string metadata, + address validator, + bytes data + ); + + /** + * @dev Modifier to check if the validator is set for the request + */ + modifier checkRequestExistence(uint64 requestId, bool existence) { + if (existence) { + require(requestIdExists(requestId), "request id doesn't exist"); + } else { + require(!requestIdExists(requestId), "request id already exists"); + } + _; + } + + /** + * @dev Sets a ZKP request + * @param requestId The ID of the ZKP request + * @param request The ZKP request data + */ + function setRequest( + uint256 requestId, + Request calldata request + ) public checkRequestExistence(requestId, false) { + UniversalVerifierMultiQueryStorage storage s = _getZKPVerifierStorage(); + s._requests[requestId] = request; + s._requestIds.push(requestId); + + emit RequestSet( + requestId, + _msgSender(), + request.metadata, + address(request.validator), + request.data + ); + } +} From 74b24a1c566ad23105af3f6cff645deb4bc0b469 Mon Sep 17 00:00:00 2001 From: Andriian Chestnykh Date: Tue, 26 Nov 2024 10:43:36 +0000 Subject: [PATCH 02/69] Discussion results --- .../verifiers/UniversalVerifierMultiQuery.sol | 32 +++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/contracts/verifiers/UniversalVerifierMultiQuery.sol b/contracts/verifiers/UniversalVerifierMultiQuery.sol index 88714237..be4e30a4 100644 --- a/contracts/verifiers/UniversalVerifierMultiQuery.sol +++ b/contracts/verifiers/UniversalVerifierMultiQuery.sol @@ -21,7 +21,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { struct Request { string metadata; IRequestValidator validator; - bytes data; + bytes params; } /** @@ -33,9 +33,37 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { struct MultiQuery { uint256 multiQueryId; uint256[] requestIds; - string[] linkedOutputParamsNames; // is Output params names linked between requests + string[] linkedResponseFields; // is Output params names linked between requests } + //TODO ___start___ + +// [1, 2] +// ["linkID","linkID"] +// +// [1, 3, 4] +// ["issuerID","issuerID","issuerID"] +// +// struct SignalRequestTuple { +// uint256 requestId; +// string signalName; +// } +// +// function setMultiRequest( +// uint256 multiRequestID, // "id": "f8aee09d-f592-4fcc-8d2a-8938aa26676c", +// string conditionString, // (1 || 2) && 3 && (100 || 101 || 102) // TODO consider replacing with structure +// SignalRequestTuple[][] memory linkedSignals // is Signal name too specific and should be replaced by Property or something +// // [ +// // [{userID, 1}, {userID, 2}, {userID, 3}, {identity, 100}, {id, 101}, {user, 102}], +// // [{linkID, 1}, {linkID, 2}, {linkID, 3}] +// // ] +// // is this logic flexible enough? Though it might not be a problem for the first version +// // Maybe, operatorOutput = "something" is what we need as well??? +// ) public {} + + //TODO ___end___ + + /// @custom:storage-location erc7201:iden3.storage.UniversalVerifierMultiQuery struct UniversalVerifierMultiQueryStorage { mapping(address user => mapping(uint256 requestId => VerifierLib.Proof)) _proofs; From 16a8b2798559560827eac5ec9fec31f7f68dbfaf Mon Sep 17 00:00:00 2001 From: Andriian Chestnykh Date: Tue, 26 Nov 2024 10:59:29 +0000 Subject: [PATCH 03/69] Discussion results 2 --- contracts/interfaces/IRequestValidator.sol | 6 ------ 1 file changed, 6 deletions(-) diff --git a/contracts/interfaces/IRequestValidator.sol b/contracts/interfaces/IRequestValidator.sol index 72a3fb7b..eccde06a 100644 --- a/contracts/interfaces/IRequestValidator.sol +++ b/contracts/interfaces/IRequestValidator.sol @@ -37,10 +37,4 @@ interface IRequestValidator { address sender, IState state ) external returns (IRequestValidator.OutputParam[] memory); - - /** - * @dev Get supported methods ids. - * @return ids Array of methods ids supported. - */ - function getSupportedMethodsIds() external view returns (string[] memory ids); } From f05104d996711e6cc210ff04f3777ffa88a9f1ac Mon Sep 17 00:00:00 2001 From: daveroga Date: Tue, 26 Nov 2024 13:08:43 +0100 Subject: [PATCH 04/69] update universal verifier multi query --- contracts/interfaces/IRequestValidator.sol | 12 +- .../verifiers/UniversalVerifierMultiQuery.sol | 215 ++++++++++++++---- 2 files changed, 182 insertions(+), 45 deletions(-) diff --git a/contracts/interfaces/IRequestValidator.sol b/contracts/interfaces/IRequestValidator.sol index eccde06a..1e8789b4 100644 --- a/contracts/interfaces/IRequestValidator.sol +++ b/contracts/interfaces/IRequestValidator.sol @@ -8,11 +8,11 @@ import {IState} from "./IState.sol"; */ interface IRequestValidator { /** - * @dev OutputParam. Information about output params from verification. Used in verify function. - * @param name Name of the output param - * @param value Value of the output param + * @dev ResponseField. Information about response fields from verification. Used in verify function. + * @param name Name of the response field + * @param value Value of the response field */ - struct OutputParam { + struct ResponseField { string name; uint256 value; } @@ -29,12 +29,12 @@ interface IRequestValidator { * @param data Request query data of the credential to verify. * @param sender Sender of the proof. * @param state State contract to get identities and gist states to check. - * @return Array of output params as result. + * @return Array of response fields as result. */ function verify( bytes calldata proof, bytes calldata data, address sender, IState state - ) external returns (IRequestValidator.OutputParam[] memory); + ) external returns (IRequestValidator.ResponseField[] memory); } diff --git a/contracts/verifiers/UniversalVerifierMultiQuery.sol b/contracts/verifiers/UniversalVerifierMultiQuery.sol index be4e30a4..8b3d8cd2 100644 --- a/contracts/verifiers/UniversalVerifierMultiQuery.sol +++ b/contracts/verifiers/UniversalVerifierMultiQuery.sol @@ -3,7 +3,6 @@ pragma solidity 0.8.27; import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; import {IRequestValidator} from "../interfaces/IRequestValidator.sol"; -import {VerifierLib} from "../lib/VerifierLib.sol"; import {IState} from "../interfaces/IState.sol"; contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { @@ -16,13 +15,46 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { * @dev Request. Structure for request. * @param metadata Metadata of the request. * @param validator Validator circuit. - * @param data Data of the request. Proof parameters could be ZK groth16, plonk, ESDSA, EIP712, etc. + * @param params Params of the request. Proof parameters could be ZK groth16, plonk, ESDSA, EIP712, etc. */ struct Request { string metadata; IRequestValidator validator; bytes params; } + /** + * @dev Struct to store ZKP proof and associated data + */ + struct Proof { + bool isVerified; + mapping(string key => uint256 inputValue) storageFields; + string validatorVersion; + uint256 blockNumber; + uint256 blockTimestamp; + mapping(string key => bytes) metadata; + } + + /** + * @dev Response. Structure for response. + * @param requestId Request id of the request. + * @param proof proof to verify. + * @param metadata Metadata of the request. + */ + struct Response { + uint256 requestId; + bytes proof; + bytes metadata; + } + + /** + * @dev ResponseFieldFromRequest. Structure for response field from request to be linked. + * @param requestId Request id. + * @param responseFieldName Response field name. + */ + struct ResponseFieldFromRequest { + uint256 requestId; + string responseFieldName; + } /** * @dev MultiQuery. Structure for request. @@ -33,43 +65,27 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { struct MultiQuery { uint256 multiQueryId; uint256[] requestIds; - string[] linkedResponseFields; // is Output params names linked between requests + ResponseFieldFromRequest[][] linkedResponseFields; // is response fields linked between requests } - - //TODO ___start___ - -// [1, 2] -// ["linkID","linkID"] -// -// [1, 3, 4] -// ["issuerID","issuerID","issuerID"] -// -// struct SignalRequestTuple { -// uint256 requestId; -// string signalName; -// } -// -// function setMultiRequest( -// uint256 multiRequestID, // "id": "f8aee09d-f592-4fcc-8d2a-8938aa26676c", -// string conditionString, // (1 || 2) && 3 && (100 || 101 || 102) // TODO consider replacing with structure -// SignalRequestTuple[][] memory linkedSignals // is Signal name too specific and should be replaced by Property or something -// // [ -// // [{userID, 1}, {userID, 2}, {userID, 3}, {identity, 100}, {id, 101}, {user, 102}], -// // [{linkID, 1}, {linkID, 2}, {linkID, 3}] -// // ] -// // is this logic flexible enough? Though it might not be a problem for the first version -// // Maybe, operatorOutput = "something" is what we need as well??? -// ) public {} - - //TODO ___end___ - + // Example of linkedResponseFields: + // [ + // [{linkID, 1}, {linkID, 2}, {linkID, 3}] + // [{userID, 1}, {userID, 2}, {userID, 3}], + // ] /// @custom:storage-location erc7201:iden3.storage.UniversalVerifierMultiQuery struct UniversalVerifierMultiQueryStorage { - mapping(address user => mapping(uint256 requestId => VerifierLib.Proof)) _proofs; - mapping(uint64 requestId => Request) _requests; - uint64[] _requestIds; + // Information about requests + mapping(uint256 userID => mapping(uint256 requestId => Proof)) _proofs; + mapping(uint256 requestId => Request) _requests; + uint256[] _requestIds; IState _state; + // Information about multi-queries + mapping(uint256 multiQueryId => MultiQuery) _multiQueries; + uint256[] _multiQueryIds; + // Information linked between users and their addresses + mapping(address userAddress => uint256 userID) _user_address_to_id; + mapping(uint256 userID => address userAddress) _id_to_user_address; // check address[] to allow multiple addresses for the same userID? } // keccak256(abi.encode(uint256(keccak256("iden3.storage.UniversalVerifierMultiQuery")) - 1)) & ~bytes32(uint256(0xff)); @@ -89,7 +105,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { address indexed requestOwner, string metadata, address validator, - bytes data + bytes params ); /** @@ -100,13 +116,23 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { address indexed requestOwner, string metadata, address validator, - bytes data + bytes params ); + /** + * @dev Event emitted upon adding a multi query + */ + event MultiQuerySet(uint256 indexed multiQueryId, uint256[] requestIds); + + /** + * @dev Event emitted upon updating a multi query + */ + event MultiQueryUpdate(uint256 indexed multiQueryId, uint256[] requestIds); + /** * @dev Modifier to check if the validator is set for the request */ - modifier checkRequestExistence(uint64 requestId, bool existence) { + modifier checkRequestExistence(uint256 requestId, bool existence) { if (existence) { require(requestIdExists(requestId), "request id doesn't exist"); } else { @@ -115,6 +141,30 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { _; } + /** + * @dev Checks if a request ID exists + * @param requestId The ID of the request + * @return Whether the request ID exists + */ + function requestIdExists(uint256 requestId) public view returns (bool) { + return + _getUniversalVerifierMultiQueryStorage()._requests[requestId].validator != + IRequestValidator(address(0)); + } + + /** + * @dev Get the main storage using assembly to ensure specific storage location + */ + function _getUniversalVerifierMultiQueryStorage() + private + pure + returns (UniversalVerifierMultiQueryStorage storage $) + { + assembly { + $.slot := UniversalVerifierMultiQueryStorageLocation + } + } + /** * @dev Sets a ZKP request * @param requestId The ID of the ZKP request @@ -124,7 +174,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { uint256 requestId, Request calldata request ) public checkRequestExistence(requestId, false) { - UniversalVerifierMultiQueryStorage storage s = _getZKPVerifierStorage(); + UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); s._requests[requestId] = request; s._requestIds.push(requestId); @@ -133,7 +183,94 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { _msgSender(), request.metadata, address(request.validator), - request.data + request.params ); } + + /** + * @dev Gets a specific request by ID + * @param requestId The ID of the request + * @return request The request data + */ + function getRequest( + uint256 requestId + ) public view checkRequestExistence(requestId, true) returns (Request memory request) { + return _getUniversalVerifierMultiQueryStorage()._requests[requestId]; + } + + /** + * @dev Sets a multi query + * @param multiQueryId The ID of the multi query + * @param linkedResponseFields The params for linking response fields between requests + */ + function setMultiQuery( + uint256 multiQueryId, + ResponseFieldFromRequest[][] memory linkedResponseFields + ) public { + //TODO; + } + + /** + * @dev Submits an array of responses and updates proofs status + * @param responses The list of responses including request ID, proof and metadata + * @param crossChainProofs The list of cross chain proofs from universal resolver (oracle). This + * includes identities and global states. + */ + function submitZKPResponse(Response[] memory responses, bytes memory crossChainProofs) public { + UniversalVerifierMultiQueryStorage storage $ = _getUniversalVerifierMultiQueryStorage(); + + $._state.processCrossChainProofs(crossChainProofs); + + for (uint256 i = 0; i < responses.length; i++) { + Response memory response = responses[i]; + + address sender = _msgSender(); + + // TODO some internal method and storage location to save gas? + Request memory request = getRequest(response.requestId); + IRequestValidator.ResponseField[] memory signals = request.validator.verify( + response.proof, + request.params, + sender, + $._state + ); + + //TODO: Find the userID of the sender? or the userID is in the request? + uint256 userID = $._user_address_to_id[sender]; + + writeProofResults(userID, response.requestId, signals); + + if (response.metadata.length > 0) { + revert("Metadata not supported yet"); + } + } + + for (uint256 i = 0; i < responses.length; i++) { + emit ResponseSubmitted(responses[i].requestId, _msgSender()); + } + } + + /** + * @dev Writes proof results. + * @param userID The userID of the proof + * @param requestId The request ID of the proof + * @param responseFields The array of response fields of the proof + */ + function writeProofResults( + uint256 userID, + uint256 requestId, + IRequestValidator.ResponseField[] memory responseFields + ) public { + UniversalVerifierMultiQueryStorage storage $ = _getUniversalVerifierMultiQueryStorage(); + + Proof storage proof = $._proofs[userID][requestId]; + for (uint256 i = 0; i < responseFields.length; i++) { + proof.storageFields[responseFields[i].name] = responseFields[i].value; + } + + proof.isVerified = true; + proof.validatorVersion = $._requests[requestId].validator.version(); + proof.blockNumber = block.number; + proof.blockTimestamp = block.timestamp; + } } From 779513f099a299e663d90061a37c0375e70b1a24 Mon Sep 17 00:00:00 2001 From: daveroga Date: Tue, 26 Nov 2024 13:24:40 +0100 Subject: [PATCH 05/69] remove ZKP --- .../verifiers/UniversalVerifierMultiQuery.sol | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/contracts/verifiers/UniversalVerifierMultiQuery.sol b/contracts/verifiers/UniversalVerifierMultiQuery.sol index 8b3d8cd2..2358755b 100644 --- a/contracts/verifiers/UniversalVerifierMultiQuery.sol +++ b/contracts/verifiers/UniversalVerifierMultiQuery.sol @@ -23,7 +23,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { bytes params; } /** - * @dev Struct to store ZKP proof and associated data + * @dev Struct to store proof and associated data */ struct Proof { bool isVerified; @@ -166,9 +166,9 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { } /** - * @dev Sets a ZKP request - * @param requestId The ID of the ZKP request - * @param request The ZKP request data + * @dev Sets a request + * @param requestId The ID of the request + * @param request The request data */ function setRequest( uint256 requestId, @@ -210,13 +210,22 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { //TODO; } + /** + * @dev Gets a specific multi query by ID + * @param multiQueryId The ID of the multi query + * @return multiQuery The multi query data + */ + function getMultiQuery(uint256 multiQueryId) public view returns (MultiQuery memory) { + //TODO; + } + /** * @dev Submits an array of responses and updates proofs status * @param responses The list of responses including request ID, proof and metadata * @param crossChainProofs The list of cross chain proofs from universal resolver (oracle). This * includes identities and global states. */ - function submitZKPResponse(Response[] memory responses, bytes memory crossChainProofs) public { + function submitResponse(Response[] memory responses, bytes memory crossChainProofs) public { UniversalVerifierMultiQueryStorage storage $ = _getUniversalVerifierMultiQueryStorage(); $._state.processCrossChainProofs(crossChainProofs); From 8da12c7b63ac8c0e5f8a8ea58b35b5b9164895b1 Mon Sep 17 00:00:00 2001 From: daveroga Date: Tue, 26 Nov 2024 13:35:23 +0100 Subject: [PATCH 06/69] add auth validators --- .../verifiers/UniversalVerifierMultiQuery.sol | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/contracts/verifiers/UniversalVerifierMultiQuery.sol b/contracts/verifiers/UniversalVerifierMultiQuery.sol index 2358755b..d5ceac14 100644 --- a/contracts/verifiers/UniversalVerifierMultiQuery.sol +++ b/contracts/verifiers/UniversalVerifierMultiQuery.sol @@ -86,6 +86,10 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { // Information linked between users and their addresses mapping(address userAddress => uint256 userID) _user_address_to_id; mapping(uint256 userID => address userAddress) _id_to_user_address; // check address[] to allow multiple addresses for the same userID? + + // Information about auth validators + mapping(uint256 authID => address authValidator) _auth_validators; + uint256[] _authIds; } // keccak256(abi.encode(uint256(keccak256("iden3.storage.UniversalVerifierMultiQuery")) - 1)) & ~bytes32(uint256(0xff)); @@ -282,4 +286,24 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { proof.blockNumber = block.number; proof.blockTimestamp = block.timestamp; } + + /** + * @dev Adds an auth validator + * @param authID The Id of the auth validator + * @param authValidator The auth validator address + */ + function addAuthValidator(uint256 authID, address authValidator) public onlyOwner { + UniversalVerifierMultiQueryStorage storage $ = _getUniversalVerifierMultiQueryStorage(); + $._auth_validators[authID] = authValidator; + $._authIds.push(authID); + } + + /** + * @dev Gets an auth validator + * @param authID The Id of the auth validator + * @return The auth validator address + */ + function getAuthValidator(uint256 authID) public view returns (address) { + return _getUniversalVerifierMultiQueryStorage()._auth_validators[authID]; + } } From ba75a5d062f9ee405601380112ddd95bb38b7c55 Mon Sep 17 00:00:00 2001 From: Andriian Chestnykh Date: Tue, 26 Nov 2024 14:53:15 +0000 Subject: [PATCH 07/69] Broader discussion results --- .../verifiers/UniversalVerifierMultiQuery.sol | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/contracts/verifiers/UniversalVerifierMultiQuery.sol b/contracts/verifiers/UniversalVerifierMultiQuery.sol index d5ceac14..2a2499a8 100644 --- a/contracts/verifiers/UniversalVerifierMultiQuery.sol +++ b/contracts/verifiers/UniversalVerifierMultiQuery.sol @@ -56,22 +56,36 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { string responseFieldName; } + // submitMultiQueryResponse ??? + // submitResponse + // getMultiQueryResult + + // MultiQuery1 -> request 1 + // -> request 2 + // -> request 3 + + // MultiQuery2 -> request 4 + // -> request 5 + // -> request 2 + + /** * @dev MultiQuery. Structure for request. * @param multiQueryId Multi query id. * @param requestIds Request ids for this multi query. * @param linkedOutputParamsNames Output params to link from every request proof. */ - struct MultiQuery { + struct Query { uint256 multiQueryId; - uint256[] requestIds; + uint256[1, 2, 3, 4, 5] requestIds; ResponseFieldFromRequest[][] linkedResponseFields; // is response fields linked between requests } // Example of linkedResponseFields: - // [ - // [{linkID, 1}, {linkID, 2}, {linkID, 3}] - // [{userID, 1}, {userID, 2}, {userID, 3}], - // ] + // [ + // [{linkID, 1}, {linkID, 2}] + // [{linkID, 2}, {linkID, 3}], + // [{issuerID, 2}, {issuer, 3}], + // ] /// @custom:storage-location erc7201:iden3.storage.UniversalVerifierMultiQuery struct UniversalVerifierMultiQueryStorage { @@ -84,10 +98,12 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { mapping(uint256 multiQueryId => MultiQuery) _multiQueries; uint256[] _multiQueryIds; // Information linked between users and their addresses + // TODO think about arrays for these two mappings mapping(address userAddress => uint256 userID) _user_address_to_id; mapping(uint256 userID => address userAddress) _id_to_user_address; // check address[] to allow multiple addresses for the same userID? - + // Information about auth validators + // TODO remove this mapping and reuse _proofs; mapping(uint256 authID => address authValidator) _auth_validators; uint256[] _authIds; } From ae9cec1bfc2c88971c8185d5256715fe8d073769 Mon Sep 17 00:00:00 2001 From: Andriian Chestnykh Date: Wed, 27 Nov 2024 16:52:50 +0000 Subject: [PATCH 08/69] Add agreed universal verifier solution --- ...ersalVerifierMultiQuery_AgreedSolution.sol | 112 ++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 contracts/verifiers/UniversalVerifierMultiQuery_AgreedSolution.sol diff --git a/contracts/verifiers/UniversalVerifierMultiQuery_AgreedSolution.sol b/contracts/verifiers/UniversalVerifierMultiQuery_AgreedSolution.sol new file mode 100644 index 00000000..3757f834 --- /dev/null +++ b/contracts/verifiers/UniversalVerifierMultiQuery_AgreedSolution.sol @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.27; + +import {IRequestValidator} from "../interfaces/IRequestValidator.sol"; +import {IState} from "../interfaces/IState.sol"; +import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; + +contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { + struct Request { + string metadata; + IRequestValidator validator; + bytes params; + } + + function setRequest( + uint256 requestId, + Request calldata request + ) public checkRequestExistence(requestId, false) { + // only existing logic + // + requestId validation logic (to be defined) + } + + function setAuthRequest( + uint256 requestId, + Request calldata request + ) public checkRequestExistence(requestId, true) onlyOwner { + // similar to regular request existing logic + // but need to think about the authRequests ids validation + // reuse a special byte in the requestId for that maybe + + // Note: there will be two auth requests pre-set at deploy time + // (a) authV2 circuit based and (b) eth-based + } + + struct Response { + uint256 requestId; + bytes proof; + bytes metadata; + } + + struct ResponseField { + uint256 name; + uint256 value; + } + + function submitResponse(Response[] memory responses, bytes memory crossChainProofs) public { + // 1. Check for auth responses (throw if not provided exactly 1) + // and remember the userID from the response + // 2. Process crossChainProofs + // 3. Verify regular responses, write proof results (under the userID key from the step 1), + // emit events (existing logic) + } + + struct Query { + uint256[] requestIds; + bytes metadata; //TODO do we need it? + } + + function setQuery( + uint256 queryId, + Query calldata query + ) public { + //TODO implement + } + + function getQueryStatus(uint256 queryId, address userAddress) public view returns (Query memory) { + //TODO implement + // 1. Get the latest userId by the userAddress arg (in the mapping) + // 2. Check if all requests statuses are true for the userId + // 2. Check if any active auth for the userId is true + + + // NOTE: its possible to implement function getQueryStatus(queryId, userAddress, userId) + // as the data structures have the information but it would be a bit redundant for now + return new Query(); + } + + +//a1 => u1 +//a1 => u2 + +// a1 => u1 => true +// a1 => u2 => true + + struct Proof { + bool isVerified; + mapping(string key => uint256 inputValue) storageFields; + string validatorVersion; + uint256 blockTimestamp; + mapping(string key => bytes) metadata; + } + + /// @custom:storage-location erc7201:iden3.storage.UniversalVerifierMultiQuery + struct UniversalVerifierMultiQueryStorage { + // Information about requests + mapping(uint256 requestId => mapping(uint256 userID => Proof)) _proofs; + mapping(uint256 requestId => Request) _requests; + uint256[] _requestIds; + IState _state; + // Information about multi-queries + mapping(uint256 queryId => Query) _queries; + uint256[] _queryIds; + // Information linked between users and their addresses + // TODO think about arrays for these two mappings + mapping(address userAddress => uint256 userID) _user_address_to_id; + mapping(uint256 userID => address userAddress) _id_to_user_address; + mapping(uint256 userID => mapping(address userAddress => bool hasAuth)) _user_id_and_address_auth; + + uint256[] _authRequestIds; // reuses the same _requests mapping to store the auth requests + } +} From 790a0e37822cc21f8c6795d5a1408b03b4324bdc Mon Sep 17 00:00:00 2001 From: daveroga Date: Thu, 28 Nov 2024 08:40:57 +0100 Subject: [PATCH 09/69] update universal verifier multi query with agreed design --- .../verifiers/UniversalVerifierMultiQuery.sol | 205 +++++++++++------- 1 file changed, 132 insertions(+), 73 deletions(-) diff --git a/contracts/verifiers/UniversalVerifierMultiQuery.sol b/contracts/verifiers/UniversalVerifierMultiQuery.sol index 2a2499a8..5e7c54a0 100644 --- a/contracts/verifiers/UniversalVerifierMultiQuery.sol +++ b/contracts/verifiers/UniversalVerifierMultiQuery.sol @@ -11,6 +11,11 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { */ string public constant VERSION = "1.0.0"; + /** + * @dev Auth request type + */ + uint256 constant AUTH_REQUEST_TYPE = 1; + /** * @dev Request. Structure for request. * @param metadata Metadata of the request. @@ -47,65 +52,33 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { } /** - * @dev ResponseFieldFromRequest. Structure for response field from request to be linked. - * @param requestId Request id. - * @param responseFieldName Response field name. - */ - struct ResponseFieldFromRequest { - uint256 requestId; - string responseFieldName; - } - - // submitMultiQueryResponse ??? - // submitResponse - // getMultiQueryResult - - // MultiQuery1 -> request 1 - // -> request 2 - // -> request 3 - - // MultiQuery2 -> request 4 - // -> request 5 - // -> request 2 - - - /** - * @dev MultiQuery. Structure for request. - * @param multiQueryId Multi query id. + * @dev Query. Structure for query. + * @param queryId Query id. * @param requestIds Request ids for this multi query. - * @param linkedOutputParamsNames Output params to link from every request proof. + * @param metadata Metadata for the query. Empty in first version. */ struct Query { - uint256 multiQueryId; - uint256[1, 2, 3, 4, 5] requestIds; - ResponseFieldFromRequest[][] linkedResponseFields; // is response fields linked between requests + uint256 queryId; + uint256[] requestIds; + bytes metadata; } - // Example of linkedResponseFields: - // [ - // [{linkID, 1}, {linkID, 2}] - // [{linkID, 2}, {linkID, 3}], - // [{issuerID, 2}, {issuer, 3}], - // ] /// @custom:storage-location erc7201:iden3.storage.UniversalVerifierMultiQuery struct UniversalVerifierMultiQueryStorage { // Information about requests - mapping(uint256 userID => mapping(uint256 requestId => Proof)) _proofs; + mapping(uint256 requestId => mapping(uint256 userID => Proof)) _proofs; mapping(uint256 requestId => Request) _requests; uint256[] _requestIds; IState _state; - // Information about multi-queries - mapping(uint256 multiQueryId => MultiQuery) _multiQueries; - uint256[] _multiQueryIds; + // Information about queries + mapping(uint256 queryId => Query) _queries; + uint256[] _queryIds; // Information linked between users and their addresses // TODO think about arrays for these two mappings mapping(address userAddress => uint256 userID) _user_address_to_id; mapping(uint256 userID => address userAddress) _id_to_user_address; // check address[] to allow multiple addresses for the same userID? - - // Information about auth validators - // TODO remove this mapping and reuse _proofs; - mapping(uint256 authID => address authValidator) _auth_validators; - uint256[] _authIds; + mapping(uint256 userID => mapping(address userAddress => bool hasAuth)) _user_id_and_address_auth; + uint256[] _authRequestIds; // reuses the same _requests mapping to store the auth requests } // keccak256(abi.encode(uint256(keccak256("iden3.storage.UniversalVerifierMultiQuery")) - 1)) & ~bytes32(uint256(0xff)); @@ -128,6 +101,17 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { bytes params ); + /** + * @dev Event emitted upon adding an auth request by the owner + */ + event AuthRequestSet( + uint256 indexed requestId, + address indexed requestOwner, + string metadata, + address validator, + bytes params + ); + /** * @dev Event emitted upon updating a request */ @@ -140,17 +124,17 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { ); /** - * @dev Event emitted upon adding a multi query + * @dev Event emitted upon adding a query */ - event MultiQuerySet(uint256 indexed multiQueryId, uint256[] requestIds); + event QuerySet(uint256 indexed queryId, uint256[] requestIds); /** - * @dev Event emitted upon updating a multi query + * @dev Event emitted upon updating a query */ - event MultiQueryUpdate(uint256 indexed multiQueryId, uint256[] requestIds); + event QueryUpdate(uint256 indexed queryId, uint256[] requestIds); /** - * @dev Modifier to check if the validator is set for the request + * @dev Modifier to check if the request exists */ modifier checkRequestExistence(uint256 requestId, bool existence) { if (existence) { @@ -161,6 +145,48 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { _; } + /** + * @dev Modifier to check if the query exists + */ + modifier checkQueryExistence(uint256 queryId, bool existence) { + if (existence) { + require(queryIdExists(queryId), "query id doesn't exist"); + } else { + require(!queryIdExists(queryId), "query id already exists"); + } + _; + } + + /** + * @dev Modifier to check if the auth request exists + */ + modifier checkAuthRequestExistence(uint256 authRequestId, bool existence) { + if (existence) { + require( + requestIdExists(authRequestId) && typeOfRequest(authRequestId) == AUTH_REQUEST_TYPE, + "auth request id doesn't exist" + ); + } else { + require( + !(requestIdExists(authRequestId) && + typeOfRequest(authRequestId) == AUTH_REQUEST_TYPE), + "auth request id already exists" + ); + } + _; + } + + /** + * @dev Checks if a request ID exists + * @param requestId The ID of the request + * @return requestType Type of the request + */ + function typeOfRequest(uint256 requestId) public pure returns (uint256 requestType) { + //TODO: analyze first byte of the request and return its type + uint256 typeOfTheRequest = 0; + return typeOfTheRequest = 0; + } + /** * @dev Checks if a request ID exists * @param requestId The ID of the request @@ -172,6 +198,15 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { IRequestValidator(address(0)); } + /** + * @dev Checks if a query ID exists + * @param queryId The ID of the query + * @return Whether the query ID exists + */ + function queryIdExists(uint256 queryId) public view returns (bool) { + return _getUniversalVerifierMultiQueryStorage()._queries[queryId].requestIds.length > 0; + } + /** * @dev Get the main storage using assembly to ensure specific storage location */ @@ -219,24 +254,28 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { } /** - * @dev Sets a multi query - * @param multiQueryId The ID of the multi query - * @param linkedResponseFields The params for linking response fields between requests + * @dev Sets a query + * @param queryId The ID of the query + * @param query The query data */ - function setMultiQuery( - uint256 multiQueryId, - ResponseFieldFromRequest[][] memory linkedResponseFields - ) public { - //TODO; + function setQuery( + uint256 queryId, + Query calldata query + ) public checkQueryExistence(queryId, false) { + UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); + s._queries[queryId] = query; + s._queryIds.push(queryId); + + emit QuerySet(queryId, query.requestIds); } /** * @dev Gets a specific multi query by ID - * @param multiQueryId The ID of the multi query - * @return multiQuery The multi query data + * @param queryId The ID of the multi query + * @return query The query data */ - function getMultiQuery(uint256 multiQueryId) public view returns (MultiQuery memory) { - //TODO; + function getQuery(uint256 queryId) public view returns (Query memory query) { + return _getUniversalVerifierMultiQueryStorage()._queries[queryId]; } /** @@ -304,22 +343,42 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { } /** - * @dev Adds an auth validator - * @param authID The Id of the auth validator - * @param authValidator The auth validator address + * @dev Adds an auth request + * @param requestId The Id of the auth request to add */ - function addAuthValidator(uint256 authID, address authValidator) public onlyOwner { - UniversalVerifierMultiQueryStorage storage $ = _getUniversalVerifierMultiQueryStorage(); - $._auth_validators[authID] = authValidator; - $._authIds.push(authID); + function setAuthRequest( + uint256 requestId, + Request calldata request + ) public checkAuthRequestExistence(requestId, false) onlyOwner { + UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); + //TODO: Calculate the requestId for auth request + s._requests[requestId] = request; + s._requestIds.push(requestId); + + s._authRequestIds.push(requestId); + + emit AuthRequestSet( + requestId, + _msgSender(), + request.metadata, + address(request.validator), + request.params + ); } /** - * @dev Gets an auth validator - * @param authID The Id of the auth validator - * @return The auth validator address + * @dev Gets an auth request + * @param authRequestId The Id of the auth request to get + * @return authRequest The auth request data */ - function getAuthValidator(uint256 authID) public view returns (address) { - return _getUniversalVerifierMultiQueryStorage()._auth_validators[authID]; + function getAuthRequest( + uint256 authRequestId + ) + public + view + checkAuthRequestExistence(authRequestId, true) + returns (Request memory authRequest) + { + return _getUniversalVerifierMultiQueryStorage()._requests[authRequestId]; } } From 1858dcec029b2f3b3b15ba869920e8e595668887 Mon Sep 17 00:00:00 2001 From: daveroga Date: Thu, 28 Nov 2024 09:43:38 +0100 Subject: [PATCH 10/69] query status and submit response --- .../verifiers/UniversalVerifierMultiQuery.sol | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/contracts/verifiers/UniversalVerifierMultiQuery.sol b/contracts/verifiers/UniversalVerifierMultiQuery.sol index 5e7c54a0..44f68a02 100644 --- a/contracts/verifiers/UniversalVerifierMultiQuery.sol +++ b/contracts/verifiers/UniversalVerifierMultiQuery.sol @@ -278,6 +278,22 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { return _getUniversalVerifierMultiQueryStorage()._queries[queryId]; } + function checkAuthResponses(Response[] memory responses) internal pure returns (uint256) { + uint256 numAuthResponses = 0; + uint256 userID; + + for (uint256 i = 0; i < responses.length; i++) { + if (typeOfRequest(responses[i].requestId) == AUTH_REQUEST_TYPE) { + numAuthResponses++; + } + + //TODO: Get the userID from the response + } + require(numAuthResponses == 1, "Exactly 1 auth response is required"); + + return userID; + } + /** * @dev Submits an array of responses and updates proofs status * @param responses The list of responses including request ID, proof and metadata @@ -287,8 +303,15 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { function submitResponse(Response[] memory responses, bytes memory crossChainProofs) public { UniversalVerifierMultiQueryStorage storage $ = _getUniversalVerifierMultiQueryStorage(); + // 1. Check for auth responses (throw if not provided exactly 1) + // and remember the userID from the response + uint256 userIDFromResponses = checkAuthResponses(responses); + + // 2. Process crossChainProofs $._state.processCrossChainProofs(crossChainProofs); + // 3. Verify regular responses, write proof results (under the userID key from the step 1), + // emit events (existing logic) for (uint256 i = 0; i < responses.length; i++) { Response memory response = responses[i]; @@ -296,6 +319,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { // TODO some internal method and storage location to save gas? Request memory request = getRequest(response.requestId); + IRequestValidator.ResponseField[] memory signals = request.validator.verify( response.proof, request.params, @@ -305,6 +329,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { //TODO: Find the userID of the sender? or the userID is in the request? uint256 userID = $._user_address_to_id[sender]; + require(userID == userIDFromResponses, "User ID mismatch"); writeProofResults(userID, response.requestId, signals); @@ -381,4 +406,36 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { { return _getUniversalVerifierMultiQueryStorage()._requests[authRequestId]; } + + /** + * @dev Gets the status of the query verification + * @param queryId The ID of the query + * @param userAddress The address of the user + * @return status The status of the query. "True" if all requests are verified, "false" otherwise + */ + function getQueryStatus( + uint256 queryId, + address userAddress + ) public view returns (bool status) { + //TODO implement + UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); + + // 1. Get the latest userId by the userAddress arg (in the mapping) + uint256 userID = s._user_address_to_id[userAddress]; + require(userID != 0, "UserID not found"); + + // 2. Check if any active auth for the userId is true + bool activeAuth = s._user_id_and_address_auth[userID][userAddress]; + require(activeAuth, "No active auth for the user found"); + + // 3. Check if all requests statuses are true for the userId + for (uint256 i = 0; i < s._queries[queryId].requestIds.length; i++) { + uint256 requestId = s._queries[queryId].requestIds[i]; + if (!s._proofs[userID][requestId].isVerified) { + return false; + } + } + + return true; + } } From 452783409dc3408e031d821a0d75792e1c097f76 Mon Sep 17 00:00:00 2001 From: daveroga Date: Thu, 28 Nov 2024 12:23:07 +0100 Subject: [PATCH 11/69] get request type from 30 byte of the requestId --- .../verifiers/UniversalVerifierMultiQuery.sol | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/contracts/verifiers/UniversalVerifierMultiQuery.sol b/contracts/verifiers/UniversalVerifierMultiQuery.sol index 44f68a02..8b972f13 100644 --- a/contracts/verifiers/UniversalVerifierMultiQuery.sol +++ b/contracts/verifiers/UniversalVerifierMultiQuery.sol @@ -11,10 +11,15 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { */ string public constant VERSION = "1.0.0"; + // requestId. 32 bytes (in Big Endian): 31-0x00(not used), 30-0x01(requestType), 29..0 hash calculated Id, + // + // For requestType: + // 0x00 - regular request + // 0x01 - auth request /** * @dev Auth request type */ - uint256 constant AUTH_REQUEST_TYPE = 1; + uint8 constant AUTH_REQUEST_TYPE = 1; /** * @dev Request. Structure for request. @@ -160,16 +165,16 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { /** * @dev Modifier to check if the auth request exists */ - modifier checkAuthRequestExistence(uint256 authRequestId, bool existence) { + modifier checkAuthRequestExistence(uint256 requestId, bool existence) { if (existence) { require( - requestIdExists(authRequestId) && typeOfRequest(authRequestId) == AUTH_REQUEST_TYPE, + requestIdExists(requestId) && _getRequestType(requestId) == AUTH_REQUEST_TYPE, "auth request id doesn't exist" ); } else { require( - !(requestIdExists(authRequestId) && - typeOfRequest(authRequestId) == AUTH_REQUEST_TYPE), + !(requestIdExists(requestId) && + _getRequestType(requestId) == AUTH_REQUEST_TYPE), "auth request id already exists" ); } @@ -181,10 +186,8 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { * @param requestId The ID of the request * @return requestType Type of the request */ - function typeOfRequest(uint256 requestId) public pure returns (uint256 requestType) { - //TODO: analyze first byte of the request and return its type - uint256 typeOfTheRequest = 0; - return typeOfTheRequest = 0; + function _getRequestType(uint256 requestId) internal pure returns (uint8 requestType) { + return uint8(requestId >> 248); } /** @@ -283,7 +286,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { uint256 userID; for (uint256 i = 0; i < responses.length; i++) { - if (typeOfRequest(responses[i].requestId) == AUTH_REQUEST_TYPE) { + if (_getRequestType(responses[i].requestId) == AUTH_REQUEST_TYPE) { numAuthResponses++; } @@ -393,18 +396,18 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { /** * @dev Gets an auth request - * @param authRequestId The Id of the auth request to get + * @param requestId The Id of the auth request to get * @return authRequest The auth request data */ function getAuthRequest( - uint256 authRequestId + uint256 requestId ) public view - checkAuthRequestExistence(authRequestId, true) + checkAuthRequestExistence(requestId, true) returns (Request memory authRequest) { - return _getUniversalVerifierMultiQueryStorage()._requests[authRequestId]; + return _getUniversalVerifierMultiQueryStorage()._requests[requestId]; } /** From bb82ff355c4324a636279e27e3155aa0b93e497c Mon Sep 17 00:00:00 2001 From: daveroga Date: Thu, 28 Nov 2024 13:13:59 +0100 Subject: [PATCH 12/69] add EthIdentityValidator --- contracts/validators/EthIdentityValidator.sol | 123 ++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 contracts/validators/EthIdentityValidator.sol diff --git a/contracts/validators/EthIdentityValidator.sol b/contracts/validators/EthIdentityValidator.sol new file mode 100644 index 00000000..ecddc5bb --- /dev/null +++ b/contracts/validators/EthIdentityValidator.sol @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.27; + +import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; +import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; +import {IVerifier} from "../interfaces/IVerifier.sol"; +import {GenesisUtils} from "../lib/GenesisUtils.sol"; +import {IRequestValidator} from "../interfaces/IRequestValidator.sol"; +import {IState} from "../interfaces/IState.sol"; + +/** + * @dev EthIdentityValidator validator + */ +contract EthIdentityValidator is Ownable2StepUpgradeable, IRequestValidator, ERC165 { + struct PubSignals { + uint256 userID; + } + + /** + * @dev Version of contract + */ + string public constant VERSION = "1.0.0"; + + /// @dev Main storage structure for the contract + /// @custom:storage-location iden3.storage.EthIdentityValidator + struct EthIdentityValidatorBaseStorage { + IState state; + } + + // keccak256(abi.encode(uint256(keccak256("iden3.storage.EthIdentityValidator")) - 1)) + // & ~bytes32(uint256(0xff)); + bytes32 private constant EthIdentityValidatorBaseStorageLocation = + 0x28c92975a30f1f2f7970a65953987652034d896ba2d3b7a4961ada9e18287510; + //TODO: need to calc properly + + /// @dev Get the main storage using assembly to ensure specific storage location + function _getEthIdentityValidatorBaseStorage() + private + pure + returns (EthIdentityValidatorBaseStorage storage $) + { + assembly { + $.slot := EthIdentityValidatorBaseStorageLocation + } + } + + /** + * @dev Initialize the contract + * @param _verifierContractAddr Address of the verifier contract + * @param _stateContractAddr Address of the state contract + * @param owner Owner of the contract + */ + function initialize( + address _verifierContractAddr, + address _stateContractAddr, + address owner + ) public initializer { + _setInputToIndex("userID", 0); + + _initDefaultStateVariables(_stateContractAddr, owner); + } + + function _initDefaultStateVariables( + address _stateContractAddr, + address owner + ) internal { + EthIdentityValidatorBaseStorage + storage s = _getEthIdentityValidatorBaseStorage(); + + s.revocationStateExpirationTimeout = 1 hours; + s.proofExpirationTimeout = 1 hours; + s.gistRootExpirationTimeout = 1 hours; + s.state = IState(_stateContractAddr); + __Ownable_init(owner); + } + + /** + * @dev Get the version of the contract + * @return Version of the contract + */ + function version() public pure override returns (string memory) { + return VERSION; + } + + /** + * @dev Verify the proof and check the request query data + * @param proof Proof packed as bytes to verify. + * @param data Request query data of the credential to verify. + * @param sender Sender of the proof. + * @param stateContract State contract to get identities and gist states to check. + * @return Array of signals as result. + */ + function verify( + bytes calldata proof, + // solhint-disable-next-line no-unused-vars + bytes calldata data, + address sender, + IState stateContract + ) public view override returns (IRequestValidator.Signal[] memory) { + ( + uint256 userID + ) = abi.decode(proof, (uint256)); + + _verifyEthIdentity(userID, sender); + IRequestValidator.Signal[] memory signals = new IRequestValidator.Signal[](1); + signals[0] = IRequestValidator.Signal({name: "userID", value: userID}); + return signals; + } + + + function _getState() internal view returns (IState) { + return _getEthIdentityValidatorBaseStorage().state; + } + + function _verifyEthIdentity( + uint256 id, + address sender + ) internal view { + bytes2 idType = _getState().getIdTypeIfSupported(id); + uint256 calcId = GenesisUtils.calcIdFromEthAddress(idType, sender); + require(calcId == id, "Sender is not owner of the ethereum identity"); + } +} From e66a160fdc78555963635447838aa17617e69a25 Mon Sep 17 00:00:00 2001 From: daveroga Date: Thu, 28 Nov 2024 17:22:16 +0100 Subject: [PATCH 13/69] updates from review --- contracts/validators/EthIdentityValidator.sol | 3 -- .../verifiers/UniversalVerifierMultiQuery.sol | 52 +++++++++++-------- 2 files changed, 31 insertions(+), 24 deletions(-) diff --git a/contracts/validators/EthIdentityValidator.sol b/contracts/validators/EthIdentityValidator.sol index ecddc5bb..c4878ba2 100644 --- a/contracts/validators/EthIdentityValidator.sol +++ b/contracts/validators/EthIdentityValidator.sol @@ -67,9 +67,6 @@ contract EthIdentityValidator is Ownable2StepUpgradeable, IRequestValidator, ERC EthIdentityValidatorBaseStorage storage s = _getEthIdentityValidatorBaseStorage(); - s.revocationStateExpirationTimeout = 1 hours; - s.proofExpirationTimeout = 1 hours; - s.gistRootExpirationTimeout = 1 hours; s.state = IState(_stateContractAddr); __Ownable_init(owner); } diff --git a/contracts/verifiers/UniversalVerifierMultiQuery.sol b/contracts/verifiers/UniversalVerifierMultiQuery.sol index 8b972f13..f7adeb32 100644 --- a/contracts/verifiers/UniversalVerifierMultiQuery.sol +++ b/contracts/verifiers/UniversalVerifierMultiQuery.sol @@ -173,8 +173,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { ); } else { require( - !(requestIdExists(requestId) && - _getRequestType(requestId) == AUTH_REQUEST_TYPE), + !(requestIdExists(requestId) && _getRequestType(requestId) == AUTH_REQUEST_TYPE), "auth request id already exists" ); } @@ -308,17 +307,37 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { // 1. Check for auth responses (throw if not provided exactly 1) // and remember the userID from the response - uint256 userIDFromResponses = checkAuthResponses(responses); + //TODO: return requestId of the auth response and execute verification for this response + // in order to get "userID" from the output response field + uint256 authRequestId = checkAuthResponses(responses); // 2. Process crossChainProofs $._state.processCrossChainProofs(crossChainProofs); - // 3. Verify regular responses, write proof results (under the userID key from the step 1), + // Verify for the auth request + // authRequest.validator.verify(...) -> userIDFromAuth + // write results for the auth request + // emit + uint256 userIDFromAuth = 0; + + address sender = _msgSender(); + + //TODO: Check all the mappings + $._user_address_to_id[sender] = userIDFromAuth; + $._id_to_user_address[userIDFromAuth] = sender; + $._user_id_and_address_auth[userIDFromAuth][sender] = true; + + + // 3. Verify regular responses, write proof results (under the userID key from the step 1), // emit events (existing logic) for (uint256 i = 0; i < responses.length; i++) { - Response memory response = responses[i]; + emit ResponseSubmitted(responses[i].requestId, _msgSender()); + + if (_getRequestType(responses[i].requestId) == AUTH_REQUEST_TYPE) { + continue; + } - address sender = _msgSender(); + Response memory response = responses[i]; // TODO some internal method and storage location to save gas? Request memory request = getRequest(response.requestId); @@ -330,19 +349,12 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { $._state ); - //TODO: Find the userID of the sender? or the userID is in the request? - uint256 userID = $._user_address_to_id[sender]; - require(userID == userIDFromResponses, "User ID mismatch"); - - writeProofResults(userID, response.requestId, signals); + writeProofResults(userIDFromAuth, response.requestId, signals); if (response.metadata.length > 0) { revert("Metadata not supported yet"); } - } - for (uint256 i = 0; i < responses.length; i++) { - emit ResponseSubmitted(responses[i].requestId, _msgSender()); } } @@ -378,8 +390,11 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { uint256 requestId, Request calldata request ) public checkAuthRequestExistence(requestId, false) onlyOwner { + if (_getRequestType(requestId) != AUTH_REQUEST_TYPE) { + revert("Request ID is not an auth request"); + } + UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); - //TODO: Calculate the requestId for auth request s._requests[requestId] = request; s._requestIds.push(requestId); @@ -401,12 +416,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { */ function getAuthRequest( uint256 requestId - ) - public - view - checkAuthRequestExistence(requestId, true) - returns (Request memory authRequest) - { + ) public view checkAuthRequestExistence(requestId, true) returns (Request memory authRequest) { return _getUniversalVerifierMultiQueryStorage()._requests[requestId]; } From d416b9ad3c4b5c82b99e64b495001283e1b25b15 Mon Sep 17 00:00:00 2001 From: Andriian Chestnykh Date: Fri, 29 Nov 2024 12:20:01 +0000 Subject: [PATCH 14/69] Discussion result --- .../verifiers/UniversalVerifierMultiQuery.sol | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/contracts/verifiers/UniversalVerifierMultiQuery.sol b/contracts/verifiers/UniversalVerifierMultiQuery.sol index f7adeb32..2e96d359 100644 --- a/contracts/verifiers/UniversalVerifierMultiQuery.sol +++ b/contracts/verifiers/UniversalVerifierMultiQuery.sol @@ -51,11 +51,31 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { * @param metadata Metadata of the request. */ struct Response { + uint256 queryId; // TODO new field uint256 requestId; + uint256 requestIndexInQuery; // TODO new field bytes proof; bytes metadata; } + +// TODO discussion result start +// struct ResponseFieldFromRequest { +// uint256 requestId; +// string responseFieldName; +// uint256 requestIndexInQuery; +// } +// +//ResponseFieldFromRequest[][] + +// [ +// [{linkID, 1, 0}, {linkID, 2, 0}] +// [{linkID, 2, 2}, {linkID, 3, 1}], +// [{issuerID, 2, 3}, {issuer, 3, 4}], +// ] +// TODO discussion result start + + /** * @dev Query. Structure for query. * @param queryId Query id. From a26861c9f9672062acd645ed30e6ff26dfab25c3 Mon Sep 17 00:00:00 2001 From: daveroga Date: Fri, 29 Nov 2024 14:24:58 +0100 Subject: [PATCH 15/69] updates from discussion --- .../verifiers/UniversalVerifierMultiQuery.sol | 112 +++++++++++------- 1 file changed, 71 insertions(+), 41 deletions(-) diff --git a/contracts/verifiers/UniversalVerifierMultiQuery.sol b/contracts/verifiers/UniversalVerifierMultiQuery.sol index 2e96d359..dfffe22a 100644 --- a/contracts/verifiers/UniversalVerifierMultiQuery.sol +++ b/contracts/verifiers/UniversalVerifierMultiQuery.sol @@ -53,28 +53,26 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { struct Response { uint256 queryId; // TODO new field uint256 requestId; - uint256 requestIndexInQuery; // TODO new field + uint256 requestIndexInQuery; // TODO new field bytes proof; bytes metadata; } + // TODO discussion result start + // struct ResponseFieldFromRequest { + // uint256 requestId; + // string responseFieldName; + // uint256 requestIndexInQuery; + // } + // + //ResponseFieldFromRequest[][] -// TODO discussion result start -// struct ResponseFieldFromRequest { -// uint256 requestId; -// string responseFieldName; -// uint256 requestIndexInQuery; -// } -// -//ResponseFieldFromRequest[][] - -// [ -// [{linkID, 1, 0}, {linkID, 2, 0}] -// [{linkID, 2, 2}, {linkID, 3, 1}], -// [{issuerID, 2, 3}, {issuer, 3, 4}], -// ] -// TODO discussion result start - + // [ + // [{linkID, 1, 0}, {linkID, 2, 0}] + // [{linkID, 2, 2}, {linkID, 3, 1}], + // [{issuerID, 2, 3}, {issuer, 3, 4}], + // ] + // TODO discussion result start /** * @dev Query. Structure for query. @@ -91,7 +89,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { /// @custom:storage-location erc7201:iden3.storage.UniversalVerifierMultiQuery struct UniversalVerifierMultiQueryStorage { // Information about requests - mapping(uint256 requestId => mapping(uint256 userID => Proof)) _proofs; + mapping(uint256 queryId => mapping(uint256 requestIndexInQuery => mapping(uint256 requestId => mapping(uint256 userID => Proof)))) _proofs; mapping(uint256 requestId => Request) _requests; uint256[] _requestIds; IState _state; @@ -300,20 +298,24 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { return _getUniversalVerifierMultiQueryStorage()._queries[queryId]; } + /** + * @dev Checks for exactly 1 auth response and returns the index of the auth response + * @param responses The list of responses + * @return The index of the auth response + */ function checkAuthResponses(Response[] memory responses) internal pure returns (uint256) { uint256 numAuthResponses = 0; - uint256 userID; + uint256 authResponseIndex; for (uint256 i = 0; i < responses.length; i++) { if (_getRequestType(responses[i].requestId) == AUTH_REQUEST_TYPE) { + authResponseIndex = i; numAuthResponses++; } - - //TODO: Get the userID from the response } require(numAuthResponses == 1, "Exactly 1 auth response is required"); - return userID; + return authResponseIndex; } /** @@ -324,35 +326,54 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { */ function submitResponse(Response[] memory responses, bytes memory crossChainProofs) public { UniversalVerifierMultiQueryStorage storage $ = _getUniversalVerifierMultiQueryStorage(); + address sender = _msgSender(); // 1. Check for auth responses (throw if not provided exactly 1) - // and remember the userID from the response - //TODO: return requestId of the auth response and execute verification for this response - // in order to get "userID" from the output response field - uint256 authRequestId = checkAuthResponses(responses); + // and return auth response index in the responses array + // in order to get "userID" from the output response field then + uint256 authResponseIndex = checkAuthResponses(responses); // 2. Process crossChainProofs $._state.processCrossChainProofs(crossChainProofs); - // Verify for the auth request - // authRequest.validator.verify(...) -> userIDFromAuth - // write results for the auth request - // emit - uint256 userIDFromAuth = 0; - - address sender = _msgSender(); + // Verify for the auth request and get the userID + Response memory authResponse = responses[authResponseIndex]; + Request memory authRequest = getRequest(authResponse.requestId); + IRequestValidator.ResponseField[] memory authSignals = authRequest.validator.verify( + authResponse.proof, + authRequest.params, + sender, + $._state + ); + + uint256 userIDFromAuth; + for (uint256 i = 0; i < authSignals.length; i++) { + if (keccak256(bytes(authSignals[i].name)) == keccak256(bytes("userID"))) { + userIDFromAuth = authSignals[i].value; + break; + } + } + + writeProofResults( + authResponse.queryId, + authResponse.requestId, + authResponse.requestIndexInQuery, + userIDFromAuth, + authSignals + ); //TODO: Check all the mappings $._user_address_to_id[sender] = userIDFromAuth; $._id_to_user_address[userIDFromAuth] = sender; $._user_id_and_address_auth[userIDFromAuth][sender] = true; - - // 3. Verify regular responses, write proof results (under the userID key from the step 1), + // 3. Verify regular responses, write proof results (under the userID key from the step 1), // emit events (existing logic) for (uint256 i = 0; i < responses.length; i++) { + // emit for all the responses emit ResponseSubmitted(responses[i].requestId, _msgSender()); + // avoid to process auth request again if (_getRequestType(responses[i].requestId) == AUTH_REQUEST_TYPE) { continue; } @@ -369,29 +390,38 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { $._state ); - writeProofResults(userIDFromAuth, response.requestId, signals); + writeProofResults( + response.queryId, + response.requestId, + response.requestIndexInQuery, + userIDFromAuth, + signals + ); if (response.metadata.length > 0) { revert("Metadata not supported yet"); } - } } /** * @dev Writes proof results. - * @param userID The userID of the proof + * @param queryId The query ID of the proof * @param requestId The request ID of the proof + * @param requestIndexInQuery The index of the request in the query definition + * @param userID The userID of the proof * @param responseFields The array of response fields of the proof */ function writeProofResults( - uint256 userID, + uint256 queryId, uint256 requestId, + uint256 requestIndexInQuery, + uint256 userID, IRequestValidator.ResponseField[] memory responseFields ) public { UniversalVerifierMultiQueryStorage storage $ = _getUniversalVerifierMultiQueryStorage(); - Proof storage proof = $._proofs[userID][requestId]; + Proof storage proof = $._proofs[queryId][requestId][requestIndexInQuery][userID]; for (uint256 i = 0; i < responseFields.length; i++) { proof.storageFields[responseFields[i].name] = responseFields[i].value; } @@ -464,7 +494,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { // 3. Check if all requests statuses are true for the userId for (uint256 i = 0; i < s._queries[queryId].requestIds.length; i++) { uint256 requestId = s._queries[queryId].requestIds[i]; - if (!s._proofs[userID][requestId].isVerified) { + if (!s._proofs[queryId][requestId][i][userID].isVerified) { return false; } } From 0c2e792097db771910cfe4ec96096d4695235d5d Mon Sep 17 00:00:00 2001 From: daveroga Date: Fri, 29 Nov 2024 16:38:21 +0100 Subject: [PATCH 16/69] check response field values from linked response fields --- .../verifiers/UniversalVerifierMultiQuery.sol | 80 +++++++++++++++++-- 1 file changed, 73 insertions(+), 7 deletions(-) diff --git a/contracts/verifiers/UniversalVerifierMultiQuery.sol b/contracts/verifiers/UniversalVerifierMultiQuery.sol index dfffe22a..43b9cb93 100644 --- a/contracts/verifiers/UniversalVerifierMultiQuery.sol +++ b/contracts/verifiers/UniversalVerifierMultiQuery.sol @@ -51,9 +51,9 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { * @param metadata Metadata of the request. */ struct Response { - uint256 queryId; // TODO new field + uint256 queryId; uint256 requestId; - uint256 requestIndexInQuery; // TODO new field + uint256 requestIndexInQuery; bytes proof; bytes metadata; } @@ -74,6 +74,12 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { // ] // TODO discussion result start + struct ResponseFieldFromRequest { + uint256 requestId; + uint256 requestIndexInQuery; + string responseFieldName; + } + /** * @dev Query. Structure for query. * @param queryId Query id. @@ -83,6 +89,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { struct Query { uint256 queryId; uint256[] requestIds; + ResponseFieldFromRequest[][] linkedResponseFields; // this are response fields linked between requests bytes metadata; } @@ -97,7 +104,6 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { mapping(uint256 queryId => Query) _queries; uint256[] _queryIds; // Information linked between users and their addresses - // TODO think about arrays for these two mappings mapping(address userAddress => uint256 userID) _user_address_to_id; mapping(uint256 userID => address userAddress) _id_to_user_address; // check address[] to allow multiple addresses for the same userID? mapping(uint256 userID => mapping(address userAddress => bool hasAuth)) _user_id_and_address_auth; @@ -337,7 +343,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { $._state.processCrossChainProofs(crossChainProofs); // Verify for the auth request and get the userID - Response memory authResponse = responses[authResponseIndex]; + Response memory authResponse = responses[authResponseIndex]; Request memory authRequest = getRequest(authResponse.requestId); IRequestValidator.ResponseField[] memory authSignals = authRequest.validator.verify( authResponse.proof, @@ -362,7 +368,6 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { authSignals ); - //TODO: Check all the mappings $._user_address_to_id[sender] = userIDFromAuth; $._id_to_user_address[userIDFromAuth] = sender; $._user_id_and_address_auth[userIDFromAuth][sender] = true; @@ -380,7 +385,6 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { Response memory response = responses[i]; - // TODO some internal method and storage location to save gas? Request memory request = getRequest(response.requestId); IRequestValidator.ResponseField[] memory signals = request.validator.verify( @@ -470,6 +474,66 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { return _getUniversalVerifierMultiQueryStorage()._requests[requestId]; } + /** + * @dev Gets response field value + * @param queryId Id of the query + * @param requestId Id of the request + * @param requestIndexInQuery Index of the request in the query + * @param userID Id of the user + * @param responseFieldName Name of the response field to get + */ + function getResponseFieldValue( + uint256 queryId, + uint256 requestId, + uint256 requestIndexInQuery, + uint256 userID, + string memory responseFieldName + ) public view returns (uint256) { + UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); + return + s._proofs[queryId][requestId][requestIndexInQuery][userID].storageFields[ + responseFieldName + ]; + } + + /** + * @dev Checks if all linked response fields are the same + * @param queryId The ID of the query + * @param userID The ID of the user + * @return Whether all linked response fields are the same + */ + function checkLinkedResponseFields(uint256 queryId, uint256 userID) public view returns (bool) { + UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); + + for (uint256 i = 0; i < s._queries[queryId].linkedResponseFields.length; i++) { + // check if we have linked response fields + if (s._queries[queryId].linkedResponseFields[i].length > 0) { + // Get first value and check if all the values for the same linked responses are the same + uint256 firstValue = getResponseFieldValue( + queryId, + s._queries[queryId].linkedResponseFields[i][0].requestId, + s._queries[queryId].linkedResponseFields[i][0].requestIndexInQuery, + userID, + s._queries[queryId].linkedResponseFields[i][0].responseFieldName + ); + + for (uint256 j = 1; j < s._queries[queryId].linkedResponseFields[i].length; j++) { + uint256 valueToCompare = getResponseFieldValue( + queryId, + s._queries[queryId].linkedResponseFields[i][j].requestId, + s._queries[queryId].linkedResponseFields[i][j].requestIndexInQuery, + userID, + s._queries[queryId].linkedResponseFields[i][j].responseFieldName + ); + + if (firstValue != valueToCompare) { + return false; + } + } + } + } + } + /** * @dev Gets the status of the query verification * @param queryId The ID of the query @@ -480,7 +544,6 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { uint256 queryId, address userAddress ) public view returns (bool status) { - //TODO implement UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); // 1. Get the latest userId by the userAddress arg (in the mapping) @@ -499,6 +562,9 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { } } + // 4. Check if all linked response fields are the same + checkLinkedResponseFields(queryId, userID); + return true; } } From 16f8b5aa40596e28207f8008fafa62255f45d69b Mon Sep 17 00:00:00 2001 From: daveroga Date: Mon, 2 Dec 2024 11:48:47 +0100 Subject: [PATCH 17/69] fix some errors in compilation --- contracts/interfaces/IRequestValidator.sol | 2 +- contracts/validators/EthIdentityValidator.sol | 15 +++++---------- ...salVerifierMultiQuery_AgreedSolution.sol.skip} | 0 3 files changed, 6 insertions(+), 11 deletions(-) rename contracts/verifiers/{UniversalVerifierMultiQuery_AgreedSolution.sol => UniversalVerifierMultiQuery_AgreedSolution.sol.skip} (100%) diff --git a/contracts/interfaces/IRequestValidator.sol b/contracts/interfaces/IRequestValidator.sol index 1e8789b4..a66729d9 100644 --- a/contracts/interfaces/IRequestValidator.sol +++ b/contracts/interfaces/IRequestValidator.sol @@ -36,5 +36,5 @@ interface IRequestValidator { bytes calldata data, address sender, IState state - ) external returns (IRequestValidator.ResponseField[] memory); + ) external returns (ResponseField[] memory); } diff --git a/contracts/validators/EthIdentityValidator.sol b/contracts/validators/EthIdentityValidator.sol index c4878ba2..87a5fe91 100644 --- a/contracts/validators/EthIdentityValidator.sol +++ b/contracts/validators/EthIdentityValidator.sol @@ -30,10 +30,9 @@ contract EthIdentityValidator is Ownable2StepUpgradeable, IRequestValidator, ERC // keccak256(abi.encode(uint256(keccak256("iden3.storage.EthIdentityValidator")) - 1)) // & ~bytes32(uint256(0xff)); bytes32 private constant EthIdentityValidatorBaseStorageLocation = - 0x28c92975a30f1f2f7970a65953987652034d896ba2d3b7a4961ada9e18287510; - //TODO: need to calc properly + 0x1816cff28d525c2e505742319020369d0e29e8fafd5168e127e29766cf2be1fb; - /// @dev Get the main storage using assembly to ensure specific storage location + /// @dev Get the main storage using assembly to ensure specific storage location function _getEthIdentityValidatorBaseStorage() private pure @@ -46,17 +45,13 @@ contract EthIdentityValidator is Ownable2StepUpgradeable, IRequestValidator, ERC /** * @dev Initialize the contract - * @param _verifierContractAddr Address of the verifier contract * @param _stateContractAddr Address of the state contract * @param owner Owner of the contract */ function initialize( - address _verifierContractAddr, address _stateContractAddr, address owner ) public initializer { - _setInputToIndex("userID", 0); - _initDefaultStateVariables(_stateContractAddr, owner); } @@ -93,14 +88,14 @@ contract EthIdentityValidator is Ownable2StepUpgradeable, IRequestValidator, ERC bytes calldata data, address sender, IState stateContract - ) public view override returns (IRequestValidator.Signal[] memory) { + ) public view override returns (IRequestValidator.ResponseField[] memory) { ( uint256 userID ) = abi.decode(proof, (uint256)); _verifyEthIdentity(userID, sender); - IRequestValidator.Signal[] memory signals = new IRequestValidator.Signal[](1); - signals[0] = IRequestValidator.Signal({name: "userID", value: userID}); + IRequestValidator.ResponseField[] memory signals = new IRequestValidator.ResponseField[](1); + signals[0] = IRequestValidator.ResponseField({name: "userID", value: userID}); return signals; } diff --git a/contracts/verifiers/UniversalVerifierMultiQuery_AgreedSolution.sol b/contracts/verifiers/UniversalVerifierMultiQuery_AgreedSolution.sol.skip similarity index 100% rename from contracts/verifiers/UniversalVerifierMultiQuery_AgreedSolution.sol rename to contracts/verifiers/UniversalVerifierMultiQuery_AgreedSolution.sol.skip From 4afb814eec0c8d5cbe516d7826c135e5fe8b204f Mon Sep 17 00:00:00 2001 From: daveroga Date: Tue, 3 Dec 2024 03:31:47 +0100 Subject: [PATCH 18/69] test universal verifier multi-query --- .../test-helpers/RequestValidatorStub.sol | 37 ++++ .../verifiers/UniversalVerifierMultiQuery.sol | 173 ++++++++++++--- hardhat.config.ts | 6 + helpers/DeployHelper.ts | 98 +++++++++ helpers/constants.ts | 19 ++ ignition/modules/universalVerifier.ts | 23 ++ .../universal-verifier-multi-query.ts | 198 ++++++++++++++++++ 7 files changed, 527 insertions(+), 27 deletions(-) create mode 100644 contracts/test-helpers/RequestValidatorStub.sol create mode 100644 test/verifier/universal-verifier-multi-query.ts diff --git a/contracts/test-helpers/RequestValidatorStub.sol b/contracts/test-helpers/RequestValidatorStub.sol new file mode 100644 index 00000000..ef97f7d6 --- /dev/null +++ b/contracts/test-helpers/RequestValidatorStub.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.27; + +import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; +import {IRequestValidator} from "../interfaces/IRequestValidator.sol"; +import {IState} from "../interfaces/IState.sol"; + +/** + * @dev RequestValidatorStub validator + */ +contract RequestValidatorStub is IRequestValidator, ERC165 { + string public constant VERSION = "1.0.0-mock"; + + function version() public pure override returns (string memory) { + return VERSION; + } + + function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + return + interfaceId == type(IRequestValidator).interfaceId || + super.supportsInterface(interfaceId); + } + + function verify( + bytes calldata, + bytes calldata, + address, + IState + ) external pure override returns (IRequestValidator.ResponseField[] memory) { + IRequestValidator.ResponseField[] memory signals = new IRequestValidator.ResponseField[](2); + signals[0].name = "userID"; + signals[0].value = 1; + signals[1].name = "issuerID"; + signals[1].value = 2; + return signals; + } +} diff --git a/contracts/verifiers/UniversalVerifierMultiQuery.sol b/contracts/verifiers/UniversalVerifierMultiQuery.sol index 43b9cb93..3949fa7d 100644 --- a/contracts/verifiers/UniversalVerifierMultiQuery.sol +++ b/contracts/verifiers/UniversalVerifierMultiQuery.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.27; +import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; import {IRequestValidator} from "../interfaces/IRequestValidator.sol"; import {IState} from "../interfaces/IState.sol"; @@ -53,7 +54,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { struct Response { uint256 queryId; uint256 requestId; - uint256 requestIndexInQuery; + //uint256 groupId; // TODO: We remove from response and add to the query groupIdFromRequestId bytes proof; bytes metadata; } @@ -62,7 +63,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { // struct ResponseFieldFromRequest { // uint256 requestId; // string responseFieldName; - // uint256 requestIndexInQuery; + // uint256 groupId; // } // //ResponseFieldFromRequest[][] @@ -76,7 +77,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { struct ResponseFieldFromRequest { uint256 requestId; - uint256 requestIndexInQuery; + uint256 groupId; string responseFieldName; } @@ -89,6 +90,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { struct Query { uint256 queryId; uint256[] requestIds; + uint256[] groupIdFromRequests; // TODO: We need it to know the group id of the request ResponseFieldFromRequest[][] linkedResponseFields; // this are response fields linked between requests bytes metadata; } @@ -96,7 +98,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { /// @custom:storage-location erc7201:iden3.storage.UniversalVerifierMultiQuery struct UniversalVerifierMultiQueryStorage { // Information about requests - mapping(uint256 queryId => mapping(uint256 requestIndexInQuery => mapping(uint256 requestId => mapping(uint256 userID => Proof)))) _proofs; + mapping(uint256 queryId => mapping(uint256 groupId => mapping(uint256 requestId => mapping(uint256 userID => Proof)))) _proofs; mapping(uint256 requestId => Request) _requests; uint256[] _requestIds; IState _state; @@ -108,6 +110,8 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { mapping(uint256 userID => address userAddress) _id_to_user_address; // check address[] to allow multiple addresses for the same userID? mapping(uint256 userID => mapping(address userAddress => bool hasAuth)) _user_id_and_address_auth; uint256[] _authRequestIds; // reuses the same _requests mapping to store the auth requests + // Whitelisted validators + mapping(IRequestValidator => bool isApproved) _validatorWhitelist; } // keccak256(abi.encode(uint256(keccak256("iden3.storage.UniversalVerifierMultiQuery")) - 1)) & ~bytes32(uint256(0xff)); @@ -117,7 +121,12 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { /** * @dev Event emitted upon submitting a request */ - event ResponseSubmitted(uint256 indexed requestId, address indexed caller); + event ResponseSubmitted( + uint256 queryId, + uint256 indexed requestId, + uint256 groupId, + address indexed caller + ); /** * @dev Event emitted upon adding a request @@ -204,6 +213,24 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { _; } + /** + * @dev Modifier to check if the validator is whitelisted + */ + modifier onlyWhitelistedValidator(IRequestValidator validator) { + require(isWhitelistedValidator(validator), "Validator is not whitelisted"); + _; + } + + /** + * @dev Initializes the contract + * @param state The state contract + * @param owner The owner of the contract + */ + function initialize(IState state, address owner) public initializer { + __Ownable_init(owner); + _getUniversalVerifierMultiQueryStorage()._state = state; + } + /** * @dev Checks if a request ID exists * @param requestId The ID of the request @@ -254,7 +281,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { function setRequest( uint256 requestId, Request calldata request - ) public checkRequestExistence(requestId, false) { + ) public checkRequestExistence(requestId, false) onlyWhitelistedValidator(request.validator) { UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); s._requests[requestId] = request; s._requestIds.push(requestId); @@ -279,6 +306,22 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { return _getUniversalVerifierMultiQueryStorage()._requests[requestId]; } + /** + * @dev Checks auth request in query + * @param query The query data + */ + function _checkAuthRequestInQuery(Query calldata query) internal pure { + uint256 numAuthRequests = 0; + + for (uint256 i = 0; i < query.requestIds.length; i++) { + if (_getRequestType(query.requestIds[i]) == AUTH_REQUEST_TYPE) { + numAuthRequests++; + } + } + + require(numAuthRequests == 1, "Exactly 1 auth request is required"); + } + /** * @dev Sets a query * @param queryId The ID of the query @@ -289,6 +332,8 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { Query calldata query ) public checkQueryExistence(queryId, false) { UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); + + _checkAuthRequestInQuery(query); s._queries[queryId] = query; s._queryIds.push(queryId); @@ -309,7 +354,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { * @param responses The list of responses * @return The index of the auth response */ - function checkAuthResponses(Response[] memory responses) internal pure returns (uint256) { + function _checkAuthResponses(Response[] memory responses) internal pure returns (uint256) { uint256 numAuthResponses = 0; uint256 authResponseIndex; @@ -324,6 +369,23 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { return authResponseIndex; } + /** + * @dev Gets the group ID from the query request + * @param queryId The ID of the query + * @param requestId The ID of the request + * @return The group ID of the request in the query + */ + function getGroupIdFromQueryRequest(uint256 queryId, uint256 requestId) public view returns (uint256) { + UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); + Query memory query = s._queries[queryId]; + for (uint256 i = 0; i < query.requestIds.length; i++) { + if (query.requestIds[i] == requestId) { + return query.groupIdFromRequests[i]; + } + } + revert("Request not found in the query"); + } + /** * @dev Submits an array of responses and updates proofs status * @param responses The list of responses including request ID, proof and metadata @@ -337,7 +399,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { // 1. Check for auth responses (throw if not provided exactly 1) // and return auth response index in the responses array // in order to get "userID" from the output response field then - uint256 authResponseIndex = checkAuthResponses(responses); + uint256 authResponseIndex = _checkAuthResponses(responses); // 2. Process crossChainProofs $._state.processCrossChainProofs(crossChainProofs); @@ -363,7 +425,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { writeProofResults( authResponse.queryId, authResponse.requestId, - authResponse.requestIndexInQuery, + getGroupIdFromQueryRequest(authResponse.queryId, authResponse.requestId), userIDFromAuth, authSignals ); @@ -376,7 +438,12 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { // emit events (existing logic) for (uint256 i = 0; i < responses.length; i++) { // emit for all the responses - emit ResponseSubmitted(responses[i].requestId, _msgSender()); + emit ResponseSubmitted( + responses[i].queryId, + responses[i].requestId, + getGroupIdFromQueryRequest(responses[i].queryId, responses[i].requestId), + _msgSender() + ); // avoid to process auth request again if (_getRequestType(responses[i].requestId) == AUTH_REQUEST_TYPE) { @@ -397,7 +464,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { writeProofResults( response.queryId, response.requestId, - response.requestIndexInQuery, + getGroupIdFromQueryRequest(response.queryId, response.requestId), userIDFromAuth, signals ); @@ -412,20 +479,20 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { * @dev Writes proof results. * @param queryId The query ID of the proof * @param requestId The request ID of the proof - * @param requestIndexInQuery The index of the request in the query definition + * @param groupId The index of the request in the query definition * @param userID The userID of the proof * @param responseFields The array of response fields of the proof */ function writeProofResults( uint256 queryId, uint256 requestId, - uint256 requestIndexInQuery, + uint256 groupId, uint256 userID, IRequestValidator.ResponseField[] memory responseFields ) public { UniversalVerifierMultiQueryStorage storage $ = _getUniversalVerifierMultiQueryStorage(); - Proof storage proof = $._proofs[queryId][requestId][requestIndexInQuery][userID]; + Proof storage proof = $._proofs[queryId][requestId][groupId][userID]; for (uint256 i = 0; i < responseFields.length; i++) { proof.storageFields[responseFields[i].name] = responseFields[i].value; } @@ -478,22 +545,40 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { * @dev Gets response field value * @param queryId Id of the query * @param requestId Id of the request - * @param requestIndexInQuery Index of the request in the query + * @param groupId Group id of the request in the query * @param userID Id of the user * @param responseFieldName Name of the response field to get */ function getResponseFieldValue( uint256 queryId, uint256 requestId, - uint256 requestIndexInQuery, + uint256 groupId, uint256 userID, string memory responseFieldName - ) public view returns (uint256) { + ) public view checkQueryExistence(queryId, true) checkRequestExistence(requestId, true) returns (uint256) { UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); - return - s._proofs[queryId][requestId][requestIndexInQuery][userID].storageFields[ - responseFieldName - ]; + return s._proofs[queryId][requestId][groupId][userID].storageFields[responseFieldName]; + } + + + /** + * @dev Gets response field value + * @param queryId Id of the query + * @param requestId Id of the request + * @param groupId Group id of the request in the query + * @param sender Address of the sender + * @param responseFieldName Name of the response field to get + */ + function getResponseFieldValueFromAddress( + uint256 queryId, + uint256 requestId, + uint256 groupId, + address sender, + string memory responseFieldName + ) public view checkQueryExistence(queryId, true) checkRequestExistence(requestId, true) returns (uint256) { + UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); + uint256 userID = s._user_address_to_id[sender]; + return s._proofs[queryId][requestId][groupId][userID].storageFields[responseFieldName]; } /** @@ -502,7 +587,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { * @param userID The ID of the user * @return Whether all linked response fields are the same */ - function checkLinkedResponseFields(uint256 queryId, uint256 userID) public view returns (bool) { + function _checkLinkedResponseFields(uint256 queryId, uint256 userID) internal view returns (bool) { UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); for (uint256 i = 0; i < s._queries[queryId].linkedResponseFields.length; i++) { @@ -512,7 +597,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { uint256 firstValue = getResponseFieldValue( queryId, s._queries[queryId].linkedResponseFields[i][0].requestId, - s._queries[queryId].linkedResponseFields[i][0].requestIndexInQuery, + s._queries[queryId].linkedResponseFields[i][0].groupId, userID, s._queries[queryId].linkedResponseFields[i][0].responseFieldName ); @@ -521,7 +606,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { uint256 valueToCompare = getResponseFieldValue( queryId, s._queries[queryId].linkedResponseFields[i][j].requestId, - s._queries[queryId].linkedResponseFields[i][j].requestIndexInQuery, + s._queries[queryId].linkedResponseFields[i][j].groupId, userID, s._queries[queryId].linkedResponseFields[i][j].responseFieldName ); @@ -532,6 +617,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { } } } + return true; } /** @@ -543,7 +629,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { function getQueryStatus( uint256 queryId, address userAddress - ) public view returns (bool status) { + ) public view checkQueryExistence(queryId, true) returns (bool status) { UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); // 1. Get the latest userId by the userAddress arg (in the mapping) @@ -557,14 +643,47 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { // 3. Check if all requests statuses are true for the userId for (uint256 i = 0; i < s._queries[queryId].requestIds.length; i++) { uint256 requestId = s._queries[queryId].requestIds[i]; - if (!s._proofs[queryId][requestId][i][userID].isVerified) { + uint256 groupId = s._queries[queryId].groupIdFromRequests[i]; + if (!s._proofs[queryId][requestId][groupId][userID].isVerified) { return false; } } // 4. Check if all linked response fields are the same - checkLinkedResponseFields(queryId, userID); + _checkLinkedResponseFields(queryId, userID); return true; } + + /** + * @dev Adds a validator to the whitelist + * @param validator The validator to add + */ + function addValidatorToWhitelist(IRequestValidator validator) public { + require( + IERC165(address(validator)).supportsInterface(type(IRequestValidator).interfaceId), + "Validator doesn't support relevant interface" + ); + + _getUniversalVerifierMultiQueryStorage()._validatorWhitelist[validator] = true; + } + + /** + * @dev Removes a validator from the whitelist + * @param validator The validator to remove + */ + function removeValidatorFromWhitelist(IRequestValidator validator) public { + _getUniversalVerifierMultiQueryStorage()._validatorWhitelist[validator] = false; + } + + /** + * @dev Checks if a validator is whitelisted + * @param validator The validator to check + * @return Whether the validator is whitelisted + */ + function isWhitelistedValidator( + IRequestValidator validator + ) public view virtual returns (bool) { + return _getUniversalVerifierMultiQueryStorage()._validatorWhitelist[validator]; + } } diff --git a/hardhat.config.ts b/hardhat.config.ts index 94e2ce18..6a895f38 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -39,6 +39,12 @@ const config: HardhatUserConfig = { compilers: [ { version: "0.8.27", + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + }, }, ], }, diff --git a/helpers/DeployHelper.ts b/helpers/DeployHelper.ts index fa25cbe3..995d7b2e 100644 --- a/helpers/DeployHelper.ts +++ b/helpers/DeployHelper.ts @@ -13,6 +13,7 @@ import { CredentialAtomicQueryV3ValidatorProxyModule, UniversalVerifierProxyModule, AuthV2ValidatorProxyModule, + UniversalVerifierMultiQueryProxyModule, } from "../ignition"; import { chainIdInfoMap, contractsInfo } from "./constants"; import { @@ -796,6 +797,16 @@ export class DeployHelper { return stubInstance; } + async deployRequestValidatorStub(): Promise { + const stub = await ethers.getContractFactory("RequestValidatorStub"); + const stubInstance = await stub.deploy(); + await stubInstance.waitForDeployment(); + + console.log("RequestValidator stub deployed to:", await stubInstance.getAddress()); + + return stubInstance; + } + async deployGroth16VerifierValidatorStub(): Promise { const stub = await ethers.getContractFactory("Groth16VerifierValidatorStub"); const stubInstance = await stub.deploy(); @@ -1010,6 +1021,93 @@ export class DeployHelper { return universalVerifier; } + async deployUniversalVerifierMultiQuery( + owner: SignerWithAddress | undefined, + stateAddr: string, + deployStrategy: "basic" | "create2" = "basic", + ): Promise { + if (!owner) { + owner = this.signers[0]; + } + const UniversalVerifierMultiQueryFactory = await ethers.getContractFactory( + contractsInfo.UNIVERSAL_VERIFIER_MULTIQUERY.name, + { + signer: owner, + }, + ); + const Create2AddressAnchorFactory = await ethers.getContractFactory( + contractsInfo.CREATE2_ADDRESS_ANCHOR.name, + ); + + let universalVerifierMultiQuery; + let create2AlreadyDeployed = false; + + if (deployStrategy === "create2") { + this.log("deploying with CREATE2 strategy..."); + + universalVerifierMultiQuery = await getUnifiedContract( + contractsInfo.UNIVERSAL_VERIFIER_MULTIQUERY.name, + ); + if (universalVerifierMultiQuery) { + let version; + try { + version = await universalVerifierMultiQuery.VERSION(); + } catch (e) { + create2AlreadyDeployed = true; + Logger.warning( + `Create2AnchorAddress implementation already deployed to TransparentUpgradeableProxy of ${contractsInfo.UNIVERSAL_VERIFIER_MULTIQUERY.name}.`, + ); + } + + if (version) { + Logger.warning( + `${contractsInfo.UNIVERSAL_VERIFIER.name} found already deployed to: ${await universalVerifierMultiQuery?.getAddress()}`, + ); + return universalVerifierMultiQuery; + } + } + + if (!create2AlreadyDeployed) { + // Deploying UniversalVerifier contract to predictable address but with dummy implementation + universalVerifierMultiQuery = ( + await ignition.deploy(UniversalVerifierMultiQueryProxyModule, { + strategy: deployStrategy, + }) + ).proxy; + await universalVerifierMultiQuery.waitForDeployment(); + } + // Upgrading UniversalVerifierMultiQuery contract to the first real implementation + // and force network files import, so creation, as they do not exist at the moment + const universalVerifierMultiQueryAddress = await universalVerifierMultiQuery.getAddress(); + await upgrades.forceImport(universalVerifierMultiQueryAddress, Create2AddressAnchorFactory); + universalVerifierMultiQuery = await upgrades.upgradeProxy( + universalVerifierMultiQuery, + UniversalVerifierMultiQueryFactory, + { + redeployImplementation: "always", + call: { + fn: "initialize", + args: [stateAddr, await owner.getAddress()], + }, + }, + ); + } else { + this.log("deploying with BASIC strategy..."); + + universalVerifierMultiQuery = await upgrades.deployProxy(UniversalVerifierMultiQueryFactory, [ + stateAddr, + await owner.getAddress(), + ]); + } + + await universalVerifierMultiQuery.waitForDeployment(); + Logger.success( + `${contractsInfo.UNIVERSAL_VERIFIER_MULTIQUERY.name} deployed to: ${await universalVerifierMultiQuery.getAddress()}`, + ); + + return universalVerifierMultiQuery; + } + async getDefaultIdType(): Promise<{ defaultIdType: string; chainId: number }> { const chainId = parseInt(await network.provider.send("eth_chainId"), 16); const defaultIdType = chainIdInfoMap.get(chainId)?.idType; diff --git a/helpers/constants.ts b/helpers/constants.ts index afde6e08..0af3b957 100644 --- a/helpers/constants.ts +++ b/helpers/constants.ts @@ -123,6 +123,25 @@ export const contractsInfo = Object.freeze({ libraries: {}, }, }, + UNIVERSAL_VERIFIER_MULTIQUERY: { + name: "UniversalVerifierMultiQuery", + version: "1.0.0", + unifiedAddress: "", + create2Calldata: ethers.hexlify( + ethers.toUtf8Bytes("iden3.create2.UniversalVerifierMultiQuery"), + ), + verificationOpts: { + // For verifying the different contracts with proxy we need verification with different constructor arguments + constructorArgsImplementation: [], + constructorArgsProxy: [ + "0x56fF81aBB5cdaC478bF236db717e4976b2ff841e", + "0xae15d2023a76174a940cbb2b7f44012c728b9d74", + "0x6964656e332e637265617465322e556e6976657273616c5665726966696572", + ], + constructorArgsProxyAdmin: ["0xAe15d2023A76174a940cbb2b7F44012C728B9d74"], + libraries: {}, + }, + }, STATE: { name: "State", version: "2.6.1", diff --git a/ignition/modules/universalVerifier.ts b/ignition/modules/universalVerifier.ts index c834058b..ee3e3154 100644 --- a/ignition/modules/universalVerifier.ts +++ b/ignition/modules/universalVerifier.ts @@ -20,3 +20,26 @@ export const UniversalVerifierProxyModule = buildModule("UniversalVerifierProxyM const proxyAdmin = m.contractAt("ProxyAdmin", proxyAdminAddress); return { proxyAdmin, proxy }; }); + +export const UniversalVerifierMultiQueryProxyModule = buildModule( + "UniversalVerifierMultiQueryProxyModule", + (m) => { + const proxyAdminOwner = m.getAccount(0); + + // This contract is supposed to be deployed to the same address across many networks, + // so the first implementation address is a dummy contract that does nothing but accepts any calldata. + // Therefore, it is a mechanism to deploy TransparentUpgradeableProxy contract + // with constant constructor arguments, so predictable init bytecode and predictable CREATE2 address. + // Subsequent upgrades are supposed to switch this proxy to the real implementation. + + const proxy = m.contract("TransparentUpgradeableProxy", [ + contractsInfo.CREATE2_ADDRESS_ANCHOR.unifiedAddress, + proxyAdminOwner, + contractsInfo.UNIVERSAL_VERIFIER_MULTIQUERY.create2Calldata, + ]); + + const proxyAdminAddress = m.readEventArgument(proxy, "AdminChanged", "newAdmin"); + const proxyAdmin = m.contractAt("ProxyAdmin", proxyAdminAddress); + return { proxyAdmin, proxy }; + }, +); diff --git a/test/verifier/universal-verifier-multi-query.ts b/test/verifier/universal-verifier-multi-query.ts new file mode 100644 index 00000000..6d7aa3b3 --- /dev/null +++ b/test/verifier/universal-verifier-multi-query.ts @@ -0,0 +1,198 @@ +import { expect } from "chai"; +import { DeployHelper } from "../../helpers/DeployHelper"; +import { ethers } from "hardhat"; +import { packValidatorParams } from "../utils/validator-pack-utils"; +import { prepareInputs } from "../utils/state-utils"; +import { Block, Contract } from "ethers"; +import proofJson from "../validators/sig/data/valid_sig_user_genesis.json"; +import { buildCrossChainProofs, packCrossChainProofs, packZKProof } from "../utils/packData"; +import { CircuitId } from "@0xpolygonid/js-sdk"; +import { loadFixture } from "@nomicfoundation/hardhat-toolbox/network-helpers"; + +describe("Universal Verifier Multi-query", function () { + let verifier: any, sig: any; + let signer; + let signerAddress: string; + let deployHelper: DeployHelper; + let stateCrossChainStub, crossChainProofValidatorStub, validatorStub: Contract; + + const globalStateMessage = { + timestamp: BigInt(Math.floor(Date.now() / 1000)), + idType: "0x01A1", + root: 0n, + replacedAtTimestamp: 0n, + }; + + const identityStateMessage1 = { + timestamp: BigInt(Math.floor(Date.now() / 1000)), + id: 25530185136167283063987925153802803371825564143650291260157676786685420033n, + state: 4595702004868323299100310062178085028712435650290319955390778053863052230284n, + replacedAtTimestamp: 0n, + }; + + const identityStateUpdate2 = { + timestamp: BigInt(Math.floor(Date.now() / 1000)), + id: 25530185136167283063987925153802803371825564143650291260157676786685420033n, + state: 16775015541053109108201708100382933592407720757224325883910784163897594100403n, + replacedAtTimestamp: 1724858009n, + }; + + const requestQuery = { + schema: BigInt("180410020913331409885634153623124536270"), + claimPathKey: BigInt( + "8566939875427719562376598811066985304309117528846759529734201066483458512800", + ), + operator: 1n, + slotIndex: 0n, + value: [1420070400000000000n, ...new Array(63).fill("0").map((x) => BigInt(x))], + queryHash: BigInt( + "1496222740463292783938163206931059379817846775593932664024082849882751356658", + ), + circuitIds: [CircuitId.AtomicQuerySigV2], + claimPathNotExists: 0, + }; + + async function deployContractsFixture() { + [signer] = await ethers.getSigners(); + signerAddress = await signer.getAddress(); + + deployHelper = await DeployHelper.initialize(null, true); + crossChainProofValidatorStub = await deployHelper.deployCrossChainProofValidator(); + + const { state } = await deployHelper.deployStateWithLibraries(["0x01A1", "0x0102"]); + await state.setCrossChainProofValidator(crossChainProofValidatorStub); + stateCrossChainStub = state; + + verifier = await deployHelper.deployUniversalVerifierMultiQuery( + signer, + await stateCrossChainStub.getAddress(), + ); + + validatorStub = await deployHelper.deployRequestValidatorStub(); + + sig = validatorStub; + await verifier.addValidatorToWhitelist(await sig.getAddress()); + await verifier.connect(); + } + + async function checkStorageFields( + verifier: any, + queryId: bigint, + requestId: bigint, + groupId: bigint, + ) { + const fieldsToCheck = ["userID", "issuerID"]; + for (const field of fieldsToCheck) { + const value = await verifier.getResponseFieldValueFromAddress( + queryId, + requestId, + groupId, + await signer.getAddress(), + field, + ); + expect(value).to.be.greaterThan(0n); + } + } + + beforeEach(async () => { + await loadFixture(deployContractsFixture); + }); + + it("Test submit response multiquery", async () => { + const requestId = 0; + // authRequestId: 0x0100000000000000000000000000000000000000000000000000000000000001 + const authRequestId = + 452312848583266388373324160190187140051835877600158453279131187530910662657n; + const queryId = 0; + const groupId = 0; + const nonExistingQueryId = 1; + const params = packValidatorParams(requestQuery); + + await verifier.setRequest(requestId, { + metadata: "metadata", + validator: await sig.getAddress(), + params: params, + }); + + const requestStored = await verifier.getRequest(requestId); + // check if the request is stored correctly checking metadata and validator + expect(requestStored[0]).to.be.equal("metadata"); + expect(requestStored[1]).to.be.equal(await sig.getAddress()); + expect(requestStored[2]).to.be.equal(params); + + await verifier.setAuthRequest(authRequestId, { + metadata: "metadata", + validator: await sig.getAddress(), + params: params, + }); + const authRequestStored = await verifier.getAuthRequest(authRequestId); + expect(authRequestStored[0]).to.be.equal("metadata"); + expect(authRequestStored[1]).to.be.equal(await sig.getAddress()); + expect(authRequestStored[2]).to.be.equal(params); + + const query = { + queryId, + requestIds: [requestId, authRequestId], + groupIdFromRequests: [0, 0], + linkedResponseFields: [[]], + metadata: "0x", + }; + await verifier.setQuery(0, query); + const queryStored = await verifier.getQuery(queryId); + expect(queryStored[0]).to.be.equal(queryId); + expect(queryStored[1]).to.be.deep.equal(query.requestIds); + expect(queryStored[2]).to.be.deep.equal(query.groupIdFromRequests); + expect(queryStored[3]).to.be.deep.equal(query.linkedResponseFields); + + const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); + + const proof = packZKProof(inputs, pi_a, pi_b, pi_c); + + const crossChainProofs = packCrossChainProofs( + await buildCrossChainProofs( + [globalStateMessage, identityStateMessage1, identityStateUpdate2], + signer, + ), + ); + + const metadatas = "0x"; + + const tx = await verifier.submitResponse( + [ + { + queryId, + requestId, + proof, + metadata: metadatas, + }, + { + queryId, + requestId: authRequestId, + proof, + metadata: metadatas, + }, + ], + crossChainProofs, + ); + + await tx.wait(); + + await checkStorageFields(verifier, BigInt(queryId), authRequestId, BigInt(groupId)); + await checkStorageFields(verifier, BigInt(queryId), BigInt(requestId), BigInt(groupId)); + const filter = verifier.filters.ResponseSubmitted; + + const events = await verifier.queryFilter(filter, -1); + expect(events[0].eventName).to.be.equal("ResponseSubmitted"); + expect(events[0].args.queryId).to.be.equal(queryId); + expect(events[0].args.requestId).to.be.equal(requestId); + expect(events[0].args.groupId).to.be.equal(groupId); + expect(events[0].args.caller).to.be.equal(signerAddress); + + await expect(verifier.getQueryStatus(nonExistingQueryId, signerAddress)).to.be.rejectedWith( + "query id doesn't exist", + ); + + const status = await verifier.getQueryStatus(queryId, signerAddress); + expect(status).to.be.true; + }); +}); From 8f55467193a1df72004ec72b00c634c96911ff01 Mon Sep 17 00:00:00 2001 From: daveroga Date: Tue, 3 Dec 2024 10:22:30 +0100 Subject: [PATCH 19/69] different validators for sig and authv2 --- test/verifier/universal-verifier-multi-query.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/verifier/universal-verifier-multi-query.ts b/test/verifier/universal-verifier-multi-query.ts index 6d7aa3b3..581dad2a 100644 --- a/test/verifier/universal-verifier-multi-query.ts +++ b/test/verifier/universal-verifier-multi-query.ts @@ -3,18 +3,18 @@ import { DeployHelper } from "../../helpers/DeployHelper"; import { ethers } from "hardhat"; import { packValidatorParams } from "../utils/validator-pack-utils"; import { prepareInputs } from "../utils/state-utils"; -import { Block, Contract } from "ethers"; +import { Contract } from "ethers"; import proofJson from "../validators/sig/data/valid_sig_user_genesis.json"; import { buildCrossChainProofs, packCrossChainProofs, packZKProof } from "../utils/packData"; import { CircuitId } from "@0xpolygonid/js-sdk"; import { loadFixture } from "@nomicfoundation/hardhat-toolbox/network-helpers"; describe("Universal Verifier Multi-query", function () { - let verifier: any, sig: any; + let verifier: any, sig: any, authV2: any; let signer; let signerAddress: string; let deployHelper: DeployHelper; - let stateCrossChainStub, crossChainProofValidatorStub, validatorStub: Contract; + let stateCrossChainStub, crossChainProofValidatorStub: Contract; const globalStateMessage = { timestamp: BigInt(Math.floor(Date.now() / 1000)), @@ -68,9 +68,9 @@ describe("Universal Verifier Multi-query", function () { await stateCrossChainStub.getAddress(), ); - validatorStub = await deployHelper.deployRequestValidatorStub(); + sig = await deployHelper.deployRequestValidatorStub(); + authV2 = await deployHelper.deployRequestValidatorStub(); - sig = validatorStub; await verifier.addValidatorToWhitelist(await sig.getAddress()); await verifier.connect(); } @@ -122,12 +122,12 @@ describe("Universal Verifier Multi-query", function () { await verifier.setAuthRequest(authRequestId, { metadata: "metadata", - validator: await sig.getAddress(), + validator: await authV2.getAddress(), params: params, }); const authRequestStored = await verifier.getAuthRequest(authRequestId); expect(authRequestStored[0]).to.be.equal("metadata"); - expect(authRequestStored[1]).to.be.equal(await sig.getAddress()); + expect(authRequestStored[1]).to.be.equal(await authV2.getAddress()); expect(authRequestStored[2]).to.be.equal(params); const query = { From 657fa6c1c6f44d851baeb91c533d441fd98aa445 Mon Sep 17 00:00:00 2001 From: daveroga Date: Tue, 3 Dec 2024 10:30:16 +0100 Subject: [PATCH 20/69] check specific storage values --- .../universal-verifier-multi-query.ts | 35 +++++++++++++++---- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/test/verifier/universal-verifier-multi-query.ts b/test/verifier/universal-verifier-multi-query.ts index 581dad2a..a21a67ad 100644 --- a/test/verifier/universal-verifier-multi-query.ts +++ b/test/verifier/universal-verifier-multi-query.ts @@ -52,6 +52,17 @@ describe("Universal Verifier Multi-query", function () { claimPathNotExists: 0, }; + const storageFields = [ + { + name: "userID", + value: 1, + }, + { + name: "issuerID", + value: 2, + }, + ]; + async function deployContractsFixture() { [signer] = await ethers.getSigners(); signerAddress = await signer.getAddress(); @@ -80,17 +91,17 @@ describe("Universal Verifier Multi-query", function () { queryId: bigint, requestId: bigint, groupId: bigint, + storageFields: any[], ) { - const fieldsToCheck = ["userID", "issuerID"]; - for (const field of fieldsToCheck) { + for (const field of storageFields) { const value = await verifier.getResponseFieldValueFromAddress( queryId, requestId, groupId, await signer.getAddress(), - field, + field.name, ); - expect(value).to.be.greaterThan(0n); + expect(value).to.be.equal(field.value); } } @@ -177,8 +188,20 @@ describe("Universal Verifier Multi-query", function () { await tx.wait(); - await checkStorageFields(verifier, BigInt(queryId), authRequestId, BigInt(groupId)); - await checkStorageFields(verifier, BigInt(queryId), BigInt(requestId), BigInt(groupId)); + await checkStorageFields( + verifier, + BigInt(queryId), + authRequestId, + BigInt(groupId), + storageFields, + ); + await checkStorageFields( + verifier, + BigInt(queryId), + BigInt(requestId), + BigInt(groupId), + storageFields, + ); const filter = verifier.filters.ResponseSubmitted; const events = await verifier.queryFilter(filter, -1); From 46d17abb2309a31c316b8972e7568ecf5b08e54b Mon Sep 17 00:00:00 2001 From: daveroga Date: Tue, 3 Dec 2024 10:52:53 +0100 Subject: [PATCH 21/69] overrides solidity compiler optimizations for specific contract --- hardhat.config.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/hardhat.config.ts b/hardhat.config.ts index 6a895f38..5246ba30 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -39,6 +39,11 @@ const config: HardhatUserConfig = { compilers: [ { version: "0.8.27", + }, + ], + overrides: { + "contracts/verifiers/UniversalVerifierMultiQuery.sol": { + version: "0.8.27", settings: { optimizer: { enabled: true, @@ -46,7 +51,7 @@ const config: HardhatUserConfig = { }, }, }, - ], + }, }, networks: { "privado-main": { From 1e7b87774184957444804b05f817dc6304b518be Mon Sep 17 00:00:00 2001 From: daveroga Date: Tue, 3 Dec 2024 12:06:56 +0100 Subject: [PATCH 22/69] remove some methods --- .../verifiers/UniversalVerifierMultiQuery.sol | 51 +++---------------- hardhat.config.ts | 11 ---- .../universal-verifier-multi-query.ts | 2 + 3 files changed, 10 insertions(+), 54 deletions(-) diff --git a/contracts/verifiers/UniversalVerifierMultiQuery.sol b/contracts/verifiers/UniversalVerifierMultiQuery.sol index 3949fa7d..04068387 100644 --- a/contracts/verifiers/UniversalVerifierMultiQuery.sol +++ b/contracts/verifiers/UniversalVerifierMultiQuery.sol @@ -54,7 +54,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { struct Response { uint256 queryId; uint256 requestId; - //uint256 groupId; // TODO: We remove from response and add to the query groupIdFromRequestId + uint256 groupId; bytes proof; bytes metadata; } @@ -176,9 +176,9 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { */ modifier checkRequestExistence(uint256 requestId, bool existence) { if (existence) { - require(requestIdExists(requestId), "request id doesn't exist"); + require(requestIdExists(requestId), "request id or auth request id doesn't exist"); } else { - require(!requestIdExists(requestId), "request id already exists"); + require(!requestIdExists(requestId), "request id or auth request id already exists"); } _; } @@ -195,24 +195,6 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { _; } - /** - * @dev Modifier to check if the auth request exists - */ - modifier checkAuthRequestExistence(uint256 requestId, bool existence) { - if (existence) { - require( - requestIdExists(requestId) && _getRequestType(requestId) == AUTH_REQUEST_TYPE, - "auth request id doesn't exist" - ); - } else { - require( - !(requestIdExists(requestId) && _getRequestType(requestId) == AUTH_REQUEST_TYPE), - "auth request id already exists" - ); - } - _; - } - /** * @dev Modifier to check if the validator is whitelisted */ @@ -369,23 +351,6 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { return authResponseIndex; } - /** - * @dev Gets the group ID from the query request - * @param queryId The ID of the query - * @param requestId The ID of the request - * @return The group ID of the request in the query - */ - function getGroupIdFromQueryRequest(uint256 queryId, uint256 requestId) public view returns (uint256) { - UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); - Query memory query = s._queries[queryId]; - for (uint256 i = 0; i < query.requestIds.length; i++) { - if (query.requestIds[i] == requestId) { - return query.groupIdFromRequests[i]; - } - } - revert("Request not found in the query"); - } - /** * @dev Submits an array of responses and updates proofs status * @param responses The list of responses including request ID, proof and metadata @@ -425,7 +390,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { writeProofResults( authResponse.queryId, authResponse.requestId, - getGroupIdFromQueryRequest(authResponse.queryId, authResponse.requestId), + authResponse.groupId, //getGroupIdFromQueryRequest(authResponse.queryId, authResponse.requestId), userIDFromAuth, authSignals ); @@ -441,7 +406,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { emit ResponseSubmitted( responses[i].queryId, responses[i].requestId, - getGroupIdFromQueryRequest(responses[i].queryId, responses[i].requestId), + responses[i].groupId, _msgSender() ); @@ -464,7 +429,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { writeProofResults( response.queryId, response.requestId, - getGroupIdFromQueryRequest(response.queryId, response.requestId), + response.groupId, userIDFromAuth, signals ); @@ -510,7 +475,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { function setAuthRequest( uint256 requestId, Request calldata request - ) public checkAuthRequestExistence(requestId, false) onlyOwner { + ) public checkRequestExistence(requestId, false) onlyOwner { if (_getRequestType(requestId) != AUTH_REQUEST_TYPE) { revert("Request ID is not an auth request"); } @@ -537,7 +502,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { */ function getAuthRequest( uint256 requestId - ) public view checkAuthRequestExistence(requestId, true) returns (Request memory authRequest) { + ) public view checkRequestExistence(requestId, true) returns (Request memory authRequest) { return _getUniversalVerifierMultiQueryStorage()._requests[requestId]; } diff --git a/hardhat.config.ts b/hardhat.config.ts index 5246ba30..94e2ce18 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -41,17 +41,6 @@ const config: HardhatUserConfig = { version: "0.8.27", }, ], - overrides: { - "contracts/verifiers/UniversalVerifierMultiQuery.sol": { - version: "0.8.27", - settings: { - optimizer: { - enabled: true, - runs: 200, - }, - }, - }, - }, }, networks: { "privado-main": { diff --git a/test/verifier/universal-verifier-multi-query.ts b/test/verifier/universal-verifier-multi-query.ts index a21a67ad..6da94211 100644 --- a/test/verifier/universal-verifier-multi-query.ts +++ b/test/verifier/universal-verifier-multi-query.ts @@ -173,12 +173,14 @@ describe("Universal Verifier Multi-query", function () { { queryId, requestId, + groupId, proof, metadata: metadatas, }, { queryId, requestId: authRequestId, + groupId, proof, metadata: metadatas, }, From 2942cdfb18fed81d2d8e1fd4921193f548feac36 Mon Sep 17 00:00:00 2001 From: daveroga Date: Tue, 3 Dec 2024 12:16:51 +0100 Subject: [PATCH 23/69] fix solhint --- contracts/validators/EthIdentityValidator.sol | 25 ++++------------ .../verifiers/UniversalVerifierMultiQuery.sol | 30 ++++++++++++++----- 2 files changed, 29 insertions(+), 26 deletions(-) diff --git a/contracts/validators/EthIdentityValidator.sol b/contracts/validators/EthIdentityValidator.sol index 87a5fe91..dda13c2d 100644 --- a/contracts/validators/EthIdentityValidator.sol +++ b/contracts/validators/EthIdentityValidator.sol @@ -31,7 +31,7 @@ contract EthIdentityValidator is Ownable2StepUpgradeable, IRequestValidator, ERC // & ~bytes32(uint256(0xff)); bytes32 private constant EthIdentityValidatorBaseStorageLocation = 0x1816cff28d525c2e505742319020369d0e29e8fafd5168e127e29766cf2be1fb; - + /// @dev Get the main storage using assembly to ensure specific storage location function _getEthIdentityValidatorBaseStorage() private @@ -48,19 +48,12 @@ contract EthIdentityValidator is Ownable2StepUpgradeable, IRequestValidator, ERC * @param _stateContractAddr Address of the state contract * @param owner Owner of the contract */ - function initialize( - address _stateContractAddr, - address owner - ) public initializer { + function initialize(address _stateContractAddr, address owner) public initializer { _initDefaultStateVariables(_stateContractAddr, owner); } - function _initDefaultStateVariables( - address _stateContractAddr, - address owner - ) internal { - EthIdentityValidatorBaseStorage - storage s = _getEthIdentityValidatorBaseStorage(); + function _initDefaultStateVariables(address _stateContractAddr, address owner) internal { + EthIdentityValidatorBaseStorage storage s = _getEthIdentityValidatorBaseStorage(); s.state = IState(_stateContractAddr); __Ownable_init(owner); @@ -89,9 +82,7 @@ contract EthIdentityValidator is Ownable2StepUpgradeable, IRequestValidator, ERC address sender, IState stateContract ) public view override returns (IRequestValidator.ResponseField[] memory) { - ( - uint256 userID - ) = abi.decode(proof, (uint256)); + uint256 userID = abi.decode(proof, (uint256)); _verifyEthIdentity(userID, sender); IRequestValidator.ResponseField[] memory signals = new IRequestValidator.ResponseField[](1); @@ -99,15 +90,11 @@ contract EthIdentityValidator is Ownable2StepUpgradeable, IRequestValidator, ERC return signals; } - function _getState() internal view returns (IState) { return _getEthIdentityValidatorBaseStorage().state; } - function _verifyEthIdentity( - uint256 id, - address sender - ) internal view { + function _verifyEthIdentity(uint256 id, address sender) internal view { bytes2 idType = _getState().getIdTypeIfSupported(id); uint256 calcId = GenesisUtils.calcIdFromEthAddress(idType, sender); require(calcId == id, "Sender is not owner of the ethereum identity"); diff --git a/contracts/verifiers/UniversalVerifierMultiQuery.sol b/contracts/verifiers/UniversalVerifierMultiQuery.sol index 04068387..7a21996e 100644 --- a/contracts/verifiers/UniversalVerifierMultiQuery.sol +++ b/contracts/verifiers/UniversalVerifierMultiQuery.sol @@ -98,6 +98,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { /// @custom:storage-location erc7201:iden3.storage.UniversalVerifierMultiQuery struct UniversalVerifierMultiQueryStorage { // Information about requests + // solhint-disable-next-line mapping(uint256 queryId => mapping(uint256 groupId => mapping(uint256 requestId => mapping(uint256 userID => Proof)))) _proofs; mapping(uint256 requestId => Request) _requests; uint256[] _requestIds; @@ -107,14 +108,15 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { uint256[] _queryIds; // Information linked between users and their addresses mapping(address userAddress => uint256 userID) _user_address_to_id; - mapping(uint256 userID => address userAddress) _id_to_user_address; // check address[] to allow multiple addresses for the same userID? + mapping(uint256 userID => address userAddress) _id_to_user_address; mapping(uint256 userID => mapping(address userAddress => bool hasAuth)) _user_id_and_address_auth; uint256[] _authRequestIds; // reuses the same _requests mapping to store the auth requests // Whitelisted validators mapping(IRequestValidator => bool isApproved) _validatorWhitelist; } - // keccak256(abi.encode(uint256(keccak256("iden3.storage.UniversalVerifierMultiQuery")) - 1)) & ~bytes32(uint256(0xff)); + // solhint-disable-next-line + // keccak256(abi.encode(uint256(keccak256("iden3.storage.UniversalVerifierMultiQuery")) -1 )) & ~bytes32(uint256(0xff)); bytes32 internal constant UniversalVerifierMultiQueryStorageLocation = 0x4235c64ddf027641dd9aa586d246e4cc3acfcb3f7016a8aa68f7ac21d38b3b00; @@ -176,7 +178,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { */ modifier checkRequestExistence(uint256 requestId, bool existence) { if (existence) { - require(requestIdExists(requestId), "request id or auth request id doesn't exist"); + require(requestIdExists(requestId), "requestu id or auth request id doesn't exist"); } else { require(!requestIdExists(requestId), "request id or auth request id already exists"); } @@ -520,12 +522,17 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { uint256 groupId, uint256 userID, string memory responseFieldName - ) public view checkQueryExistence(queryId, true) checkRequestExistence(requestId, true) returns (uint256) { + ) + public + view + checkQueryExistence(queryId, true) + checkRequestExistence(requestId, true) + returns (uint256) + { UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); return s._proofs[queryId][requestId][groupId][userID].storageFields[responseFieldName]; } - /** * @dev Gets response field value * @param queryId Id of the query @@ -540,7 +547,13 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { uint256 groupId, address sender, string memory responseFieldName - ) public view checkQueryExistence(queryId, true) checkRequestExistence(requestId, true) returns (uint256) { + ) + public + view + checkQueryExistence(queryId, true) + checkRequestExistence(requestId, true) + returns (uint256) + { UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); uint256 userID = s._user_address_to_id[sender]; return s._proofs[queryId][requestId][groupId][userID].storageFields[responseFieldName]; @@ -552,7 +565,10 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { * @param userID The ID of the user * @return Whether all linked response fields are the same */ - function _checkLinkedResponseFields(uint256 queryId, uint256 userID) internal view returns (bool) { + function _checkLinkedResponseFields( + uint256 queryId, + uint256 userID + ) internal view returns (bool) { UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); for (uint256 i = 0; i < s._queries[queryId].linkedResponseFields.length; i++) { From a91c1e71a8f7ebdf0df368789899f0b2fce8bd78 Mon Sep 17 00:00:00 2001 From: daveroga Date: Wed, 4 Dec 2024 08:37:09 +0100 Subject: [PATCH 24/69] update from discussion --- contracts/interfaces/IRequestValidator.sol | 8 + .../test-helpers/RequestValidatorStub.sol | 4 + contracts/validators/EthIdentityValidator.sol | 5 + .../verifiers/UniversalVerifierMultiQuery.sol | 177 +++++++++--------- test/utils/validator-pack-utils.ts | 2 +- ...=> universal-verifier-multi-query.test.ts} | 36 +--- 6 files changed, 106 insertions(+), 126 deletions(-) rename test/verifier/{universal-verifier-multi-query.ts => universal-verifier-multi-query.test.ts} (87%) diff --git a/contracts/interfaces/IRequestValidator.sol b/contracts/interfaces/IRequestValidator.sol index a66729d9..c3261c87 100644 --- a/contracts/interfaces/IRequestValidator.sol +++ b/contracts/interfaces/IRequestValidator.sol @@ -37,4 +37,12 @@ interface IRequestValidator { address sender, IState state ) external returns (ResponseField[] memory); + + /** + * @dev Get the group ID of the request query data. + * @param params Request query data of the credential to verify. + * @return Group ID of the request query data. + */ + function getGroupID(bytes calldata params) external view returns (uint256); + } diff --git a/contracts/test-helpers/RequestValidatorStub.sol b/contracts/test-helpers/RequestValidatorStub.sol index ef97f7d6..47d19513 100644 --- a/contracts/test-helpers/RequestValidatorStub.sol +++ b/contracts/test-helpers/RequestValidatorStub.sol @@ -34,4 +34,8 @@ contract RequestValidatorStub is IRequestValidator, ERC165 { signals[1].value = 2; return signals; } + + function getGroupID(bytes calldata) external pure override returns (uint256) { + return 0; + } } diff --git a/contracts/validators/EthIdentityValidator.sol b/contracts/validators/EthIdentityValidator.sol index dda13c2d..01285bb2 100644 --- a/contracts/validators/EthIdentityValidator.sol +++ b/contracts/validators/EthIdentityValidator.sol @@ -99,4 +99,9 @@ contract EthIdentityValidator is Ownable2StepUpgradeable, IRequestValidator, ERC uint256 calcId = GenesisUtils.calcIdFromEthAddress(idType, sender); require(calcId == id, "Sender is not owner of the ethereum identity"); } + + function getGroupID(bytes calldata) external pure override returns (uint256) { + // TODO: Implement group ID + return 0; + } } diff --git a/contracts/verifiers/UniversalVerifierMultiQuery.sol b/contracts/verifiers/UniversalVerifierMultiQuery.sol index 7a21996e..742d4210 100644 --- a/contracts/verifiers/UniversalVerifierMultiQuery.sol +++ b/contracts/verifiers/UniversalVerifierMultiQuery.sol @@ -52,9 +52,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { * @param metadata Metadata of the request. */ struct Response { - uint256 queryId; uint256 requestId; - uint256 groupId; bytes proof; bytes metadata; } @@ -77,7 +75,6 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { struct ResponseFieldFromRequest { uint256 requestId; - uint256 groupId; string responseFieldName; } @@ -90,8 +87,6 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { struct Query { uint256 queryId; uint256[] requestIds; - uint256[] groupIdFromRequests; // TODO: We need it to know the group id of the request - ResponseFieldFromRequest[][] linkedResponseFields; // this are response fields linked between requests bytes metadata; } @@ -99,9 +94,36 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { struct UniversalVerifierMultiQueryStorage { // Information about requests // solhint-disable-next-line - mapping(uint256 queryId => mapping(uint256 groupId => mapping(uint256 requestId => mapping(uint256 userID => Proof)))) _proofs; + mapping(uint256 requestId => mapping(uint256 userID => Proof)) _proofs; mapping(uint256 requestId => Request) _requests; uint256[] _requestIds; + mapping(uint256 groupId => uint256[] requestIds) _groupedRequests; + // 1. Set Req 1 (groupID = 1) + // Check that groupID doesn't exist yet + // 2. Set Req 2 (groupID = 1) + // Check that groupID doesn't exist yet + // 3. Set Req 200 (groupID = 1) + // Check that groupID doesn't exist yet + + // 4. Set Req 3 (groupID = 2) + // 5. Set Req 201 (groupID = 2) + + // 6. setQuery[1,2] + // Check that groupID doesn't exist yet + // 1 => [1, 2] + // 2 => [3, 201] + + // 7. submitResponse: requests 1, 2, 200 + // 7.1 Check that all the group are full + // 7.2 Check LinkID is the same for each of the groups + + // 8. getQueryStatus: it result to FALSE cuz requests 3 and 201 are false + // 9. submitResponse: requests 3,201 + // 10. getQueryStatus: it result to TRUE as all requests are true + + // Query1 => (1, 2, 200), (3, 201) + // Query2 => (1, 2, 200), 10 + IState _state; // Information about queries mapping(uint256 queryId => Query) _queries; @@ -123,12 +145,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { /** * @dev Event emitted upon submitting a request */ - event ResponseSubmitted( - uint256 queryId, - uint256 indexed requestId, - uint256 groupId, - address indexed caller - ); + event ResponseSubmitted(uint256 indexed requestId, address indexed caller); /** * @dev Event emitted upon adding a request @@ -178,13 +195,25 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { */ modifier checkRequestExistence(uint256 requestId, bool existence) { if (existence) { - require(requestIdExists(requestId), "requestu id or auth request id doesn't exist"); + require(requestIdExists(requestId), "request id or auth request id doesn't exist"); } else { require(!requestIdExists(requestId), "request id or auth request id already exists"); } _; } + /** + * @dev Modifier to check if the request exists + */ + modifier checkRequestGroupExistence(uint256 groupId, bool existence) { + if (existence) { + require(groupIdExists(groupId), "group id doesn't exist"); + } else { + require(!groupIdExists(groupId), "group id already exists"); + } + _; + } + /** * @dev Modifier to check if the query exists */ @@ -235,6 +264,15 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { IRequestValidator(address(0)); } + /** + * @dev Checks if a group ID exists + * @param groupId The ID of the group + * @return Whether the group ID exists + */ + function groupIdExists(uint256 groupId) public view returns (bool) { + return _getUniversalVerifierMultiQueryStorage()._groupedRequests[groupId].length > 0; + } + /** * @dev Checks if a query ID exists * @param queryId The ID of the query @@ -265,7 +303,12 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { function setRequest( uint256 requestId, Request calldata request - ) public checkRequestExistence(requestId, false) onlyWhitelistedValidator(request.validator) { + ) + public + checkRequestExistence(requestId, false) + checkRequestGroupExistence(request.validator.getGroupID(request.params), false) + onlyWhitelistedValidator(request.validator) + { UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); s._requests[requestId] = request; s._requestIds.push(requestId); @@ -389,13 +432,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { } } - writeProofResults( - authResponse.queryId, - authResponse.requestId, - authResponse.groupId, //getGroupIdFromQueryRequest(authResponse.queryId, authResponse.requestId), - userIDFromAuth, - authSignals - ); + writeProofResults(authResponse.requestId, userIDFromAuth, authSignals); $._user_address_to_id[sender] = userIDFromAuth; $._id_to_user_address[userIDFromAuth] = sender; @@ -405,12 +442,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { // emit events (existing logic) for (uint256 i = 0; i < responses.length; i++) { // emit for all the responses - emit ResponseSubmitted( - responses[i].queryId, - responses[i].requestId, - responses[i].groupId, - _msgSender() - ); + emit ResponseSubmitted(responses[i].requestId, _msgSender()); // avoid to process auth request again if (_getRequestType(responses[i].requestId) == AUTH_REQUEST_TYPE) { @@ -418,7 +450,6 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { } Response memory response = responses[i]; - Request memory request = getRequest(response.requestId); IRequestValidator.ResponseField[] memory signals = request.validator.verify( @@ -428,13 +459,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { $._state ); - writeProofResults( - response.queryId, - response.requestId, - response.groupId, - userIDFromAuth, - signals - ); + writeProofResults(response.requestId, userIDFromAuth, signals); if (response.metadata.length > 0) { revert("Metadata not supported yet"); @@ -444,22 +469,18 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { /** * @dev Writes proof results. - * @param queryId The query ID of the proof * @param requestId The request ID of the proof - * @param groupId The index of the request in the query definition * @param userID The userID of the proof * @param responseFields The array of response fields of the proof */ function writeProofResults( - uint256 queryId, uint256 requestId, - uint256 groupId, uint256 userID, IRequestValidator.ResponseField[] memory responseFields ) public { UniversalVerifierMultiQueryStorage storage $ = _getUniversalVerifierMultiQueryStorage(); - Proof storage proof = $._proofs[queryId][requestId][groupId][userID]; + Proof storage proof = $._proofs[requestId][userID]; for (uint256 i = 0; i < responseFields.length; i++) { proof.storageFields[responseFields[i].name] = responseFields[i].value; } @@ -510,53 +531,33 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { /** * @dev Gets response field value - * @param queryId Id of the query * @param requestId Id of the request - * @param groupId Group id of the request in the query * @param userID Id of the user * @param responseFieldName Name of the response field to get */ function getResponseFieldValue( - uint256 queryId, uint256 requestId, - uint256 groupId, uint256 userID, string memory responseFieldName - ) - public - view - checkQueryExistence(queryId, true) - checkRequestExistence(requestId, true) - returns (uint256) - { + ) public view checkRequestExistence(requestId, true) returns (uint256) { UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); - return s._proofs[queryId][requestId][groupId][userID].storageFields[responseFieldName]; + return s._proofs[requestId][userID].storageFields[responseFieldName]; } /** * @dev Gets response field value - * @param queryId Id of the query * @param requestId Id of the request - * @param groupId Group id of the request in the query * @param sender Address of the sender * @param responseFieldName Name of the response field to get */ function getResponseFieldValueFromAddress( - uint256 queryId, uint256 requestId, - uint256 groupId, address sender, string memory responseFieldName - ) - public - view - checkQueryExistence(queryId, true) - checkRequestExistence(requestId, true) - returns (uint256) - { + ) public view checkRequestExistence(requestId, true) returns (uint256) { UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); uint256 userID = s._user_address_to_id[sender]; - return s._proofs[queryId][requestId][groupId][userID].storageFields[responseFieldName]; + return s._proofs[requestId][userID].storageFields[responseFieldName]; } /** @@ -571,33 +572,26 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { ) internal view returns (bool) { UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); - for (uint256 i = 0; i < s._queries[queryId].linkedResponseFields.length; i++) { - // check if we have linked response fields - if (s._queries[queryId].linkedResponseFields[i].length > 0) { - // Get first value and check if all the values for the same linked responses are the same - uint256 firstValue = getResponseFieldValue( - queryId, - s._queries[queryId].linkedResponseFields[i][0].requestId, - s._queries[queryId].linkedResponseFields[i][0].groupId, - userID, - s._queries[queryId].linkedResponseFields[i][0].responseFieldName - ); - - for (uint256 j = 1; j < s._queries[queryId].linkedResponseFields[i].length; j++) { - uint256 valueToCompare = getResponseFieldValue( - queryId, - s._queries[queryId].linkedResponseFields[i][j].requestId, - s._queries[queryId].linkedResponseFields[i][j].groupId, - userID, - s._queries[queryId].linkedResponseFields[i][j].responseFieldName - ); - - if (firstValue != valueToCompare) { - return false; - } - } + // TODO: Check hardcoded linkID only for now by groupID + + // Get first value and check if all the values for the same linked responses are the same + /*uint256 firstValue = getResponseFieldValue( + s._queries[queryId].requestIds[0].requestId, + userID, + "linkID" + ); + + for (uint256 j = 1; j < s._queries[queryId].linkedResponseFields[i].length; j++) { + uint256 valueToCompare = getResponseFieldValue( + s._queries[queryId].linkedResponseFields[i][j].requestId, + userID, + s._queries[queryId].linkedResponseFields[i][j].responseFieldName + ); + + if (firstValue != valueToCompare) { + return false; } - } + }*/ return true; } @@ -624,8 +618,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { // 3. Check if all requests statuses are true for the userId for (uint256 i = 0; i < s._queries[queryId].requestIds.length; i++) { uint256 requestId = s._queries[queryId].requestIds[i]; - uint256 groupId = s._queries[queryId].groupIdFromRequests[i]; - if (!s._proofs[queryId][requestId][groupId][userID].isVerified) { + if (!s._proofs[requestId][userID].isVerified) { return false; } } diff --git a/test/utils/validator-pack-utils.ts b/test/utils/validator-pack-utils.ts index 68cde7af..e4917403 100644 --- a/test/utils/validator-pack-utils.ts +++ b/test/utils/validator-pack-utils.ts @@ -15,7 +15,7 @@ export function packValidatorParams(query: any, allowedIssuers: any[] = []): str "uint256[] allowedIssuers," + "string[] circuitIds," + "bool skipClaimRevocationCheck," + - "uint256 claimPathNotExists" + + "uint256 claimPathNotExists," + ")", ], [ diff --git a/test/verifier/universal-verifier-multi-query.ts b/test/verifier/universal-verifier-multi-query.test.ts similarity index 87% rename from test/verifier/universal-verifier-multi-query.ts rename to test/verifier/universal-verifier-multi-query.test.ts index 6da94211..0d39add4 100644 --- a/test/verifier/universal-verifier-multi-query.ts +++ b/test/verifier/universal-verifier-multi-query.test.ts @@ -86,18 +86,10 @@ describe("Universal Verifier Multi-query", function () { await verifier.connect(); } - async function checkStorageFields( - verifier: any, - queryId: bigint, - requestId: bigint, - groupId: bigint, - storageFields: any[], - ) { + async function checkStorageFields(verifier: any, requestId: bigint, storageFields: any[]) { for (const field of storageFields) { const value = await verifier.getResponseFieldValueFromAddress( - queryId, requestId, - groupId, await signer.getAddress(), field.name, ); @@ -144,16 +136,12 @@ describe("Universal Verifier Multi-query", function () { const query = { queryId, requestIds: [requestId, authRequestId], - groupIdFromRequests: [0, 0], - linkedResponseFields: [[]], metadata: "0x", }; await verifier.setQuery(0, query); const queryStored = await verifier.getQuery(queryId); expect(queryStored[0]).to.be.equal(queryId); expect(queryStored[1]).to.be.deep.equal(query.requestIds); - expect(queryStored[2]).to.be.deep.equal(query.groupIdFromRequests); - expect(queryStored[3]).to.be.deep.equal(query.linkedResponseFields); const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); @@ -171,16 +159,12 @@ describe("Universal Verifier Multi-query", function () { const tx = await verifier.submitResponse( [ { - queryId, requestId, - groupId, proof, metadata: metadatas, }, { - queryId, requestId: authRequestId, - groupId, proof, metadata: metadatas, }, @@ -190,27 +174,13 @@ describe("Universal Verifier Multi-query", function () { await tx.wait(); - await checkStorageFields( - verifier, - BigInt(queryId), - authRequestId, - BigInt(groupId), - storageFields, - ); - await checkStorageFields( - verifier, - BigInt(queryId), - BigInt(requestId), - BigInt(groupId), - storageFields, - ); + await checkStorageFields(verifier, authRequestId, storageFields); + await checkStorageFields(verifier, BigInt(requestId), storageFields); const filter = verifier.filters.ResponseSubmitted; const events = await verifier.queryFilter(filter, -1); expect(events[0].eventName).to.be.equal("ResponseSubmitted"); - expect(events[0].args.queryId).to.be.equal(queryId); expect(events[0].args.requestId).to.be.equal(requestId); - expect(events[0].args.groupId).to.be.equal(groupId); expect(events[0].args.caller).to.be.equal(signerAddress); await expect(verifier.getQueryStatus(nonExistingQueryId, signerAddress)).to.be.rejectedWith( From 849e790aeb3a20b82bc1617494ee808e825b7a68 Mon Sep 17 00:00:00 2001 From: daveroga Date: Thu, 5 Dec 2024 12:27:38 +0100 Subject: [PATCH 25/69] set group of requests and check linkID --- contracts/interfaces/IRequestValidator.sol | 6 + .../RequestValidatorAuthV2Stub.sol | 43 +++ ...torStub.sol => RequestValidatorV2Stub.sol} | 10 +- .../test-helpers/RequestValidatorV3Stub.sol | 68 ++++ .../test-helpers/RequestValidatorV3_2Stub.sol | 68 ++++ contracts/validators/EthIdentityValidator.sol | 5 + .../verifiers/UniversalVerifierMultiQuery.sol | 167 +++++++++- hardhat.config.ts | 11 + helpers/DeployHelper.ts | 6 +- test/utils/validator-pack-utils.ts | 4 +- .../universal-verifier-multi-query.test.ts | 310 ++++++++++++++++-- 11 files changed, 642 insertions(+), 56 deletions(-) create mode 100644 contracts/test-helpers/RequestValidatorAuthV2Stub.sol rename contracts/test-helpers/{RequestValidatorStub.sol => RequestValidatorV2Stub.sol} (78%) create mode 100644 contracts/test-helpers/RequestValidatorV3Stub.sol create mode 100644 contracts/test-helpers/RequestValidatorV3_2Stub.sol diff --git a/contracts/interfaces/IRequestValidator.sol b/contracts/interfaces/IRequestValidator.sol index c3261c87..5087ec31 100644 --- a/contracts/interfaces/IRequestValidator.sol +++ b/contracts/interfaces/IRequestValidator.sol @@ -45,4 +45,10 @@ interface IRequestValidator { */ function getGroupID(bytes calldata params) external view returns (uint256); + /** + * @dev Get the hash of the group Id of the request query data. + * @param params Request query data of the credential to verify. + * @return Hash of the group Id of the request query data. + */ + function getGroupFieldHash(bytes calldata params) external view returns (bytes32); } diff --git a/contracts/test-helpers/RequestValidatorAuthV2Stub.sol b/contracts/test-helpers/RequestValidatorAuthV2Stub.sol new file mode 100644 index 00000000..3f62e2a3 --- /dev/null +++ b/contracts/test-helpers/RequestValidatorAuthV2Stub.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.27; + +import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; +import {IRequestValidator} from "../interfaces/IRequestValidator.sol"; +import {IState} from "../interfaces/IState.sol"; + +/** + * @dev RequestValidatorAuthV2Stub validator + */ +contract RequestValidatorAuthV2Stub is IRequestValidator, ERC165 { + string public constant VERSION = "1.0.0-mock"; + + function version() public pure override returns (string memory) { + return VERSION; + } + + function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + return + interfaceId == type(IRequestValidator).interfaceId || + super.supportsInterface(interfaceId); + } + + function verify( + bytes calldata, + bytes calldata, + address, + IState + ) external pure override returns (IRequestValidator.ResponseField[] memory) { + IRequestValidator.ResponseField[] memory signals = new IRequestValidator.ResponseField[](1); + signals[0].name = "userID"; + signals[0].value = 1; + return signals; + } + + function getGroupID(bytes calldata) external pure override returns (uint256) { + revert("AuthV2 validator does not support groupId field"); + } + + function getGroupFieldHash(bytes calldata params) external pure override returns (bytes32) { + revert("AuthV2 validator does not support groupId field"); + } +} diff --git a/contracts/test-helpers/RequestValidatorStub.sol b/contracts/test-helpers/RequestValidatorV2Stub.sol similarity index 78% rename from contracts/test-helpers/RequestValidatorStub.sol rename to contracts/test-helpers/RequestValidatorV2Stub.sol index 47d19513..d765c61b 100644 --- a/contracts/test-helpers/RequestValidatorStub.sol +++ b/contracts/test-helpers/RequestValidatorV2Stub.sol @@ -6,9 +6,9 @@ import {IRequestValidator} from "../interfaces/IRequestValidator.sol"; import {IState} from "../interfaces/IState.sol"; /** - * @dev RequestValidatorStub validator + * @dev RequestValidatorV2Stub validator */ -contract RequestValidatorStub is IRequestValidator, ERC165 { +contract RequestValidatorV2Stub is IRequestValidator, ERC165 { string public constant VERSION = "1.0.0-mock"; function version() public pure override returns (string memory) { @@ -36,6 +36,10 @@ contract RequestValidatorStub is IRequestValidator, ERC165 { } function getGroupID(bytes calldata) external pure override returns (uint256) { - return 0; + revert("V2 validator does not support groupId field"); + } + + function getGroupFieldHash(bytes calldata params) external pure override returns (bytes32) { + revert("V2 validator does not support groupId field"); } } diff --git a/contracts/test-helpers/RequestValidatorV3Stub.sol b/contracts/test-helpers/RequestValidatorV3Stub.sol new file mode 100644 index 00000000..1ae6e223 --- /dev/null +++ b/contracts/test-helpers/RequestValidatorV3Stub.sol @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.27; + +import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; +import {IRequestValidator} from "../interfaces/IRequestValidator.sol"; +import {IState} from "../interfaces/IState.sol"; + +/** + * @dev RequestValidatorV3Stub validator + */ +contract RequestValidatorV3Stub is IRequestValidator, ERC165 { + string public constant VERSION = "1.0.0-mock"; + + struct CredentialAtomicQueryV3 { + uint256 schema; + uint256 claimPathKey; + uint256 operator; + uint256 slotIndex; + uint256[] value; + uint256 queryHash; + uint256[] allowedIssuers; + string[] circuitIds; + bool skipClaimRevocationCheck; + uint256 groupID; + uint256 nullifierSessionID; + uint256 proofType; + uint256 verifierID; + } + + function version() public pure override returns (string memory) { + return VERSION; + } + + function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + return + interfaceId == type(IRequestValidator).interfaceId || + super.supportsInterface(interfaceId); + } + + function verify( + bytes calldata, + bytes calldata, + address, + IState + ) external pure override returns (IRequestValidator.ResponseField[] memory) { + IRequestValidator.ResponseField[] memory signals = new IRequestValidator.ResponseField[](3); + signals[0].name = "userID"; + signals[0].value = 1; + signals[1].name = "issuerID"; + signals[1].value = 2; + signals[2].name = "linkID"; + signals[2].value = 3; + return signals; + } + + function getGroupID(bytes calldata params) external pure override returns (uint256) { + CredentialAtomicQueryV3 memory credAtomicQuery = abi.decode( + params, + (CredentialAtomicQueryV3) + ); + return credAtomicQuery.groupID; + } + + function getGroupFieldHash(bytes calldata params) external pure override returns (bytes32) { + // TODO: Implement hash function + return keccak256(params); + } +} diff --git a/contracts/test-helpers/RequestValidatorV3_2Stub.sol b/contracts/test-helpers/RequestValidatorV3_2Stub.sol new file mode 100644 index 00000000..b77b845f --- /dev/null +++ b/contracts/test-helpers/RequestValidatorV3_2Stub.sol @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.27; + +import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; +import {IRequestValidator} from "../interfaces/IRequestValidator.sol"; +import {IState} from "../interfaces/IState.sol"; + +/** + * @dev RequestValidatorV3Stub validator + */ +contract RequestValidatorV3_2Stub is IRequestValidator, ERC165 { + string public constant VERSION = "1.0.0-mock"; + + struct CredentialAtomicQueryV3 { + uint256 schema; + uint256 claimPathKey; + uint256 operator; + uint256 slotIndex; + uint256[] value; + uint256 queryHash; + uint256[] allowedIssuers; + string[] circuitIds; + bool skipClaimRevocationCheck; + uint256 groupID; + uint256 nullifierSessionID; + uint256 proofType; + uint256 verifierID; + } + + function version() public pure override returns (string memory) { + return VERSION; + } + + function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + return + interfaceId == type(IRequestValidator).interfaceId || + super.supportsInterface(interfaceId); + } + + function verify( + bytes calldata, + bytes calldata, + address, + IState + ) external pure override returns (IRequestValidator.ResponseField[] memory) { + IRequestValidator.ResponseField[] memory signals = new IRequestValidator.ResponseField[](3); + signals[0].name = "userID"; + signals[0].value = 1; + signals[1].name = "issuerID"; + signals[1].value = 2; + signals[2].name = "linkID"; + signals[2].value = 4; + return signals; + } + + function getGroupID(bytes calldata params) external pure override returns (uint256) { + CredentialAtomicQueryV3 memory credAtomicQuery = abi.decode( + params, + (CredentialAtomicQueryV3) + ); + return credAtomicQuery.groupID; + } + + function getGroupFieldHash(bytes calldata params) external pure override returns (bytes32) { + // TODO: Implement hash function + return keccak256(params); + } +} diff --git a/contracts/validators/EthIdentityValidator.sol b/contracts/validators/EthIdentityValidator.sol index 01285bb2..b7d91646 100644 --- a/contracts/validators/EthIdentityValidator.sol +++ b/contracts/validators/EthIdentityValidator.sol @@ -104,4 +104,9 @@ contract EthIdentityValidator is Ownable2StepUpgradeable, IRequestValidator, ERC // TODO: Implement group ID return 0; } + + function getGroupFieldHash(bytes calldata params) external pure override returns (bytes32) { + // TODO: Implement hash function + return keccak256(params); + } } diff --git a/contracts/verifiers/UniversalVerifierMultiQuery.sol b/contracts/verifiers/UniversalVerifierMultiQuery.sol index 742d4210..b4c0e6ec 100644 --- a/contracts/verifiers/UniversalVerifierMultiQuery.sol +++ b/contracts/verifiers/UniversalVerifierMultiQuery.sol @@ -322,6 +322,29 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { ); } + /** + * @dev Sets a group of requests + * @param groupId The ID of the group + * @param requestIds The IDs of the requests + * @param requests The requests data + */ + function setGroupOfRequests( + uint256 groupId, + uint256[] memory requestIds, + Request[] calldata requests + ) public { + UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); + + for (uint256 i = 0; i < requestIds.length; i++) { + setRequest(requestIds[i], requests[i]); + } + // check that all the requests are set before adding them to the group + // because there is a check in setRequest that the group doesn't exist yet + for (uint256 i = 0; i < requestIds.length; i++) { + s._groupedRequests[groupId].push(requestIds[i]); + } + } + /** * @dev Gets a specific request by ID * @param requestId The ID of the request @@ -360,10 +383,14 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { ) public checkQueryExistence(queryId, false) { UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); + // check that exactly 1 auth request is included in the query _checkAuthRequestInQuery(query); s._queries[queryId] = query; s._queryIds.push(queryId); + // check that all the requests of the groups are included in this query + _checkRequestsInGroupsIncluded(queryId); + emit QuerySet(queryId, query.requestIds); } @@ -396,6 +423,95 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { return authResponseIndex; } + /** + * @dev Checks that all the requests of the groups are included in this query + * @param queryId The query id to check + */ + function _checkRequestsInGroupsIncluded(uint256 queryId) internal view { + UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); + + uint256[] memory requestIds = s._queries[queryId].requestIds; + // check that groupId from requests exist and all the requests of the groups are used in this query + uint256[] memory groupIds = new uint256[](requestIds.length); + uint256[] memory groupLength = new uint256[](requestIds.length); + uint256[][] memory groupedRequestQuery = new uint256[][](requestIds.length); + uint256 numGroups = 0; + + // build the groups based on the requests of the query + for (uint256 i = 0; i < requestIds.length; i++) { + if (_getRequestType(s._queries[queryId].requestIds[i]) == AUTH_REQUEST_TYPE) { + continue; + } + uint256 groupId = s._requests[requestIds[i]].validator.getGroupID( + s._requests[requestIds[i]].params + ); + + if (groupId > 0) { + uint256 iGroup; + bool found = false; + // check if groupId already exists in the local groupIds variable + for (uint256 j = 0; j < numGroups; j++) { + if (groupIds[j] == groupId) { + iGroup = j; + found = true; + break; + } + } + + // If not found we added to the end of the groupIds and groupedRequestQuery + if (numGroups == 0 || found == false) { + groupIds[numGroups] = groupId; + groupedRequestQuery[numGroups] = new uint256[](requestIds.length); + groupedRequestQuery[numGroups][0] = requestIds[i]; + groupLength[numGroups] = 1; + numGroups++; + } else { + // add request id to the end of the group request query + groupedRequestQuery[iGroup][groupLength[iGroup]] = requestIds[i]; + groupLength[iGroup]++; + } + } + } + + // check that all the requests of the group are used in this query + for (uint256 i = 0; i < numGroups; i++) { + // Check that all the requests of the group are used in this query + require( + _allRequestsIncluded( + s._groupedRequests[groupIds[i]], + groupedRequestQuery[i], + groupLength[i] + ), + "The requests of the group in this query doesn't match the existing group" + ); + } + } + + function _allRequestsIncluded( + uint256[] storage reqIds1, + uint256[] memory reqIds2, + uint256 numRequests2 + ) internal view returns (bool) { + if (reqIds1.length != numRequests2) { + return false; + } + + for (uint256 i = 0; i < reqIds1.length; i++) { + bool found = false; + for (uint256 j = 0; j < numRequests2; j++) { + if (reqIds1[i] == reqIds2[j]) { + found = true; + break; + } + } + if (!found) { + return false; + } + } + + return true; + } + /** * @dev Submits an array of responses and updates proofs status * @param responses The list of responses including request ID, proof and metadata @@ -564,35 +680,51 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { * @dev Checks if all linked response fields are the same * @param queryId The ID of the query * @param userID The ID of the user - * @return Whether all linked response fields are the same */ function _checkLinkedResponseFields( uint256 queryId, uint256 userID - ) internal view returns (bool) { + ) internal view { UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); - // TODO: Check hardcoded linkID only for now by groupID + uint256[] memory groupIndex = new uint256[](s._queries[queryId].requestIds.length); + uint256[] memory groupLinkID = new uint256[](s._queries[queryId].requestIds.length); + uint256 numGroups = 0; - // Get first value and check if all the values for the same linked responses are the same - /*uint256 firstValue = getResponseFieldValue( - s._queries[queryId].requestIds[0].requestId, - userID, - "linkID" - ); + for (uint256 i = 0; i < s._queries[queryId].requestIds.length; i++) { + Request memory request = getRequest(s._queries[queryId].requestIds[i]); + + if (_getRequestType(s._queries[queryId].requestIds[i]) == AUTH_REQUEST_TYPE) { + continue; + } - for (uint256 j = 1; j < s._queries[queryId].linkedResponseFields[i].length; j++) { - uint256 valueToCompare = getResponseFieldValue( - s._queries[queryId].linkedResponseFields[i][j].requestId, + uint256 requestGroupId = request.validator.getGroupID(request.params); + + if (requestGroupId == 0) { + continue; + } + + uint256 requestLinkID = getResponseFieldValue( + s._queries[queryId].requestIds[i], userID, - s._queries[queryId].linkedResponseFields[i][j].responseFieldName + "linkID" ); - if (firstValue != valueToCompare) { - return false; + bool found = false; + for (uint256 j = 0; j < numGroups; j++) { + if (groupIndex[j] == requestGroupId) { + require(groupLinkID[j] == requestLinkID, "linkID is not the same for each of the requests of the group"); + found = true; + } } - }*/ - return true; + // If not found we added to the end of the groupIndex and groupLinkID to check + // linkID with the same groupID later + if (!found) { + groupIndex[numGroups] = requestGroupId; + groupLinkID[numGroups] = requestLinkID; + numGroups++; + } + } } /** @@ -622,7 +754,6 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { return false; } } - // 4. Check if all linked response fields are the same _checkLinkedResponseFields(queryId, userID); diff --git a/hardhat.config.ts b/hardhat.config.ts index 94e2ce18..5246ba30 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -41,6 +41,17 @@ const config: HardhatUserConfig = { version: "0.8.27", }, ], + overrides: { + "contracts/verifiers/UniversalVerifierMultiQuery.sol": { + version: "0.8.27", + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + }, + }, + }, }, networks: { "privado-main": { diff --git a/helpers/DeployHelper.ts b/helpers/DeployHelper.ts index 995d7b2e..2466643e 100644 --- a/helpers/DeployHelper.ts +++ b/helpers/DeployHelper.ts @@ -797,12 +797,12 @@ export class DeployHelper { return stubInstance; } - async deployRequestValidatorStub(): Promise { - const stub = await ethers.getContractFactory("RequestValidatorStub"); + async deployRequestValidatorStub(requestValidatorName: string): Promise { + const stub = await ethers.getContractFactory(requestValidatorName); const stubInstance = await stub.deploy(); await stubInstance.waitForDeployment(); - console.log("RequestValidator stub deployed to:", await stubInstance.getAddress()); + console.log(`${requestValidatorName} stub deployed to:`, await stubInstance.getAddress()); return stubInstance; } diff --git a/test/utils/validator-pack-utils.ts b/test/utils/validator-pack-utils.ts index e4917403..ed0d4b37 100644 --- a/test/utils/validator-pack-utils.ts +++ b/test/utils/validator-pack-utils.ts @@ -31,7 +31,7 @@ export function packValidatorParams(query: any, allowedIssuers: any[] = []): str skipClaimRevocationCheck: query.skipClaimRevocationCheck, claimPathNotExists: query.claimPathNotExists, }, - ] + ], ); } @@ -70,6 +70,6 @@ export function packV3ValidatorParams(query: any, allowedIssuers: any[] = []): s proofType: query.proofType, verifierID: query.verifierID, }, - ] + ], ); } diff --git a/test/verifier/universal-verifier-multi-query.test.ts b/test/verifier/universal-verifier-multi-query.test.ts index 0d39add4..b3f07464 100644 --- a/test/verifier/universal-verifier-multi-query.test.ts +++ b/test/verifier/universal-verifier-multi-query.test.ts @@ -1,16 +1,17 @@ import { expect } from "chai"; import { DeployHelper } from "../../helpers/DeployHelper"; import { ethers } from "hardhat"; -import { packValidatorParams } from "../utils/validator-pack-utils"; +import { packV3ValidatorParams } from "../utils/validator-pack-utils"; import { prepareInputs } from "../utils/state-utils"; import { Contract } from "ethers"; import proofJson from "../validators/sig/data/valid_sig_user_genesis.json"; import { buildCrossChainProofs, packCrossChainProofs, packZKProof } from "../utils/packData"; import { CircuitId } from "@0xpolygonid/js-sdk"; import { loadFixture } from "@nomicfoundation/hardhat-toolbox/network-helpers"; +import { calculateQueryHashV3 } from "../utils/query-hash-utils"; describe("Universal Verifier Multi-query", function () { - let verifier: any, sig: any, authV2: any; + let verifier: any, v3Validator: any, authV2Validator: any, v3_2Validator: any; let signer; let signerAddress: string; let deployHelper: DeployHelper; @@ -37,19 +38,73 @@ describe("Universal Verifier Multi-query", function () { replacedAtTimestamp: 1724858009n, }; - const requestQuery = { - schema: BigInt("180410020913331409885634153623124536270"), - claimPathKey: BigInt( - "8566939875427719562376598811066985304309117528846759529734201066483458512800", - ), - operator: 1n, - slotIndex: 0n, - value: [1420070400000000000n, ...new Array(63).fill("0").map((x) => BigInt(x))], - queryHash: BigInt( - "1496222740463292783938163206931059379817846775593932664024082849882751356658", - ), - circuitIds: [CircuitId.AtomicQuerySigV2], - claimPathNotExists: 0, + const value = ["20010101", ...new Array(63).fill("0")]; + + const schema = "267831521922558027206082390043321796944"; + const slotIndex = 0; // 0 for signature + const operator = 2; + const claimPathKey = + "20376033832371109177683048456014525905119173674985843915445634726167450989630"; + const [merklized, isRevocationChecked, valueArrSize] = [1, 1, 1]; + const nullifierSessionId = "0"; + const verifierId = "21929109382993718606847853573861987353620810345503358891473103689157378049"; + const queryHash = calculateQueryHashV3( + value, + schema, + slotIndex, + operator, + claimPathKey, + valueArrSize, + merklized, + isRevocationChecked, + verifierId, + nullifierSessionId, + ); + + const requestQuery1 = { + schema, + claimPathKey, + operator, + slotIndex, + value, + // we can use the same offchain circuit id because now an auth request is used for authentication + circuitIds: [CircuitId.AuthV2], + skipClaimRevocationCheck: false, + queryHash, + groupID: 0, + nullifierSessionID: nullifierSessionId, // for ethereum based user + proofType: 1, // 1 for BJJ + verifierID: verifierId, + }; + + const requestQuery2 = { + schema, + claimPathKey, + operator, + slotIndex, + value, + circuitIds: [CircuitId.AtomicQueryV3], + skipClaimRevocationCheck: false, + queryHash, + groupID: 1, + nullifierSessionID: nullifierSessionId, // for ethereum based user + proofType: 1, // 1 for BJJ + verifierID: verifierId, + }; + + const requestQuery3 = { + schema, + claimPathKey, + operator, + slotIndex, + value, + circuitIds: [CircuitId.AtomicQueryV3], + skipClaimRevocationCheck: false, + queryHash, + groupID: 1, + nullifierSessionID: nullifierSessionId, // for ethereum based user + proofType: 1, // 1 for BJJ + verifierID: verifierId, }; const storageFields = [ @@ -63,6 +118,13 @@ describe("Universal Verifier Multi-query", function () { }, ]; + const authStorageFields = [ + { + name: "userID", + value: 1, + }, + ]; + async function deployContractsFixture() { [signer] = await ethers.getSigners(); signerAddress = await signer.getAddress(); @@ -79,10 +141,13 @@ describe("Universal Verifier Multi-query", function () { await stateCrossChainStub.getAddress(), ); - sig = await deployHelper.deployRequestValidatorStub(); - authV2 = await deployHelper.deployRequestValidatorStub(); + v3Validator = await deployHelper.deployRequestValidatorStub("RequestValidatorV3Stub"); + v3_2Validator = await deployHelper.deployRequestValidatorStub("RequestValidatorV3_2Stub"); + authV2Validator = await deployHelper.deployRequestValidatorStub("RequestValidatorAuthV2Stub"); - await verifier.addValidatorToWhitelist(await sig.getAddress()); + await verifier.addValidatorToWhitelist(await v3Validator.getAddress()); + await verifier.addValidatorToWhitelist(await v3_2Validator.getAddress()); + await verifier.addValidatorToWhitelist(await authV2Validator.getAddress()); await verifier.connect(); } @@ -101,36 +166,35 @@ describe("Universal Verifier Multi-query", function () { await loadFixture(deployContractsFixture); }); - it("Test submit response multiquery", async () => { - const requestId = 0; + it("Test submit response multiquery without groupID", async () => { + const requestId = 1; // authRequestId: 0x0100000000000000000000000000000000000000000000000000000000000001 const authRequestId = 452312848583266388373324160190187140051835877600158453279131187530910662657n; - const queryId = 0; - const groupId = 0; - const nonExistingQueryId = 1; - const params = packValidatorParams(requestQuery); + const queryId = 1; + const nonExistingQueryId = 5; + const params = packV3ValidatorParams(requestQuery1); await verifier.setRequest(requestId, { metadata: "metadata", - validator: await sig.getAddress(), + validator: await v3Validator.getAddress(), params: params, }); const requestStored = await verifier.getRequest(requestId); // check if the request is stored correctly checking metadata and validator expect(requestStored[0]).to.be.equal("metadata"); - expect(requestStored[1]).to.be.equal(await sig.getAddress()); + expect(requestStored[1]).to.be.equal(await v3Validator.getAddress()); expect(requestStored[2]).to.be.equal(params); await verifier.setAuthRequest(authRequestId, { metadata: "metadata", - validator: await authV2.getAddress(), + validator: await authV2Validator.getAddress(), params: params, }); const authRequestStored = await verifier.getAuthRequest(authRequestId); expect(authRequestStored[0]).to.be.equal("metadata"); - expect(authRequestStored[1]).to.be.equal(await authV2.getAddress()); + expect(authRequestStored[1]).to.be.equal(await authV2Validator.getAddress()); expect(authRequestStored[2]).to.be.equal(params); const query = { @@ -138,7 +202,7 @@ describe("Universal Verifier Multi-query", function () { requestIds: [requestId, authRequestId], metadata: "0x", }; - await verifier.setQuery(0, query); + await verifier.setQuery(queryId, query); const queryStored = await verifier.getQuery(queryId); expect(queryStored[0]).to.be.equal(queryId); expect(queryStored[1]).to.be.deep.equal(query.requestIds); @@ -174,7 +238,7 @@ describe("Universal Verifier Multi-query", function () { await tx.wait(); - await checkStorageFields(verifier, authRequestId, storageFields); + await checkStorageFields(verifier, authRequestId, authStorageFields); await checkStorageFields(verifier, BigInt(requestId), storageFields); const filter = verifier.filters.ResponseSubmitted; @@ -190,4 +254,190 @@ describe("Universal Verifier Multi-query", function () { const status = await verifier.getQueryStatus(queryId, signerAddress); expect(status).to.be.true; }); + + it("Test submit response multiquery with same groupID and linkID", async () => { + const requestId2 = 2; + const requestId3 = 3; + const groupId = 1; + // authRequestId: 0x0100000000000000000000000000000000000000000000000000000000000001 + const authRequestId = + 452312848583266388373324160190187140051835877600158453279131187530910662657n; + const queryId = 1; + const nonExistingQueryId = 5; + const authParams = "0x"; + const paramsRequest2 = packV3ValidatorParams(requestQuery2); + const paramsRequest3 = packV3ValidatorParams(requestQuery3); + + const txGroupRequests = await verifier.setGroupOfRequests( + groupId, + [requestId2, requestId3], + [ + { metadata: "metadata", validator: await v3Validator.getAddress(), params: paramsRequest2 }, + { + metadata: "metadata", + validator: await v3Validator.getAddress(), + params: paramsRequest3, + }, + ], + ); + await txGroupRequests.wait(); + + const requestStored = await verifier.getRequest(requestId2); + // check if the request is stored correctly checking metadata and validator + expect(requestStored[0]).to.be.equal("metadata"); + expect(requestStored[1]).to.be.equal(await v3Validator.getAddress()); + expect(requestStored[2]).to.be.equal(paramsRequest2); + + await verifier.setAuthRequest(authRequestId, { + metadata: "metadata", + validator: await authV2Validator.getAddress(), + params: authParams, + }); + const authRequestStored = await verifier.getAuthRequest(authRequestId); + expect(authRequestStored[0]).to.be.equal("metadata"); + expect(authRequestStored[1]).to.be.equal(await authV2Validator.getAddress()); + expect(authRequestStored[2]).to.be.equal(authParams); + + const query = { + queryId, + requestIds: [requestId2, authRequestId, requestId3], + metadata: "0x", + }; + await verifier.setQuery(queryId, query); + const queryStored = await verifier.getQuery(queryId); + expect(queryStored[0]).to.be.equal(queryId); + expect(queryStored[1]).to.be.deep.equal(query.requestIds); + + const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); + + const proof = packZKProof(inputs, pi_a, pi_b, pi_c); + + const crossChainProofs = packCrossChainProofs( + await buildCrossChainProofs( + [globalStateMessage, identityStateMessage1, identityStateUpdate2], + signer, + ), + ); + + const metadatas = "0x"; + + const tx = await verifier.submitResponse( + [ + { + requestId: requestId2, + proof, + metadata: metadatas, + }, + { + requestId: authRequestId, + proof, + metadata: metadatas, + }, + { + requestId: requestId3, + proof, + metadata: metadatas, + }, + ], + crossChainProofs, + ); + + await tx.wait(); + + await checkStorageFields(verifier, authRequestId, authStorageFields); + await checkStorageFields(verifier, BigInt(requestId2), storageFields); + const filter = verifier.filters.ResponseSubmitted; + + const events = await verifier.queryFilter(filter, -1); + expect(events[0].eventName).to.be.equal("ResponseSubmitted"); + expect(events[0].args.requestId).to.be.equal(requestId2); + expect(events[0].args.caller).to.be.equal(signerAddress); + + await expect(verifier.getQueryStatus(nonExistingQueryId, signerAddress)).to.be.rejectedWith( + "query id doesn't exist", + ); + + const status = await verifier.getQueryStatus(queryId, signerAddress); + expect(status).to.be.true; + }); + + it("Test submit response multiquery with same groupID and different linkID", async () => { + const requestId2 = 2; + const requestId3 = 3; + const groupId = 1; + // authRequestId: 0x0100000000000000000000000000000000000000000000000000000000000001 + const authRequestId = + 452312848583266388373324160190187140051835877600158453279131187530910662657n; + const queryId = 1; + const authParams = "0x"; + const paramsRequest2 = packV3ValidatorParams(requestQuery2); + const paramsRequest3 = packV3ValidatorParams(requestQuery3); + + const txGroupRequests = await verifier.setGroupOfRequests( + groupId, + [requestId2, requestId3], + [ + { metadata: "metadata", validator: await v3Validator.getAddress(), params: paramsRequest2 }, + { + metadata: "metadata", + validator: await v3_2Validator.getAddress(), + params: paramsRequest3, + }, + ], + ); + await txGroupRequests.wait(); + + await verifier.setAuthRequest(authRequestId, { + metadata: "metadata", + validator: await authV2Validator.getAddress(), + params: authParams, + }); + + const query = { + queryId, + requestIds: [requestId2, authRequestId, requestId3], + metadata: "0x", + }; + await verifier.setQuery(queryId, query); + + const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); + + const proof = packZKProof(inputs, pi_a, pi_b, pi_c); + + const crossChainProofs = packCrossChainProofs( + await buildCrossChainProofs( + [globalStateMessage, identityStateMessage1, identityStateUpdate2], + signer, + ), + ); + + const metadatas = "0x"; + + const tx = await verifier.submitResponse( + [ + { + requestId: requestId2, + proof, + metadata: metadatas, + }, + { + requestId: authRequestId, + proof, + metadata: metadatas, + }, + { + requestId: requestId3, + proof, + metadata: metadatas, + }, + ], + crossChainProofs, + ); + + await tx.wait(); + + await expect(verifier.getQueryStatus(queryId, signerAddress)).to.be.rejectedWith( + "linkID is not the same for each of the requests of the group", + ); + }); }); From a02592cb455d324348bb6590b7ff88a2c4610374 Mon Sep 17 00:00:00 2001 From: daveroga Date: Thu, 5 Dec 2024 12:33:10 +0100 Subject: [PATCH 26/69] fix solhint --- .../verifiers/UniversalVerifierMultiQuery.sol | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/contracts/verifiers/UniversalVerifierMultiQuery.sol b/contracts/verifiers/UniversalVerifierMultiQuery.sol index b4c0e6ec..6cc4a365 100644 --- a/contracts/verifiers/UniversalVerifierMultiQuery.sol +++ b/contracts/verifiers/UniversalVerifierMultiQuery.sol @@ -487,6 +487,13 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { } } + /** + * @dev Checks if all requests are included + * @param reqIds1 The list of request IDs from storage for a specific group + * @param reqIds2 The list of request IDs from the query for a specific group + * @param numRequests2 The number of requests in the query for a specific group + * @return Whether all requests in reqIds1 are included in reqIds2 + */ function _allRequestsIncluded( uint256[] storage reqIds1, uint256[] memory reqIds2, @@ -681,10 +688,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { * @param queryId The ID of the query * @param userID The ID of the user */ - function _checkLinkedResponseFields( - uint256 queryId, - uint256 userID - ) internal view { + function _checkLinkedResponseFields(uint256 queryId, uint256 userID) internal view { UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); uint256[] memory groupIndex = new uint256[](s._queries[queryId].requestIds.length); @@ -692,7 +696,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { uint256 numGroups = 0; for (uint256 i = 0; i < s._queries[queryId].requestIds.length; i++) { - Request memory request = getRequest(s._queries[queryId].requestIds[i]); + Request memory request = getRequest(s._queries[queryId].requestIds[i]); if (_getRequestType(s._queries[queryId].requestIds[i]) == AUTH_REQUEST_TYPE) { continue; @@ -711,9 +715,12 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { ); bool found = false; - for (uint256 j = 0; j < numGroups; j++) { + for (uint256 j = 0; j < numGroups; j++) { if (groupIndex[j] == requestGroupId) { - require(groupLinkID[j] == requestLinkID, "linkID is not the same for each of the requests of the group"); + require( + groupLinkID[j] == requestLinkID, + "linkID is not the same for each of the requests of the group" + ); found = true; } } From 6b82ff0885eb901696592584bf2f9e96f18ed248 Mon Sep 17 00:00:00 2001 From: daveroga Date: Fri, 6 Dec 2024 10:00:04 +0100 Subject: [PATCH 27/69] updates from review --- .../verifiers/UniversalVerifierMultiQuery.sol | 408 +++++++----------- .../universal-verifier-multi-query.test.ts | 132 +++--- 2 files changed, 239 insertions(+), 301 deletions(-) diff --git a/contracts/verifiers/UniversalVerifierMultiQuery.sol b/contracts/verifiers/UniversalVerifierMultiQuery.sol index 6cc4a365..b214d7f2 100644 --- a/contracts/verifiers/UniversalVerifierMultiQuery.sol +++ b/contracts/verifiers/UniversalVerifierMultiQuery.sol @@ -23,16 +23,29 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { uint8 constant AUTH_REQUEST_TYPE = 1; /** - * @dev Request. Structure for request. + * @dev Request. Structure for request for storage. * @param metadata Metadata of the request. * @param validator Validator circuit. * @param params Params of the request. Proof parameters could be ZK groth16, plonk, ESDSA, EIP712, etc. */ + struct RequestData { + string metadata; + IRequestValidator validator; + bytes params; + } + struct Request { + uint256 requestId; string metadata; IRequestValidator validator; bytes params; } + + struct GroupedRequests { + uint256 groupId; + Request[] requests; + } + /** * @dev Struct to store proof and associated data */ @@ -57,36 +70,22 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { bytes metadata; } - // TODO discussion result start - // struct ResponseFieldFromRequest { - // uint256 requestId; - // string responseFieldName; - // uint256 groupId; - // } - // - //ResponseFieldFromRequest[][] - - // [ - // [{linkID, 1, 0}, {linkID, 2, 0}] - // [{linkID, 2, 2}, {linkID, 3, 1}], - // [{issuerID, 2, 3}, {issuer, 3, 4}], - // ] - // TODO discussion result start - - struct ResponseFieldFromRequest { - uint256 requestId; - string responseFieldName; + struct GroupedResponses { + uint256 groupId; + Response[] responses; } /** * @dev Query. Structure for query. * @param queryId Query id. - * @param requestIds Request ids for this multi query. + * @param requestIds Request ids for this multi query (without groupId. Single requests). + * @param groupIds Group ids for this multi query (all the requests included in the group. Grouped requests). * @param metadata Metadata for the query. Empty in first version. */ struct Query { uint256 queryId; uint256[] requestIds; + uint256[] groupIds; bytes metadata; } @@ -95,7 +94,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { // Information about requests // solhint-disable-next-line mapping(uint256 requestId => mapping(uint256 userID => Proof)) _proofs; - mapping(uint256 requestId => Request) _requests; + mapping(uint256 requestId => RequestData) _requests; uint256[] _requestIds; mapping(uint256 groupId => uint256[] requestIds) _groupedRequests; // 1. Set Req 1 (groupID = 1) @@ -244,11 +243,6 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { _getUniversalVerifierMultiQueryStorage()._state = state; } - /** - * @dev Checks if a request ID exists - * @param requestId The ID of the request - * @return requestType Type of the request - */ function _getRequestType(uint256 requestId) internal pure returns (uint8 requestType) { return uint8(requestId >> 248); } @@ -279,12 +273,9 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { * @return Whether the query ID exists */ function queryIdExists(uint256 queryId) public view returns (bool) { - return _getUniversalVerifierMultiQueryStorage()._queries[queryId].requestIds.length > 0; + return _getUniversalVerifierMultiQueryStorage()._queries[queryId].queryId == queryId; } - /** - * @dev Get the main storage using assembly to ensure specific storage location - */ function _getUniversalVerifierMultiQueryStorage() private pure @@ -297,24 +288,30 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { /** * @dev Sets a request - * @param requestId The ID of the request * @param request The request data */ function setRequest( - uint256 requestId, Request calldata request ) public - checkRequestExistence(requestId, false) + checkRequestExistence(request.requestId, false) checkRequestGroupExistence(request.validator.getGroupID(request.params), false) onlyWhitelistedValidator(request.validator) { + _setRequest(request); + } + + function _setRequest(Request calldata request) internal { UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); - s._requests[requestId] = request; - s._requestIds.push(requestId); + s._requests[request.requestId] = RequestData({ + metadata: request.metadata, + validator: request.validator, + params: request.params + }); + s._requestIds.push(request.requestId); emit RequestSet( - requestId, + request.requestId, _msgSender(), request.metadata, address(request.validator), @@ -323,25 +320,26 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { } /** - * @dev Sets a group of requests - * @param groupId The ID of the group - * @param requestIds The IDs of the requests - * @param requests The requests data + * @dev Sets different requests + * @param singleRequests The requests that are not in any group + * @param groupedRequests The requests that are in a group */ - function setGroupOfRequests( - uint256 groupId, - uint256[] memory requestIds, - Request[] calldata requests + function setRequests( + Request[] calldata singleRequests, + GroupedRequests[] calldata groupedRequests ) public { UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); - for (uint256 i = 0; i < requestIds.length; i++) { - setRequest(requestIds[i], requests[i]); + for (uint256 i = 0; i < singleRequests.length; i++) { + setRequest(singleRequests[i]); // checkRequestGroupExistence is done in setRequest } - // check that all the requests are set before adding them to the group - // because there is a check in setRequest that the group doesn't exist yet - for (uint256 i = 0; i < requestIds.length; i++) { - s._groupedRequests[groupId].push(requestIds[i]); + for (uint256 i = 0; i < groupedRequests.length; i++) { + for (uint256 j = 0; j < groupedRequests[i].requests.length; j++) { + _setRequest(groupedRequests[i].requests[j]); // checkRequestGroupExistence is not done here + s._groupedRequests[groupedRequests[i].groupId].push( + groupedRequests[i].requests[j].requestId + ); + } } } @@ -352,26 +350,10 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { */ function getRequest( uint256 requestId - ) public view checkRequestExistence(requestId, true) returns (Request memory request) { + ) public view checkRequestExistence(requestId, true) returns (RequestData memory request) { return _getUniversalVerifierMultiQueryStorage()._requests[requestId]; } - /** - * @dev Checks auth request in query - * @param query The query data - */ - function _checkAuthRequestInQuery(Query calldata query) internal pure { - uint256 numAuthRequests = 0; - - for (uint256 i = 0; i < query.requestIds.length; i++) { - if (_getRequestType(query.requestIds[i]) == AUTH_REQUEST_TYPE) { - numAuthRequests++; - } - } - - require(numAuthRequests == 1, "Exactly 1 auth request is required"); - } - /** * @dev Sets a query * @param queryId The ID of the query @@ -382,14 +364,11 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { Query calldata query ) public checkQueryExistence(queryId, false) { UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); - - // check that exactly 1 auth request is included in the query - _checkAuthRequestInQuery(query); s._queries[queryId] = query; s._queryIds.push(queryId); - // check that all the requests of the groups are included in this query - _checkRequestsInGroupsIncluded(queryId); + // checks for all the requests in this query + _checkRequestsInQuery(queryId); emit QuerySet(queryId, query.requestIds); } @@ -403,11 +382,6 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { return _getUniversalVerifierMultiQueryStorage()._queries[queryId]; } - /** - * @dev Checks for exactly 1 auth response and returns the index of the auth response - * @param responses The list of responses - * @return The index of the auth response - */ function _checkAuthResponses(Response[] memory responses) internal pure returns (uint256) { uint256 numAuthResponses = 0; uint256 authResponseIndex; @@ -423,157 +397,85 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { return authResponseIndex; } - /** - * @dev Checks that all the requests of the groups are included in this query - * @param queryId The query id to check - */ - function _checkRequestsInGroupsIncluded(uint256 queryId) internal view { + function _checkRequestsInQuery(uint256 queryId) internal view { UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); uint256[] memory requestIds = s._queries[queryId].requestIds; - // check that groupId from requests exist and all the requests of the groups are used in this query - uint256[] memory groupIds = new uint256[](requestIds.length); - uint256[] memory groupLength = new uint256[](requestIds.length); - uint256[][] memory groupedRequestQuery = new uint256[][](requestIds.length); - uint256 numGroups = 0; - // build the groups based on the requests of the query + // check that all the single requests doesn't have group for (uint256 i = 0; i < requestIds.length; i++) { - if (_getRequestType(s._queries[queryId].requestIds[i]) == AUTH_REQUEST_TYPE) { - continue; - } - uint256 groupId = s._requests[requestIds[i]].validator.getGroupID( - s._requests[requestIds[i]].params - ); - - if (groupId > 0) { - uint256 iGroup; - bool found = false; - // check if groupId already exists in the local groupIds variable - for (uint256 j = 0; j < numGroups; j++) { - if (groupIds[j] == groupId) { - iGroup = j; - found = true; - break; - } - } - - // If not found we added to the end of the groupIds and groupedRequestQuery - if (numGroups == 0 || found == false) { - groupIds[numGroups] = groupId; - groupedRequestQuery[numGroups] = new uint256[](requestIds.length); - groupedRequestQuery[numGroups][0] = requestIds[i]; - groupLength[numGroups] = 1; - numGroups++; - } else { - // add request id to the end of the group request query - groupedRequestQuery[iGroup][groupLength[iGroup]] = requestIds[i]; - groupLength[iGroup]++; - } - } - } - - // check that all the requests of the group are used in this query - for (uint256 i = 0; i < numGroups; i++) { - // Check that all the requests of the group are used in this query require( - _allRequestsIncluded( - s._groupedRequests[groupIds[i]], - groupedRequestQuery[i], - groupLength[i] - ), - "The requests of the group in this query doesn't match the existing group" + s._requests[requestIds[i]].validator.getGroupID( + s._requests[requestIds[i]].params + ) == 0, + "A single request in this query is a grouped request" ); } } - /** - * @dev Checks if all requests are included - * @param reqIds1 The list of request IDs from storage for a specific group - * @param reqIds2 The list of request IDs from the query for a specific group - * @param numRequests2 The number of requests in the query for a specific group - * @return Whether all requests in reqIds1 are included in reqIds2 - */ - function _allRequestsIncluded( - uint256[] storage reqIds1, - uint256[] memory reqIds2, - uint256 numRequests2 - ) internal view returns (bool) { - if (reqIds1.length != numRequests2) { - return false; - } - - for (uint256 i = 0; i < reqIds1.length; i++) { - bool found = false; - for (uint256 j = 0; j < numRequests2; j++) { - if (reqIds1[i] == reqIds2[j]) { - found = true; - break; - } - } - if (!found) { - return false; - } - } - - return true; - } - /** * @dev Submits an array of responses and updates proofs status - * @param responses The list of responses including request ID, proof and metadata + * @param authResponses The list of auth responses including request ID, proof and metadata for auth requests + * @param singleResponses The list of responses including request ID, proof and metadata for single requests + * @param groupedResponses The list of responses including request ID, proof and metadata for grouped requests * @param crossChainProofs The list of cross chain proofs from universal resolver (oracle). This * includes identities and global states. */ - function submitResponse(Response[] memory responses, bytes memory crossChainProofs) public { + function submitResponse( + Response[] memory authResponses, + Response[] memory singleResponses, + GroupedResponses[] memory groupedResponses, + bytes memory crossChainProofs + ) public { UniversalVerifierMultiQueryStorage storage $ = _getUniversalVerifierMultiQueryStorage(); address sender = _msgSender(); - // 1. Check for auth responses (throw if not provided exactly 1) - // and return auth response index in the responses array - // in order to get "userID" from the output response field then - uint256 authResponseIndex = _checkAuthResponses(responses); - - // 2. Process crossChainProofs + // 1. Process crossChainProofs $._state.processCrossChainProofs(crossChainProofs); - // Verify for the auth request and get the userID - Response memory authResponse = responses[authResponseIndex]; - Request memory authRequest = getRequest(authResponse.requestId); - IRequestValidator.ResponseField[] memory authSignals = authRequest.validator.verify( - authResponse.proof, - authRequest.params, - sender, - $._state - ); + // 2. Process auth responses first - uint256 userIDFromAuth; - for (uint256 i = 0; i < authSignals.length; i++) { - if (keccak256(bytes(authSignals[i].name)) == keccak256(bytes("userID"))) { - userIDFromAuth = authSignals[i].value; - break; + for (uint256 i = 0; i < authResponses.length; i++) { + uint256 userIDFromReponse; + if (_getRequestType(authResponses[i].requestId) != AUTH_REQUEST_TYPE) { + revert("Request ID is not an auth request"); } - } + RequestData memory authRequest = $._requests[authResponses[i].requestId]; + IRequestValidator.ResponseField[] memory authSignals = authRequest.validator.verify( + authResponses[i].proof, + authRequest.params, + sender, + $._state + ); - writeProofResults(authResponse.requestId, userIDFromAuth, authSignals); + for (uint256 j = 0; j < authSignals.length; j++) { + if (keccak256(bytes(authSignals[j].name)) == keccak256(bytes("userID"))) { + userIDFromReponse = authSignals[j].value; + break; + } + } - $._user_address_to_id[sender] = userIDFromAuth; - $._id_to_user_address[userIDFromAuth] = sender; - $._user_id_and_address_auth[userIDFromAuth][sender] = true; + writeProofResults(authResponses[i].requestId, userIDFromReponse, authSignals); - // 3. Verify regular responses, write proof results (under the userID key from the step 1), - // emit events (existing logic) - for (uint256 i = 0; i < responses.length; i++) { - // emit for all the responses - emit ResponseSubmitted(responses[i].requestId, _msgSender()); + emit ResponseSubmitted(authResponses[i].requestId, _msgSender()); - // avoid to process auth request again - if (_getRequestType(responses[i].requestId) == AUTH_REQUEST_TYPE) { - continue; - } + $._user_address_to_id[sender] = userIDFromReponse; + $._id_to_user_address[userIDFromReponse] = sender; + $._user_id_and_address_auth[userIDFromReponse][sender] = true; + } + + // 3. Get userID from latest auth response processed in this submitResponse or before + uint256 userID = $._user_address_to_id[sender]; - Response memory response = responses[i]; - Request memory request = getRequest(response.requestId); + if (userID == 0) { + revert("The user is not authenticated"); + } + + // 4. Verify all the single responses, write proof results (under the userID key from the auth of the user), + // emit events (existing logic) + for (uint256 i = 0; i < singleResponses.length; i++) { + Response memory response = singleResponses[i]; + RequestData memory request = $._requests[response.requestId]; IRequestValidator.ResponseField[] memory signals = request.validator.verify( response.proof, @@ -582,11 +484,37 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { $._state ); - writeProofResults(response.requestId, userIDFromAuth, signals); + writeProofResults(response.requestId, userID, signals); if (response.metadata.length > 0) { revert("Metadata not supported yet"); } + // emit for all the responses + emit ResponseSubmitted(response.requestId, _msgSender()); + } + + // 5. Verify all the grouped responses, write proof results (under the userID key from the auth of the user), + // emit events (existing logic) + for (uint256 i = 0; i < groupedResponses.length; i++) { + for (uint256 j = 0; j < groupedResponses[i].responses.length; j++) { + Response memory response = groupedResponses[i].responses[j]; + RequestData memory request = $._requests[response.requestId]; + + IRequestValidator.ResponseField[] memory signals = request.validator.verify( + response.proof, + request.params, + sender, + $._state + ); + + writeProofResults(response.requestId, userID, signals); + + if (response.metadata.length > 0) { + revert("Metadata not supported yet"); + } + + emit ResponseSubmitted(response.requestId, _msgSender()); + } } } @@ -616,24 +544,27 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { /** * @dev Adds an auth request - * @param requestId The Id of the auth request to add + * @param request The data of the auth request to add */ function setAuthRequest( - uint256 requestId, Request calldata request - ) public checkRequestExistence(requestId, false) onlyOwner { - if (_getRequestType(requestId) != AUTH_REQUEST_TYPE) { + ) public checkRequestExistence(request.requestId, false) onlyOwner { + if (_getRequestType(request.requestId) != AUTH_REQUEST_TYPE) { revert("Request ID is not an auth request"); } UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); - s._requests[requestId] = request; - s._requestIds.push(requestId); + s._requests[request.requestId] = RequestData({ + metadata: request.metadata, + validator: request.validator, + params: request.params + }); + s._requestIds.push(request.requestId); - s._authRequestIds.push(requestId); + s._authRequestIds.push(request.requestId); emit AuthRequestSet( - requestId, + request.requestId, _msgSender(), request.metadata, address(request.validator), @@ -648,7 +579,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { */ function getAuthRequest( uint256 requestId - ) public view checkRequestExistence(requestId, true) returns (Request memory authRequest) { + ) public view checkRequestExistence(requestId, true) returns (RequestData memory authRequest) { return _getUniversalVerifierMultiQueryStorage()._requests[requestId]; } @@ -683,53 +614,28 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { return s._proofs[requestId][userID].storageFields[responseFieldName]; } - /** - * @dev Checks if all linked response fields are the same - * @param queryId The ID of the query - * @param userID The ID of the user - */ function _checkLinkedResponseFields(uint256 queryId, uint256 userID) internal view { UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); - uint256[] memory groupIndex = new uint256[](s._queries[queryId].requestIds.length); - uint256[] memory groupLinkID = new uint256[](s._queries[queryId].requestIds.length); - uint256 numGroups = 0; - - for (uint256 i = 0; i < s._queries[queryId].requestIds.length; i++) { - Request memory request = getRequest(s._queries[queryId].requestIds[i]); - - if (_getRequestType(s._queries[queryId].requestIds[i]) == AUTH_REQUEST_TYPE) { - continue; - } - - uint256 requestGroupId = request.validator.getGroupID(request.params); - - if (requestGroupId == 0) { - continue; - } + for (uint256 i = 0; i < s._queries[queryId].groupIds.length; i++) { + uint256 groupId = s._queries[queryId].groupIds[i]; + // Check linkID in the same group or requests is the same uint256 requestLinkID = getResponseFieldValue( - s._queries[queryId].requestIds[i], + s._groupedRequests[groupId][0], userID, "linkID" ); - - bool found = false; - for (uint256 j = 0; j < numGroups; j++) { - if (groupIndex[j] == requestGroupId) { - require( - groupLinkID[j] == requestLinkID, - "linkID is not the same for each of the requests of the group" - ); - found = true; - } - } - // If not found we added to the end of the groupIndex and groupLinkID to check - // linkID with the same groupID later - if (!found) { - groupIndex[numGroups] = requestGroupId; - groupLinkID[numGroups] = requestLinkID; - numGroups++; + for (uint256 j = 1; j < s._groupedRequests[groupId].length; j++) { + uint256 requestLinkIDToCompare = getResponseFieldValue( + s._groupedRequests[groupId][j], + userID, + "linkID" + ); + require( + requestLinkIDToCompare == requestLinkID, + "linkID is not the same for each of the requests of the group" + ); } } } diff --git a/test/verifier/universal-verifier-multi-query.test.ts b/test/verifier/universal-verifier-multi-query.test.ts index b3f07464..c3308414 100644 --- a/test/verifier/universal-verifier-multi-query.test.ts +++ b/test/verifier/universal-verifier-multi-query.test.ts @@ -9,6 +9,7 @@ import { buildCrossChainProofs, packCrossChainProofs, packZKProof } from "../uti import { CircuitId } from "@0xpolygonid/js-sdk"; import { loadFixture } from "@nomicfoundation/hardhat-toolbox/network-helpers"; import { calculateQueryHashV3 } from "../utils/query-hash-utils"; +import { request } from "http"; describe("Universal Verifier Multi-query", function () { let verifier: any, v3Validator: any, authV2Validator: any, v3_2Validator: any; @@ -175,11 +176,13 @@ describe("Universal Verifier Multi-query", function () { const nonExistingQueryId = 5; const params = packV3ValidatorParams(requestQuery1); - await verifier.setRequest(requestId, { + const txSetRequests = await verifier.setRequest({ + requestId: requestId, metadata: "metadata", validator: await v3Validator.getAddress(), params: params, }); + await txSetRequests.wait(); const requestStored = await verifier.getRequest(requestId); // check if the request is stored correctly checking metadata and validator @@ -187,7 +190,8 @@ describe("Universal Verifier Multi-query", function () { expect(requestStored[1]).to.be.equal(await v3Validator.getAddress()); expect(requestStored[2]).to.be.equal(params); - await verifier.setAuthRequest(authRequestId, { + await verifier.setAuthRequest({ + requestId: authRequestId, metadata: "metadata", validator: await authV2Validator.getAddress(), params: params, @@ -199,10 +203,12 @@ describe("Universal Verifier Multi-query", function () { const query = { queryId, - requestIds: [requestId, authRequestId], + requestIds: [requestId], + groupIds: [], metadata: "0x", }; - await verifier.setQuery(queryId, query); + const txSetQuery = await verifier.setQuery(queryId, query); + await txSetQuery.wait(); const queryStored = await verifier.getQuery(queryId); expect(queryStored[0]).to.be.equal(queryId); expect(queryStored[1]).to.be.deep.equal(query.requestIds); @@ -223,16 +229,19 @@ describe("Universal Verifier Multi-query", function () { const tx = await verifier.submitResponse( [ { - requestId, + requestId: authRequestId, proof, metadata: metadatas, }, + ], + [ { - requestId: authRequestId, + requestId, proof, metadata: metadatas, }, ], + [], crossChainProofs, ); @@ -244,7 +253,7 @@ describe("Universal Verifier Multi-query", function () { const events = await verifier.queryFilter(filter, -1); expect(events[0].eventName).to.be.equal("ResponseSubmitted"); - expect(events[0].args.requestId).to.be.equal(requestId); + expect(events[0].args.requestId).to.be.equal(authRequestId); expect(events[0].args.caller).to.be.equal(signerAddress); await expect(verifier.getQueryStatus(nonExistingQueryId, signerAddress)).to.be.rejectedWith( @@ -268,19 +277,29 @@ describe("Universal Verifier Multi-query", function () { const paramsRequest2 = packV3ValidatorParams(requestQuery2); const paramsRequest3 = packV3ValidatorParams(requestQuery3); - const txGroupRequests = await verifier.setGroupOfRequests( - groupId, - [requestId2, requestId3], + const txSetRequests = await verifier.setRequests( + [], [ - { metadata: "metadata", validator: await v3Validator.getAddress(), params: paramsRequest2 }, { - metadata: "metadata", - validator: await v3Validator.getAddress(), - params: paramsRequest3, + groupId: groupId, + requests: [ + { + requestId: requestId2, + metadata: "metadata", + validator: await v3Validator.getAddress(), + params: paramsRequest2, + }, + { + requestId: requestId3, + metadata: "metadata", + validator: await v3Validator.getAddress(), + params: paramsRequest3, + }, + ], }, ], ); - await txGroupRequests.wait(); + await txSetRequests.wait(); const requestStored = await verifier.getRequest(requestId2); // check if the request is stored correctly checking metadata and validator @@ -288,7 +307,8 @@ describe("Universal Verifier Multi-query", function () { expect(requestStored[1]).to.be.equal(await v3Validator.getAddress()); expect(requestStored[2]).to.be.equal(paramsRequest2); - await verifier.setAuthRequest(authRequestId, { + await verifier.setAuthRequest({ + requestId: authRequestId, metadata: "metadata", validator: await authV2Validator.getAddress(), params: authParams, @@ -300,10 +320,13 @@ describe("Universal Verifier Multi-query", function () { const query = { queryId, - requestIds: [requestId2, authRequestId, requestId3], + requestIds: [], + groupIds: [groupId], metadata: "0x", }; - await verifier.setQuery(queryId, query); + const txSetQuery = await verifier.setQuery(queryId, query); + await txSetQuery.wait(); + const queryStored = await verifier.getQuery(queryId); expect(queryStored[0]).to.be.equal(queryId); expect(queryStored[1]).to.be.deep.equal(query.requestIds); @@ -323,25 +346,24 @@ describe("Universal Verifier Multi-query", function () { const tx = await verifier.submitResponse( [ - { - requestId: requestId2, - proof, - metadata: metadatas, - }, { requestId: authRequestId, proof, metadata: metadatas, }, + ], + [], + [ { - requestId: requestId3, - proof, - metadata: metadatas, + groupId: groupId, + responses: [ + { requestId: requestId2, proof, metadata: metadatas }, + { requestId: requestId3, proof, metadata: metadatas }, + ], }, ], crossChainProofs, ); - await tx.wait(); await checkStorageFields(verifier, authRequestId, authStorageFields); @@ -350,13 +372,12 @@ describe("Universal Verifier Multi-query", function () { const events = await verifier.queryFilter(filter, -1); expect(events[0].eventName).to.be.equal("ResponseSubmitted"); - expect(events[0].args.requestId).to.be.equal(requestId2); + expect(events[0].args.requestId).to.be.equal(authRequestId); expect(events[0].args.caller).to.be.equal(signerAddress); await expect(verifier.getQueryStatus(nonExistingQueryId, signerAddress)).to.be.rejectedWith( "query id doesn't exist", ); - const status = await verifier.getQueryStatus(queryId, signerAddress); expect(status).to.be.true; }); @@ -373,21 +394,32 @@ describe("Universal Verifier Multi-query", function () { const paramsRequest2 = packV3ValidatorParams(requestQuery2); const paramsRequest3 = packV3ValidatorParams(requestQuery3); - const txGroupRequests = await verifier.setGroupOfRequests( - groupId, - [requestId2, requestId3], + const txSetRequests = await verifier.setRequests( + [], [ - { metadata: "metadata", validator: await v3Validator.getAddress(), params: paramsRequest2 }, { - metadata: "metadata", - validator: await v3_2Validator.getAddress(), - params: paramsRequest3, + groupId: groupId, + requests: [ + { + requestId: requestId2, + metadata: "metadata", + validator: await v3Validator.getAddress(), + params: paramsRequest2, + }, + { + requestId: requestId3, + metadata: "metadata", + validator: await v3_2Validator.getAddress(), + params: paramsRequest3, + }, + ], }, ], ); - await txGroupRequests.wait(); + await txSetRequests.wait(); - await verifier.setAuthRequest(authRequestId, { + await verifier.setAuthRequest({ + requestId: authRequestId, metadata: "metadata", validator: await authV2Validator.getAddress(), params: authParams, @@ -395,10 +427,12 @@ describe("Universal Verifier Multi-query", function () { const query = { queryId, - requestIds: [requestId2, authRequestId, requestId3], + requestIds: [], + groupIds: [groupId], metadata: "0x", }; - await verifier.setQuery(queryId, query); + const txSetQuery = await verifier.setQuery(queryId, query); + await txSetQuery.wait(); const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); @@ -412,28 +446,26 @@ describe("Universal Verifier Multi-query", function () { ); const metadatas = "0x"; - const tx = await verifier.submitResponse( [ - { - requestId: requestId2, - proof, - metadata: metadatas, - }, { requestId: authRequestId, proof, metadata: metadatas, }, + ], + [], + [ { - requestId: requestId3, - proof, - metadata: metadatas, + groupId: groupId, + responses: [ + { requestId: requestId2, proof, metadata: metadatas }, + { requestId: requestId3, proof, metadata: metadatas }, + ], }, ], crossChainProofs, ); - await tx.wait(); await expect(verifier.getQueryStatus(queryId, signerAddress)).to.be.rejectedWith( From d35e4b4a50dfd96bbb310272ba537f893545e0ec Mon Sep 17 00:00:00 2001 From: daveroga Date: Fri, 6 Dec 2024 10:20:28 +0100 Subject: [PATCH 28/69] fix cyclomatic-complexity --- contracts/verifiers/UniversalVerifierMultiQuery.sol | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/contracts/verifiers/UniversalVerifierMultiQuery.sol b/contracts/verifiers/UniversalVerifierMultiQuery.sol index b214d7f2..9126e043 100644 --- a/contracts/verifiers/UniversalVerifierMultiQuery.sol +++ b/contracts/verifiers/UniversalVerifierMultiQuery.sol @@ -495,6 +495,16 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { // 5. Verify all the grouped responses, write proof results (under the userID key from the auth of the user), // emit events (existing logic) + _writeGroupedResponses(groupedResponses, userID, sender); + } + + function _writeGroupedResponses( + GroupedResponses[] memory groupedResponses, + uint256 userID, + address sender + ) internal { + UniversalVerifierMultiQueryStorage storage $ = _getUniversalVerifierMultiQueryStorage(); + for (uint256 i = 0; i < groupedResponses.length; i++) { for (uint256 j = 0; j < groupedResponses[i].responses.length; j++) { Response memory response = groupedResponses[i].responses[j]; From 3811b9cbe373b593a58908f7b79b1756365bfe7b Mon Sep 17 00:00:00 2001 From: daveroga Date: Mon, 9 Dec 2024 09:56:28 +0100 Subject: [PATCH 29/69] some fixes from review --- .../verifiers/UniversalVerifierMultiQuery.sol | 114 +++++++----------- .../universal-verifier-multi-query.test.ts | 53 ++++---- 2 files changed, 72 insertions(+), 95 deletions(-) diff --git a/contracts/verifiers/UniversalVerifierMultiQuery.sol b/contracts/verifiers/UniversalVerifierMultiQuery.sol index 9126e043..ac413798 100644 --- a/contracts/verifiers/UniversalVerifierMultiQuery.sol +++ b/contracts/verifiers/UniversalVerifierMultiQuery.sol @@ -97,32 +97,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { mapping(uint256 requestId => RequestData) _requests; uint256[] _requestIds; mapping(uint256 groupId => uint256[] requestIds) _groupedRequests; - // 1. Set Req 1 (groupID = 1) - // Check that groupID doesn't exist yet - // 2. Set Req 2 (groupID = 1) - // Check that groupID doesn't exist yet - // 3. Set Req 200 (groupID = 1) - // Check that groupID doesn't exist yet - - // 4. Set Req 3 (groupID = 2) - // 5. Set Req 201 (groupID = 2) - - // 6. setQuery[1,2] - // Check that groupID doesn't exist yet - // 1 => [1, 2] - // 2 => [3, 201] - - // 7. submitResponse: requests 1, 2, 200 - // 7.1 Check that all the group are full - // 7.2 Check LinkID is the same for each of the groups - - // 8. getQueryStatus: it result to FALSE cuz requests 3 and 201 are false - // 9. submitResponse: requests 3,201 - // 10. getQueryStatus: it result to TRUE as all requests are true - - // Query1 => (1, 2, 200), (3, 201) - // Query2 => (1, 2, 200), 10 - + uint256[] _groupIds; IState _state; // Information about queries mapping(uint256 queryId => Query) _queries; @@ -130,7 +105,6 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { // Information linked between users and their addresses mapping(address userAddress => uint256 userID) _user_address_to_id; mapping(uint256 userID => address userAddress) _id_to_user_address; - mapping(uint256 userID => mapping(address userAddress => bool hasAuth)) _user_id_and_address_auth; uint256[] _authRequestIds; // reuses the same _requests mapping to store the auth requests // Whitelisted validators mapping(IRequestValidator => bool isApproved) _validatorWhitelist; @@ -204,7 +178,9 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { /** * @dev Modifier to check if the request exists */ - modifier checkRequestGroupExistence(uint256 groupId, bool existence) { + modifier checkRequestGroupExistence(Request memory request, bool existence) { + uint256 groupId = request.validator.getGroupID(request.params); + if (existence) { require(groupIdExists(groupId), "group id doesn't exist"); } else { @@ -264,7 +240,9 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { * @return Whether the group ID exists */ function groupIdExists(uint256 groupId) public view returns (bool) { - return _getUniversalVerifierMultiQueryStorage()._groupedRequests[groupId].length > 0; + return + _getUniversalVerifierMultiQueryStorage()._groupIds.length > 0 && + _getUniversalVerifierMultiQueryStorage()._groupIds[groupId] != 0; } /** @@ -290,12 +268,12 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { * @dev Sets a request * @param request The request data */ - function setRequest( + function _setRequestWithChecks( Request calldata request ) - public + internal checkRequestExistence(request.requestId, false) - checkRequestGroupExistence(request.validator.getGroupID(request.params), false) + checkRequestGroupExistence(request, false) onlyWhitelistedValidator(request.validator) { _setRequest(request); @@ -331,11 +309,16 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); for (uint256 i = 0; i < singleRequests.length; i++) { - setRequest(singleRequests[i]); // checkRequestGroupExistence is done in setRequest + _setRequestWithChecks(singleRequests[i]); } for (uint256 i = 0; i < groupedRequests.length; i++) { + if (groupIdExists(groupedRequests[i].groupId)) { + revert("Group ID already exists"); + } + s._groupIds.push(groupedRequests[i].groupId); + for (uint256 j = 0; j < groupedRequests[i].requests.length; j++) { - _setRequest(groupedRequests[i].requests[j]); // checkRequestGroupExistence is not done here + _setRequest(groupedRequests[i].requests[j]); s._groupedRequests[groupedRequests[i].groupId].push( groupedRequests[i].requests[j].requestId ); @@ -415,14 +398,14 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { /** * @dev Submits an array of responses and updates proofs status - * @param authResponses The list of auth responses including request ID, proof and metadata for auth requests + * @param authResponse The auth response including request ID, proof and metadata for auth requests * @param singleResponses The list of responses including request ID, proof and metadata for single requests * @param groupedResponses The list of responses including request ID, proof and metadata for grouped requests * @param crossChainProofs The list of cross chain proofs from universal resolver (oracle). This * includes identities and global states. */ function submitResponse( - Response[] memory authResponses, + Response memory authResponse, Response[] memory singleResponses, GroupedResponses[] memory groupedResponses, bytes memory crossChainProofs @@ -433,35 +416,34 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { // 1. Process crossChainProofs $._state.processCrossChainProofs(crossChainProofs); - // 2. Process auth responses first - - for (uint256 i = 0; i < authResponses.length; i++) { - uint256 userIDFromReponse; - if (_getRequestType(authResponses[i].requestId) != AUTH_REQUEST_TYPE) { - revert("Request ID is not an auth request"); - } - RequestData memory authRequest = $._requests[authResponses[i].requestId]; - IRequestValidator.ResponseField[] memory authSignals = authRequest.validator.verify( - authResponses[i].proof, - authRequest.params, - sender, - $._state - ); + // 2. Process auth response first + uint256 userIDFromReponse; + if (_getRequestType(authResponse.requestId) != AUTH_REQUEST_TYPE) { + revert("Request ID is not an auth request"); + } + RequestData storage authRequest = $._requests[authResponse.requestId]; + // Authenticate user + IRequestValidator.ResponseField[] memory authSignals = authRequest.validator.verify( + authResponse.proof, + authRequest.params, + sender, + $._state + ); - for (uint256 j = 0; j < authSignals.length; j++) { - if (keccak256(bytes(authSignals[j].name)) == keccak256(bytes("userID"))) { - userIDFromReponse = authSignals[j].value; - break; - } + for (uint256 j = 0; j < authSignals.length; j++) { + if (keccak256(bytes(authSignals[j].name)) == keccak256(bytes("userID"))) { + userIDFromReponse = authSignals[j].value; + break; } + } - writeProofResults(authResponses[i].requestId, userIDFromReponse, authSignals); - - emit ResponseSubmitted(authResponses[i].requestId, _msgSender()); - + // For some reason the auth request doesn't return the userID in the response + if (userIDFromReponse != 0) { + writeProofResults(authResponse.requestId, userIDFromReponse, authSignals); + emit ResponseSubmitted(authResponse.requestId, _msgSender()); + // Link userID and user address $._user_address_to_id[sender] = userIDFromReponse; $._id_to_user_address[userIDFromReponse] = sender; - $._user_id_and_address_auth[userIDFromReponse][sender] = true; } // 3. Get userID from latest auth response processed in this submitResponse or before @@ -475,7 +457,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { // emit events (existing logic) for (uint256 i = 0; i < singleResponses.length; i++) { Response memory response = singleResponses[i]; - RequestData memory request = $._requests[response.requestId]; + RequestData storage request = $._requests[response.requestId]; IRequestValidator.ResponseField[] memory signals = request.validator.verify( response.proof, @@ -508,7 +490,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { for (uint256 i = 0; i < groupedResponses.length; i++) { for (uint256 j = 0; j < groupedResponses[i].responses.length; j++) { Response memory response = groupedResponses[i].responses[j]; - RequestData memory request = $._requests[response.requestId]; + RequestData storage request = $._requests[response.requestId]; IRequestValidator.ResponseField[] memory signals = request.validator.verify( response.proof, @@ -666,18 +648,14 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { uint256 userID = s._user_address_to_id[userAddress]; require(userID != 0, "UserID not found"); - // 2. Check if any active auth for the userId is true - bool activeAuth = s._user_id_and_address_auth[userID][userAddress]; - require(activeAuth, "No active auth for the user found"); - - // 3. Check if all requests statuses are true for the userId + // 2. Check if all requests statuses are true for the userId for (uint256 i = 0; i < s._queries[queryId].requestIds.length; i++) { uint256 requestId = s._queries[queryId].requestIds[i]; if (!s._proofs[requestId][userID].isVerified) { return false; } } - // 4. Check if all linked response fields are the same + // 3. Check if all linked response fields are the same _checkLinkedResponseFields(queryId, userID); return true; diff --git a/test/verifier/universal-verifier-multi-query.test.ts b/test/verifier/universal-verifier-multi-query.test.ts index c3308414..17fd9ff6 100644 --- a/test/verifier/universal-verifier-multi-query.test.ts +++ b/test/verifier/universal-verifier-multi-query.test.ts @@ -176,12 +176,17 @@ describe("Universal Verifier Multi-query", function () { const nonExistingQueryId = 5; const params = packV3ValidatorParams(requestQuery1); - const txSetRequests = await verifier.setRequest({ - requestId: requestId, - metadata: "metadata", - validator: await v3Validator.getAddress(), - params: params, - }); + const txSetRequests = await verifier.setRequests( + [ + { + requestId: requestId, + metadata: "metadata", + validator: await v3Validator.getAddress(), + params: params, + }, + ], + [], + ); await txSetRequests.wait(); const requestStored = await verifier.getRequest(requestId); @@ -227,13 +232,11 @@ describe("Universal Verifier Multi-query", function () { const metadatas = "0x"; const tx = await verifier.submitResponse( - [ - { - requestId: authRequestId, - proof, - metadata: metadatas, - }, - ], + { + requestId: authRequestId, + proof, + metadata: metadatas, + }, [ { requestId, @@ -345,13 +348,11 @@ describe("Universal Verifier Multi-query", function () { const metadatas = "0x"; const tx = await verifier.submitResponse( - [ - { - requestId: authRequestId, - proof, - metadata: metadatas, - }, - ], + { + requestId: authRequestId, + proof, + metadata: metadatas, + }, [], [ { @@ -447,13 +448,11 @@ describe("Universal Verifier Multi-query", function () { const metadatas = "0x"; const tx = await verifier.submitResponse( - [ - { - requestId: authRequestId, - proof, - metadata: metadatas, - }, - ], + { + requestId: authRequestId, + proof, + metadata: metadatas, + }, [], [ { From 40300671be423b7f557bf0bccdae6c9b7c3f9d35 Mon Sep 17 00:00:00 2001 From: daveroga Date: Tue, 10 Dec 2024 00:01:48 +0100 Subject: [PATCH 30/69] updates from tech spec --- contracts/interfaces/IAuthValidator.sol | 40 +++ .../verifiers/UniversalVerifierMultiQuery.sol | 298 +++++++++++++----- 2 files changed, 257 insertions(+), 81 deletions(-) create mode 100644 contracts/interfaces/IAuthValidator.sol diff --git a/contracts/interfaces/IAuthValidator.sol b/contracts/interfaces/IAuthValidator.sol new file mode 100644 index 00000000..8578b07f --- /dev/null +++ b/contracts/interfaces/IAuthValidator.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.27; + +import {IState} from "./IState.sol"; + +/** + * @dev IAuthValidator. Interface for verification of auth data. + */ +interface IAuthValidator { + /** + * @dev ResponseField. Information about response fields from verification. Used in verify function. + * @param name Name of the response field + * @param value Value of the response field + */ + struct ResponseField { + string name; + uint256 value; + } + + /** + * @dev Get version of the contract + */ + function version() external view returns (string memory); + + /** + * @dev Verify the proof with the supported method informed in the auth query data + * packed as bytes and that the proof was generated by the sender. + * @param proof Proof packed as bytes to verify. + * @param data Request query data of the credential to verify. + * @param sender Sender of the proof. + * @param state State contract to get identities and gist states to check. + * @return Array of response fields as result. + */ + function verify( + bytes calldata proof, + bytes calldata data, + address sender, + IState state + ) external returns (ResponseField[] memory); +} diff --git a/contracts/verifiers/UniversalVerifierMultiQuery.sol b/contracts/verifiers/UniversalVerifierMultiQuery.sol index ac413798..b49af0be 100644 --- a/contracts/verifiers/UniversalVerifierMultiQuery.sol +++ b/contracts/verifiers/UniversalVerifierMultiQuery.sol @@ -4,6 +4,7 @@ pragma solidity 0.8.27; import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; import {IRequestValidator} from "../interfaces/IRequestValidator.sol"; +import {IAuthValidator} from "../interfaces/IAuthValidator.sol"; import {IState} from "../interfaces/IState.sol"; contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { @@ -12,16 +13,6 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { */ string public constant VERSION = "1.0.0"; - // requestId. 32 bytes (in Big Endian): 31-0x00(not used), 30-0x01(requestType), 29..0 hash calculated Id, - // - // For requestType: - // 0x00 - regular request - // 0x01 - auth request - /** - * @dev Auth request type - */ - uint8 constant AUTH_REQUEST_TYPE = 1; - /** * @dev Request. Structure for request for storage. * @param metadata Metadata of the request. @@ -70,6 +61,35 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { bytes metadata; } + struct AuthType { + string authType; + IAuthValidator validator; + bytes params; + } + + struct AuthTypeData { + IAuthValidator validator; + bytes params; + bool isActive; + } + struct AuthResponse { + string authType; //zkp-auth-v2, zkp-auth-v3, etc. will deside later + bytes proof; + } + + struct RequestProofStatus { + uint256 requestId; + bool isVerified; + string validatorVersion; + uint256 timestamp; + } + struct AuthProofStatus { + string authType; + bool isVerified; + string validatorVersion; + uint256 timestamp; + } + struct GroupedResponses { uint256 groupId; Response[] responses; @@ -105,9 +125,12 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { // Information linked between users and their addresses mapping(address userAddress => uint256 userID) _user_address_to_id; mapping(uint256 userID => address userAddress) _id_to_user_address; - uint256[] _authRequestIds; // reuses the same _requests mapping to store the auth requests // Whitelisted validators mapping(IRequestValidator => bool isApproved) _validatorWhitelist; + // Information about auth types and validators + string[] _authTypes; + mapping(string authType => AuthTypeData) _authMethods; + mapping(string authType => mapping(uint256 userID => Proof)) _authProofs; } // solhint-disable-next-line @@ -120,6 +143,11 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { */ event ResponseSubmitted(uint256 indexed requestId, address indexed caller); + /** + * @dev Event emitted upon submitting an auth response + */ + event AuthResponseSubmitted(string indexed authType, address indexed caller); + /** * @dev Event emitted upon adding a request */ @@ -132,15 +160,9 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { ); /** - * @dev Event emitted upon adding an auth request by the owner + * @dev Event emitted upon adding an auth type by the owner */ - event AuthRequestSet( - uint256 indexed requestId, - address indexed requestOwner, - string metadata, - address validator, - bytes params - ); + event AuthSet(string indexed authType, address validator, bytes params); /** * @dev Event emitted upon updating a request @@ -201,6 +223,18 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { _; } + /** + * @dev Modifier to check if the auth type exists + */ + modifier checkAuthTypeExistence(string memory authType, bool existence) { + if (existence) { + require(authTypeExists(authType), "auth type doesn't exist"); + } else { + require(!authTypeExists(authType), "auth type already exists"); + } + _; + } + /** * @dev Modifier to check if the validator is whitelisted */ @@ -219,6 +253,16 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { _getUniversalVerifierMultiQueryStorage()._state = state; } + function _getUniversalVerifierMultiQueryStorage() + private + pure + returns (UniversalVerifierMultiQueryStorage storage $) + { + assembly { + $.slot := UniversalVerifierMultiQueryStorageLocation + } + } + function _getRequestType(uint256 requestId) internal pure returns (uint8 requestType) { return uint8(requestId >> 248); } @@ -254,14 +298,13 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { return _getUniversalVerifierMultiQueryStorage()._queries[queryId].queryId == queryId; } - function _getUniversalVerifierMultiQueryStorage() - private - pure - returns (UniversalVerifierMultiQueryStorage storage $) - { - assembly { - $.slot := UniversalVerifierMultiQueryStorageLocation - } + /** + * @dev Checks if an auth type exists + * @param authType The auth type + * @return Whether the auth type exists + */ + function authTypeExists(string memory authType) public view returns (bool) { + return _getUniversalVerifierMultiQueryStorage()._authMethods[authType].isActive == true; } /** @@ -365,21 +408,6 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { return _getUniversalVerifierMultiQueryStorage()._queries[queryId]; } - function _checkAuthResponses(Response[] memory responses) internal pure returns (uint256) { - uint256 numAuthResponses = 0; - uint256 authResponseIndex; - - for (uint256 i = 0; i < responses.length; i++) { - if (_getRequestType(responses[i].requestId) == AUTH_REQUEST_TYPE) { - authResponseIndex = i; - numAuthResponses++; - } - } - require(numAuthResponses == 1, "Exactly 1 auth response is required"); - - return authResponseIndex; - } - function _checkRequestsInQuery(uint256 queryId) internal view { UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); @@ -398,14 +426,14 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { /** * @dev Submits an array of responses and updates proofs status - * @param authResponse The auth response including request ID, proof and metadata for auth requests + * @param authResponses The list of auth responses including auth type and proof * @param singleResponses The list of responses including request ID, proof and metadata for single requests * @param groupedResponses The list of responses including request ID, proof and metadata for grouped requests * @param crossChainProofs The list of cross chain proofs from universal resolver (oracle). This * includes identities and global states. */ function submitResponse( - Response memory authResponse, + AuthResponse[] memory authResponses, Response[] memory singleResponses, GroupedResponses[] memory groupedResponses, bytes memory crossChainProofs @@ -417,15 +445,14 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { $._state.processCrossChainProofs(crossChainProofs); // 2. Process auth response first + require(authResponses.length == 1, "Only one auth response is required"); + uint256 userIDFromReponse; - if (_getRequestType(authResponse.requestId) != AUTH_REQUEST_TYPE) { - revert("Request ID is not an auth request"); - } - RequestData storage authRequest = $._requests[authResponse.requestId]; + AuthTypeData storage authTypeData = $._authMethods[authResponses[0].authType]; // Authenticate user - IRequestValidator.ResponseField[] memory authSignals = authRequest.validator.verify( - authResponse.proof, - authRequest.params, + IAuthValidator.ResponseField[] memory authSignals = authTypeData.validator.verify( + authResponses[0].proof, + authTypeData.params, sender, $._state ); @@ -439,8 +466,8 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { // For some reason the auth request doesn't return the userID in the response if (userIDFromReponse != 0) { - writeProofResults(authResponse.requestId, userIDFromReponse, authSignals); - emit ResponseSubmitted(authResponse.requestId, _msgSender()); + writeAuthProofResults(authResponses[0].authType, userIDFromReponse, authSignals); + emit AuthResponseSubmitted(authResponses[0].authType, _msgSender()); // Link userID and user address $._user_address_to_id[sender] = userIDFromReponse; $._id_to_user_address[userIDFromReponse] = sender; @@ -535,33 +562,67 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { } /** - * @dev Adds an auth request - * @param request The data of the auth request to add + * @dev Writes proof results. + * @param authType The auth type of the proof + * @param userID The userID of the proof + * @param responseFields The array of response fields of the proof */ - function setAuthRequest( - Request calldata request - ) public checkRequestExistence(request.requestId, false) onlyOwner { - if (_getRequestType(request.requestId) != AUTH_REQUEST_TYPE) { - revert("Request ID is not an auth request"); + function writeAuthProofResults( + string memory authType, + uint256 userID, + IAuthValidator.ResponseField[] memory responseFields + ) public { + UniversalVerifierMultiQueryStorage storage $ = _getUniversalVerifierMultiQueryStorage(); + + Proof storage proof = $._authProofs[authType][userID]; + for (uint256 i = 0; i < responseFields.length; i++) { + proof.storageFields[responseFields[i].name] = responseFields[i].value; } + proof.isVerified = true; + proof.validatorVersion = $._authMethods[authType].validator.version(); + proof.blockNumber = block.number; + proof.blockTimestamp = block.timestamp; + } + + /** + * @dev Sets an auth type + * @param authType The auth type to add + */ + function setAuthType( + AuthType calldata authType + ) public onlyOwner checkAuthTypeExistence(authType.authType, false) { UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); - s._requests[request.requestId] = RequestData({ - metadata: request.metadata, - validator: request.validator, - params: request.params + s._authTypes.push(authType.authType); + s._authMethods[authType.authType] = AuthTypeData({ + validator: authType.validator, + params: authType.params, + isActive: true }); - s._requestIds.push(request.requestId); - s._authRequestIds.push(request.requestId); + emit AuthSet(authType.authType, address(authType.validator), authType.params); + } - emit AuthRequestSet( - request.requestId, - _msgSender(), - request.metadata, - address(request.validator), - request.params - ); + /** + * @dev Disables an auth type + * @param authType The auth type to disable + */ + function disableAuthType( + string calldata authType + ) public onlyOwner checkAuthTypeExistence(authType, true) { + UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); + s._authMethods[authType].isActive = false; + } + + /** + * @dev Enables an auth type + * @param authType The auth type to enable + */ + function enableAuthType( + string calldata authType + ) public onlyOwner checkAuthTypeExistence(authType, true) { + UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); + s._authMethods[authType].isActive = true; } /** @@ -641,7 +702,12 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { function getQueryStatus( uint256 queryId, address userAddress - ) public view checkQueryExistence(queryId, true) returns (bool status) { + ) + public + view + checkQueryExistence(queryId, true) + returns (AuthProofStatus[] memory, RequestProofStatus[] memory) + { UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); // 1. Get the latest userId by the userAddress arg (in the mapping) @@ -649,16 +715,86 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { require(userID != 0, "UserID not found"); // 2. Check if all requests statuses are true for the userId - for (uint256 i = 0; i < s._queries[queryId].requestIds.length; i++) { - uint256 requestId = s._queries[queryId].requestIds[i]; - if (!s._proofs[requestId][userID].isVerified) { - return false; - } - } + ( + AuthProofStatus[] memory authProofStatus, + RequestProofStatus[] memory requestProofStatus + ) = _getQueryStatus(queryId, userID); + // 3. Check if all linked response fields are the same _checkLinkedResponseFields(queryId, userID); - return true; + return (authProofStatus, requestProofStatus); + } + + function _getQueryStatus( + uint256 queryId, + uint256 userID + ) internal view returns (AuthProofStatus[] memory, RequestProofStatus[] memory) { + UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); + Query storage query = s._queries[queryId]; + AuthProofStatus[] memory authProofStatus = new AuthProofStatus[](s._authTypes.length); + RequestProofStatus[] memory requestProofStatus = new RequestProofStatus[]( + query.requestIds.length + ); + + for (uint256 i = 0; i < s._authTypes.length; i++) { + string memory authType = s._authTypes[i]; + authProofStatus[i] = AuthProofStatus({ + authType: authType, + isVerified: s._authProofs[authType][userID].isVerified, + validatorVersion: s._authProofs[authType][userID].validatorVersion, + timestamp: s._authProofs[authType][userID].blockTimestamp + }); + } + + for (uint256 i = 0; i < query.requestIds.length; i++) { + uint256 requestId = query.requestIds[i]; + + requestProofStatus[i] = RequestProofStatus({ + requestId: requestId, + isVerified: s._proofs[requestId][userID].isVerified, + validatorVersion: s._proofs[requestId][userID].validatorVersion, + timestamp: s._proofs[requestId][userID].blockTimestamp + }); + } + + return (authProofStatus, requestProofStatus); + } + + function getQueryStatus( + uint256 queryId, + address userAddress, + uint256 userID + ) + public + view + checkQueryExistence(queryId, true) + returns (AuthProofStatus[] memory, RequestProofStatus[] memory) + { + UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); + + // 1. Get the latest userId by the userAddress arg (in the mapping) + uint256 userIDFromAddress = s._user_address_to_id[userAddress]; + uint256 userIDSelected; + + if (userIDFromAddress != userID) { + address addressFromUserID = s._id_to_user_address[userID]; + require(addressFromUserID == userAddress, "The userAddress and userID are not linked"); + userIDSelected = s._user_address_to_id[addressFromUserID]; + } else { + userIDSelected = userID; + } + + // 2. Check if all requests statuses are true for the userId + ( + AuthProofStatus[] memory authProofStatus, + RequestProofStatus[] memory requestProofStatus + ) = _getQueryStatus(queryId, userIDSelected); + + // 3. Check if all linked response fields are the same + _checkLinkedResponseFields(queryId, userIDSelected); + + return (authProofStatus, requestProofStatus); } /** From 6135f8404c2662194e4026c7f39fb2931f9ccbc8 Mon Sep 17 00:00:00 2001 From: daveroga Date: Tue, 10 Dec 2024 01:01:07 +0100 Subject: [PATCH 31/69] add auth validator stub and fix test --- contracts/test-helpers/AuthValidatorStub.sol | 34 ++++++ .../verifiers/UniversalVerifierMultiQuery.sol | 57 +++++++-- helpers/DeployHelper.ts | 16 +-- .../universal-verifier-multi-query.test.ts | 115 ++++++++++-------- 4 files changed, 150 insertions(+), 72 deletions(-) create mode 100644 contracts/test-helpers/AuthValidatorStub.sol diff --git a/contracts/test-helpers/AuthValidatorStub.sol b/contracts/test-helpers/AuthValidatorStub.sol new file mode 100644 index 00000000..e56f8aa8 --- /dev/null +++ b/contracts/test-helpers/AuthValidatorStub.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.27; + +import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; +import {IAuthValidator} from "../interfaces/IAuthValidator.sol"; +import {IState} from "../interfaces/IState.sol"; + +/** + * @dev AuthValidatorStub validator + */ +contract AuthValidatorStub is IAuthValidator, ERC165 { + string public constant VERSION = "1.0.0-mock"; + + function version() public pure override returns (string memory) { + return VERSION; + } + + function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + return + interfaceId == type(IAuthValidator).interfaceId || super.supportsInterface(interfaceId); + } + + function verify( + bytes calldata, + bytes calldata, + address, + IState + ) external pure override returns (IAuthValidator.ResponseField[] memory) { + IAuthValidator.ResponseField[] memory signals = new IAuthValidator.ResponseField[](1); + signals[0].name = "userID"; + signals[0].value = 1; + return signals; + } +} diff --git a/contracts/verifiers/UniversalVerifierMultiQuery.sol b/contracts/verifiers/UniversalVerifierMultiQuery.sol index b49af0be..e1e8b932 100644 --- a/contracts/verifiers/UniversalVerifierMultiQuery.sol +++ b/contracts/verifiers/UniversalVerifierMultiQuery.sol @@ -626,14 +626,14 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { } /** - * @dev Gets an auth request - * @param requestId The Id of the auth request to get - * @return authRequest The auth request data + * @dev Gets an auth type + * @param authType The Id of the auth type to get + * @return authMethod The auth type data */ - function getAuthRequest( - uint256 requestId - ) public view checkRequestExistence(requestId, true) returns (RequestData memory authRequest) { - return _getUniversalVerifierMultiQueryStorage()._requests[requestId]; + function getAuthType( + string calldata authType + ) public view checkAuthTypeExistence(authType, true) returns (AuthTypeData memory authMethod) { + return _getUniversalVerifierMultiQueryStorage()._authMethods[authType]; } /** @@ -667,6 +667,22 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { return s._proofs[requestId][userID].storageFields[responseFieldName]; } + /** + * @dev Gets response field value + * @param authType Auth type of the proof response + * @param sender Address of the sender + * @param responseFieldName Name of the response field to get + */ + function getAuthResponseFieldValueFromAddress( + string memory authType, + address sender, + string memory responseFieldName + ) public view checkAuthTypeExistence(authType, true) returns (uint256) { + UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); + uint256 userID = s._user_address_to_id[sender]; + return s._authProofs[authType][userID].storageFields[responseFieldName]; + } + function _checkLinkedResponseFields(uint256 queryId, uint256 userID) internal view { UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); @@ -732,9 +748,19 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { ) internal view returns (AuthProofStatus[] memory, RequestProofStatus[] memory) { UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); Query storage query = s._queries[queryId]; + + uint256 lengthGroupIds; + + if (query.groupIds.length > 0) { + for (uint256 i = 0; i < query.groupIds.length; i++) { + uint256 groupId = query.groupIds[i]; + lengthGroupIds += s._groupedRequests[groupId].length; + } + } + AuthProofStatus[] memory authProofStatus = new AuthProofStatus[](s._authTypes.length); RequestProofStatus[] memory requestProofStatus = new RequestProofStatus[]( - query.requestIds.length + query.requestIds.length + lengthGroupIds ); for (uint256 i = 0; i < s._authTypes.length; i++) { @@ -758,6 +784,21 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { }); } + for (uint256 i = 0; i < query.groupIds.length; i++) { + uint256 groupId = query.groupIds[i]; + + for (uint256 j = 0; j < s._groupedRequests[groupId].length; j++) { + uint256 requestId = s._groupedRequests[groupId][j]; + + requestProofStatus[query.requestIds.length + j] = RequestProofStatus({ + requestId: requestId, + isVerified: s._proofs[requestId][userID].isVerified, + validatorVersion: s._proofs[requestId][userID].validatorVersion, + timestamp: s._proofs[requestId][userID].blockTimestamp + }); + } + } + return (authProofStatus, requestProofStatus); } diff --git a/helpers/DeployHelper.ts b/helpers/DeployHelper.ts index 2466643e..1ea17d87 100644 --- a/helpers/DeployHelper.ts +++ b/helpers/DeployHelper.ts @@ -787,22 +787,12 @@ export class DeployHelper { }; } - async deployValidatorStub(): Promise { - const stub = await ethers.getContractFactory("ValidatorStub"); + async deployValidatorStub(validatorName: string = "ValidatorStub"): Promise { + const stub = await ethers.getContractFactory(validatorName); const stubInstance = await stub.deploy(); await stubInstance.waitForDeployment(); - console.log("Validator stub deployed to:", await stubInstance.getAddress()); - - return stubInstance; - } - - async deployRequestValidatorStub(requestValidatorName: string): Promise { - const stub = await ethers.getContractFactory(requestValidatorName); - const stubInstance = await stub.deploy(); - await stubInstance.waitForDeployment(); - - console.log(`${requestValidatorName} stub deployed to:`, await stubInstance.getAddress()); + console.log(`${validatorName} stub deployed to:`, await stubInstance.getAddress()); return stubInstance; } diff --git a/test/verifier/universal-verifier-multi-query.test.ts b/test/verifier/universal-verifier-multi-query.test.ts index 17fd9ff6..fe9d18de 100644 --- a/test/verifier/universal-verifier-multi-query.test.ts +++ b/test/verifier/universal-verifier-multi-query.test.ts @@ -142,13 +142,12 @@ describe("Universal Verifier Multi-query", function () { await stateCrossChainStub.getAddress(), ); - v3Validator = await deployHelper.deployRequestValidatorStub("RequestValidatorV3Stub"); - v3_2Validator = await deployHelper.deployRequestValidatorStub("RequestValidatorV3_2Stub"); - authV2Validator = await deployHelper.deployRequestValidatorStub("RequestValidatorAuthV2Stub"); + v3Validator = await deployHelper.deployValidatorStub("RequestValidatorV3Stub"); + v3_2Validator = await deployHelper.deployValidatorStub("RequestValidatorV3_2Stub"); + authV2Validator = await deployHelper.deployValidatorStub("AuthValidatorStub"); await verifier.addValidatorToWhitelist(await v3Validator.getAddress()); await verifier.addValidatorToWhitelist(await v3_2Validator.getAddress()); - await verifier.addValidatorToWhitelist(await authV2Validator.getAddress()); await verifier.connect(); } @@ -163,17 +162,26 @@ describe("Universal Verifier Multi-query", function () { } } + async function checkAuthStorageFields(verifier: any, authType: string, storageFields: any[]) { + for (const field of storageFields) { + const value = await verifier.getAuthResponseFieldValueFromAddress( + authType, + await signer.getAddress(), + field.name, + ); + expect(value).to.be.equal(field.value); + } + } + beforeEach(async () => { await loadFixture(deployContractsFixture); }); it("Test submit response multiquery without groupID", async () => { const requestId = 1; - // authRequestId: 0x0100000000000000000000000000000000000000000000000000000000000001 - const authRequestId = - 452312848583266388373324160190187140051835877600158453279131187530910662657n; const queryId = 1; const nonExistingQueryId = 5; + const authType = "authV2"; const params = packV3ValidatorParams(requestQuery1); const txSetRequests = await verifier.setRequests( @@ -195,16 +203,15 @@ describe("Universal Verifier Multi-query", function () { expect(requestStored[1]).to.be.equal(await v3Validator.getAddress()); expect(requestStored[2]).to.be.equal(params); - await verifier.setAuthRequest({ - requestId: authRequestId, - metadata: "metadata", + await verifier.setAuthType({ + authType: authType, validator: await authV2Validator.getAddress(), params: params, }); - const authRequestStored = await verifier.getAuthRequest(authRequestId); - expect(authRequestStored[0]).to.be.equal("metadata"); - expect(authRequestStored[1]).to.be.equal(await authV2Validator.getAddress()); - expect(authRequestStored[2]).to.be.equal(params); + const authRequestStored = await verifier.getAuthType(authType); + expect(authRequestStored[0]).to.be.equal(await authV2Validator.getAddress()); + expect(authRequestStored[1]).to.be.equal(params); + expect(authRequestStored[2]).to.be.equal(true); const query = { queryId, @@ -232,11 +239,12 @@ describe("Universal Verifier Multi-query", function () { const metadatas = "0x"; const tx = await verifier.submitResponse( - { - requestId: authRequestId, - proof, - metadata: metadatas, - }, + [ + { + authType: authType, + proof, + }, + ], [ { requestId, @@ -250,13 +258,13 @@ describe("Universal Verifier Multi-query", function () { await tx.wait(); - await checkStorageFields(verifier, authRequestId, authStorageFields); + await checkAuthStorageFields(verifier, authType, authStorageFields); await checkStorageFields(verifier, BigInt(requestId), storageFields); const filter = verifier.filters.ResponseSubmitted; const events = await verifier.queryFilter(filter, -1); expect(events[0].eventName).to.be.equal("ResponseSubmitted"); - expect(events[0].args.requestId).to.be.equal(authRequestId); + expect(events[0].args.requestId).to.be.equal(requestId); expect(events[0].args.caller).to.be.equal(signerAddress); await expect(verifier.getQueryStatus(nonExistingQueryId, signerAddress)).to.be.rejectedWith( @@ -264,16 +272,17 @@ describe("Universal Verifier Multi-query", function () { ); const status = await verifier.getQueryStatus(queryId, signerAddress); - expect(status).to.be.true; + expect(status[0][0][0]).to.be.equal(authType); + expect(status[0][0][1]).to.be.equal(true); // auth type isVerified + expect(status[1][0][0]).to.be.equal(requestId); + expect(status[1][0][1]).to.be.equal(true); // request isVerified }); it("Test submit response multiquery with same groupID and linkID", async () => { const requestId2 = 2; const requestId3 = 3; const groupId = 1; - // authRequestId: 0x0100000000000000000000000000000000000000000000000000000000000001 - const authRequestId = - 452312848583266388373324160190187140051835877600158453279131187530910662657n; + const authType = "authV2"; const queryId = 1; const nonExistingQueryId = 5; const authParams = "0x"; @@ -310,16 +319,15 @@ describe("Universal Verifier Multi-query", function () { expect(requestStored[1]).to.be.equal(await v3Validator.getAddress()); expect(requestStored[2]).to.be.equal(paramsRequest2); - await verifier.setAuthRequest({ - requestId: authRequestId, - metadata: "metadata", + await verifier.setAuthType({ + authType: authType, validator: await authV2Validator.getAddress(), params: authParams, }); - const authRequestStored = await verifier.getAuthRequest(authRequestId); - expect(authRequestStored[0]).to.be.equal("metadata"); - expect(authRequestStored[1]).to.be.equal(await authV2Validator.getAddress()); - expect(authRequestStored[2]).to.be.equal(authParams); + const authRequestStored = await verifier.getAuthType(authType); + expect(authRequestStored[0]).to.be.equal(await authV2Validator.getAddress()); + expect(authRequestStored[1]).to.be.equal(authParams); + expect(authRequestStored[2]).to.be.equal(true); const query = { queryId, @@ -348,11 +356,12 @@ describe("Universal Verifier Multi-query", function () { const metadatas = "0x"; const tx = await verifier.submitResponse( - { - requestId: authRequestId, - proof, - metadata: metadatas, - }, + [ + { + authType: authType, + proof, + }, + ], [], [ { @@ -367,29 +376,33 @@ describe("Universal Verifier Multi-query", function () { ); await tx.wait(); - await checkStorageFields(verifier, authRequestId, authStorageFields); + await checkAuthStorageFields(verifier, authType, authStorageFields); await checkStorageFields(verifier, BigInt(requestId2), storageFields); + await checkStorageFields(verifier, BigInt(requestId3), storageFields); const filter = verifier.filters.ResponseSubmitted; const events = await verifier.queryFilter(filter, -1); expect(events[0].eventName).to.be.equal("ResponseSubmitted"); - expect(events[0].args.requestId).to.be.equal(authRequestId); + expect(events[0].args.requestId).to.be.equal(requestId2); expect(events[0].args.caller).to.be.equal(signerAddress); await expect(verifier.getQueryStatus(nonExistingQueryId, signerAddress)).to.be.rejectedWith( "query id doesn't exist", ); const status = await verifier.getQueryStatus(queryId, signerAddress); - expect(status).to.be.true; + expect(status[0][0][0]).to.be.equal(authType); + expect(status[0][0][1]).to.be.equal(true); // auth type isVerified + expect(status[1][0][0]).to.be.equal(requestId2); + expect(status[1][0][1]).to.be.equal(true); // requestId2 isVerified + expect(status[1][1][0]).to.be.equal(requestId3); + expect(status[1][1][1]).to.be.equal(true); // requestId3 isVerified }); it("Test submit response multiquery with same groupID and different linkID", async () => { const requestId2 = 2; const requestId3 = 3; const groupId = 1; - // authRequestId: 0x0100000000000000000000000000000000000000000000000000000000000001 - const authRequestId = - 452312848583266388373324160190187140051835877600158453279131187530910662657n; + const authType = "authV2"; const queryId = 1; const authParams = "0x"; const paramsRequest2 = packV3ValidatorParams(requestQuery2); @@ -419,9 +432,8 @@ describe("Universal Verifier Multi-query", function () { ); await txSetRequests.wait(); - await verifier.setAuthRequest({ - requestId: authRequestId, - metadata: "metadata", + await verifier.setAuthType({ + authType: authType, validator: await authV2Validator.getAddress(), params: authParams, }); @@ -448,11 +460,12 @@ describe("Universal Verifier Multi-query", function () { const metadatas = "0x"; const tx = await verifier.submitResponse( - { - requestId: authRequestId, - proof, - metadata: metadatas, - }, + [ + { + authType: authType, + proof, + }, + ], [], [ { From b860ab06a14d6784f7bf9a4d0ca5452bbe39cf51 Mon Sep 17 00:00:00 2001 From: daveroga Date: Tue, 10 Dec 2024 10:01:40 +0100 Subject: [PATCH 32/69] add changes for user authentication structs --- .../verifiers/UniversalVerifierMultiQuery.sol | 131 +++++++++++++----- .../universal-verifier-multi-query.test.ts | 15 ++ 2 files changed, 108 insertions(+), 38 deletions(-) diff --git a/contracts/verifiers/UniversalVerifierMultiQuery.sol b/contracts/verifiers/UniversalVerifierMultiQuery.sol index e1e8b932..3acb38d7 100644 --- a/contracts/verifiers/UniversalVerifierMultiQuery.sol +++ b/contracts/verifiers/UniversalVerifierMultiQuery.sol @@ -95,6 +95,16 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { Response[] responses; } + struct UserAddressToIdInfo { + uint256 userID; + uint256 timestamp; + } + + struct UserIdToAddressInfo { + address userAddress; + uint256 timestamp; + } + /** * @dev Query. Structure for query. * @param queryId Query id. @@ -123,8 +133,8 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { mapping(uint256 queryId => Query) _queries; uint256[] _queryIds; // Information linked between users and their addresses - mapping(address userAddress => uint256 userID) _user_address_to_id; - mapping(uint256 userID => address userAddress) _id_to_user_address; + mapping(address userAddress => UserAddressToIdInfo) _user_address_to_id; + mapping(uint256 userID => UserIdToAddressInfo) _id_to_user_address; // Whitelisted validators mapping(IRequestValidator => bool isApproved) _validatorWhitelist; // Information about auth types and validators @@ -469,12 +479,18 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { writeAuthProofResults(authResponses[0].authType, userIDFromReponse, authSignals); emit AuthResponseSubmitted(authResponses[0].authType, _msgSender()); // Link userID and user address - $._user_address_to_id[sender] = userIDFromReponse; - $._id_to_user_address[userIDFromReponse] = sender; + $._user_address_to_id[sender] = UserAddressToIdInfo({ + userID: userIDFromReponse, + timestamp: block.timestamp + }); + $._id_to_user_address[userIDFromReponse] = UserIdToAddressInfo({ + userAddress: sender, + timestamp: block.timestamp + }); } // 3. Get userID from latest auth response processed in this submitResponse or before - uint256 userID = $._user_address_to_id[sender]; + uint256 userID = $._user_address_to_id[sender].userID; if (userID == 0) { revert("The user is not authenticated"); @@ -663,7 +679,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { string memory responseFieldName ) public view checkRequestExistence(requestId, true) returns (uint256) { UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); - uint256 userID = s._user_address_to_id[sender]; + uint256 userID = s._user_address_to_id[sender].userID; return s._proofs[requestId][userID].storageFields[responseFieldName]; } @@ -679,7 +695,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { string memory responseFieldName ) public view checkAuthTypeExistence(authType, true) returns (uint256) { UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); - uint256 userID = s._user_address_to_id[sender]; + uint256 userID = s._user_address_to_id[sender].userID; return s._authProofs[authType][userID].storageFields[responseFieldName]; } @@ -727,7 +743,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); // 1. Get the latest userId by the userAddress arg (in the mapping) - uint256 userID = s._user_address_to_id[userAddress]; + uint256 userID = s._user_address_to_id[userAddress].userID; require(userID != 0, "UserID not found"); // 2. Check if all requests statuses are true for the userId @@ -742,6 +758,49 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { return (authProofStatus, requestProofStatus); } + /** + * @dev Gets the status of the query verification + * @param queryId The ID of the query + * @param userAddress The address of the user + * @param userID The user id of the user + * @return status The status of the query. "True" if all requests are verified, "false" otherwise + */ + function getQueryStatus( + uint256 queryId, + address userAddress, + uint256 userID + ) + public + view + checkQueryExistence(queryId, true) + returns (AuthProofStatus[] memory, RequestProofStatus[] memory) + { + UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); + + // 1. Get the latest userId by the userAddress arg (in the mapping) + uint256 userIDFromAddress = s._user_address_to_id[userAddress].userID; + uint256 userIDSelected; + + if (userIDFromAddress != userID) { + address addressFromUserID = s._id_to_user_address[userID].userAddress; + require(addressFromUserID == userAddress, "The userAddress and userID are not linked"); + userIDSelected = s._user_address_to_id[addressFromUserID].userID; + } else { + userIDSelected = userID; + } + + // 2. Check if all requests statuses are true for the userId + ( + AuthProofStatus[] memory authProofStatus, + RequestProofStatus[] memory requestProofStatus + ) = _getQueryStatus(queryId, userIDSelected); + + // 3. Check if all linked response fields are the same + _checkLinkedResponseFields(queryId, userIDSelected); + + return (authProofStatus, requestProofStatus); + } + function _getQueryStatus( uint256 queryId, uint256 userID @@ -802,40 +861,36 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { return (authProofStatus, requestProofStatus); } - function getQueryStatus( - uint256 queryId, - address userAddress, - uint256 userID - ) - public - view - checkQueryExistence(queryId, true) - returns (AuthProofStatus[] memory, RequestProofStatus[] memory) - { + /** + * @dev Checks if a user is authenticated + * @param userId The ID of the user + * @param userAddress The address of the user + * @return Whether the user is authenticated + */ + function isUserAuth(uint256 userId, address userAddress) public view returns (bool) { UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); + return + s._user_address_to_id[userAddress].userID == userId || + s._id_to_user_address[userId].userAddress == userAddress; + } - // 1. Get the latest userId by the userAddress arg (in the mapping) - uint256 userIDFromAddress = s._user_address_to_id[userAddress]; - uint256 userIDSelected; - - if (userIDFromAddress != userID) { - address addressFromUserID = s._id_to_user_address[userID]; - require(addressFromUserID == userAddress, "The userAddress and userID are not linked"); - userIDSelected = s._user_address_to_id[addressFromUserID]; + /** + * @dev Gets the timestamp of the authentication of a user + * @param userId The user id of the user + * @param userAddress The address of the user + * @return The user ID + */ + function userAuthTimestamp(uint256 userId, address userAddress) public view returns (uint256) { + if (isUserAuth(userId, userAddress)) { + UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); + if (s._user_address_to_id[userAddress].userID == userId) { + return s._user_address_to_id[userAddress].timestamp; + } + + return s._id_to_user_address[userId].timestamp; } else { - userIDSelected = userID; + return 0; } - - // 2. Check if all requests statuses are true for the userId - ( - AuthProofStatus[] memory authProofStatus, - RequestProofStatus[] memory requestProofStatus - ) = _getQueryStatus(queryId, userIDSelected); - - // 3. Check if all linked response fields are the same - _checkLinkedResponseFields(queryId, userIDSelected); - - return (authProofStatus, requestProofStatus); } /** diff --git a/test/verifier/universal-verifier-multi-query.test.ts b/test/verifier/universal-verifier-multi-query.test.ts index fe9d18de..c66b652b 100644 --- a/test/verifier/universal-verifier-multi-query.test.ts +++ b/test/verifier/universal-verifier-multi-query.test.ts @@ -181,6 +181,8 @@ describe("Universal Verifier Multi-query", function () { const requestId = 1; const queryId = 1; const nonExistingQueryId = 5; + const userId = 1; + const userId2 = 2; const authType = "authV2"; const params = packV3ValidatorParams(requestQuery1); @@ -260,6 +262,14 @@ describe("Universal Verifier Multi-query", function () { await checkAuthStorageFields(verifier, authType, authStorageFields); await checkStorageFields(verifier, BigInt(requestId), storageFields); + + const isUserAuth = await verifier.isUserAuth(userId, await signer.getAddress()); + expect(isUserAuth).to.be.equal(true); + + const isUserAuth2 = await verifier.isUserAuth(userId2, await signer.getAddress()); + expect(isUserAuth2).to.be.equal(false); + + const filter = verifier.filters.ResponseSubmitted; const events = await verifier.queryFilter(filter, -1); @@ -285,6 +295,7 @@ describe("Universal Verifier Multi-query", function () { const authType = "authV2"; const queryId = 1; const nonExistingQueryId = 5; + const userId = 1; const authParams = "0x"; const paramsRequest2 = packV3ValidatorParams(requestQuery2); const paramsRequest3 = packV3ValidatorParams(requestQuery3); @@ -379,6 +390,10 @@ describe("Universal Verifier Multi-query", function () { await checkAuthStorageFields(verifier, authType, authStorageFields); await checkStorageFields(verifier, BigInt(requestId2), storageFields); await checkStorageFields(verifier, BigInt(requestId3), storageFields); + + const isUserAuth = await verifier.isUserAuth(userId, await signer.getAddress()); + expect(isUserAuth).to.be.equal(true); + const filter = verifier.filters.ResponseSubmitted; const events = await verifier.queryFilter(filter, -1); From fc7bc910d4ab5272b5e3e497f9238b17a6527bb5 Mon Sep 17 00:00:00 2001 From: daveroga Date: Tue, 10 Dec 2024 10:02:11 +0100 Subject: [PATCH 33/69] fix solhint --- contracts/verifiers/UniversalVerifierMultiQuery.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/verifiers/UniversalVerifierMultiQuery.sol b/contracts/verifiers/UniversalVerifierMultiQuery.sol index 3acb38d7..56599bc2 100644 --- a/contracts/verifiers/UniversalVerifierMultiQuery.sol +++ b/contracts/verifiers/UniversalVerifierMultiQuery.sol @@ -885,9 +885,9 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); if (s._user_address_to_id[userAddress].userID == userId) { return s._user_address_to_id[userAddress].timestamp; - } - - return s._id_to_user_address[userId].timestamp; + } + + return s._id_to_user_address[userId].timestamp; } else { return 0; } From c3bb7e300bdf1436d7c1e31e84fd76088b5ac526 Mon Sep 17 00:00:00 2001 From: daveroga Date: Wed, 11 Dec 2024 08:40:08 +0100 Subject: [PATCH 34/69] reestructure link between userID and address and mapping for authentication timestamp --- .../verifiers/UniversalVerifierMultiQuery.sol | 80 +++++++++---------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/contracts/verifiers/UniversalVerifierMultiQuery.sol b/contracts/verifiers/UniversalVerifierMultiQuery.sol index 56599bc2..3be32022 100644 --- a/contracts/verifiers/UniversalVerifierMultiQuery.sol +++ b/contracts/verifiers/UniversalVerifierMultiQuery.sol @@ -44,11 +44,24 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { bool isVerified; mapping(string key => uint256 inputValue) storageFields; string validatorVersion; - uint256 blockNumber; + // TODO: discuss if we need this field + // uint256 blockNumber; uint256 blockTimestamp; mapping(string key => bytes) metadata; } + /** + * @dev Struct to store proof and associated data + */ + struct AuthProof { + bool isVerified; + mapping(string key => uint256 inputValue) storageFields; + string validatorVersion; + uint256 blockTimestamp; + } + + //TODO: custom errors to save some gas. + /** * @dev Response. Structure for response. * @param requestId Request id of the request. @@ -133,8 +146,9 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { mapping(uint256 queryId => Query) _queries; uint256[] _queryIds; // Information linked between users and their addresses - mapping(address userAddress => UserAddressToIdInfo) _user_address_to_id; - mapping(uint256 userID => UserIdToAddressInfo) _id_to_user_address; + mapping(address userAddress => uint256 userID) _user_address_to_id; + mapping(uint256 userID => address userAddress) _id_to_user_address; + mapping(uint256 userID => mapping(address userAddress => uint256 timestamp)) _user_auth_timestamp; // Whitelisted validators mapping(IRequestValidator => bool isApproved) _validatorWhitelist; // Information about auth types and validators @@ -172,7 +186,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { /** * @dev Event emitted upon adding an auth type by the owner */ - event AuthSet(string indexed authType, address validator, bytes params); + event AuthTypeSet(string indexed authType, address validator, bytes params); /** * @dev Event emitted upon updating a request @@ -273,10 +287,6 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { } } - function _getRequestType(uint256 requestId) internal pure returns (uint8 requestType) { - return uint8(requestId >> 248); - } - /** * @dev Checks if a request ID exists * @param requestId The ID of the request @@ -332,7 +342,9 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { _setRequest(request); } - function _setRequest(Request calldata request) internal { + function _setRequest( + Request calldata request + ) internal checkRequestExistence(request.requestId, false) { UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); s._requests[request.requestId] = RequestData({ metadata: request.metadata, @@ -455,7 +467,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { $._state.processCrossChainProofs(crossChainProofs); // 2. Process auth response first - require(authResponses.length == 1, "Only one auth response is required"); + require(authResponses.length == 1, "Exactly one auth response is required"); uint256 userIDFromReponse; AuthTypeData storage authTypeData = $._authMethods[authResponses[0].authType]; @@ -479,18 +491,13 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { writeAuthProofResults(authResponses[0].authType, userIDFromReponse, authSignals); emit AuthResponseSubmitted(authResponses[0].authType, _msgSender()); // Link userID and user address - $._user_address_to_id[sender] = UserAddressToIdInfo({ - userID: userIDFromReponse, - timestamp: block.timestamp - }); - $._id_to_user_address[userIDFromReponse] = UserIdToAddressInfo({ - userAddress: sender, - timestamp: block.timestamp - }); + $._user_address_to_id[sender] = userIDFromReponse; + $._id_to_user_address[userIDFromReponse] = sender; + $._user_auth_timestamp[userIDFromReponse][sender] = block.timestamp; } // 3. Get userID from latest auth response processed in this submitResponse or before - uint256 userID = $._user_address_to_id[sender].userID; + uint256 userID = $._user_address_to_id[sender]; if (userID == 0) { revert("The user is not authenticated"); @@ -573,7 +580,6 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { proof.isVerified = true; proof.validatorVersion = $._requests[requestId].validator.version(); - proof.blockNumber = block.number; proof.blockTimestamp = block.timestamp; } @@ -597,7 +603,6 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { proof.isVerified = true; proof.validatorVersion = $._authMethods[authType].validator.version(); - proof.blockNumber = block.number; proof.blockTimestamp = block.timestamp; } @@ -616,7 +621,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { isActive: true }); - emit AuthSet(authType.authType, address(authType.validator), authType.params); + emit AuthTypeSet(authType.authType, address(authType.validator), authType.params); } /** @@ -679,7 +684,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { string memory responseFieldName ) public view checkRequestExistence(requestId, true) returns (uint256) { UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); - uint256 userID = s._user_address_to_id[sender].userID; + uint256 userID = s._user_address_to_id[sender]; return s._proofs[requestId][userID].storageFields[responseFieldName]; } @@ -695,7 +700,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { string memory responseFieldName ) public view checkAuthTypeExistence(authType, true) returns (uint256) { UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); - uint256 userID = s._user_address_to_id[sender].userID; + uint256 userID = s._user_address_to_id[sender]; return s._authProofs[authType][userID].storageFields[responseFieldName]; } @@ -743,7 +748,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); // 1. Get the latest userId by the userAddress arg (in the mapping) - uint256 userID = s._user_address_to_id[userAddress].userID; + uint256 userID = s._user_address_to_id[userAddress]; require(userID != 0, "UserID not found"); // 2. Check if all requests statuses are true for the userId @@ -778,13 +783,13 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); // 1. Get the latest userId by the userAddress arg (in the mapping) - uint256 userIDFromAddress = s._user_address_to_id[userAddress].userID; + uint256 userIDFromAddress = s._user_address_to_id[userAddress]; uint256 userIDSelected; if (userIDFromAddress != userID) { - address addressFromUserID = s._id_to_user_address[userID].userAddress; + address addressFromUserID = s._id_to_user_address[userID]; require(addressFromUserID == userAddress, "The userAddress and userID are not linked"); - userIDSelected = s._user_address_to_id[addressFromUserID].userID; + userIDSelected = s._user_address_to_id[addressFromUserID]; } else { userIDSelected = userID; } @@ -863,31 +868,26 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { /** * @dev Checks if a user is authenticated - * @param userId The ID of the user + * @param userID The ID of the user * @param userAddress The address of the user * @return Whether the user is authenticated */ - function isUserAuth(uint256 userId, address userAddress) public view returns (bool) { + function isUserAuth(uint256 userID, address userAddress) public view returns (bool) { UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); - return - s._user_address_to_id[userAddress].userID == userId || - s._id_to_user_address[userId].userAddress == userAddress; + return s._user_auth_timestamp[userID][userAddress] != 0; } /** * @dev Gets the timestamp of the authentication of a user - * @param userId The user id of the user + * @param userID The user id of the user * @param userAddress The address of the user * @return The user ID */ - function userAuthTimestamp(uint256 userId, address userAddress) public view returns (uint256) { - if (isUserAuth(userId, userAddress)) { + function userAuthTimestamp(uint256 userID, address userAddress) public view returns (uint256) { + if (isUserAuth(userID, userAddress)) { UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); - if (s._user_address_to_id[userAddress].userID == userId) { - return s._user_address_to_id[userAddress].timestamp; - } - return s._id_to_user_address[userId].timestamp; + return s._user_auth_timestamp[userID][userAddress]; } else { return 0; } From 14d08fb022b4d56a27fde21cfd7b2294fa4fa6f1 Mon Sep 17 00:00:00 2001 From: daveroga Date: Wed, 11 Dec 2024 08:47:04 +0100 Subject: [PATCH 35/69] auth proofs different structure --- .../verifiers/UniversalVerifierMultiQuery.sol | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/contracts/verifiers/UniversalVerifierMultiQuery.sol b/contracts/verifiers/UniversalVerifierMultiQuery.sol index 3be32022..f0de37ea 100644 --- a/contracts/verifiers/UniversalVerifierMultiQuery.sol +++ b/contracts/verifiers/UniversalVerifierMultiQuery.sol @@ -12,6 +12,8 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { * @dev Version of the contract */ string public constant VERSION = "1.0.0"; + /// @dev Key to retrieve the linkID from the proof storage + string constant LINKED_PROOF_KEY = "linkID"; /** * @dev Request. Structure for request for storage. @@ -51,7 +53,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { } /** - * @dev Struct to store proof and associated data + * @dev Struct to store auth proof and associated data */ struct AuthProof { bool isVerified; @@ -154,7 +156,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { // Information about auth types and validators string[] _authTypes; mapping(string authType => AuthTypeData) _authMethods; - mapping(string authType => mapping(uint256 userID => Proof)) _authProofs; + mapping(string authType => mapping(uint256 userID => AuthProof)) _authProofs; } // solhint-disable-next-line @@ -527,10 +529,10 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { // 5. Verify all the grouped responses, write proof results (under the userID key from the auth of the user), // emit events (existing logic) - _writeGroupedResponses(groupedResponses, userID, sender); + _verifyGroupedResponses(groupedResponses, userID, sender); } - function _writeGroupedResponses( + function _verifyGroupedResponses( GroupedResponses[] memory groupedResponses, uint256 userID, address sender @@ -596,7 +598,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { ) public { UniversalVerifierMultiQueryStorage storage $ = _getUniversalVerifierMultiQueryStorage(); - Proof storage proof = $._authProofs[authType][userID]; + AuthProof storage proof = $._authProofs[authType][userID]; for (uint256 i = 0; i < responseFields.length; i++) { proof.storageFields[responseFields[i].name] = responseFields[i].value; } @@ -714,13 +716,13 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { uint256 requestLinkID = getResponseFieldValue( s._groupedRequests[groupId][0], userID, - "linkID" + LINKED_PROOF_KEY ); for (uint256 j = 1; j < s._groupedRequests[groupId].length; j++) { uint256 requestLinkIDToCompare = getResponseFieldValue( s._groupedRequests[groupId][j], userID, - "linkID" + LINKED_PROOF_KEY ); require( requestLinkIDToCompare == requestLinkID, From 3734eb53d618a08885dd6a9ad7fd49fd8c142fb1 Mon Sep 17 00:00:00 2001 From: daveroga Date: Wed, 11 Dec 2024 10:54:13 +0100 Subject: [PATCH 36/69] add custom errors to UniversalVerifierMultiQuery --- .../verifiers/UniversalVerifierMultiQuery.sol | 90 ++++++++++---- ...VerifierMultiQuery_AgreedSolution.sol.skip | 112 ------------------ .../universal-verifier-multi-query.test.ts | 7 +- 3 files changed, 69 insertions(+), 140 deletions(-) delete mode 100644 contracts/verifiers/UniversalVerifierMultiQuery_AgreedSolution.sol.skip diff --git a/contracts/verifiers/UniversalVerifierMultiQuery.sol b/contracts/verifiers/UniversalVerifierMultiQuery.sol index f0de37ea..226119c5 100644 --- a/contracts/verifiers/UniversalVerifierMultiQuery.sol +++ b/contracts/verifiers/UniversalVerifierMultiQuery.sol @@ -7,6 +7,22 @@ import {IRequestValidator} from "../interfaces/IRequestValidator.sol"; import {IAuthValidator} from "../interfaces/IAuthValidator.sol"; import {IState} from "../interfaces/IState.sol"; +error RequestIdNotFound(uint256 requestId); +error RequestAlreadyExists(uint256 requestId); +error GroupIdNotFound(uint256 groupId); +error GroupIdAlreadyExists(uint256 groupId); +error QueryIdNotFound(uint256 queryId); +error QueryIdAlreadyExists(uint256 queryId); +error AuthTypeNotFound(string authType); +error AuthTypeAlreadyExists(string authType); +error ValidatorNotWhitelisted(address validator); +error RequestIsAlreadyGrouped(uint256 requestId); +error AuthResponsesExactlyOneRequired(); +error LinkIDNotTheSameForGroupedRequests(uint256 requestLinkID, uint256 requestLinkIDToCompare); +error UserIDNotFound(uint256 userID); +error UserIDNotLinkedToAddress(uint256 userID, address userAddress); +error ValidatorNotSupportInterface(address validator); + contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { /** * @dev Version of the contract @@ -216,9 +232,13 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { */ modifier checkRequestExistence(uint256 requestId, bool existence) { if (existence) { - require(requestIdExists(requestId), "request id or auth request id doesn't exist"); + if (!requestIdExists(requestId)) { + revert RequestIdNotFound(requestId); + } } else { - require(!requestIdExists(requestId), "request id or auth request id already exists"); + if (requestIdExists(requestId)) { + revert RequestAlreadyExists(requestId); + } } _; } @@ -230,9 +250,13 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { uint256 groupId = request.validator.getGroupID(request.params); if (existence) { - require(groupIdExists(groupId), "group id doesn't exist"); + if (!groupIdExists(groupId)) { + revert GroupIdNotFound(groupId); + } } else { - require(!groupIdExists(groupId), "group id already exists"); + if (groupIdExists(groupId)) { + revert GroupIdAlreadyExists(groupId); + } } _; } @@ -242,9 +266,13 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { */ modifier checkQueryExistence(uint256 queryId, bool existence) { if (existence) { - require(queryIdExists(queryId), "query id doesn't exist"); + if (!queryIdExists(queryId)) { + revert QueryIdNotFound(queryId); + } } else { - require(!queryIdExists(queryId), "query id already exists"); + if (queryIdExists(queryId)) { + revert QueryIdAlreadyExists(queryId); + } } _; } @@ -254,9 +282,13 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { */ modifier checkAuthTypeExistence(string memory authType, bool existence) { if (existence) { - require(authTypeExists(authType), "auth type doesn't exist"); + if (!authTypeExists(authType)) { + revert AuthTypeNotFound(authType); + } } else { - require(!authTypeExists(authType), "auth type already exists"); + if (authTypeExists(authType)) { + revert AuthTypeAlreadyExists(authType); + } } _; } @@ -265,7 +297,9 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { * @dev Modifier to check if the validator is whitelisted */ modifier onlyWhitelistedValidator(IRequestValidator validator) { - require(isWhitelistedValidator(validator), "Validator is not whitelisted"); + if (!isWhitelistedValidator(validator)) { + revert ValidatorNotWhitelisted(address(validator)); + } _; } @@ -439,12 +473,13 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { // check that all the single requests doesn't have group for (uint256 i = 0; i < requestIds.length; i++) { - require( + if ( s._requests[requestIds[i]].validator.getGroupID( s._requests[requestIds[i]].params - ) == 0, - "A single request in this query is a grouped request" - ); + ) != 0 + ) { + revert RequestIsAlreadyGrouped(requestIds[i]); + } } } @@ -469,7 +504,9 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { $._state.processCrossChainProofs(crossChainProofs); // 2. Process auth response first - require(authResponses.length == 1, "Exactly one auth response is required"); + if (authResponses.length != 1) { + revert AuthResponsesExactlyOneRequired(); + } uint256 userIDFromReponse; AuthTypeData storage authTypeData = $._authMethods[authResponses[0].authType]; @@ -724,10 +761,12 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { userID, LINKED_PROOF_KEY ); - require( - requestLinkIDToCompare == requestLinkID, - "linkID is not the same for each of the requests of the group" - ); + if (requestLinkID != requestLinkIDToCompare) { + revert LinkIDNotTheSameForGroupedRequests( + requestLinkID, + requestLinkIDToCompare + ); + } } } } @@ -751,7 +790,9 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { // 1. Get the latest userId by the userAddress arg (in the mapping) uint256 userID = s._user_address_to_id[userAddress]; - require(userID != 0, "UserID not found"); + if (userID == 0) { + revert UserIDNotFound(userID); + } // 2. Check if all requests statuses are true for the userId ( @@ -790,7 +831,9 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { if (userIDFromAddress != userID) { address addressFromUserID = s._id_to_user_address[userID]; - require(addressFromUserID == userAddress, "The userAddress and userID are not linked"); + if (addressFromUserID != userAddress) { + revert UserIDNotLinkedToAddress(userID, userAddress); + } userIDSelected = s._user_address_to_id[addressFromUserID]; } else { userIDSelected = userID; @@ -900,10 +943,9 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { * @param validator The validator to add */ function addValidatorToWhitelist(IRequestValidator validator) public { - require( - IERC165(address(validator)).supportsInterface(type(IRequestValidator).interfaceId), - "Validator doesn't support relevant interface" - ); + if (!IERC165(address(validator)).supportsInterface(type(IRequestValidator).interfaceId)) { + revert ValidatorNotSupportInterface(address(validator)); + } _getUniversalVerifierMultiQueryStorage()._validatorWhitelist[validator] = true; } diff --git a/contracts/verifiers/UniversalVerifierMultiQuery_AgreedSolution.sol.skip b/contracts/verifiers/UniversalVerifierMultiQuery_AgreedSolution.sol.skip deleted file mode 100644 index 3757f834..00000000 --- a/contracts/verifiers/UniversalVerifierMultiQuery_AgreedSolution.sol.skip +++ /dev/null @@ -1,112 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.27; - -import {IRequestValidator} from "../interfaces/IRequestValidator.sol"; -import {IState} from "../interfaces/IState.sol"; -import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; -import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; - -contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { - struct Request { - string metadata; - IRequestValidator validator; - bytes params; - } - - function setRequest( - uint256 requestId, - Request calldata request - ) public checkRequestExistence(requestId, false) { - // only existing logic - // + requestId validation logic (to be defined) - } - - function setAuthRequest( - uint256 requestId, - Request calldata request - ) public checkRequestExistence(requestId, true) onlyOwner { - // similar to regular request existing logic - // but need to think about the authRequests ids validation - // reuse a special byte in the requestId for that maybe - - // Note: there will be two auth requests pre-set at deploy time - // (a) authV2 circuit based and (b) eth-based - } - - struct Response { - uint256 requestId; - bytes proof; - bytes metadata; - } - - struct ResponseField { - uint256 name; - uint256 value; - } - - function submitResponse(Response[] memory responses, bytes memory crossChainProofs) public { - // 1. Check for auth responses (throw if not provided exactly 1) - // and remember the userID from the response - // 2. Process crossChainProofs - // 3. Verify regular responses, write proof results (under the userID key from the step 1), - // emit events (existing logic) - } - - struct Query { - uint256[] requestIds; - bytes metadata; //TODO do we need it? - } - - function setQuery( - uint256 queryId, - Query calldata query - ) public { - //TODO implement - } - - function getQueryStatus(uint256 queryId, address userAddress) public view returns (Query memory) { - //TODO implement - // 1. Get the latest userId by the userAddress arg (in the mapping) - // 2. Check if all requests statuses are true for the userId - // 2. Check if any active auth for the userId is true - - - // NOTE: its possible to implement function getQueryStatus(queryId, userAddress, userId) - // as the data structures have the information but it would be a bit redundant for now - return new Query(); - } - - -//a1 => u1 -//a1 => u2 - -// a1 => u1 => true -// a1 => u2 => true - - struct Proof { - bool isVerified; - mapping(string key => uint256 inputValue) storageFields; - string validatorVersion; - uint256 blockTimestamp; - mapping(string key => bytes) metadata; - } - - /// @custom:storage-location erc7201:iden3.storage.UniversalVerifierMultiQuery - struct UniversalVerifierMultiQueryStorage { - // Information about requests - mapping(uint256 requestId => mapping(uint256 userID => Proof)) _proofs; - mapping(uint256 requestId => Request) _requests; - uint256[] _requestIds; - IState _state; - // Information about multi-queries - mapping(uint256 queryId => Query) _queries; - uint256[] _queryIds; - // Information linked between users and their addresses - // TODO think about arrays for these two mappings - mapping(address userAddress => uint256 userID) _user_address_to_id; - mapping(uint256 userID => address userAddress) _id_to_user_address; - mapping(uint256 userID => mapping(address userAddress => bool hasAuth)) _user_id_and_address_auth; - - uint256[] _authRequestIds; // reuses the same _requests mapping to store the auth requests - } -} diff --git a/test/verifier/universal-verifier-multi-query.test.ts b/test/verifier/universal-verifier-multi-query.test.ts index c66b652b..f16b88aa 100644 --- a/test/verifier/universal-verifier-multi-query.test.ts +++ b/test/verifier/universal-verifier-multi-query.test.ts @@ -269,7 +269,6 @@ describe("Universal Verifier Multi-query", function () { const isUserAuth2 = await verifier.isUserAuth(userId2, await signer.getAddress()); expect(isUserAuth2).to.be.equal(false); - const filter = verifier.filters.ResponseSubmitted; const events = await verifier.queryFilter(filter, -1); @@ -278,7 +277,7 @@ describe("Universal Verifier Multi-query", function () { expect(events[0].args.caller).to.be.equal(signerAddress); await expect(verifier.getQueryStatus(nonExistingQueryId, signerAddress)).to.be.rejectedWith( - "query id doesn't exist", + `QueryIdNotFound(${nonExistingQueryId})`, ); const status = await verifier.getQueryStatus(queryId, signerAddress); @@ -402,7 +401,7 @@ describe("Universal Verifier Multi-query", function () { expect(events[0].args.caller).to.be.equal(signerAddress); await expect(verifier.getQueryStatus(nonExistingQueryId, signerAddress)).to.be.rejectedWith( - "query id doesn't exist", + `QueryIdNotFound(${nonExistingQueryId})`, ); const status = await verifier.getQueryStatus(queryId, signerAddress); expect(status[0][0][0]).to.be.equal(authType); @@ -496,7 +495,7 @@ describe("Universal Verifier Multi-query", function () { await tx.wait(); await expect(verifier.getQueryStatus(queryId, signerAddress)).to.be.rejectedWith( - "linkID is not the same for each of the requests of the group", + "LinkIDNotTheSameForGroupedRequests(3, 4)", ); }); }); From 4c8e01d10c62e93e4ae1dc089a0c80ee54031af2 Mon Sep 17 00:00:00 2001 From: daveroga Date: Thu, 12 Dec 2024 13:25:24 +0100 Subject: [PATCH 37/69] add __gap in proofs storage for future updates and array of proofs for history --- .../verifiers/UniversalVerifierMultiQuery.sol | 61 +++++++++++-------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/contracts/verifiers/UniversalVerifierMultiQuery.sol b/contracts/verifiers/UniversalVerifierMultiQuery.sol index 226119c5..9f674e61 100644 --- a/contracts/verifiers/UniversalVerifierMultiQuery.sol +++ b/contracts/verifiers/UniversalVerifierMultiQuery.sol @@ -66,6 +66,9 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { // uint256 blockNumber; uint256 blockTimestamp; mapping(string key => bytes) metadata; + // This empty reserved space is put in place to allow future versions + // (see https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#storage-gaps) + uint256[45] __gap; } /** @@ -76,6 +79,9 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { mapping(string key => uint256 inputValue) storageFields; string validatorVersion; uint256 blockTimestamp; + // This empty reserved space is put in place to allow future versions + // (see https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#storage-gaps) + uint256[45] __gap; } //TODO: custom errors to save some gas. @@ -154,7 +160,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { struct UniversalVerifierMultiQueryStorage { // Information about requests // solhint-disable-next-line - mapping(uint256 requestId => mapping(uint256 userID => Proof)) _proofs; + mapping(uint256 requestId => mapping(uint256 userID => Proof[])) _proofs; mapping(uint256 requestId => RequestData) _requests; uint256[] _requestIds; mapping(uint256 groupId => uint256[] requestIds) _groupedRequests; @@ -172,7 +178,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { // Information about auth types and validators string[] _authTypes; mapping(string authType => AuthTypeData) _authMethods; - mapping(string authType => mapping(uint256 userID => AuthProof)) _authProofs; + mapping(string authType => mapping(uint256 userID => AuthProof[])) _authProofs; } // solhint-disable-next-line @@ -612,14 +618,18 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { ) public { UniversalVerifierMultiQueryStorage storage $ = _getUniversalVerifierMultiQueryStorage(); - Proof storage proof = $._proofs[requestId][userID]; + Proof[] storage proofs = $._proofs[requestId][userID]; + // We only keep only 1 proof now without history. Prepared for the future if needed. + if (proofs.length == 0) { + proofs.push(); + } for (uint256 i = 0; i < responseFields.length; i++) { - proof.storageFields[responseFields[i].name] = responseFields[i].value; + proofs[0].storageFields[responseFields[i].name] = responseFields[i].value; } - proof.isVerified = true; - proof.validatorVersion = $._requests[requestId].validator.version(); - proof.blockTimestamp = block.timestamp; + proofs[0].isVerified = true; + proofs[0].validatorVersion = $._requests[requestId].validator.version(); + proofs[0].blockTimestamp = block.timestamp; } /** @@ -635,14 +645,17 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { ) public { UniversalVerifierMultiQueryStorage storage $ = _getUniversalVerifierMultiQueryStorage(); - AuthProof storage proof = $._authProofs[authType][userID]; + AuthProof[] storage proofs = $._authProofs[authType][userID]; + if (proofs.length == 0) { + proofs.push(); + } for (uint256 i = 0; i < responseFields.length; i++) { - proof.storageFields[responseFields[i].name] = responseFields[i].value; + proofs[0].storageFields[responseFields[i].name] = responseFields[i].value; } - proof.isVerified = true; - proof.validatorVersion = $._authMethods[authType].validator.version(); - proof.blockTimestamp = block.timestamp; + proofs[0].isVerified = true; + proofs[0].validatorVersion = $._authMethods[authType].validator.version(); + proofs[0].blockTimestamp = block.timestamp; } /** @@ -708,7 +721,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { string memory responseFieldName ) public view checkRequestExistence(requestId, true) returns (uint256) { UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); - return s._proofs[requestId][userID].storageFields[responseFieldName]; + return s._proofs[requestId][userID][0].storageFields[responseFieldName]; } /** @@ -724,7 +737,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { ) public view checkRequestExistence(requestId, true) returns (uint256) { UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); uint256 userID = s._user_address_to_id[sender]; - return s._proofs[requestId][userID].storageFields[responseFieldName]; + return s._proofs[requestId][userID][0].storageFields[responseFieldName]; } /** @@ -740,7 +753,7 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { ) public view checkAuthTypeExistence(authType, true) returns (uint256) { UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); uint256 userID = s._user_address_to_id[sender]; - return s._authProofs[authType][userID].storageFields[responseFieldName]; + return s._authProofs[authType][userID][0].storageFields[responseFieldName]; } function _checkLinkedResponseFields(uint256 queryId, uint256 userID) internal view { @@ -876,9 +889,9 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { string memory authType = s._authTypes[i]; authProofStatus[i] = AuthProofStatus({ authType: authType, - isVerified: s._authProofs[authType][userID].isVerified, - validatorVersion: s._authProofs[authType][userID].validatorVersion, - timestamp: s._authProofs[authType][userID].blockTimestamp + isVerified: s._authProofs[authType][userID][0].isVerified, + validatorVersion: s._authProofs[authType][userID][0].validatorVersion, + timestamp: s._authProofs[authType][userID][0].blockTimestamp }); } @@ -887,9 +900,9 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { requestProofStatus[i] = RequestProofStatus({ requestId: requestId, - isVerified: s._proofs[requestId][userID].isVerified, - validatorVersion: s._proofs[requestId][userID].validatorVersion, - timestamp: s._proofs[requestId][userID].blockTimestamp + isVerified: s._proofs[requestId][userID][0].isVerified, + validatorVersion: s._proofs[requestId][userID][0].validatorVersion, + timestamp: s._proofs[requestId][userID][0].blockTimestamp }); } @@ -901,9 +914,9 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { requestProofStatus[query.requestIds.length + j] = RequestProofStatus({ requestId: requestId, - isVerified: s._proofs[requestId][userID].isVerified, - validatorVersion: s._proofs[requestId][userID].validatorVersion, - timestamp: s._proofs[requestId][userID].blockTimestamp + isVerified: s._proofs[requestId][userID][0].isVerified, + validatorVersion: s._proofs[requestId][userID][0].validatorVersion, + timestamp: s._proofs[requestId][userID][0].blockTimestamp }); } } From 24888ad9981daec874756670b68068f3483d6350 Mon Sep 17 00:00:00 2001 From: daveroga Date: Thu, 12 Dec 2024 20:15:23 +0100 Subject: [PATCH 38/69] param names in returned structs from solidity --- .../universal-verifier-multi-query.test.ts | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/test/verifier/universal-verifier-multi-query.test.ts b/test/verifier/universal-verifier-multi-query.test.ts index f16b88aa..48b678b0 100644 --- a/test/verifier/universal-verifier-multi-query.test.ts +++ b/test/verifier/universal-verifier-multi-query.test.ts @@ -201,9 +201,9 @@ describe("Universal Verifier Multi-query", function () { const requestStored = await verifier.getRequest(requestId); // check if the request is stored correctly checking metadata and validator - expect(requestStored[0]).to.be.equal("metadata"); - expect(requestStored[1]).to.be.equal(await v3Validator.getAddress()); - expect(requestStored[2]).to.be.equal(params); + expect(requestStored.metadata).to.be.equal("metadata"); + expect(requestStored.validator).to.be.equal(await v3Validator.getAddress()); + expect(requestStored.params).to.be.equal(params); await verifier.setAuthType({ authType: authType, @@ -211,9 +211,9 @@ describe("Universal Verifier Multi-query", function () { params: params, }); const authRequestStored = await verifier.getAuthType(authType); - expect(authRequestStored[0]).to.be.equal(await authV2Validator.getAddress()); - expect(authRequestStored[1]).to.be.equal(params); - expect(authRequestStored[2]).to.be.equal(true); + expect(authRequestStored.validator).to.be.equal(await authV2Validator.getAddress()); + expect(authRequestStored.params).to.be.equal(params); + expect(authRequestStored.isActive).to.be.equal(true); const query = { queryId, @@ -224,8 +224,8 @@ describe("Universal Verifier Multi-query", function () { const txSetQuery = await verifier.setQuery(queryId, query); await txSetQuery.wait(); const queryStored = await verifier.getQuery(queryId); - expect(queryStored[0]).to.be.equal(queryId); - expect(queryStored[1]).to.be.deep.equal(query.requestIds); + expect(queryStored.queryId).to.be.equal(queryId); + expect(queryStored.requestIds).to.be.deep.equal(query.requestIds); const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); @@ -281,10 +281,10 @@ describe("Universal Verifier Multi-query", function () { ); const status = await verifier.getQueryStatus(queryId, signerAddress); - expect(status[0][0][0]).to.be.equal(authType); - expect(status[0][0][1]).to.be.equal(true); // auth type isVerified - expect(status[1][0][0]).to.be.equal(requestId); - expect(status[1][0][1]).to.be.equal(true); // request isVerified + expect(status[0][0].authType).to.be.equal(authType); + expect(status[0][0].isVerified).to.be.equal(true); // auth type isVerified + expect(status[1][0].requestId).to.be.equal(requestId); + expect(status[1][0].isVerified).to.be.equal(true); // request isVerified }); it("Test submit response multiquery with same groupID and linkID", async () => { @@ -325,9 +325,9 @@ describe("Universal Verifier Multi-query", function () { const requestStored = await verifier.getRequest(requestId2); // check if the request is stored correctly checking metadata and validator - expect(requestStored[0]).to.be.equal("metadata"); - expect(requestStored[1]).to.be.equal(await v3Validator.getAddress()); - expect(requestStored[2]).to.be.equal(paramsRequest2); + expect(requestStored.metadata).to.be.equal("metadata"); + expect(requestStored.validator).to.be.equal(await v3Validator.getAddress()); + expect(requestStored.params).to.be.equal(paramsRequest2); await verifier.setAuthType({ authType: authType, @@ -335,9 +335,9 @@ describe("Universal Verifier Multi-query", function () { params: authParams, }); const authRequestStored = await verifier.getAuthType(authType); - expect(authRequestStored[0]).to.be.equal(await authV2Validator.getAddress()); - expect(authRequestStored[1]).to.be.equal(authParams); - expect(authRequestStored[2]).to.be.equal(true); + expect(authRequestStored.validator).to.be.equal(await authV2Validator.getAddress()); + expect(authRequestStored.params).to.be.equal(authParams); + expect(authRequestStored.isActive).to.be.equal(true); const query = { queryId, @@ -404,12 +404,12 @@ describe("Universal Verifier Multi-query", function () { `QueryIdNotFound(${nonExistingQueryId})`, ); const status = await verifier.getQueryStatus(queryId, signerAddress); - expect(status[0][0][0]).to.be.equal(authType); - expect(status[0][0][1]).to.be.equal(true); // auth type isVerified - expect(status[1][0][0]).to.be.equal(requestId2); - expect(status[1][0][1]).to.be.equal(true); // requestId2 isVerified - expect(status[1][1][0]).to.be.equal(requestId3); - expect(status[1][1][1]).to.be.equal(true); // requestId3 isVerified + expect(status[0][0].authType).to.be.equal(authType); + expect(status[0][0].isVerified).to.be.equal(true); // auth type isVerified + expect(status[1][0].requestId).to.be.equal(requestId2); + expect(status[1][0].isVerified).to.be.equal(true); // requestId2 isVerified + expect(status[1][1].requestId).to.be.equal(requestId3); + expect(status[1][1].isVerified).to.be.equal(true); // requestId3 isVerified }); it("Test submit response multiquery with same groupID and different linkID", async () => { From 14ca44b52989fbbfebe33ffc7c9684d17708957e Mon Sep 17 00:00:00 2001 From: Andriian Chestnykh Date: Tue, 17 Dec 2024 16:45:48 +0000 Subject: [PATCH 39/69] Initial verifier authentication (#323) Co-authored-by: daveroga --- contracts/interfaces/IRequestValidator.sol | 7 ++++ .../RequestValidatorAuthV2Stub.sol | 4 +++ .../test-helpers/RequestValidatorV2Stub.sol | 4 +++ .../test-helpers/RequestValidatorV3Stub.sol | 8 +++++ .../test-helpers/RequestValidatorV3_2Stub.sol | 8 +++++ contracts/validators/EthIdentityValidator.sol | 9 +++++ .../verifiers/UniversalVerifierMultiQuery.sol | 35 ++++++++++++++++--- .../universal-verifier-multi-query.test.ts | 15 ++++++-- 8 files changed, 84 insertions(+), 6 deletions(-) diff --git a/contracts/interfaces/IRequestValidator.sol b/contracts/interfaces/IRequestValidator.sol index 5087ec31..4a1d15e1 100644 --- a/contracts/interfaces/IRequestValidator.sol +++ b/contracts/interfaces/IRequestValidator.sol @@ -51,4 +51,11 @@ interface IRequestValidator { * @return Hash of the group Id of the request query data. */ function getGroupFieldHash(bytes calldata params) external view returns (bytes32); + + /** + * @dev Get the verifier ID of the request query data. + * @param params Request query data of the credential to verify. + * @return Verifier ID encoded in the request query data. + */ + function getVerifierId(bytes calldata params) external view returns (uint256); } diff --git a/contracts/test-helpers/RequestValidatorAuthV2Stub.sol b/contracts/test-helpers/RequestValidatorAuthV2Stub.sol index 3f62e2a3..a0330020 100644 --- a/contracts/test-helpers/RequestValidatorAuthV2Stub.sol +++ b/contracts/test-helpers/RequestValidatorAuthV2Stub.sol @@ -40,4 +40,8 @@ contract RequestValidatorAuthV2Stub is IRequestValidator, ERC165 { function getGroupFieldHash(bytes calldata params) external pure override returns (bytes32) { revert("AuthV2 validator does not support groupId field"); } + + function getVerifierId(bytes calldata) external pure override returns (uint256) { + return 0; + } } diff --git a/contracts/test-helpers/RequestValidatorV2Stub.sol b/contracts/test-helpers/RequestValidatorV2Stub.sol index d765c61b..605307c4 100644 --- a/contracts/test-helpers/RequestValidatorV2Stub.sol +++ b/contracts/test-helpers/RequestValidatorV2Stub.sol @@ -42,4 +42,8 @@ contract RequestValidatorV2Stub is IRequestValidator, ERC165 { function getGroupFieldHash(bytes calldata params) external pure override returns (bytes32) { revert("V2 validator does not support groupId field"); } + + function getVerifierId(bytes calldata) external pure override returns (uint256) { + return 0; + } } diff --git a/contracts/test-helpers/RequestValidatorV3Stub.sol b/contracts/test-helpers/RequestValidatorV3Stub.sol index 1ae6e223..0d772810 100644 --- a/contracts/test-helpers/RequestValidatorV3Stub.sol +++ b/contracts/test-helpers/RequestValidatorV3Stub.sol @@ -65,4 +65,12 @@ contract RequestValidatorV3Stub is IRequestValidator, ERC165 { // TODO: Implement hash function return keccak256(params); } + + function getVerifierId(bytes calldata params) external pure override returns (uint256) { + CredentialAtomicQueryV3 memory credAtomicQuery = abi.decode( + params, + (CredentialAtomicQueryV3) + ); + return credAtomicQuery.verifierID; + } } diff --git a/contracts/test-helpers/RequestValidatorV3_2Stub.sol b/contracts/test-helpers/RequestValidatorV3_2Stub.sol index b77b845f..9236a43b 100644 --- a/contracts/test-helpers/RequestValidatorV3_2Stub.sol +++ b/contracts/test-helpers/RequestValidatorV3_2Stub.sol @@ -65,4 +65,12 @@ contract RequestValidatorV3_2Stub is IRequestValidator, ERC165 { // TODO: Implement hash function return keccak256(params); } + + function getVerifierId(bytes calldata params) external pure override returns (uint256) { + CredentialAtomicQueryV3 memory credAtomicQuery = abi.decode( + params, + (CredentialAtomicQueryV3) + ); + return credAtomicQuery.verifierID; + } } diff --git a/contracts/validators/EthIdentityValidator.sol b/contracts/validators/EthIdentityValidator.sol index b7d91646..f9761811 100644 --- a/contracts/validators/EthIdentityValidator.sol +++ b/contracts/validators/EthIdentityValidator.sol @@ -109,4 +109,13 @@ contract EthIdentityValidator is Ownable2StepUpgradeable, IRequestValidator, ERC // TODO: Implement hash function return keccak256(params); } + + /** + * @dev Get the verifier ID of the request query data. + * @param params Request query data of the credential to verify. + * @return Verifier ID encoded in the request query data. + */ + function getVerifierId(bytes calldata params) external view returns (uint256) { + return 0; + } } diff --git a/contracts/verifiers/UniversalVerifierMultiQuery.sol b/contracts/verifiers/UniversalVerifierMultiQuery.sol index 9f674e61..30550d97 100644 --- a/contracts/verifiers/UniversalVerifierMultiQuery.sol +++ b/contracts/verifiers/UniversalVerifierMultiQuery.sol @@ -41,6 +41,8 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { string metadata; IRequestValidator validator; bytes params; + address creator; + uint256 verifierId; } struct Request { @@ -50,6 +52,16 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { bytes params; } + struct RequestInfo { + uint256 requestId; + string metadata; + IRequestValidator validator; + bytes params; + address creator; + uint256 verifierId; + bool isVerifierAuthenticated; + } + struct GroupedRequests { uint256 groupId; Request[] requests; @@ -388,10 +400,14 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { Request calldata request ) internal checkRequestExistence(request.requestId, false) { UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); + uint256 verifierId = request.validator.getVerifierId(request.params); + s._requests[request.requestId] = RequestData({ metadata: request.metadata, validator: request.validator, - params: request.params + params: request.params, + creator: _msgSender(), + verifierId: verifierId }); s._requestIds.push(request.requestId); @@ -436,12 +452,23 @@ contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { /** * @dev Gets a specific request by ID * @param requestId The ID of the request - * @return request The request data + * @return request The request info */ function getRequest( uint256 requestId - ) public view checkRequestExistence(requestId, true) returns (RequestData memory request) { - return _getUniversalVerifierMultiQueryStorage()._requests[requestId]; + ) public view checkRequestExistence(requestId, true) returns (RequestInfo memory request) { + UniversalVerifierMultiQueryStorage storage $ = _getUniversalVerifierMultiQueryStorage(); + RequestData storage rd = $._requests[requestId]; + return + RequestInfo({ + requestId: requestId, + metadata: rd.metadata, + validator: rd.validator, + params: rd.params, + creator: rd.creator, + verifierId: rd.verifierId, + isVerifierAuthenticated: $._user_auth_timestamp[rd.verifierId][rd.creator] != 0 + }); } /** diff --git a/test/verifier/universal-verifier-multi-query.test.ts b/test/verifier/universal-verifier-multi-query.test.ts index 48b678b0..042d57e7 100644 --- a/test/verifier/universal-verifier-multi-query.test.ts +++ b/test/verifier/universal-verifier-multi-query.test.ts @@ -48,7 +48,7 @@ describe("Universal Verifier Multi-query", function () { "20376033832371109177683048456014525905119173674985843915445634726167450989630"; const [merklized, isRevocationChecked, valueArrSize] = [1, 1, 1]; const nullifierSessionId = "0"; - const verifierId = "21929109382993718606847853573861987353620810345503358891473103689157378049"; + const verifierId = "1"; const queryHash = calculateQueryHashV3( value, schema, @@ -199,11 +199,14 @@ describe("Universal Verifier Multi-query", function () { ); await txSetRequests.wait(); - const requestStored = await verifier.getRequest(requestId); + let requestStored = await verifier.getRequest(requestId); // check if the request is stored correctly checking metadata and validator expect(requestStored.metadata).to.be.equal("metadata"); expect(requestStored.validator).to.be.equal(await v3Validator.getAddress()); expect(requestStored.params).to.be.equal(params); + expect(requestStored.creator).to.be.equal(await signer.getAddress()); + expect(requestStored.verifierId).to.be.equal(verifierId); + expect(requestStored.isVerifierAuthenticated).to.be.equal(false); await verifier.setAuthType({ authType: authType, @@ -285,6 +288,11 @@ describe("Universal Verifier Multi-query", function () { expect(status[0][0].isVerified).to.be.equal(true); // auth type isVerified expect(status[1][0].requestId).to.be.equal(requestId); expect(status[1][0].isVerified).to.be.equal(true); // request isVerified + + requestStored = await verifier.getRequest(requestId); + // check if validator is authenticated + // TODO reorg the tests to decouple validator from user + expect(requestStored.isVerifierAuthenticated).to.be.equal(true); }); it("Test submit response multiquery with same groupID and linkID", async () => { @@ -328,6 +336,9 @@ describe("Universal Verifier Multi-query", function () { expect(requestStored.metadata).to.be.equal("metadata"); expect(requestStored.validator).to.be.equal(await v3Validator.getAddress()); expect(requestStored.params).to.be.equal(paramsRequest2); + expect(requestStored.creator).to.be.equal(await signer.getAddress()); + expect(requestStored.verifierId).to.be.equal(verifierId); + expect(requestStored.isVerifierAuthenticated).to.be.equal(false); await verifier.setAuthType({ authType: authType, From 636f590c7c9c5f03a5d025cd144bb25b369e593e Mon Sep 17 00:00:00 2001 From: daveroga Date: Wed, 18 Dec 2024 16:52:27 +0100 Subject: [PATCH 40/69] updates for iteration 2 --- contracts/interfaces/IGroth16Verifier.sol | 22 + contracts/interfaces/IVerifier.sol | 219 ++++- contracts/interfaces/IZKPVerifier.sol | 143 --- contracts/lib/VerifierLib.sol | 95 +- .../Groth16VerifierAuthV2Wrapper.sol | 4 +- .../Groth16VerifierMTPWrapper.sol | 4 +- .../Groth16VerifierSigWrapper.sol | 4 +- .../Groth16VerifierV3Wrapper.sol | 4 +- .../test-helpers/EmbeddedVerifierWrapper.sol | 40 + .../EmbeddedZKPVerifierWrapper.sol | 42 - .../Groth16VerifierValidatorStub.sol | 4 +- contracts/validators/AuthV2Validator.sol | 6 +- .../CredentialAtomicQueryV2ValidatorBase.sol | 6 +- .../CredentialAtomicQueryV3Validator.sol | 6 +- .../CredentialAtomicQueryValidatorBase.sol | 8 +- contracts/validators/EthIdentityValidator.sol | 2 +- contracts/verifiers/EmbeddedVerifier.sol | 82 ++ contracts/verifiers/EmbeddedZKPVerifier.sol | 106 --- contracts/verifiers/RequestDisableable.sol | 96 +-- contracts/verifiers/RequestOwnership.sol | 52 +- contracts/verifiers/UniversalVerifier.sol | 250 +++--- contracts/verifiers/ValidatorWhitelist.sol | 129 +-- contracts/verifiers/Verifier.sol | 816 ++++++++++++++++++ contracts/verifiers/ZKPVerifier.sol | 6 +- contracts/verifiers/ZKPVerifierBase.sol | 315 ------- 25 files changed, 1523 insertions(+), 938 deletions(-) create mode 100644 contracts/interfaces/IGroth16Verifier.sol delete mode 100644 contracts/interfaces/IZKPVerifier.sol create mode 100644 contracts/test-helpers/EmbeddedVerifierWrapper.sol delete mode 100644 contracts/test-helpers/EmbeddedZKPVerifierWrapper.sol create mode 100644 contracts/verifiers/EmbeddedVerifier.sol delete mode 100644 contracts/verifiers/EmbeddedZKPVerifier.sol create mode 100644 contracts/verifiers/Verifier.sol delete mode 100644 contracts/verifiers/ZKPVerifierBase.sol diff --git a/contracts/interfaces/IGroth16Verifier.sol b/contracts/interfaces/IGroth16Verifier.sol new file mode 100644 index 00000000..bef805ea --- /dev/null +++ b/contracts/interfaces/IGroth16Verifier.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.27; + +/** + * @dev IGroth16Verifier. Interface for verification of groth16 proofs. + */ +interface IGroth16Verifier { + /** + * @dev Verify the circuit with the groth16 proof π=([πa]1,[πb]2,[πc]1). + * @param a πa element of the groth16 proof. + * @param b πb element of the groth16 proof. + * @param c πc element of the groth16 proof. + * @param input Public inputs of the circuit. + * @return r true if the proof is verified. + */ + function verify( + uint256[2] calldata a, + uint256[2][2] calldata b, + uint256[2] calldata c, + uint256[] calldata input + ) external view returns (bool r); +} diff --git a/contracts/interfaces/IVerifier.sol b/contracts/interfaces/IVerifier.sol index b7ba452f..b8fe6810 100644 --- a/contracts/interfaces/IVerifier.sol +++ b/contracts/interfaces/IVerifier.sol @@ -1,22 +1,213 @@ // SPDX-License-Identifier: GPL-3.0 + pragma solidity 0.8.27; +import {IAuthValidator} from "./IAuthValidator.sol"; +import {IRequestValidator} from "./IRequestValidator.sol"; + /** - * @dev IVerifier. Interface for verification of groth16 proofs. + * @dev IVerifier. Interface for verification of groth16 proofs for validators circuits. */ interface IVerifier { /** - * @dev Verify the circuit with the groth16 proof π=([πa]1,[πb]2,[πc]1). - * @param a πa element of the groth16 proof. - * @param b πb element of the groth16 proof. - * @param c πc element of the groth16 proof. - * @param input Public inputs of the circuit. - * @return r true if the proof is verified. - */ - function verify( - uint256[2] calldata a, - uint256[2][2] calldata b, - uint256[2] calldata c, - uint256[] calldata input - ) external view returns (bool r); + * @dev Request. Structure for request. + * @param requestId Request id. + * @param metadata Metadata of the request. + * @param validator Validator to verify the response. + * @param params Parameters data of the request. + */ + struct Request { + uint256 requestId; + string metadata; + IRequestValidator validator; + bytes params; + } + /** + * @dev RequestInfo. Structure for request info. + * @param requestId Request id. + * @param metadata Metadata of the request. + * @param validator Validator to verify the response. + * @param params Parameters data of the request. + * @param creator Creator of the request. + * @param verifierId Verifier id. + * @param isVerifierAuthenticated True if the verifier is authenticated. + */ + struct RequestInfo { + uint256 requestId; + string metadata; + IRequestValidator validator; + bytes params; + address creator; + uint256 verifierId; + bool isVerifierAuthenticated; + } + /** + * @dev AuthProofStatus. Structure for auth proof status. + * @param groupId Group id of the requests. + * @param requests Requests of the group. + */ + struct GroupedRequests { + uint256 groupId; + Request[] requests; + } + + /** + * @dev ProofStatus. Structure for proof status. + * @param isVerified True if the proof is verified. + * @param validatorVersion Version of the validator. + * @param blockNumber Block number of the proof. + * @param blockTimestamp Block timestamp of the proof. + */ + struct ProofStatus { + bool isVerified; + string validatorVersion; + uint256 blockNumber; + uint256 blockTimestamp; + } + + /** + * @dev Response. Structure for response. + * @param requestId Request id of the request. + * @param proof proof to verify. + * @param metadata Metadata of the request. + */ + struct Response { + uint256 requestId; + bytes proof; + bytes metadata; + } + /** + * @dev GroupedResponses. Structure for grouped responses. + * @param groupId Group id of the responses. + * @param responses Responses of the group. + */ + struct GroupedResponses { + uint256 groupId; + Response[] responses; + } + /** + * @dev AuthResponse. Structure for auth response. + * @param authType Auth type of the proof response. + * @param proof proof to verify. + */ + struct AuthResponse { + string authType; //zkp-auth-v2, zkp-auth-v3, etc. will deside later + bytes proof; + } + + /** + * @dev RequestProofStatus. Structure for request proof status. + * @param requestId Request id of the proof. + * @param isVerified True if the proof is verified. + * @param validatorVersion Version of the validator. + * @param timestamp Timestamp of the proof. + */ + struct RequestProofStatus { + uint256 requestId; + bool isVerified; + string validatorVersion; + uint256 timestamp; + } + + struct AuthType { + string authType; + IAuthValidator validator; + bytes params; + } + + /** + * @dev AuthProofStatus. Structure for auth proof status. + * @param authType Auth type of the auth proof. + * @param isVerified True if the proof is verified. + * @param validatorVersion Version of the validator. + * @param timestamp Timestamp of the proof. + */ + struct AuthProofStatus { + string authType; + bool isVerified; + string validatorVersion; + uint256 timestamp; + } + + /** + * @dev Submits an array of responses and updates proofs status + * @param authResponses The list of auth responses including auth type and proof + * @param singleResponses The list of responses including request ID, proof and metadata for single requests + * @param groupedResponses The list of responses including request ID, proof and metadata for grouped requests + * @param crossChainProofs The list of cross chain proofs from universal resolver (oracle). This + * includes identities and global states. + */ + function submitResponse( + AuthResponse[] memory authResponses, + Response[] memory singleResponses, + GroupedResponses[] memory groupedResponses, + bytes memory crossChainProofs + ) external; + + /** + * @dev Sets different requests + * @param singleRequests The requests that are not in any group + * @param groupedRequests The requests that are in a group + */ + function setRequests( + Request[] calldata singleRequests, + GroupedRequests[] calldata groupedRequests + ) external; + + /** + * @dev Gets a specific request by ID + * @param requestId The ID of the request + * @return request The request info + */ + function getRequest(uint256 requestId) external view returns (RequestInfo memory request); + + /** + * @dev Get the requests count. + * @return Requests count. + */ + function getRequestsCount() external view returns (uint256); + + /** + * @dev Checks if a request ID exists + * @param requestId The ID of the request + * @return Whether the request ID exists + */ + function requestIdExists(uint256 requestId) external view returns (bool); + + /** + * @dev Gets the status of the query verification + * @param queryId The ID of the query + * @param userAddress The address of the user + * @return status The status of the query. "True" if all requests are verified, "false" otherwise + */ + function getQueryStatus( + uint256 queryId, + address userAddress + ) external view returns (AuthProofStatus[] memory, RequestProofStatus[] memory); + + /** + * @dev Gets proof storage response field value + * @param requestId Id of the request + * @param userID Id of the user + * @param responseFieldName Name of the proof storage response field to get + */ + function getResponseFieldValue( + uint256 requestId, + uint256 userID, + string memory responseFieldName + ) external view returns (uint256); + + /** + * @dev Get if proof is verified for the sender and request with requestId. + * @param sender Sender of the proof. + * @param requestId Request id of the Request to verify. + * @return True if proof is verified for the sender and request id. + */ + function isProofVerified(address sender, uint256 requestId) external view returns (bool); + + /** + * @dev Sets an auth type + * @param authType The auth type to add + */ + function setAuthType(AuthType calldata authType) external; } diff --git a/contracts/interfaces/IZKPVerifier.sol b/contracts/interfaces/IZKPVerifier.sol deleted file mode 100644 index 585dd43c..00000000 --- a/contracts/interfaces/IZKPVerifier.sol +++ /dev/null @@ -1,143 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 - -pragma solidity 0.8.27; - -import {ICircuitValidator} from "./ICircuitValidator.sol"; - -/** - * @dev IZKPVerifier. Interface for verification of groth16 proofs for validators circuits. - */ -interface IZKPVerifier { - /** - * @dev ZKPRequest. Structure for ZKP request. - * @param metadata Metadata of the request. - * @param validator Validator circuit. - * @param data Data of the request. - */ - struct ZKPRequest { - string metadata; - ICircuitValidator validator; - bytes data; - } - /** - * @dev ProofStatus. Structure for proof status. - * @param isVerified True if the proof is verified. - * @param validatorVersion Version of the validator. - * @param blockNumber Block number of the proof. - * @param blockTimestamp Block timestamp of the proof. - */ - struct ProofStatus { - bool isVerified; - string validatorVersion; - uint256 blockNumber; - uint256 blockTimestamp; - } - - /** - * @dev ZKPResponse. Structure for ZKP response. - * @param requestId Request id of the ZKP request. - * @param zkProof ZKP proof to verify. - * @param data Metadata of the request. - */ - struct ZKPResponse { - uint64 requestId; - bytes zkProof; - bytes data; - } - - /** - * @dev Submit the groth16 proof π=([πa]1,[πb]2,[πc]1) for the ZKP request requestId. - * @param requestId Request id of the ZKP request. - * @param inputs Public inputs of the circuit. - * @param a πa element of the groth16 proof. - * @param b πb element of the groth16 proof. - * @param c πc element of the groth16 proof. - */ - function submitZKPResponse( - uint64 requestId, - uint256[] memory inputs, - uint256[2] memory a, - uint256[2][2] memory b, - uint256[2] memory c - ) external; - - /** - * @dev Submit the groth16 proof π=([πa]1,[πb]2,[πc]1) for the ZKP request requestId. - * @param responses The list of responses including ZKP request ID, ZK proof and metadata. - * @param crossChainProofs The list of cross chain proofs from universal resolver (oracle). - */ - function submitZKPResponseV2( - ZKPResponse[] memory responses, - bytes memory crossChainProofs - ) external; - - /** - * @dev Set the ZKP request for the requestId. - * @param requestId Request id of the ZKP request. - * @param request ZKP request to set. - */ - function setZKPRequest(uint64 requestId, ZKPRequest calldata request) external; - - /** - * @dev Get the ZKP request for the requestId. - * @param requestId Request id of the ZKP request. - * @return ZKP request. - */ - function getZKPRequest(uint64 requestId) external view returns (ZKPRequest memory); - - /** - * @dev Get the ZKP request count. - * @return ZKP request count. - */ - function getZKPRequestsCount() external view returns (uint256); - - /** - * @dev Check if the requestId exists. - * @param requestId Request id of the ZKP request. - * @return True if the requestId exists. - */ - function requestIdExists(uint64 requestId) external view returns (bool); - - /** - * @dev Get the ZKP requests. - * @param startIndex Start index of the ZKP requests. - * @param length Length of the ZKP requests. - * @return Array of the ZKP requests. - */ - function getZKPRequests( - uint256 startIndex, - uint256 length - ) external view returns (ZKPRequest[] memory); - - /** - * @dev Get if proof is verified for the sender and ZKP request with requestId. - * @param sender Sender of the proof. - * @param requestId Request id of the ZKP Request to verify. - * @return True if proof is verified for the sender and request id. - */ - function isProofVerified(address sender, uint64 requestId) external view returns (bool); - - /** - * @dev Get the proof status for the sender and ZKP request with requestId. - * @param sender Sender of the proof. - * @param requestId Request id of the proof. - * @return Proof status. - */ - function getProofStatus( - address sender, - uint64 requestId - ) external view returns (ProofStatus memory); - - /** - * @dev Get the proof storage field for the user, requestId and key. - * @param user User address. - * @param requestId Request id of the proof. - * @param key Key of the proof storage field. - * @return Proof storage field. - */ - function getProofStorageField( - address user, - uint64 requestId, - string memory key - ) external view returns (uint256); -} diff --git a/contracts/lib/VerifierLib.sol b/contracts/lib/VerifierLib.sol index a47ad53d..cac98051 100644 --- a/contracts/lib/VerifierLib.sol +++ b/contracts/lib/VerifierLib.sol @@ -1,71 +1,92 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.27; -import {ZKPVerifierBase} from "../verifiers/ZKPVerifierBase.sol"; -import {ICircuitValidator} from "../interfaces/ICircuitValidator.sol"; +import {Verifier} from "../verifiers/Verifier.sol"; +import {IRequestValidator} from "../interfaces/IRequestValidator.sol"; +import {IAuthValidator} from "../interfaces/IAuthValidator.sol"; /** * @title VerifierLib * @dev A library for writing proof results. */ library VerifierLib { - /// @dev Struct to store ZKP proof and associated data + /** + * @dev Struct to store proof and associated data + */ struct Proof { bool isVerified; mapping(string key => uint256 inputValue) storageFields; string validatorVersion; - uint256 blockNumber; + // TODO: discuss if we need this field + // uint256 blockNumber; uint256 blockTimestamp; mapping(string key => bytes) metadata; + // This empty reserved space is put in place to allow future versions + // (see https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#storage-gaps) + uint256[45] __gap; } + /** + * @dev Struct to store auth proof and associated data + */ + struct AuthProof { + bool isVerified; + mapping(string key => uint256 inputValue) storageFields; + string validatorVersion; + uint256 blockTimestamp; + // This empty reserved space is put in place to allow future versions + // (see https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#storage-gaps) + uint256[45] __gap; + } + /** * @dev Writes proof results. - * @param self The ZKPVerifierStorage storage pointer - * @param sender The sender of the proof - * @param requestId The request ID - * @param keyToInpIdxs The array of key to public inputs index mapping - * @param inputs The array of public inputs + * @param requestId The request ID of the proof + * @param userID The userID of the proof + * @param responseFields The array of response fields of the proof */ function writeProofResults( - ZKPVerifierBase.ZKPVerifierStorage storage self, - address sender, - uint64 requestId, - ICircuitValidator.KeyToInputIndex[] memory keyToInpIdxs, - uint256[] memory inputs + Verifier.VerifierStorage storage self, + uint256 requestId, + uint256 userID, + IRequestValidator.ResponseField[] memory responseFields ) public { - Proof storage proof = self._proofs[sender][requestId]; - for (uint256 i = 0; i < keyToInpIdxs.length; i++) { - proof.storageFields[keyToInpIdxs[i].key] = inputs[keyToInpIdxs[i].inputIndex]; + Proof[] storage proofs = self._proofs[requestId][userID]; + // We only keep only 1 proof now without history. Prepared for the future if needed. + if (proofs.length == 0) { + proofs.push(); + } + for (uint256 i = 0; i < responseFields.length; i++) { + proofs[0].storageFields[responseFields[i].name] = responseFields[i].value; } - proof.isVerified = true; - proof.validatorVersion = self._requests[requestId].validator.version(); - proof.blockNumber = block.number; - proof.blockTimestamp = block.timestamp; + proofs[0].isVerified = true; + proofs[0].validatorVersion = self._requests[requestId].validator.version(); + proofs[0].blockTimestamp = block.timestamp; } /** * @dev Writes proof results. - * @param self The ZKPVerifierStorage storage pointer - * @param sender The sender of the proof - * @param requestId The request ID of the proof - * @param signals The array of public signals of the proof + * @param authType The auth type of the proof + * @param userID The userID of the proof + * @param responseFields The array of response fields of the proof */ - function writeProofResultsV2( - ZKPVerifierBase.ZKPVerifierStorage storage self, - address sender, - uint64 requestId, - ICircuitValidator.Signal[] memory signals + function writeAuthProofResults( + Verifier.VerifierStorage storage self, + string memory authType, + uint256 userID, + IAuthValidator.ResponseField[] memory responseFields ) public { - Proof storage proof = self._proofs[sender][requestId]; - for (uint256 i = 0; i < signals.length; i++) { - proof.storageFields[signals[i].name] = signals[i].value; + AuthProof[] storage proofs = self._authProofs[authType][userID]; + if (proofs.length == 0) { + proofs.push(); + } + for (uint256 i = 0; i < responseFields.length; i++) { + proofs[0].storageFields[responseFields[i].name] = responseFields[i].value; } - proof.isVerified = true; - proof.validatorVersion = self._requests[requestId].validator.version(); - proof.blockNumber = block.number; - proof.blockTimestamp = block.timestamp; + proofs[0].isVerified = true; + proofs[0].validatorVersion = self._authMethods[authType].validator.version(); + proofs[0].blockTimestamp = block.timestamp; } } diff --git a/contracts/lib/groth16-verifiers/Groth16VerifierAuthV2Wrapper.sol b/contracts/lib/groth16-verifiers/Groth16VerifierAuthV2Wrapper.sol index c97d1e64..b413a857 100644 --- a/contracts/lib/groth16-verifiers/Groth16VerifierAuthV2Wrapper.sol +++ b/contracts/lib/groth16-verifiers/Groth16VerifierAuthV2Wrapper.sol @@ -24,9 +24,9 @@ pragma solidity 0.8.27; import {Groth16VerifierAuthV2} from "./Groth16VerifierAuthV2.sol"; -import {IVerifier} from "../../interfaces/IVerifier.sol"; +import {IGroth16Verifier} from "../../interfaces/IGroth16Verifier.sol"; -contract Groth16VerifierAuthV2Wrapper is Groth16VerifierAuthV2, IVerifier { +contract Groth16VerifierAuthV2Wrapper is Groth16VerifierAuthV2, IGroth16Verifier { /** * @dev Number of public signals for atomic mtp circuit */ diff --git a/contracts/lib/groth16-verifiers/Groth16VerifierMTPWrapper.sol b/contracts/lib/groth16-verifiers/Groth16VerifierMTPWrapper.sol index 19b268de..c3d157ab 100644 --- a/contracts/lib/groth16-verifiers/Groth16VerifierMTPWrapper.sol +++ b/contracts/lib/groth16-verifiers/Groth16VerifierMTPWrapper.sol @@ -14,9 +14,9 @@ pragma solidity 0.8.27; import "./Groth16VerifierMTP.sol"; -import "../../interfaces/IVerifier.sol"; +import "../../interfaces/IGroth16Verifier.sol"; -contract Groth16VerifierMTPWrapper is Groth16VerifierMTP, IVerifier { +contract Groth16VerifierMTPWrapper is Groth16VerifierMTP, IGroth16Verifier { /** * @dev Number of public signals for atomic mtp circuit */ diff --git a/contracts/lib/groth16-verifiers/Groth16VerifierSigWrapper.sol b/contracts/lib/groth16-verifiers/Groth16VerifierSigWrapper.sol index efd6665a..e02d828b 100644 --- a/contracts/lib/groth16-verifiers/Groth16VerifierSigWrapper.sol +++ b/contracts/lib/groth16-verifiers/Groth16VerifierSigWrapper.sol @@ -14,9 +14,9 @@ pragma solidity 0.8.27; import "./Groth16VerifierSig.sol"; -import "../../interfaces/IVerifier.sol"; +import "../../interfaces/IGroth16Verifier.sol"; -contract Groth16VerifierSigWrapper is Groth16VerifierSig, IVerifier { +contract Groth16VerifierSigWrapper is Groth16VerifierSig, IGroth16Verifier { /** * @dev Number of public signals for atomic sig circuit */ diff --git a/contracts/lib/groth16-verifiers/Groth16VerifierV3Wrapper.sol b/contracts/lib/groth16-verifiers/Groth16VerifierV3Wrapper.sol index 9f963726..42e6b154 100644 --- a/contracts/lib/groth16-verifiers/Groth16VerifierV3Wrapper.sol +++ b/contracts/lib/groth16-verifiers/Groth16VerifierV3Wrapper.sol @@ -14,9 +14,9 @@ pragma solidity 0.8.27; import "./Groth16VerifierV3.sol"; -import "../../interfaces/IVerifier.sol"; +import "../../interfaces/IGroth16Verifier.sol"; -contract Groth16VerifierV3Wrapper is Groth16VerifierV3, IVerifier { +contract Groth16VerifierV3Wrapper is Groth16VerifierV3, IGroth16Verifier { /** * @dev Number of public signals for atomic V3 circuit */ diff --git a/contracts/test-helpers/EmbeddedVerifierWrapper.sol b/contracts/test-helpers/EmbeddedVerifierWrapper.sol new file mode 100644 index 00000000..01652ad9 --- /dev/null +++ b/contracts/test-helpers/EmbeddedVerifierWrapper.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.27; + +import {EmbeddedVerifier} from "../verifiers/EmbeddedVerifier.sol"; +import {IState} from "../interfaces/IState.sol"; +import {IRequestValidator} from "../interfaces/IRequestValidator.sol"; +import {IVerifier} from "../interfaces/IVerifier.sol"; + +contract EmbeddedVerifierWrapper is EmbeddedVerifier { + event BeforeProofSubmit( + AuthResponse[] authResponses, + Response[] singleResponses, + GroupedResponses[] groupedResponses + ); + event AfterProofSubmit( + AuthResponse[] authResponses, + Response[] singleResponses, + GroupedResponses[] groupedResponses + ); + + function initialize(address initialOwner, IState state) public initializer { + super.__EmbeddedVerifier_init(initialOwner, state); + } + + function _beforeProofSubmit( + AuthResponse[] memory authResponses, + Response[] memory singleResponses, + GroupedResponses[] memory groupedResponses + ) internal override { + emit BeforeProofSubmit(authResponses, singleResponses, groupedResponses); + } + + function _afterProofSubmit( + AuthResponse[] memory authResponses, + Response[] memory singleResponses, + GroupedResponses[] memory groupedResponses + ) internal override { + emit AfterProofSubmit(authResponses, singleResponses, groupedResponses); + } +} diff --git a/contracts/test-helpers/EmbeddedZKPVerifierWrapper.sol b/contracts/test-helpers/EmbeddedZKPVerifierWrapper.sol deleted file mode 100644 index eb3ae3d4..00000000 --- a/contracts/test-helpers/EmbeddedZKPVerifierWrapper.sol +++ /dev/null @@ -1,42 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.27; - -import {EmbeddedZKPVerifier} from "../verifiers/EmbeddedZKPVerifier.sol"; -import {IState} from "../interfaces/IState.sol"; -import {ICircuitValidator} from "../interfaces/ICircuitValidator.sol"; -import {IZKPVerifier} from "../interfaces/IZKPVerifier.sol"; - -contract EmbeddedZKPVerifierWrapper is EmbeddedZKPVerifier { - event BeforeProofSubmit(uint64 requestId, uint256[] inputs, ICircuitValidator validator); - event AfterProofSubmit(uint64 requestId, uint256[] inputs, ICircuitValidator validator); - event BeforeProofSubmitV2(IZKPVerifier.ZKPResponse[] responses); - event AfterProofSubmitV2(IZKPVerifier.ZKPResponse[] responses); - - function initialize(address initialOwner, IState state) public initializer { - super.__EmbeddedZKPVerifier_init(initialOwner, state); - } - - function _beforeProofSubmit( - uint64 requestId, - uint256[] memory inputs, - ICircuitValidator validator - ) internal override { - emit BeforeProofSubmit(requestId, inputs, validator); - } - - function _afterProofSubmit( - uint64 requestId, - uint256[] memory inputs, - ICircuitValidator validator - ) internal override { - emit AfterProofSubmit(requestId, inputs, validator); - } - - function _beforeProofSubmitV2(IZKPVerifier.ZKPResponse[] memory responses) internal override { - emit BeforeProofSubmitV2(responses); - } - - function _afterProofSubmitV2(IZKPVerifier.ZKPResponse[] memory responses) internal override { - emit AfterProofSubmitV2(responses); - } -} diff --git a/contracts/test-helpers/Groth16VerifierValidatorStub.sol b/contracts/test-helpers/Groth16VerifierValidatorStub.sol index 91f1200d..e3feedb3 100644 --- a/contracts/test-helpers/Groth16VerifierValidatorStub.sol +++ b/contracts/test-helpers/Groth16VerifierValidatorStub.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.27; -import {IVerifier} from "../interfaces/IVerifier.sol"; +import {IGroth16Verifier} from "../interfaces/IGroth16Verifier.sol"; -contract Groth16VerifierValidatorStub is IVerifier { +contract Groth16VerifierValidatorStub is IGroth16Verifier { function verify( uint256[2] calldata, uint256[2][2] calldata, diff --git a/contracts/validators/AuthV2Validator.sol b/contracts/validators/AuthV2Validator.sol index 46ecdf0b..56e1df7d 100644 --- a/contracts/validators/AuthV2Validator.sol +++ b/contracts/validators/AuthV2Validator.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.27; import {CredentialAtomicQueryValidatorBase} from "./CredentialAtomicQueryValidatorBase.sol"; -import {IVerifier} from "../interfaces/IVerifier.sol"; +import {IGroth16Verifier} from "../interfaces/IGroth16Verifier.sol"; import {GenesisUtils} from "../lib/GenesisUtils.sol"; import {ICircuitValidator} from "../interfaces/ICircuitValidator.sol"; import {IState} from "../interfaces/IState.sol"; @@ -129,8 +129,8 @@ contract AuthV2Validator is CredentialAtomicQueryValidatorBase { uint256[2][2] memory b, uint256[2] memory c ) internal view { - IVerifier verifier = getVerifierByCircuitId(CIRCUIT_ID); - require(verifier != IVerifier(address(0)), "Verifier address should not be zero"); + IGroth16Verifier verifier = getVerifierByCircuitId(CIRCUIT_ID); + require(verifier != IGroth16Verifier(address(0)), "Verifier address should not be zero"); // verify that zkp is valid require(verifier.verify(a, b, c, inputs), "Proof is not valid"); diff --git a/contracts/validators/CredentialAtomicQueryV2ValidatorBase.sol b/contracts/validators/CredentialAtomicQueryV2ValidatorBase.sol index a307062b..1b150acf 100644 --- a/contracts/validators/CredentialAtomicQueryV2ValidatorBase.sol +++ b/contracts/validators/CredentialAtomicQueryV2ValidatorBase.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.27; import {CredentialAtomicQueryValidatorBase} from "./CredentialAtomicQueryValidatorBase.sol"; -import {IVerifier} from "../interfaces/IVerifier.sol"; +import {IGroth16Verifier} from "../interfaces/IGroth16Verifier.sol"; import {ICircuitValidator} from "../interfaces/ICircuitValidator.sol"; import {IState} from "../interfaces/IState.sol"; @@ -128,9 +128,9 @@ abstract contract CredentialAtomicQueryV2ValidatorBase is CredentialAtomicQueryV require(credAtomicQuery.circuitIds.length == 1, "circuitIds length is not equal to 1"); - IVerifier verifier = getVerifierByCircuitId(credAtomicQuery.circuitIds[0]); + IGroth16Verifier verifier = getVerifierByCircuitId(credAtomicQuery.circuitIds[0]); - require(verifier != IVerifier(address(0)), "Verifier address should not be zero"); + require(verifier != IGroth16Verifier(address(0)), "Verifier address should not be zero"); // verify that zkp is valid require(verifier.verify(a, b, c, inputs), "Proof is not valid"); diff --git a/contracts/validators/CredentialAtomicQueryV3Validator.sol b/contracts/validators/CredentialAtomicQueryV3Validator.sol index c704ee2b..7d002073 100644 --- a/contracts/validators/CredentialAtomicQueryV3Validator.sol +++ b/contracts/validators/CredentialAtomicQueryV3Validator.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.27; import {CredentialAtomicQueryValidatorBase} from "./CredentialAtomicQueryValidatorBase.sol"; -import {IVerifier} from "../interfaces/IVerifier.sol"; +import {IGroth16Verifier} from "../interfaces/IGroth16Verifier.sol"; import {GenesisUtils} from "../lib/GenesisUtils.sol"; import {ICircuitValidator} from "../interfaces/ICircuitValidator.sol"; import {IState} from "../interfaces/IState.sol"; @@ -236,8 +236,8 @@ contract CredentialAtomicQueryV3Validator is CredentialAtomicQueryValidatorBase ) internal view { require(credAtomicQuery.circuitIds.length == 1, "circuitIds length is not equal to 1"); - IVerifier verifier = getVerifierByCircuitId(credAtomicQuery.circuitIds[0]); - require(verifier != IVerifier(address(0)), "Verifier address should not be zero"); + IGroth16Verifier verifier = getVerifierByCircuitId(credAtomicQuery.circuitIds[0]); + require(verifier != IGroth16Verifier(address(0)), "Verifier address should not be zero"); // verify that zkp is valid require(verifier.verify(a, b, c, inputs), "Proof is not valid"); diff --git a/contracts/validators/CredentialAtomicQueryValidatorBase.sol b/contracts/validators/CredentialAtomicQueryValidatorBase.sol index a972a12e..46b7cfab 100644 --- a/contracts/validators/CredentialAtomicQueryValidatorBase.sol +++ b/contracts/validators/CredentialAtomicQueryValidatorBase.sol @@ -5,7 +5,7 @@ import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/acces import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; import {GenesisUtils} from "../lib/GenesisUtils.sol"; import {ICircuitValidator} from "../interfaces/ICircuitValidator.sol"; -import {IVerifier} from "../interfaces/IVerifier.sol"; +import {IGroth16Verifier} from "../interfaces/IGroth16Verifier.sol"; import {IState} from "../interfaces/IState.sol"; import {PrimitiveTypeUtils} from "../lib/PrimitiveTypeUtils.sol"; @@ -20,7 +20,7 @@ abstract contract CredentialAtomicQueryValidatorBase is /// @dev Main storage structure for the contract /// @custom:storage-location iden3.storage.CredentialAtomicQueryValidator struct CredentialAtomicQueryValidatorBaseStorage { - mapping(string => IVerifier) _circuitIdToVerifier; + mapping(string => IGroth16Verifier) _circuitIdToVerifier; string[] _supportedCircuitIds; IState state; uint256 revocationStateExpirationTimeout; @@ -58,7 +58,7 @@ abstract contract CredentialAtomicQueryValidatorBase is s.proofExpirationTimeout = 1 hours; s.gistRootExpirationTimeout = 1 hours; s._supportedCircuitIds = [circuitId]; - s._circuitIdToVerifier[circuitId] = IVerifier(_verifierContractAddr); + s._circuitIdToVerifier[circuitId] = IGroth16Verifier(_verifierContractAddr); s.state = IState(_stateContractAddr); __Ownable_init(owner); } @@ -186,7 +186,7 @@ abstract contract CredentialAtomicQueryValidatorBase is */ function getVerifierByCircuitId( string memory circuitId - ) public view virtual returns (IVerifier) { + ) public view virtual returns (IGroth16Verifier) { return _getCredentialAtomicQueryValidatorBaseStorage()._circuitIdToVerifier[circuitId]; } diff --git a/contracts/validators/EthIdentityValidator.sol b/contracts/validators/EthIdentityValidator.sol index f9761811..4f02c0e8 100644 --- a/contracts/validators/EthIdentityValidator.sol +++ b/contracts/validators/EthIdentityValidator.sol @@ -3,7 +3,7 @@ pragma solidity 0.8.27; import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; -import {IVerifier} from "../interfaces/IVerifier.sol"; +import {IGroth16Verifier} from "../interfaces/IGroth16Verifier.sol"; import {GenesisUtils} from "../lib/GenesisUtils.sol"; import {IRequestValidator} from "../interfaces/IRequestValidator.sol"; import {IState} from "../interfaces/IState.sol"; diff --git a/contracts/verifiers/EmbeddedVerifier.sol b/contracts/verifiers/EmbeddedVerifier.sol new file mode 100644 index 00000000..209d34dd --- /dev/null +++ b/contracts/verifiers/EmbeddedVerifier.sol @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.27; + +import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; +import {ICircuitValidator} from "../interfaces/ICircuitValidator.sol"; +import {IVerifier} from "../interfaces/IVerifier.sol"; +import {Verifier} from "./Verifier.sol"; +import {IState} from "../interfaces/IState.sol"; + +abstract contract EmbeddedVerifier is Ownable2StepUpgradeable, Verifier { + /** + * @dev Sets the value for Owner + */ + function __EmbeddedVerifier_init(address initialOwner, IState state) internal onlyInitializing { + __Ownable_init(initialOwner); + ___EmbeddedVerifier_init_unchained(initialOwner); + __Verifier_init(state); + } + + function ___EmbeddedVerifier_init_unchained(address initialOwner) internal onlyInitializing {} + + /// @dev Sets the state contract linked to this verifier + /// @param state The state contract address + function setState(IState state) public onlyOwner { + _setState(state); + } + + /** + * @dev Sets different requests + * @param singleRequests The requests that are not in any group + * @param groupedRequests The requests that are in a group + */ + function setRequests( + Request[] calldata singleRequests, + GroupedRequests[] calldata groupedRequests + ) public virtual override onlyOwner { + super.setRequests(singleRequests, groupedRequests); + } + + /** + * @dev Submits an array of responses and updates proofs status + * @param authResponses The list of auth responses including auth type and proof + * @param singleResponses The list of responses including request ID, proof and metadata for single requests + * @param groupedResponses The list of responses including request ID, proof and metadata for grouped requests + * @param crossChainProofs The list of cross chain proofs from universal resolver (oracle). This + * includes identities and global states. + */ + function submitResponse( + AuthResponse[] memory authResponses, + Response[] memory singleResponses, + GroupedResponses[] memory groupedResponses, + bytes memory crossChainProofs + ) public virtual override { + _beforeProofSubmit(authResponses, singleResponses, groupedResponses); + super.submitResponse(authResponses, singleResponses, groupedResponses, crossChainProofs); + _afterProofSubmit(authResponses, singleResponses, groupedResponses); + } + + /** + * @dev Hook that is called before any proof response submit + * @param authResponses The list of auth responses including auth type and proof + * @param singleResponses The list of responses including request ID, proof and metadata for single requests + * @param groupedResponses The list of responses including request ID, proof and metadata for grouped requests + */ + function _beforeProofSubmit( + AuthResponse[] memory authResponses, + Response[] memory singleResponses, + GroupedResponses[] memory groupedResponses + ) internal virtual {} + + /** + * @dev Hook that is called after any proof response submit + * @param authResponses The list of auth responses including auth type and proof + * @param singleResponses The list of responses including request ID, proof and metadata for single requests + * @param groupedResponses The list of responses including request ID, proof and metadata for grouped requests + */ + function _afterProofSubmit( + AuthResponse[] memory authResponses, + Response[] memory singleResponses, + GroupedResponses[] memory groupedResponses + ) internal virtual {} +} diff --git a/contracts/verifiers/EmbeddedZKPVerifier.sol b/contracts/verifiers/EmbeddedZKPVerifier.sol deleted file mode 100644 index c122c5a6..00000000 --- a/contracts/verifiers/EmbeddedZKPVerifier.sol +++ /dev/null @@ -1,106 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.27; - -import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; -import {ICircuitValidator} from "../interfaces/ICircuitValidator.sol"; -import {IZKPVerifier} from "../interfaces/IZKPVerifier.sol"; -import {ZKPVerifierBase} from "./ZKPVerifierBase.sol"; -import {IState} from "../interfaces/IState.sol"; - -abstract contract EmbeddedZKPVerifier is Ownable2StepUpgradeable, ZKPVerifierBase { - /** - * @dev Sets the value for Owner - */ - function __EmbeddedZKPVerifier_init( - address initialOwner, - IState state - ) internal onlyInitializing { - __Ownable_init(initialOwner); - ___EmbeddedZKPVerifier_init_unchained(initialOwner); - __ZKPVerifierBase_init(state); - } - - function ___EmbeddedZKPVerifier_init_unchained( - address initialOwner - ) internal onlyInitializing {} - - /// @dev Sets the state contract linked to this verifier - /// @param state The state contract address - function setState(IState state) public onlyOwner { - _setState(state); - } - - /// @dev Sets a ZKP request - /// @param requestId The ID of the ZKP request - /// @param request The ZKP request data - function setZKPRequest( - uint64 requestId, - IZKPVerifier.ZKPRequest calldata request - ) public virtual override onlyOwner { - super.setZKPRequest(requestId, request); - } - - /// @dev Submits a ZKP response and updates proof status - /// @param requestId The ID of the ZKP request - /// @param inputs The input data for the proof - /// @param a The first component of the proof - /// @param b The second component of the proof - /// @param c The third component of the proof - function submitZKPResponse( - uint64 requestId, - uint256[] memory inputs, - uint256[2] memory a, - uint256[2][2] memory b, - uint256[2] memory c - ) public virtual override { - IZKPVerifier.ZKPRequest memory request = getZKPRequest(requestId); - _beforeProofSubmit(requestId, inputs, request.validator); - super.submitZKPResponse(requestId, inputs, a, b, c); - _afterProofSubmit(requestId, inputs, request.validator); - } - - /** - * @dev Submits an array of ZKP responses and updates proofs status - * @param responses The list of responses including ZKP request ID, ZK proof and metadata - * @param crossChainProof The list of cross chain proofs from universal resolver (oracle). This - * includes identities and global states. - */ - function submitZKPResponseV2( - IZKPVerifier.ZKPResponse[] memory responses, - bytes memory crossChainProof - ) public override { - _beforeProofSubmitV2(responses); - super.submitZKPResponseV2(responses, crossChainProof); - _afterProofSubmitV2(responses); - } - - /** - * @dev Hook that is called before any proof response submit - */ - function _beforeProofSubmit( - uint64 requestId, - uint256[] memory inputs, - ICircuitValidator validator - ) internal virtual {} - - /** - * @dev Hook that is called after any proof response submit - */ - function _afterProofSubmit( - uint64 requestId, - uint256[] memory inputs, - ICircuitValidator validator - ) internal virtual {} - - /** - * @dev Hook that is called before any proof response submit V2 - * @param responses The list of responses including ZKP request ID, ZK proof and metadata - */ - function _beforeProofSubmitV2(IZKPVerifier.ZKPResponse[] memory responses) internal virtual {} - - /** - * @dev Hook that is called after any proof response submit V2 - * @param responses The list of responses including ZKP request ID, ZK proof and metadata - */ - function _afterProofSubmitV2(IZKPVerifier.ZKPResponse[] memory responses) internal virtual {} -} diff --git a/contracts/verifiers/RequestDisableable.sol b/contracts/verifiers/RequestDisableable.sol index f46e5a48..02a4fdce 100644 --- a/contracts/verifiers/RequestDisableable.sol +++ b/contracts/verifiers/RequestDisableable.sol @@ -1,13 +1,15 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.27; -import {ZKPVerifierBase} from "./ZKPVerifierBase.sol"; -import {ICircuitValidator} from "../interfaces/ICircuitValidator.sol"; +import {IVerifier} from "../interfaces/IVerifier.sol"; +import {Verifier} from "./Verifier.sol"; -contract RequestDisableable is ZKPVerifierBase { +error RequestIsDisabled(uint256 requestId); + +contract RequestDisableable is Verifier { /// @custom:storage-location erc7201:iden3.storage.RequestDisableable struct RequestDisableStorage { - mapping(uint64 requestId => bool isDisabled) _requestDisabling; + mapping(uint256 requestId => bool isDisabled) _requestDisabling; } // keccak256(abi.encode(uint256(keccak256("iden3.storage.RequestDisableable")) - 1)) & ~bytes32(uint256(0xff)); @@ -20,66 +22,52 @@ contract RequestDisableable is ZKPVerifierBase { } } - /// @dev Modifier to check if the ZKP request is enabled - modifier onlyEnabledRequest(uint64 requestId) { - require(isZKPRequestEnabled(requestId), "Request is disabled"); - _; - } - - /// @dev Submits a ZKP response and updates proof status - /// @param requestId The ID of the ZKP request - /// @param inputs The input data for the proof - /// @param a The first component of the proof - /// @param b The second component of the proof - /// @param c The third component of the proof - function submitZKPResponse( - uint64 requestId, - uint256[] memory inputs, - uint256[2] memory a, - uint256[2][2] memory b, - uint256[2] memory c - ) public virtual override onlyEnabledRequest(requestId) { - super.submitZKPResponse(requestId, inputs, a, b, c); - } + /** + * @dev Submits an array of responses and updates proofs status + * @param authResponses The list of auth responses including auth type and proof + * @param singleResponses The list of responses including request ID, proof and metadata for single requests + * @param groupedResponses The list of responses including request ID, proof and metadata for grouped requests + * @param crossChainProofs The list of cross chain proofs from universal resolver (oracle). This + * includes identities and global states. + */ + function submitResponse( + IVerifier.AuthResponse[] memory authResponses, + IVerifier.Response[] memory singleResponses, + IVerifier.GroupedResponses[] memory groupedResponses, + bytes memory crossChainProofs + ) public virtual override { + for (uint256 i = 0; i < singleResponses.length; i++) { + if (!isRequestEnabled(singleResponses[i].requestId)) { + revert RequestIsDisabled(singleResponses[i].requestId); + } + } - /// @dev Verifies a ZKP response without updating any proof status - /// @param requestId The ID of the ZKP request - /// @param inputs The public inputs for the proof - /// @param a The first component of the proof - /// @param b The second component of the proof - /// @param c The third component of the proof - /// @param sender The sender on behalf of which the proof is done - function verifyZKPResponse( - uint64 requestId, - uint256[] memory inputs, - uint256[2] memory a, - uint256[2][2] memory b, - uint256[2] memory c, - address sender - ) - public - virtual - override - onlyEnabledRequest(requestId) - returns (ICircuitValidator.KeyToInputIndex[] memory) - { - return super.verifyZKPResponse(requestId, inputs, a, b, c, sender); + for (uint256 i = 0; i < groupedResponses.length; i++) { + for (uint256 j = 0; j < groupedResponses[i].responses.length; j++) { + if (!isRequestEnabled(groupedResponses[i].responses[j].requestId)) { + revert RequestIsDisabled(groupedResponses[i].responses[j].requestId); + } + } + } + super.submitResponse(authResponses, singleResponses, groupedResponses, crossChainProofs); } - /// @dev Checks if ZKP Request is enabled - /// @param requestId The ID of the ZKP request - /// @return True if ZKP Request enabled, otherwise returns false - function isZKPRequestEnabled( - uint64 requestId + /** + * @dev Checks if a request is enabled + * @param requestId The ID of the request + * @return True if the request enabled, otherwise returns false + */ + function isRequestEnabled( + uint256 requestId ) public view virtual checkRequestExistence(requestId, true) returns (bool) { return !_getRequestDisableStorage()._requestDisabling[requestId]; } - function _disableZKPRequest(uint64 requestId) internal checkRequestExistence(requestId, true) { + function _disableRequest(uint256 requestId) internal checkRequestExistence(requestId, true) { _getRequestDisableStorage()._requestDisabling[requestId] = true; } - function _enableZKPRequest(uint64 requestId) internal checkRequestExistence(requestId, true) { + function _enableRequest(uint256 requestId) internal checkRequestExistence(requestId, true) { _getRequestDisableStorage()._requestDisabling[requestId] = false; } } diff --git a/contracts/verifiers/RequestOwnership.sol b/contracts/verifiers/RequestOwnership.sol index 65eb23f4..b2fb345d 100644 --- a/contracts/verifiers/RequestOwnership.sol +++ b/contracts/verifiers/RequestOwnership.sol @@ -1,13 +1,13 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.27; -import {ZKPVerifierBase} from "./ZKPVerifierBase.sol"; -import {IZKPVerifier} from "../interfaces/IZKPVerifier.sol"; +import {Verifier} from "./Verifier.sol"; +import {IVerifier} from "../interfaces/IVerifier.sol"; -abstract contract RequestOwnership is ZKPVerifierBase { +abstract contract RequestOwnership is Verifier { /// @custom:storage-location erc7201:iden3.storage.RequestOwnership struct RequestOwnershipStorage { - mapping(uint64 requestId => address requestOwner) _requestOwners; + mapping(uint256 requestId => address requestOwner) _requestOwners; } // keccak256(abi.encode(uint256(keccak256("iden3.storage.RequestOwnership")) - 1)) & ~bytes32(uint256(0xff)); @@ -24,34 +24,40 @@ abstract contract RequestOwnership is ZKPVerifierBase { } } - /// @dev Modifier to check if the caller is ZKP Request owner - modifier onlyRequestOwner(uint64 requestId) virtual { - require(getRequestOwner(requestId) == _msgSender(), "Not a request owner"); - _; - } - - /// @dev Sets a ZKP request - /// @param requestId The ID of the ZKP request - /// @param request The ZKP request data - function setZKPRequest( - uint64 requestId, - IZKPVerifier.ZKPRequest calldata request + /** + * @dev Sets different requests + * @param singleRequests The requests that are not in any group + * @param groupedRequests The requests that are in a group + */ + function setRequests( + IVerifier.Request[] calldata singleRequests, + IVerifier.GroupedRequests[] calldata groupedRequests ) public virtual override { - super.setZKPRequest(requestId, request); - _setRequestOwner(requestId, _msgSender()); + super.setRequests(singleRequests, groupedRequests); + for (uint256 i = 0; i < singleRequests.length; i++) { + _setRequestOwner(singleRequests[i].requestId, _msgSender()); + } + + for (uint256 i = 0; i < groupedRequests.length; i++) { + for (uint256 j = 0; j < groupedRequests[i].requests.length; j++) { + _setRequestOwner(groupedRequests[i].requests[j].requestId, _msgSender()); + } + } } - /// @dev Get a ZKP Request Owner address - /// @param requestId The ID of a ZKP Request - /// @return The ZKP Request Owner address + /** + * @dev Get a request owner address + * @param requestId The ID of a request + * @return The request owner address + */ function getRequestOwner( - uint64 requestId + uint256 requestId ) public view virtual checkRequestExistence(requestId, true) returns (address) { return _getRequestOwnershipStorage()._requestOwners[requestId]; } function _setRequestOwner( - uint64 requestId, + uint256 requestId, address requestOwner ) internal checkRequestExistence(requestId, true) { RequestOwnershipStorage storage $ = _getRequestOwnershipStorage(); diff --git a/contracts/verifiers/UniversalVerifier.sol b/contracts/verifiers/UniversalVerifier.sol index 8bef4a71..6c68d859 100644 --- a/contracts/verifiers/UniversalVerifier.sol +++ b/contracts/verifiers/UniversalVerifier.sol @@ -2,12 +2,12 @@ pragma solidity 0.8.27; import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; -import {ICircuitValidator} from "../interfaces/ICircuitValidator.sol"; -import {IZKPVerifier} from "../interfaces/IZKPVerifier.sol"; +import {IRequestValidator} from "../interfaces/IRequestValidator.sol"; +import {IVerifier} from "../interfaces/IVerifier.sol"; import {RequestOwnership} from "./RequestOwnership.sol"; import {RequestDisableable} from "./RequestDisableable.sol"; import {ValidatorWhitelist} from "./ValidatorWhitelist.sol"; -import {ZKPVerifierBase} from "./ZKPVerifierBase.sol"; +import {Verifier} from "./Verifier.sol"; import {IState} from "../interfaces/IState.sol"; /// @title Universal Verifier Contract @@ -21,31 +21,47 @@ contract UniversalVerifier is /** * @dev Version of contract */ - string public constant VERSION = "1.1.3"; + string public constant VERSION = "2.0.0"; - /// @dev Event emitted upon submitting a ZKP request - event ZKPResponseSubmitted(uint64 indexed requestId, address indexed caller); + /** + * @dev Event emitted upon submitting a request + */ + event ResponseSubmitted(uint256 indexed requestId, address indexed caller); + + /** + * @dev Event emitted upon submitting an auth response + */ + event AuthResponseSubmitted(string indexed authType, address indexed caller); - /// @dev Event emitted upon adding a ZKP request - event ZKPRequestSet( - uint64 indexed requestId, + /** + * @dev Event emitted upon adding a request + */ + event RequestSet( + uint256 indexed requestId, address indexed requestOwner, string metadata, address validator, - bytes data + bytes params ); - /// @dev Event emitted upon updating a ZKP request - event ZKPRequestUpdate( - uint64 indexed requestId, + /** + * @dev Event emitted upon adding an auth type by the owner + */ + event AuthTypeSet(string indexed authType, address validator, bytes params); + + /** + * @dev Event emitted upon updating a request + */ + event RequestUpdate( + uint256 indexed requestId, address indexed requestOwner, string metadata, address validator, - bytes data + bytes params ); /// @dev Modifier to check if the caller is the contract Owner or ZKP Request Owner - modifier onlyOwnerOrRequestOwner(uint64 requestId) { + modifier onlyOwnerOrRequestOwner(uint256 requestId) { address sender = _msgSender(); require( sender == getRequestOwner(requestId) || sender == owner(), @@ -57,7 +73,7 @@ contract UniversalVerifier is /// @dev Initializes the contract function initialize(IState state, address owner) public initializer { __Ownable_init(owner); - __ZKPVerifierBase_init(state); + __Verifier_init(state); } /// @dev Version of contract getter @@ -65,95 +81,94 @@ contract UniversalVerifier is return VERSION; } - /// @dev Sets a ZKP request - /// @param requestId The ID of the ZKP request - /// @param request The ZKP request data - function setZKPRequest( - uint64 requestId, - IZKPVerifier.ZKPRequest calldata request - ) public override(RequestOwnership, ValidatorWhitelist, ZKPVerifierBase) { - super.setZKPRequest(requestId, request); + /** + * @dev Sets an auth type + * @param authType The auth type to add + */ + function setAuthType(IVerifier.AuthType calldata authType) public override onlyOwner { + super.setAuthType(authType); + emit AuthTypeSet(authType.authType, address(authType.validator), authType.params); + } - emit ZKPRequestSet( - requestId, - _msgSender(), - request.metadata, - address(request.validator), - request.data - ); + /** + * @dev Sets different requests + * @param singleRequests The requests that are not in any group + * @param groupedRequests The requests that are in a group + */ + function setRequests( + Request[] calldata singleRequests, + GroupedRequests[] calldata groupedRequests + ) public override(RequestOwnership, ValidatorWhitelist, Verifier) { + super.setRequests(singleRequests, groupedRequests); + + for (uint256 i = 0; i < singleRequests.length; i++) { + emit RequestSet( + singleRequests[i].requestId, + _msgSender(), + singleRequests[i].metadata, + address(singleRequests[i].validator), + singleRequests[i].params + ); + } + + for (uint256 i = 0; i < groupedRequests.length; i++) { + for (uint256 j = 0; j < groupedRequests[i].requests.length; j++) { + emit RequestSet( + groupedRequests[i].requests[j].requestId, + _msgSender(), + groupedRequests[i].requests[j].metadata, + address(groupedRequests[i].requests[j].validator), + groupedRequests[i].requests[j].params + ); + } + } } - /// @dev Update a ZKP request - /// @param requestId The ID of the ZKP request - /// @param request The ZKP request data - function updateZKPRequest( - uint64 requestId, - IZKPVerifier.ZKPRequest calldata request - ) public onlyOwner { - super._updateZKPRequest(requestId, request); + /** + * @dev Updates a request + * @param requestId The ID of the request + * @param request The request data + */ + function updateRequest(uint256 requestId, IVerifier.Request calldata request) public onlyOwner { + super._updateRequest(requestId, request); - emit ZKPRequestUpdate( + emit RequestUpdate( requestId, _msgSender(), request.metadata, address(request.validator), - request.data + request.params ); } - /// @dev Submits a ZKP response and updates proof status - /// @param requestId The ID of the ZKP request - /// @param inputs The input data for the proof - /// @param a The first component of the proof - /// @param b The second component of the proof - /// @param c The third component of the proof - function submitZKPResponse( - uint64 requestId, - uint256[] memory inputs, - uint256[2] memory a, - uint256[2][2] memory b, - uint256[2] memory c - ) public override(RequestDisableable, ValidatorWhitelist, ZKPVerifierBase) { - super.submitZKPResponse(requestId, inputs, a, b, c); - emit ZKPResponseSubmitted(requestId, _msgSender()); - } - /** - * @dev Submits an array of ZKP responses and updates proofs status - * @param responses The list of responses including ZKP request ID, ZK proof and metadata - * @param crossChainProof The list of cross chain proofs from universal resolver (oracle). This + * @dev Submits an array of responses and updates proofs status + * @param authResponses The list of auth responses including auth type and proof + * @param singleResponses The list of responses including request ID, proof and metadata for single requests + * @param groupedResponses The list of responses including request ID, proof and metadata for grouped requests + * @param crossChainProofs The list of cross chain proofs from universal resolver (oracle). This * includes identities and global states. */ - function submitZKPResponseV2( - IZKPVerifier.ZKPResponse[] memory responses, - bytes memory crossChainProof - ) public override { - super.submitZKPResponseV2(responses, crossChainProof); - for (uint256 i = 0; i < responses.length; i++) { - emit ZKPResponseSubmitted(responses[i].requestId, _msgSender()); + function submitResponse( + AuthResponse[] memory authResponses, + Response[] memory singleResponses, + GroupedResponses[] memory groupedResponses, + bytes memory crossChainProofs + ) public override(RequestDisableable, ValidatorWhitelist, Verifier) { + super.submitResponse(authResponses, singleResponses, groupedResponses, crossChainProofs); + for (uint256 i = 0; i < authResponses.length; i++) { + emit AuthResponseSubmitted(authResponses[i].authType, _msgSender()); + } + + for (uint256 i = 0; i < singleResponses.length; i++) { + emit ResponseSubmitted(singleResponses[i].requestId, _msgSender()); } - } - /// @dev Verifies a ZKP response without updating any proof status - /// @param requestId The ID of the ZKP request - /// @param inputs The public inputs for the proof - /// @param a The first component of the proof - /// @param b The second component of the proof - /// @param c The third component of the proof - /// @param sender The sender on behalf of which the proof is done - function verifyZKPResponse( - uint64 requestId, - uint256[] memory inputs, - uint256[2] memory a, - uint256[2][2] memory b, - uint256[2] memory c, - address sender - ) - public - override(RequestDisableable, ValidatorWhitelist, ZKPVerifierBase) - returns (ICircuitValidator.KeyToInputIndex[] memory) - { - return super.verifyZKPResponse(requestId, inputs, a, b, c, sender); + for (uint256 i = 0; i < groupedResponses.length; i++) { + for (uint256 j = 0; j < groupedResponses[i].responses.length; j++) { + emit ResponseSubmitted(groupedResponses[i].responses[j].requestId, _msgSender()); + } + } } /** @@ -163,48 +178,47 @@ contract UniversalVerifier is _setState(state); } - /// @dev Gets multiple ZKP requests within a range (disabled in this contract) - /// @param startIndex The starting index of the range - /// @param length The length of the range - /// @return An array of ZKP requests within the specified range - function getZKPRequests( - uint256 startIndex, - uint256 length - ) public view override returns (IZKPVerifier.ZKPRequest[] memory) { - revert("Not implemented in this version"); - } - - /// @dev Sets ZKP Request Owner address - /// @param requestId The ID of the ZKP request - /// @param requestOwner ZKP Request Owner address + /** + * @dev Sets the request owner address + * @param requestId The ID of the request + * @param requestOwner The address of the request owner + */ function setRequestOwner( - uint64 requestId, + uint256 requestId, address requestOwner ) public onlyOwnerOrRequestOwner(requestId) { _setRequestOwner(requestId, requestOwner); } - /// @dev Disables ZKP Request - /// @param requestId The ID of the ZKP request - function disableZKPRequest(uint64 requestId) public onlyOwnerOrRequestOwner(requestId) { - _disableZKPRequest(requestId); + /** + * @dev Disables Request + * @param requestId The ID of the request + */ + function disableRequest(uint256 requestId) public onlyOwnerOrRequestOwner(requestId) { + _disableRequest(requestId); } - /// @dev Enables ZKP Request - /// @param requestId The ID of the ZKP request - function enableZKPRequest(uint64 requestId) public onlyOwnerOrRequestOwner(requestId) { - _enableZKPRequest(requestId); + /** + * @dev Enables Request + * @param requestId The ID of the request + */ + function enableRequest(uint256 requestId) public onlyOwnerOrRequestOwner(requestId) { + _enableRequest(requestId); } - /// @dev Add new validator to the whitelist - /// @param validator Validator address - function addValidatorToWhitelist(ICircuitValidator validator) public onlyOwner { + /** + * @dev Adds a validator to the whitelist + * @param validator The address of the validator + */ + function addValidatorToWhitelist(IRequestValidator validator) public onlyOwner { _addValidatorToWhitelist(validator); } - /// @dev Remove validator from the whitelist - /// @param validator Validator address - function removeValidatorFromWhitelist(ICircuitValidator validator) public onlyOwner { + /** + * @dev Removes a validator from the whitelist + * @param validator The address of the validator + */ + function removeValidatorFromWhitelist(IRequestValidator validator) public onlyOwner { _removeValidatorFromWhitelist(validator); } } diff --git a/contracts/verifiers/ValidatorWhitelist.sol b/contracts/verifiers/ValidatorWhitelist.sol index 0f5bb49c..bdbf2ff9 100644 --- a/contracts/verifiers/ValidatorWhitelist.sol +++ b/contracts/verifiers/ValidatorWhitelist.sol @@ -2,14 +2,16 @@ pragma solidity 0.8.27; import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; -import {ICircuitValidator} from "../interfaces/ICircuitValidator.sol"; -import {ZKPVerifierBase} from "./ZKPVerifierBase.sol"; -import {IZKPVerifier} from "../interfaces/IZKPVerifier.sol"; +import {IRequestValidator} from "../interfaces/IRequestValidator.sol"; +import {Verifier} from "./Verifier.sol"; +import {IVerifier} from "../interfaces/IVerifier.sol"; -contract ValidatorWhitelist is ZKPVerifierBase { +error ValidatorIsNotWhitelisted(address validator); + +contract ValidatorWhitelist is Verifier { /// @custom:storage-location erc7201:iden3.storage.ValidatorWhitelist struct ValidatorWhitelistStorage { - mapping(ICircuitValidator => bool isApproved) _validatorWhitelist; + mapping(IRequestValidator => bool isApproved) _validatorWhitelist; } // keccak256(abi.encode(uint256(keccak256("iden3.storage.ValidatorWhitelist")) - 1)) & ~bytes32(uint256(0xff)); @@ -26,79 +28,88 @@ contract ValidatorWhitelist is ZKPVerifierBase { } } - /// @dev Modifier to check if the validator is whitelisted - modifier onlyWhitelistedValidator(ICircuitValidator validator) { - require(isWhitelistedValidator(validator), "Validator is not whitelisted"); - _; - } + /** + * @dev Sets different requests + * @param singleRequests The requests that are not in any group + * @param groupedRequests The requests that are in a group + */ + function setRequests( + IVerifier.Request[] calldata singleRequests, + IVerifier.GroupedRequests[] calldata groupedRequests + ) public virtual override { + for (uint256 i = 0; i < singleRequests.length; i++) { + IRequestValidator validator = getRequest(singleRequests[i].requestId).validator; + if (!isWhitelistedValidator(validator)) { + revert ValidatorIsNotWhitelisted(address(validator)); + } + } - /// @dev Sets a ZKP request - /// @param requestId The ID of the ZKP request - /// @param request The ZKP request data - function setZKPRequest( - uint64 requestId, - IZKPVerifier.ZKPRequest calldata request - ) public virtual override onlyWhitelistedValidator(request.validator) { - super.setZKPRequest(requestId, request); + for (uint256 i = 0; i < groupedRequests.length; i++) { + for (uint256 j = 0; j < groupedRequests[i].requests.length; j++) { + IRequestValidator validator = getRequest(groupedRequests[i].requests[j].requestId) + .validator; + if (!isWhitelistedValidator(validator)) { + revert ValidatorIsNotWhitelisted(address(validator)); + } + } + } + super.setRequests(singleRequests, groupedRequests); } - /// @dev Submits a ZKP response and updates proof status - /// @param requestId The ID of the ZKP request - /// @param inputs The input data for the proof - /// @param a The first component of the proof - /// @param b The second component of the proof - /// @param c The third component of the proof - function submitZKPResponse( - uint64 requestId, - uint256[] memory inputs, - uint256[2] memory a, - uint256[2][2] memory b, - uint256[2] memory c + /** + * @dev Submits an array of responses and updates proofs status + * @param authResponses The list of auth responses including auth type and proof + * @param singleResponses The list of responses including request ID, proof and metadata for single requests + * @param groupedResponses The list of responses including request ID, proof and metadata for grouped requests + * @param crossChainProofs The list of cross chain proofs from universal resolver (oracle). This + * includes identities and global states. + */ + function submitResponse( + IVerifier.AuthResponse[] memory authResponses, + IVerifier.Response[] memory singleResponses, + IVerifier.GroupedResponses[] memory groupedResponses, + bytes memory crossChainProofs ) public virtual override { - ICircuitValidator validator = getZKPRequest(requestId).validator; - require(isWhitelistedValidator(validator), "Validator is not whitelisted"); - super.submitZKPResponse(requestId, inputs, a, b, c); - } + for (uint256 i = 0; i < singleResponses.length; i++) { + IRequestValidator validator = getRequest(singleResponses[i].requestId).validator; + if (!isWhitelistedValidator(validator)) { + revert ValidatorIsNotWhitelisted(address(validator)); + } + } - /// @dev Verifies a ZKP response without updating any proof status - /// @param requestId The ID of the ZKP request - /// @param inputs The public inputs for the proof - /// @param a The first component of the proof - /// @param b The second component of the proof - /// @param c The third component of the proof - /// @param sender The sender on behalf of which the proof is done - function verifyZKPResponse( - uint64 requestId, - uint256[] memory inputs, - uint256[2] memory a, - uint256[2][2] memory b, - uint256[2] memory c, - address sender - ) public virtual override returns (ICircuitValidator.KeyToInputIndex[] memory) { - ICircuitValidator validator = getZKPRequest(requestId).validator; - require(isWhitelistedValidator(validator), "Validator is not whitelisted"); - return super.verifyZKPResponse(requestId, inputs, a, b, c, sender); + for (uint256 i = 0; i < groupedResponses.length; i++) { + for (uint256 j = 0; j < groupedResponses[i].responses.length; j++) { + IRequestValidator validator = getRequest(groupedResponses[i].responses[j].requestId) + .validator; + if (!isWhitelistedValidator(validator)) { + revert ValidatorIsNotWhitelisted(address(validator)); + } + } + } + super.submitResponse(authResponses, singleResponses, groupedResponses, crossChainProofs); } - /// @dev Checks if validator is whitelisted - /// @param validator The validator address - /// @return True if validator is whitelisted, otherwise returns false + /** + * @dev Checks if validator is whitelisted + * @param validator The validator address + * @return True if validator is whitelisted, otherwise returns false + */ function isWhitelistedValidator( - ICircuitValidator validator + IRequestValidator validator ) public view virtual returns (bool) { return _getValidatorWhitelistStorage()._validatorWhitelist[validator]; } - function _addValidatorToWhitelist(ICircuitValidator validator) internal { + function _addValidatorToWhitelist(IRequestValidator validator) internal { require( - IERC165(address(validator)).supportsInterface(type(ICircuitValidator).interfaceId), + IERC165(address(validator)).supportsInterface(type(IRequestValidator).interfaceId), "Validator doesn't support relevant interface" ); _getValidatorWhitelistStorage()._validatorWhitelist[validator] = true; } - function _removeValidatorFromWhitelist(ICircuitValidator validator) internal { + function _removeValidatorFromWhitelist(IRequestValidator validator) internal { _getValidatorWhitelistStorage()._validatorWhitelist[validator] = false; } } diff --git a/contracts/verifiers/Verifier.sol b/contracts/verifiers/Verifier.sol new file mode 100644 index 00000000..9f6d5cc7 --- /dev/null +++ b/contracts/verifiers/Verifier.sol @@ -0,0 +1,816 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.27; + +import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; +import {ContextUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol"; +import {IRequestValidator} from "../interfaces/IRequestValidator.sol"; +import {IAuthValidator} from "../interfaces/IAuthValidator.sol"; +import {IState} from "../interfaces/IState.sol"; +import {VerifierLib} from "../lib/VerifierLib.sol"; +import {IVerifier} from "../interfaces/IVerifier.sol"; + +error RequestIdNotFound(uint256 requestId); +error RequestAlreadyExists(uint256 requestId); +error GroupIdNotFound(uint256 groupId); +error GroupIdAlreadyExists(uint256 groupId); +error QueryIdNotFound(uint256 queryId); +error QueryIdAlreadyExists(uint256 queryId); +error AuthTypeNotFound(string authType); +error AuthTypeAlreadyExists(string authType); +error ValidatorNotWhitelisted(address validator); +error RequestIsAlreadyGrouped(uint256 requestId); +error AuthResponsesExactlyOneRequired(); +error LinkIDNotTheSameForGroupedRequests(uint256 requestLinkID, uint256 requestLinkIDToCompare); +error UserIDNotFound(uint256 userID); +error UserIDNotLinkedToAddress(uint256 userID, address userAddress); +error ValidatorNotSupportInterface(address validator); + +abstract contract Verifier is IVerifier, ContextUpgradeable { + /// @dev Key to retrieve the linkID from the proof storage + string constant LINKED_PROOF_KEY = "linkID"; + + /** + * @dev Request. Structure for request for storage. + * @param metadata Metadata of the request. + * @param validator Validator circuit. + * @param params Params of the request. Proof parameters could be ZK groth16, plonk, ESDSA, EIP712, etc. + */ + struct RequestData { + string metadata; + IRequestValidator validator; + bytes params; + address creator; + uint256 verifierId; + } + + struct AuthTypeData { + IAuthValidator validator; + bytes params; + bool isActive; + } + + struct UserAddressToIdInfo { + uint256 userID; + uint256 timestamp; + } + + struct UserIdToAddressInfo { + address userAddress; + uint256 timestamp; + } + + /** + * @dev Query. Structure for query. + * @param queryId Query id. + * @param requestIds Request ids for this multi query (without groupId. Single requests). + * @param groupIds Group ids for this multi query (all the requests included in the group. Grouped requests). + * @param metadata Metadata for the query. Empty in first version. + */ + struct Query { + uint256 queryId; + uint256[] requestIds; + uint256[] groupIds; + bytes metadata; + } + + /// @custom:storage-location erc7201:iden3.storage.Verifier + struct VerifierStorage { + // Information about requests + // solhint-disable-next-line + mapping(uint256 requestId => mapping(uint256 userID => VerifierLib.Proof[])) _proofs; + mapping(uint256 requestId => RequestData) _requests; + uint256[] _requestIds; + mapping(uint256 groupId => uint256[] requestIds) _groupedRequests; + uint256[] _groupIds; + IState _state; + // Information about queries + mapping(uint256 queryId => Query) _queries; + uint256[] _queryIds; + // Information linked between users and their addresses + mapping(address userAddress => uint256 userID) _user_address_to_id; + mapping(uint256 userID => address userAddress) _id_to_user_address; + mapping(uint256 userID => mapping(address userAddress => uint256 timestamp)) _user_auth_timestamp; + // Whitelisted validators + mapping(IRequestValidator => bool isApproved) _validatorWhitelist; + // Information about auth types and validators + string[] _authTypes; + mapping(string authType => AuthTypeData) _authMethods; + mapping(string authType => mapping(uint256 userID => VerifierLib.AuthProof[])) _authProofs; + } + + // solhint-disable-next-line + // keccak256(abi.encode(uint256(keccak256("iden3.storage.Verifier")) -1 )) & ~bytes32(uint256(0xff)); + bytes32 internal constant VerifierStorageLocation = + 0x11369addde4aae8af30dcf56fa25ad3d864848d3201d1e9197f8b4da18a51a00; + + using VerifierLib for VerifierStorage; + + /** + * @dev Event emitted upon adding a query + */ + event QuerySet(uint256 indexed queryId, uint256[] requestIds); + + /** + * @dev Event emitted upon updating a query + */ + event QueryUpdate(uint256 indexed queryId, uint256[] requestIds); + + /** + * @dev Modifier to check if the request exists + */ + modifier checkRequestExistence(uint256 requestId, bool existence) { + if (existence) { + if (!requestIdExists(requestId)) { + revert RequestIdNotFound(requestId); + } + } else { + if (requestIdExists(requestId)) { + revert RequestAlreadyExists(requestId); + } + } + _; + } + + /** + * @dev Modifier to check if the request exists + */ + modifier checkRequestGroupExistence(Request memory request, bool existence) { + uint256 groupId = request.validator.getGroupID(request.params); + + if (existence) { + if (!groupIdExists(groupId)) { + revert GroupIdNotFound(groupId); + } + } else { + if (groupIdExists(groupId)) { + revert GroupIdAlreadyExists(groupId); + } + } + _; + } + + /** + * @dev Modifier to check if the query exists + */ + modifier checkQueryExistence(uint256 queryId, bool existence) { + if (existence) { + if (!queryIdExists(queryId)) { + revert QueryIdNotFound(queryId); + } + } else { + if (queryIdExists(queryId)) { + revert QueryIdAlreadyExists(queryId); + } + } + _; + } + + /** + * @dev Modifier to check if the auth type exists + */ + modifier checkAuthTypeExistence(string memory authType, bool existence) { + if (existence) { + if (!authTypeExists(authType)) { + revert AuthTypeNotFound(authType); + } + } else { + if (authTypeExists(authType)) { + revert AuthTypeAlreadyExists(authType); + } + } + _; + } + + function _setState(IState state) internal { + _getVerifierStorage()._state = state; + } + + function __Verifier_init(IState state) internal onlyInitializing { + __Verifier_init_unchained(state); + } + + function __Verifier_init_unchained(IState state) internal onlyInitializing { + _setState(state); + } + + function _getVerifierStorage() private pure returns (VerifierStorage storage $) { + assembly { + $.slot := VerifierStorageLocation + } + } + + /** + * @dev Checks if a request ID exists + * @param requestId The ID of the request + * @return Whether the request ID exists + */ + function requestIdExists(uint256 requestId) public view returns (bool) { + return + _getVerifierStorage()._requests[requestId].validator != IRequestValidator(address(0)); + } + + /** + * @dev Checks if a group ID exists + * @param groupId The ID of the group + * @return Whether the group ID exists + */ + function groupIdExists(uint256 groupId) public view returns (bool) { + return + _getVerifierStorage()._groupIds.length > 0 && + _getVerifierStorage()._groupIds[groupId] != 0; + } + + /** + * @dev Checks if a query ID exists + * @param queryId The ID of the query + * @return Whether the query ID exists + */ + function queryIdExists(uint256 queryId) public view returns (bool) { + return _getVerifierStorage()._queries[queryId].queryId == queryId; + } + + /** + * @dev Checks if an auth type exists + * @param authType The auth type + * @return Whether the auth type exists + */ + function authTypeExists(string memory authType) public view returns (bool) { + return _getVerifierStorage()._authMethods[authType].isActive == true; + } + + /** + * @dev Sets a request + * @param request The request data + */ + function _setRequestWithChecks( + Request calldata request + ) + internal + checkRequestExistence(request.requestId, false) + checkRequestGroupExistence(request, false) + { + _setRequest(request); + } + + function _setRequest( + Request calldata request + ) internal checkRequestExistence(request.requestId, false) { + VerifierStorage storage s = _getVerifierStorage(); + uint256 verifierId = request.validator.getVerifierId(request.params); + + s._requests[request.requestId] = RequestData({ + metadata: request.metadata, + validator: request.validator, + params: request.params, + creator: _msgSender(), + verifierId: verifierId + }); + s._requestIds.push(request.requestId); + } + + /** + * @dev Sets different requests + * @param singleRequests The requests that are not in any group + * @param groupedRequests The requests that are in a group + */ + function setRequests( + Request[] calldata singleRequests, + GroupedRequests[] calldata groupedRequests + ) public virtual { + VerifierStorage storage s = _getVerifierStorage(); + + for (uint256 i = 0; i < singleRequests.length; i++) { + _setRequestWithChecks(singleRequests[i]); + } + for (uint256 i = 0; i < groupedRequests.length; i++) { + if (groupIdExists(groupedRequests[i].groupId)) { + revert("Group ID already exists"); + } + s._groupIds.push(groupedRequests[i].groupId); + + for (uint256 j = 0; j < groupedRequests[i].requests.length; j++) { + _setRequest(groupedRequests[i].requests[j]); + s._groupedRequests[groupedRequests[i].groupId].push( + groupedRequests[i].requests[j].requestId + ); + } + } + } + + /** + * @dev Gets a specific request by ID + * @param requestId The ID of the request + * @return request The request info + */ + function getRequest( + uint256 requestId + ) public view checkRequestExistence(requestId, true) returns (RequestInfo memory request) { + VerifierStorage storage $ = _getVerifierStorage(); + RequestData storage rd = $._requests[requestId]; + return + RequestInfo({ + requestId: requestId, + metadata: rd.metadata, + validator: rd.validator, + params: rd.params, + creator: rd.creator, + verifierId: rd.verifierId, + isVerifierAuthenticated: $._user_auth_timestamp[rd.verifierId][rd.creator] != 0 + }); + } + + /** + * @dev Sets a query + * @param queryId The ID of the query + * @param query The query data + */ + function setQuery( + uint256 queryId, + Query calldata query + ) public checkQueryExistence(queryId, false) { + VerifierStorage storage s = _getVerifierStorage(); + s._queries[queryId] = query; + s._queryIds.push(queryId); + + // checks for all the requests in this query + _checkRequestsInQuery(queryId); + + emit QuerySet(queryId, query.requestIds); + } + + /** + * @dev Gets a specific multi query by ID + * @param queryId The ID of the multi query + * @return query The query data + */ + function getQuery(uint256 queryId) public view returns (Query memory query) { + return _getVerifierStorage()._queries[queryId]; + } + + function _checkRequestsInQuery(uint256 queryId) internal view { + VerifierStorage storage s = _getVerifierStorage(); + + uint256[] memory requestIds = s._queries[queryId].requestIds; + + // check that all the single requests doesn't have group + for (uint256 i = 0; i < requestIds.length; i++) { + if ( + s._requests[requestIds[i]].validator.getGroupID( + s._requests[requestIds[i]].params + ) != 0 + ) { + revert RequestIsAlreadyGrouped(requestIds[i]); + } + } + } + + /** + * @dev Submits an array of responses and updates proofs status + * @param authResponses The list of auth responses including auth type and proof + * @param singleResponses The list of responses including request ID, proof and metadata for single requests + * @param groupedResponses The list of responses including request ID, proof and metadata for grouped requests + * @param crossChainProofs The list of cross chain proofs from universal resolver (oracle). This + * includes identities and global states. + */ + function submitResponse( + AuthResponse[] memory authResponses, + Response[] memory singleResponses, + GroupedResponses[] memory groupedResponses, + bytes memory crossChainProofs + ) public virtual { + VerifierStorage storage $ = _getVerifierStorage(); + address sender = _msgSender(); + + // 1. Process crossChainProofs + $._state.processCrossChainProofs(crossChainProofs); + + // TODO: Get userID from responses that has userID informed (LinkedMultiquery doesn't have userID) + + // 2. Process auth response first + if (authResponses.length != 1) { + // TODO: Check if it's already authenticated or it's an ethereum identity + revert AuthResponsesExactlyOneRequired(); + } + + uint256 userIDFromReponse; + AuthTypeData storage authTypeData = $._authMethods[authResponses[0].authType]; + // Authenticate user + IAuthValidator.ResponseField[] memory authSignals = authTypeData.validator.verify( + authResponses[0].proof, + authTypeData.params, + sender, + $._state + ); + + for (uint256 j = 0; j < authSignals.length; j++) { + if (keccak256(bytes(authSignals[j].name)) == keccak256(bytes("userID"))) { + userIDFromReponse = authSignals[j].value; + break; + } + } + + // For some reason the auth request doesn't return the userID in the response + if (userIDFromReponse != 0) { + $.writeAuthProofResults(authResponses[0].authType, userIDFromReponse, authSignals); + // Link userID and user address + $._user_address_to_id[sender] = userIDFromReponse; + $._id_to_user_address[userIDFromReponse] = sender; + $._user_auth_timestamp[userIDFromReponse][sender] = block.timestamp; + } + + // 3. Get userID from latest auth response processed in this submitResponse or before + uint256 userID = $._user_address_to_id[sender]; + + if (userID == 0) { + revert("The user is not authenticated"); + } + + // 4. Verify all the single responses, write proof results (under the userID key from the auth of the user), + // emit events (existing logic) + for (uint256 i = 0; i < singleResponses.length; i++) { + IVerifier.Response memory response = singleResponses[i]; + RequestData storage request = $._requests[response.requestId]; + + IRequestValidator.ResponseField[] memory signals = request.validator.verify( + response.proof, + request.params, + sender, + $._state + ); + + $.writeProofResults(response.requestId, userID, signals); + + if (response.metadata.length > 0) { + revert("Metadata not supported yet"); + } + } + + // 5. Verify all the grouped responses, write proof results (under the userID key from the auth of the user), + // emit events (existing logic) + _verifyGroupedResponses(groupedResponses, userID, sender); + } + + /** + * @dev Updates a request + * @param requestId The ID of the request + * @param request The request data + */ + function _updateRequest( + uint256 requestId, + IVerifier.Request calldata request + ) internal checkRequestExistence(requestId, true) { + VerifierStorage storage s = _getVerifierStorage(); + uint256 verifierId = request.validator.getVerifierId(request.params); + + s._requests[requestId] = RequestData({ + metadata: request.metadata, + validator: request.validator, + params: request.params, + creator: _msgSender(), + verifierId: verifierId + }); + } + + function _verifyGroupedResponses( + GroupedResponses[] memory groupedResponses, + uint256 userID, + address sender + ) internal { + VerifierStorage storage $ = _getVerifierStorage(); + + for (uint256 i = 0; i < groupedResponses.length; i++) { + for (uint256 j = 0; j < groupedResponses[i].responses.length; j++) { + Response memory response = groupedResponses[i].responses[j]; + RequestData storage request = $._requests[response.requestId]; + + IRequestValidator.ResponseField[] memory signals = request.validator.verify( + response.proof, + request.params, + sender, + $._state + ); + + $.writeProofResults(response.requestId, userID, signals); + + if (response.metadata.length > 0) { + revert("Metadata not supported yet"); + } + } + } + } + + /** + * @dev Sets an auth type + * @param authType The auth type to add + */ + function setAuthType( + IVerifier.AuthType calldata authType + ) public virtual checkAuthTypeExistence(authType.authType, false) { + VerifierStorage storage s = _getVerifierStorage(); + s._authTypes.push(authType.authType); + s._authMethods[authType.authType] = AuthTypeData({ + validator: authType.validator, + params: authType.params, + isActive: true + }); + } + + /** + * @dev Disables an auth type + * @param authType The auth type to disable + */ + function disableAuthType( + string calldata authType + ) public checkAuthTypeExistence(authType, true) { + VerifierStorage storage s = _getVerifierStorage(); + s._authMethods[authType].isActive = false; + } + + /** + * @dev Enables an auth type + * @param authType The auth type to enable + */ + function enableAuthType( + string calldata authType + ) public checkAuthTypeExistence(authType, true) { + VerifierStorage storage s = _getVerifierStorage(); + s._authMethods[authType].isActive = true; + } + + /** + * @dev Gets an auth type + * @param authType The Id of the auth type to get + * @return authMethod The auth type data + */ + function getAuthType( + string calldata authType + ) public view checkAuthTypeExistence(authType, true) returns (AuthTypeData memory authMethod) { + return _getVerifierStorage()._authMethods[authType]; + } + + /** + * @dev Gets response field value + * @param requestId Id of the request + * @param userID Id of the user + * @param responseFieldName Name of the response field to get + */ + function getResponseFieldValue( + uint256 requestId, + uint256 userID, + string memory responseFieldName + ) public view checkRequestExistence(requestId, true) returns (uint256) { + VerifierStorage storage s = _getVerifierStorage(); + return s._proofs[requestId][userID][0].storageFields[responseFieldName]; + } + + /** + * @dev Gets response field value + * @param requestId Id of the request + * @param sender Address of the sender + * @param responseFieldName Name of the response field to get + */ + function getResponseFieldValueFromAddress( + uint256 requestId, + address sender, + string memory responseFieldName + ) public view checkRequestExistence(requestId, true) returns (uint256) { + VerifierStorage storage s = _getVerifierStorage(); + uint256 userID = s._user_address_to_id[sender]; + return s._proofs[requestId][userID][0].storageFields[responseFieldName]; + } + + /** + * @dev Gets response field value + * @param authType Auth type of the proof response + * @param sender Address of the sender + * @param responseFieldName Name of the response field to get + */ + function getAuthResponseFieldValueFromAddress( + string memory authType, + address sender, + string memory responseFieldName + ) public view checkAuthTypeExistence(authType, true) returns (uint256) { + VerifierStorage storage s = _getVerifierStorage(); + uint256 userID = s._user_address_to_id[sender]; + return s._authProofs[authType][userID][0].storageFields[responseFieldName]; + } + + function _checkLinkedResponseFields(uint256 queryId, uint256 userID) internal view { + VerifierStorage storage s = _getVerifierStorage(); + + for (uint256 i = 0; i < s._queries[queryId].groupIds.length; i++) { + uint256 groupId = s._queries[queryId].groupIds[i]; + + // Check linkID in the same group or requests is the same + uint256 requestLinkID = getResponseFieldValue( + s._groupedRequests[groupId][0], + userID, + LINKED_PROOF_KEY + ); + for (uint256 j = 1; j < s._groupedRequests[groupId].length; j++) { + uint256 requestLinkIDToCompare = getResponseFieldValue( + s._groupedRequests[groupId][j], + userID, + LINKED_PROOF_KEY + ); + if (requestLinkID != requestLinkIDToCompare) { + revert LinkIDNotTheSameForGroupedRequests( + requestLinkID, + requestLinkIDToCompare + ); + } + } + } + } + + /** + * @dev Gets the status of the query verification + * @param queryId The ID of the query + * @param userAddress The address of the user + * @return status The status of the query. "True" if all requests are verified, "false" otherwise + */ + function getQueryStatus( + uint256 queryId, + address userAddress + ) + public + view + checkQueryExistence(queryId, true) + returns (IVerifier.AuthProofStatus[] memory, IVerifier.RequestProofStatus[] memory) + { + VerifierStorage storage s = _getVerifierStorage(); + + // 1. Get the latest userId by the userAddress arg (in the mapping) + uint256 userID = s._user_address_to_id[userAddress]; + if (userID == 0) { + revert UserIDNotFound(userID); + } + + // 2. Check if all requests statuses are true for the userId + ( + IVerifier.AuthProofStatus[] memory authProofStatus, + IVerifier.RequestProofStatus[] memory requestProofStatus + ) = _getQueryStatus(queryId, userID); + + // 3. Check if all linked response fields are the same + _checkLinkedResponseFields(queryId, userID); + + return (authProofStatus, requestProofStatus); + } + + /** + * @dev Gets the status of the query verification + * @param queryId The ID of the query + * @param userAddress The address of the user + * @param userID The user id of the user + * @return status The status of the query. "True" if all requests are verified, "false" otherwise + */ + function getQueryStatus( + uint256 queryId, + address userAddress, + uint256 userID + ) + public + view + checkQueryExistence(queryId, true) + returns (IVerifier.AuthProofStatus[] memory, IVerifier.RequestProofStatus[] memory) + { + VerifierStorage storage s = _getVerifierStorage(); + + // 1. Get the latest userId by the userAddress arg (in the mapping) + uint256 userIDFromAddress = s._user_address_to_id[userAddress]; + uint256 userIDSelected; + + if (userIDFromAddress != userID) { + address addressFromUserID = s._id_to_user_address[userID]; + if (addressFromUserID != userAddress) { + revert UserIDNotLinkedToAddress(userID, userAddress); + } + userIDSelected = s._user_address_to_id[addressFromUserID]; + } else { + userIDSelected = userID; + } + + // 2. Check if all requests statuses are true for the userId + ( + IVerifier.AuthProofStatus[] memory authProofStatus, + IVerifier.RequestProofStatus[] memory requestProofStatus + ) = _getQueryStatus(queryId, userIDSelected); + + // 3. Check if all linked response fields are the same + _checkLinkedResponseFields(queryId, userIDSelected); + + return (authProofStatus, requestProofStatus); + } + + function _getQueryStatus( + uint256 queryId, + uint256 userID + ) internal view returns (IVerifier.AuthProofStatus[] memory, IVerifier.RequestProofStatus[] memory) { + VerifierStorage storage s = _getVerifierStorage(); + Query storage query = s._queries[queryId]; + + uint256 lengthGroupIds; + + if (query.groupIds.length > 0) { + for (uint256 i = 0; i < query.groupIds.length; i++) { + uint256 groupId = query.groupIds[i]; + lengthGroupIds += s._groupedRequests[groupId].length; + } + } + + IVerifier.AuthProofStatus[] memory authProofStatus = new IVerifier.AuthProofStatus[](s._authTypes.length); + IVerifier.RequestProofStatus[] memory requestProofStatus = new IVerifier.RequestProofStatus[]( + query.requestIds.length + lengthGroupIds + ); + + for (uint256 i = 0; i < s._authTypes.length; i++) { + string memory authType = s._authTypes[i]; + authProofStatus[i] = IVerifier.AuthProofStatus({ + authType: authType, + isVerified: s._authProofs[authType][userID][0].isVerified, + validatorVersion: s._authProofs[authType][userID][0].validatorVersion, + timestamp: s._authProofs[authType][userID][0].blockTimestamp + }); + } + + for (uint256 i = 0; i < query.requestIds.length; i++) { + uint256 requestId = query.requestIds[i]; + + requestProofStatus[i] = IVerifier.RequestProofStatus({ + requestId: requestId, + isVerified: s._proofs[requestId][userID][0].isVerified, + validatorVersion: s._proofs[requestId][userID][0].validatorVersion, + timestamp: s._proofs[requestId][userID][0].blockTimestamp + }); + } + + for (uint256 i = 0; i < query.groupIds.length; i++) { + uint256 groupId = query.groupIds[i]; + + for (uint256 j = 0; j < s._groupedRequests[groupId].length; j++) { + uint256 requestId = s._groupedRequests[groupId][j]; + + requestProofStatus[query.requestIds.length + j] = IVerifier.RequestProofStatus({ + requestId: requestId, + isVerified: s._proofs[requestId][userID][0].isVerified, + validatorVersion: s._proofs[requestId][userID][0].validatorVersion, + timestamp: s._proofs[requestId][userID][0].blockTimestamp + }); + } + } + + return (authProofStatus, requestProofStatus); + } + + /** + * @dev Checks if a proof submitted for a given sender and request ID is verified + * @param sender The sender's address + * @param requestId The ID of the request + * @return True if proof is verified + */ + function isProofVerified( + address sender, + uint256 requestId + ) public view checkRequestExistence(requestId, true) returns (bool) { + VerifierStorage storage s = _getVerifierStorage(); + uint256 userID = s._user_address_to_id[sender]; + return s._proofs[requestId][userID][0].isVerified; + } + + /** + * @dev Checks if a user is authenticated + * @param userID The ID of the user + * @param userAddress The address of the user + * @return Whether the user is authenticated + */ + function isUserAuth(uint256 userID, address userAddress) public view returns (bool) { + VerifierStorage storage s = _getVerifierStorage(); + return s._user_auth_timestamp[userID][userAddress] != 0; + } + + /** + * @dev Gets the timestamp of the authentication of a user + * @param userID The user id of the user + * @param userAddress The address of the user + * @return The user ID + */ + function userAuthTimestamp(uint256 userID, address userAddress) public view returns (uint256) { + if (isUserAuth(userID, userAddress)) { + VerifierStorage storage s = _getVerifierStorage(); + + return s._user_auth_timestamp[userID][userAddress]; + } else { + return 0; + } + } + + + /** + * @dev Get the requests count. + * @return Requests count. + */ + function getRequestsCount() public view returns (uint256) { + return _getVerifierStorage()._requestIds.length; + } +} diff --git a/contracts/verifiers/ZKPVerifier.sol b/contracts/verifiers/ZKPVerifier.sol index e440d23c..97c1647c 100644 --- a/contracts/verifiers/ZKPVerifier.sol +++ b/contracts/verifiers/ZKPVerifier.sol @@ -1,10 +1,10 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.27; -import {EmbeddedZKPVerifier} from "./EmbeddedZKPVerifier.sol"; +import {EmbeddedVerifier} from "./EmbeddedVerifier.sol"; /** * @dev The ZKPVerifier is deprecated and will be removed in the future major versions - * Please use EmbeddedZKPVerifier instead + * Please use EmbeddedVerifier instead */ -contract ZKPVerifier is EmbeddedZKPVerifier {} +contract ZKPVerifier is EmbeddedVerifier {} diff --git a/contracts/verifiers/ZKPVerifierBase.sol b/contracts/verifiers/ZKPVerifierBase.sol deleted file mode 100644 index de76fc68..00000000 --- a/contracts/verifiers/ZKPVerifierBase.sol +++ /dev/null @@ -1,315 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.27; - -import {IZKPVerifier} from "../interfaces/IZKPVerifier.sol"; -import {ICircuitValidator} from "../interfaces/ICircuitValidator.sol"; -import {ArrayUtils} from "../lib/ArrayUtils.sol"; -import {ContextUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol"; -import {IState} from "../interfaces/IState.sol"; -import {VerifierLib} from "../lib/VerifierLib.sol"; - -abstract contract ZKPVerifierBase is IZKPVerifier, ContextUpgradeable { - struct Metadata { - string key; - bytes value; - } - - /// @custom:storage-location erc7201:iden3.storage.ZKPVerifier - struct ZKPVerifierStorage { - mapping(address user => mapping(uint64 requestId => VerifierLib.Proof)) _proofs; - mapping(uint64 requestId => IZKPVerifier.ZKPRequest) _requests; - uint64[] _requestIds; - IState _state; - } - - // keccak256(abi.encode(uint256(keccak256("iden3.storage.ZKPVerifier")) - 1)) & ~bytes32(uint256(0xff)); - bytes32 internal constant ZKPVerifierStorageLocation = - 0x512d18c55869273fec77e70d8a8586e3fb133e90f1db24c6bcf4ff3506ef6a00; - - /// @dev Get the main storage using assembly to ensure specific storage location - function _getZKPVerifierStorage() private pure returns (ZKPVerifierStorage storage $) { - assembly { - $.slot := ZKPVerifierStorageLocation - } - } - - function _setState(IState state) internal { - _getZKPVerifierStorage()._state = state; - } - - using VerifierLib for ZKPVerifierStorage; - - function __ZKPVerifierBase_init(IState state) internal onlyInitializing { - __ZKPVerifierBase_init_unchained(state); - } - - function __ZKPVerifierBase_init_unchained(IState state) internal onlyInitializing { - _setState(state); - } - - /** - * @dev Max return array length for request queries - */ - uint256 public constant REQUESTS_RETURN_LIMIT = 1000; - - /// @dev Key to retrieve the linkID from the proof storage - string constant LINKED_PROOF_KEY = "linkID"; - - /// @dev Linked proof custom error - error LinkedProofError( - string message, - uint64 requestId, - uint256 linkID, - uint64 requestIdToCompare, - uint256 linkIdToCompare - ); - - /// @dev Modifier to check if the validator is set for the request - modifier checkRequestExistence(uint64 requestId, bool existence) { - if (existence) { - require(requestIdExists(requestId), "request id doesn't exist"); - } else { - require(!requestIdExists(requestId), "request id already exists"); - } - _; - } - - /// @dev Sets a ZKP request - /// @param requestId The ID of the ZKP request - /// @param request The ZKP request data - function setZKPRequest( - uint64 requestId, - IZKPVerifier.ZKPRequest calldata request - ) public virtual checkRequestExistence(requestId, false) { - ZKPVerifierStorage storage s = _getZKPVerifierStorage(); - s._requests[requestId] = request; - s._requestIds.push(requestId); - } - - /// @notice Submits a ZKP response and updates proof status - /// @param requestId The ID of the ZKP request - /// @param inputs The input data for the proof - /// @param a The first component of the proof - /// @param b The second component of the proof - /// @param c The third component of the proof - function submitZKPResponse( - uint64 requestId, - uint256[] memory inputs, - uint256[2] memory a, - uint256[2][2] memory b, - uint256[2] memory c - ) public virtual checkRequestExistence(requestId, true) { - address sender = _msgSender(); - ZKPVerifierStorage storage $ = _getZKPVerifierStorage(); - - IZKPVerifier.ZKPRequest memory request = $._requests[requestId]; - ICircuitValidator.KeyToInputIndex[] memory keyToInpIdxs = request.validator.verify( - inputs, - a, - b, - c, - request.data, - sender - ); - - $.writeProofResults(sender, requestId, keyToInpIdxs, inputs); - } - - /// @notice Submits a ZKP response V2 and updates proof status - /// @param responses The list of responses including ZKP request ID, ZK proof and metadata - /// @param crossChainProofs The list of cross chain proofs from universal resolver (oracle) - function submitZKPResponseV2( - IZKPVerifier.ZKPResponse[] memory responses, - bytes memory crossChainProofs - ) public virtual { - ZKPVerifierStorage storage $ = _getZKPVerifierStorage(); - - $._state.processCrossChainProofs(crossChainProofs); - - for (uint256 i = 0; i < responses.length; i++) { - IZKPVerifier.ZKPResponse memory response = responses[i]; - - address sender = _msgSender(); - - // TODO some internal method and storage location to save gas? - IZKPVerifier.ZKPRequest memory request = getZKPRequest(response.requestId); - ICircuitValidator.Signal[] memory signals = request.validator.verifyV2( - response.zkProof, - request.data, - sender, - $._state - ); - - $.writeProofResultsV2(sender, response.requestId, signals); - - if (response.data.length > 0) { - revert("Metadata not supported yet"); - } - } - } - - /// @dev Verifies a ZKP response without updating any proof status - /// @param requestId The ID of the ZKP request - /// @param inputs The public inputs for the proof - /// @param a The first component of the proof - /// @param b The second component of the proof - /// @param c The third component of the proof - /// @param sender The sender on behalf of which the proof is done - function verifyZKPResponse( - uint64 requestId, - uint256[] memory inputs, - uint256[2] memory a, - uint256[2][2] memory b, - uint256[2] memory c, - address sender - ) - public - virtual - checkRequestExistence(requestId, true) - returns (ICircuitValidator.KeyToInputIndex[] memory) - { - IZKPVerifier.ZKPRequest storage request = _getZKPVerifierStorage()._requests[requestId]; - return request.validator.verify(inputs, a, b, c, request.data, sender); - } - - /// @dev Gets the list of request IDs and verifies the proofs are linked - /// @param sender the user's address - /// @param requestIds the list of request IDs - /// Throws if the proofs are not linked - function verifyLinkedProofs(address sender, uint64[] calldata requestIds) public view virtual { - require(requestIds.length > 1, "Linked proof verification needs more than 1 request"); - - uint256 expectedLinkID = getProofStorageField(sender, requestIds[0], LINKED_PROOF_KEY); - - if (expectedLinkID == 0) { - revert("Can't find linkID for given request Ids and user address"); - } - - for (uint256 i = 1; i < requestIds.length; i++) { - uint256 actualLinkID = getProofStorageField(sender, requestIds[i], LINKED_PROOF_KEY); - - if (expectedLinkID != actualLinkID) { - revert LinkedProofError( - "Proofs are not linked", - requestIds[0], - expectedLinkID, - requestIds[i], - actualLinkID - ); - } - } - } - - /// @dev Gets a specific ZKP request by ID - /// @param requestId The ID of the ZKP request - /// @return zkpRequest The ZKP request data - function getZKPRequest( - uint64 requestId - ) - public - view - checkRequestExistence(requestId, true) - returns (IZKPVerifier.ZKPRequest memory zkpRequest) - { - return _getZKPVerifierStorage()._requests[requestId]; - } - - /// @dev Gets the count of ZKP requests - /// @return The count of ZKP requests - function getZKPRequestsCount() public view returns (uint256) { - return _getZKPVerifierStorage()._requestIds.length; - } - - /// @dev Checks if a ZKP request ID exists - /// @param requestId The ID of the ZKP request - /// @return Whether the request ID exists - function requestIdExists(uint64 requestId) public view override returns (bool) { - return - _getZKPVerifierStorage()._requests[requestId].validator != - ICircuitValidator(address(0)); - } - - /// @dev Gets multiple ZKP requests within a range - /// @param startIndex The starting index of the range - /// @param length The length of the range - /// @return An array of ZKP requests within the specified range - function getZKPRequests( - uint256 startIndex, - uint256 length - ) public view virtual returns (IZKPVerifier.ZKPRequest[] memory) { - ZKPVerifierStorage storage s = _getZKPVerifierStorage(); - (uint256 start, uint256 end) = ArrayUtils.calculateBounds( - s._requestIds.length, - startIndex, - length, - REQUESTS_RETURN_LIMIT - ); - - IZKPVerifier.ZKPRequest[] memory result = new IZKPVerifier.ZKPRequest[](end - start); - - for (uint256 i = start; i < end; i++) { - result[i - start] = s._requests[s._requestIds[i]]; - } - - return result; - } - - /// @dev Checks if proof submitted for a given sender and request ID - /// @param sender The sender's address - /// @param requestId The ID of the ZKP request - /// @return true if proof submitted - function isProofVerified( - address sender, - uint64 requestId - ) public view checkRequestExistence(requestId, true) returns (bool) { - return _getZKPVerifierStorage()._proofs[sender][requestId].isVerified; - } - - /// @dev Checks the proof status for a given user and request ID - /// @param sender The sender's address - /// @param requestId The ID of the ZKP request - /// @return The proof status structure - function getProofStatus( - address sender, - uint64 requestId - ) public view checkRequestExistence(requestId, true) returns (IZKPVerifier.ProofStatus memory) { - VerifierLib.Proof storage proof = _getZKPVerifierStorage()._proofs[sender][requestId]; - - return - IZKPVerifier.ProofStatus( - proof.isVerified, - proof.validatorVersion, - proof.blockNumber, - proof.blockTimestamp - ); - } - - /// @dev Gets the proof storage item for a given user, request ID and key - /// @param user The user's address - /// @param requestId The ID of the ZKP request - /// @return The proof - function getProofStorageField( - address user, - uint64 requestId, - string memory key - ) public view checkRequestExistence(requestId, true) returns (uint256) { - return _getZKPVerifierStorage()._proofs[user][requestId].storageFields[key]; - } - - /// @dev Gets the address of the state contract linked to the verifier - /// @return address of the state contract - function getStateAddress() public view virtual returns (address) { - return address(_getZKPVerifierStorage()._state); - } - - /// @dev Update a ZKP request - /// @param requestId The ID of the ZKP request - /// @param request The ZKP request data - function _updateZKPRequest( - uint64 requestId, - IZKPVerifier.ZKPRequest calldata request - ) internal checkRequestExistence(requestId, true) { - ZKPVerifierStorage storage s = _getZKPVerifierStorage(); - s._requests[requestId] = request; - } -} From 88ff859e6957870b9926ac92f22ac5afb4137db4 Mon Sep 17 00:00:00 2001 From: daveroga Date: Thu, 19 Dec 2024 07:58:32 +0100 Subject: [PATCH 41/69] fix test multi-query --- contracts/interfaces/IVerifier.sol | 28 + contracts/verifiers/UniversalVerifier.sol | 23 + .../verifiers/UniversalVerifierMultiQuery.sol | 1011 ----------------- contracts/verifiers/ValidatorWhitelist.sol | 5 +- contracts/verifiers/Verifier.sol | 30 +- hardhat.config.ts | 2 +- helpers/DeployHelper.ts | 87 -- helpers/constants.ts | 19 - ignition/modules/universalVerifier.ts | 23 - .../universal-verifier-multi-query.test.ts | 5 +- 10 files changed, 62 insertions(+), 1171 deletions(-) delete mode 100644 contracts/verifiers/UniversalVerifierMultiQuery.sol diff --git a/contracts/interfaces/IVerifier.sol b/contracts/interfaces/IVerifier.sol index b8fe6810..16093183 100644 --- a/contracts/interfaces/IVerifier.sol +++ b/contracts/interfaces/IVerifier.sol @@ -129,6 +129,20 @@ interface IVerifier { uint256 timestamp; } + /** + * @dev Query. Structure for query. + * @param queryId Query id. + * @param requestIds Request ids for this multi query (without groupId. Single requests). + * @param groupIds Group ids for this multi query (all the requests included in the group. Grouped requests). + * @param metadata Metadata for the query. Empty in first version. + */ + struct Query { + uint256 queryId; + uint256[] requestIds; + uint256[] groupIds; + bytes metadata; + } + /** * @dev Submits an array of responses and updates proofs status * @param authResponses The list of auth responses including auth type and proof @@ -210,4 +224,18 @@ interface IVerifier { * @param authType The auth type to add */ function setAuthType(AuthType calldata authType) external; + + /** + * @dev Sets a query + * @param queryId The ID of the query + * @param query The query data + */ + function setQuery(uint256 queryId, Query calldata query) external; + + /** + * @dev Gets a specific multi query by ID + * @param queryId The ID of the multi query + * @return query The query data + */ + function getQuery(uint256 queryId) external view returns (IVerifier.Query memory query); } diff --git a/contracts/verifiers/UniversalVerifier.sol b/contracts/verifiers/UniversalVerifier.sol index 6c68d859..fb18bae7 100644 --- a/contracts/verifiers/UniversalVerifier.sol +++ b/contracts/verifiers/UniversalVerifier.sol @@ -60,6 +60,16 @@ contract UniversalVerifier is bytes params ); + /** + * @dev Event emitted upon adding a query + */ + event QuerySet(uint256 indexed queryId, uint256[] requestIds); + + /** + * @dev Event emitted upon updating a query + */ + event QueryUpdate(uint256 indexed queryId, uint256[] requestIds); + /// @dev Modifier to check if the caller is the contract Owner or ZKP Request Owner modifier onlyOwnerOrRequestOwner(uint256 requestId) { address sender = _msgSender(); @@ -141,6 +151,19 @@ contract UniversalVerifier is ); } + /** + * @dev Sets a query + * @param queryId The ID of the query + * @param query The query data + */ + function setQuery( + uint256 queryId, + Query calldata query + ) public override checkQueryExistence(queryId, false) { + super.setQuery(queryId, query); + emit QuerySet(queryId, query.requestIds); + } + /** * @dev Submits an array of responses and updates proofs status * @param authResponses The list of auth responses including auth type and proof diff --git a/contracts/verifiers/UniversalVerifierMultiQuery.sol b/contracts/verifiers/UniversalVerifierMultiQuery.sol deleted file mode 100644 index 30550d97..00000000 --- a/contracts/verifiers/UniversalVerifierMultiQuery.sol +++ /dev/null @@ -1,1011 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.27; - -import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; -import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; -import {IRequestValidator} from "../interfaces/IRequestValidator.sol"; -import {IAuthValidator} from "../interfaces/IAuthValidator.sol"; -import {IState} from "../interfaces/IState.sol"; - -error RequestIdNotFound(uint256 requestId); -error RequestAlreadyExists(uint256 requestId); -error GroupIdNotFound(uint256 groupId); -error GroupIdAlreadyExists(uint256 groupId); -error QueryIdNotFound(uint256 queryId); -error QueryIdAlreadyExists(uint256 queryId); -error AuthTypeNotFound(string authType); -error AuthTypeAlreadyExists(string authType); -error ValidatorNotWhitelisted(address validator); -error RequestIsAlreadyGrouped(uint256 requestId); -error AuthResponsesExactlyOneRequired(); -error LinkIDNotTheSameForGroupedRequests(uint256 requestLinkID, uint256 requestLinkIDToCompare); -error UserIDNotFound(uint256 userID); -error UserIDNotLinkedToAddress(uint256 userID, address userAddress); -error ValidatorNotSupportInterface(address validator); - -contract UniversalVerifierMultiQuery is Ownable2StepUpgradeable { - /** - * @dev Version of the contract - */ - string public constant VERSION = "1.0.0"; - /// @dev Key to retrieve the linkID from the proof storage - string constant LINKED_PROOF_KEY = "linkID"; - - /** - * @dev Request. Structure for request for storage. - * @param metadata Metadata of the request. - * @param validator Validator circuit. - * @param params Params of the request. Proof parameters could be ZK groth16, plonk, ESDSA, EIP712, etc. - */ - struct RequestData { - string metadata; - IRequestValidator validator; - bytes params; - address creator; - uint256 verifierId; - } - - struct Request { - uint256 requestId; - string metadata; - IRequestValidator validator; - bytes params; - } - - struct RequestInfo { - uint256 requestId; - string metadata; - IRequestValidator validator; - bytes params; - address creator; - uint256 verifierId; - bool isVerifierAuthenticated; - } - - struct GroupedRequests { - uint256 groupId; - Request[] requests; - } - - /** - * @dev Struct to store proof and associated data - */ - struct Proof { - bool isVerified; - mapping(string key => uint256 inputValue) storageFields; - string validatorVersion; - // TODO: discuss if we need this field - // uint256 blockNumber; - uint256 blockTimestamp; - mapping(string key => bytes) metadata; - // This empty reserved space is put in place to allow future versions - // (see https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#storage-gaps) - uint256[45] __gap; - } - - /** - * @dev Struct to store auth proof and associated data - */ - struct AuthProof { - bool isVerified; - mapping(string key => uint256 inputValue) storageFields; - string validatorVersion; - uint256 blockTimestamp; - // This empty reserved space is put in place to allow future versions - // (see https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#storage-gaps) - uint256[45] __gap; - } - - //TODO: custom errors to save some gas. - - /** - * @dev Response. Structure for response. - * @param requestId Request id of the request. - * @param proof proof to verify. - * @param metadata Metadata of the request. - */ - struct Response { - uint256 requestId; - bytes proof; - bytes metadata; - } - - struct AuthType { - string authType; - IAuthValidator validator; - bytes params; - } - - struct AuthTypeData { - IAuthValidator validator; - bytes params; - bool isActive; - } - struct AuthResponse { - string authType; //zkp-auth-v2, zkp-auth-v3, etc. will deside later - bytes proof; - } - - struct RequestProofStatus { - uint256 requestId; - bool isVerified; - string validatorVersion; - uint256 timestamp; - } - struct AuthProofStatus { - string authType; - bool isVerified; - string validatorVersion; - uint256 timestamp; - } - - struct GroupedResponses { - uint256 groupId; - Response[] responses; - } - - struct UserAddressToIdInfo { - uint256 userID; - uint256 timestamp; - } - - struct UserIdToAddressInfo { - address userAddress; - uint256 timestamp; - } - - /** - * @dev Query. Structure for query. - * @param queryId Query id. - * @param requestIds Request ids for this multi query (without groupId. Single requests). - * @param groupIds Group ids for this multi query (all the requests included in the group. Grouped requests). - * @param metadata Metadata for the query. Empty in first version. - */ - struct Query { - uint256 queryId; - uint256[] requestIds; - uint256[] groupIds; - bytes metadata; - } - - /// @custom:storage-location erc7201:iden3.storage.UniversalVerifierMultiQuery - struct UniversalVerifierMultiQueryStorage { - // Information about requests - // solhint-disable-next-line - mapping(uint256 requestId => mapping(uint256 userID => Proof[])) _proofs; - mapping(uint256 requestId => RequestData) _requests; - uint256[] _requestIds; - mapping(uint256 groupId => uint256[] requestIds) _groupedRequests; - uint256[] _groupIds; - IState _state; - // Information about queries - mapping(uint256 queryId => Query) _queries; - uint256[] _queryIds; - // Information linked between users and their addresses - mapping(address userAddress => uint256 userID) _user_address_to_id; - mapping(uint256 userID => address userAddress) _id_to_user_address; - mapping(uint256 userID => mapping(address userAddress => uint256 timestamp)) _user_auth_timestamp; - // Whitelisted validators - mapping(IRequestValidator => bool isApproved) _validatorWhitelist; - // Information about auth types and validators - string[] _authTypes; - mapping(string authType => AuthTypeData) _authMethods; - mapping(string authType => mapping(uint256 userID => AuthProof[])) _authProofs; - } - - // solhint-disable-next-line - // keccak256(abi.encode(uint256(keccak256("iden3.storage.UniversalVerifierMultiQuery")) -1 )) & ~bytes32(uint256(0xff)); - bytes32 internal constant UniversalVerifierMultiQueryStorageLocation = - 0x4235c64ddf027641dd9aa586d246e4cc3acfcb3f7016a8aa68f7ac21d38b3b00; - - /** - * @dev Event emitted upon submitting a request - */ - event ResponseSubmitted(uint256 indexed requestId, address indexed caller); - - /** - * @dev Event emitted upon submitting an auth response - */ - event AuthResponseSubmitted(string indexed authType, address indexed caller); - - /** - * @dev Event emitted upon adding a request - */ - event RequestSet( - uint256 indexed requestId, - address indexed requestOwner, - string metadata, - address validator, - bytes params - ); - - /** - * @dev Event emitted upon adding an auth type by the owner - */ - event AuthTypeSet(string indexed authType, address validator, bytes params); - - /** - * @dev Event emitted upon updating a request - */ - event RequestUpdate( - uint256 indexed requestId, - address indexed requestOwner, - string metadata, - address validator, - bytes params - ); - - /** - * @dev Event emitted upon adding a query - */ - event QuerySet(uint256 indexed queryId, uint256[] requestIds); - - /** - * @dev Event emitted upon updating a query - */ - event QueryUpdate(uint256 indexed queryId, uint256[] requestIds); - - /** - * @dev Modifier to check if the request exists - */ - modifier checkRequestExistence(uint256 requestId, bool existence) { - if (existence) { - if (!requestIdExists(requestId)) { - revert RequestIdNotFound(requestId); - } - } else { - if (requestIdExists(requestId)) { - revert RequestAlreadyExists(requestId); - } - } - _; - } - - /** - * @dev Modifier to check if the request exists - */ - modifier checkRequestGroupExistence(Request memory request, bool existence) { - uint256 groupId = request.validator.getGroupID(request.params); - - if (existence) { - if (!groupIdExists(groupId)) { - revert GroupIdNotFound(groupId); - } - } else { - if (groupIdExists(groupId)) { - revert GroupIdAlreadyExists(groupId); - } - } - _; - } - - /** - * @dev Modifier to check if the query exists - */ - modifier checkQueryExistence(uint256 queryId, bool existence) { - if (existence) { - if (!queryIdExists(queryId)) { - revert QueryIdNotFound(queryId); - } - } else { - if (queryIdExists(queryId)) { - revert QueryIdAlreadyExists(queryId); - } - } - _; - } - - /** - * @dev Modifier to check if the auth type exists - */ - modifier checkAuthTypeExistence(string memory authType, bool existence) { - if (existence) { - if (!authTypeExists(authType)) { - revert AuthTypeNotFound(authType); - } - } else { - if (authTypeExists(authType)) { - revert AuthTypeAlreadyExists(authType); - } - } - _; - } - - /** - * @dev Modifier to check if the validator is whitelisted - */ - modifier onlyWhitelistedValidator(IRequestValidator validator) { - if (!isWhitelistedValidator(validator)) { - revert ValidatorNotWhitelisted(address(validator)); - } - _; - } - - /** - * @dev Initializes the contract - * @param state The state contract - * @param owner The owner of the contract - */ - function initialize(IState state, address owner) public initializer { - __Ownable_init(owner); - _getUniversalVerifierMultiQueryStorage()._state = state; - } - - function _getUniversalVerifierMultiQueryStorage() - private - pure - returns (UniversalVerifierMultiQueryStorage storage $) - { - assembly { - $.slot := UniversalVerifierMultiQueryStorageLocation - } - } - - /** - * @dev Checks if a request ID exists - * @param requestId The ID of the request - * @return Whether the request ID exists - */ - function requestIdExists(uint256 requestId) public view returns (bool) { - return - _getUniversalVerifierMultiQueryStorage()._requests[requestId].validator != - IRequestValidator(address(0)); - } - - /** - * @dev Checks if a group ID exists - * @param groupId The ID of the group - * @return Whether the group ID exists - */ - function groupIdExists(uint256 groupId) public view returns (bool) { - return - _getUniversalVerifierMultiQueryStorage()._groupIds.length > 0 && - _getUniversalVerifierMultiQueryStorage()._groupIds[groupId] != 0; - } - - /** - * @dev Checks if a query ID exists - * @param queryId The ID of the query - * @return Whether the query ID exists - */ - function queryIdExists(uint256 queryId) public view returns (bool) { - return _getUniversalVerifierMultiQueryStorage()._queries[queryId].queryId == queryId; - } - - /** - * @dev Checks if an auth type exists - * @param authType The auth type - * @return Whether the auth type exists - */ - function authTypeExists(string memory authType) public view returns (bool) { - return _getUniversalVerifierMultiQueryStorage()._authMethods[authType].isActive == true; - } - - /** - * @dev Sets a request - * @param request The request data - */ - function _setRequestWithChecks( - Request calldata request - ) - internal - checkRequestExistence(request.requestId, false) - checkRequestGroupExistence(request, false) - onlyWhitelistedValidator(request.validator) - { - _setRequest(request); - } - - function _setRequest( - Request calldata request - ) internal checkRequestExistence(request.requestId, false) { - UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); - uint256 verifierId = request.validator.getVerifierId(request.params); - - s._requests[request.requestId] = RequestData({ - metadata: request.metadata, - validator: request.validator, - params: request.params, - creator: _msgSender(), - verifierId: verifierId - }); - s._requestIds.push(request.requestId); - - emit RequestSet( - request.requestId, - _msgSender(), - request.metadata, - address(request.validator), - request.params - ); - } - - /** - * @dev Sets different requests - * @param singleRequests The requests that are not in any group - * @param groupedRequests The requests that are in a group - */ - function setRequests( - Request[] calldata singleRequests, - GroupedRequests[] calldata groupedRequests - ) public { - UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); - - for (uint256 i = 0; i < singleRequests.length; i++) { - _setRequestWithChecks(singleRequests[i]); - } - for (uint256 i = 0; i < groupedRequests.length; i++) { - if (groupIdExists(groupedRequests[i].groupId)) { - revert("Group ID already exists"); - } - s._groupIds.push(groupedRequests[i].groupId); - - for (uint256 j = 0; j < groupedRequests[i].requests.length; j++) { - _setRequest(groupedRequests[i].requests[j]); - s._groupedRequests[groupedRequests[i].groupId].push( - groupedRequests[i].requests[j].requestId - ); - } - } - } - - /** - * @dev Gets a specific request by ID - * @param requestId The ID of the request - * @return request The request info - */ - function getRequest( - uint256 requestId - ) public view checkRequestExistence(requestId, true) returns (RequestInfo memory request) { - UniversalVerifierMultiQueryStorage storage $ = _getUniversalVerifierMultiQueryStorage(); - RequestData storage rd = $._requests[requestId]; - return - RequestInfo({ - requestId: requestId, - metadata: rd.metadata, - validator: rd.validator, - params: rd.params, - creator: rd.creator, - verifierId: rd.verifierId, - isVerifierAuthenticated: $._user_auth_timestamp[rd.verifierId][rd.creator] != 0 - }); - } - - /** - * @dev Sets a query - * @param queryId The ID of the query - * @param query The query data - */ - function setQuery( - uint256 queryId, - Query calldata query - ) public checkQueryExistence(queryId, false) { - UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); - s._queries[queryId] = query; - s._queryIds.push(queryId); - - // checks for all the requests in this query - _checkRequestsInQuery(queryId); - - emit QuerySet(queryId, query.requestIds); - } - - /** - * @dev Gets a specific multi query by ID - * @param queryId The ID of the multi query - * @return query The query data - */ - function getQuery(uint256 queryId) public view returns (Query memory query) { - return _getUniversalVerifierMultiQueryStorage()._queries[queryId]; - } - - function _checkRequestsInQuery(uint256 queryId) internal view { - UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); - - uint256[] memory requestIds = s._queries[queryId].requestIds; - - // check that all the single requests doesn't have group - for (uint256 i = 0; i < requestIds.length; i++) { - if ( - s._requests[requestIds[i]].validator.getGroupID( - s._requests[requestIds[i]].params - ) != 0 - ) { - revert RequestIsAlreadyGrouped(requestIds[i]); - } - } - } - - /** - * @dev Submits an array of responses and updates proofs status - * @param authResponses The list of auth responses including auth type and proof - * @param singleResponses The list of responses including request ID, proof and metadata for single requests - * @param groupedResponses The list of responses including request ID, proof and metadata for grouped requests - * @param crossChainProofs The list of cross chain proofs from universal resolver (oracle). This - * includes identities and global states. - */ - function submitResponse( - AuthResponse[] memory authResponses, - Response[] memory singleResponses, - GroupedResponses[] memory groupedResponses, - bytes memory crossChainProofs - ) public { - UniversalVerifierMultiQueryStorage storage $ = _getUniversalVerifierMultiQueryStorage(); - address sender = _msgSender(); - - // 1. Process crossChainProofs - $._state.processCrossChainProofs(crossChainProofs); - - // 2. Process auth response first - if (authResponses.length != 1) { - revert AuthResponsesExactlyOneRequired(); - } - - uint256 userIDFromReponse; - AuthTypeData storage authTypeData = $._authMethods[authResponses[0].authType]; - // Authenticate user - IAuthValidator.ResponseField[] memory authSignals = authTypeData.validator.verify( - authResponses[0].proof, - authTypeData.params, - sender, - $._state - ); - - for (uint256 j = 0; j < authSignals.length; j++) { - if (keccak256(bytes(authSignals[j].name)) == keccak256(bytes("userID"))) { - userIDFromReponse = authSignals[j].value; - break; - } - } - - // For some reason the auth request doesn't return the userID in the response - if (userIDFromReponse != 0) { - writeAuthProofResults(authResponses[0].authType, userIDFromReponse, authSignals); - emit AuthResponseSubmitted(authResponses[0].authType, _msgSender()); - // Link userID and user address - $._user_address_to_id[sender] = userIDFromReponse; - $._id_to_user_address[userIDFromReponse] = sender; - $._user_auth_timestamp[userIDFromReponse][sender] = block.timestamp; - } - - // 3. Get userID from latest auth response processed in this submitResponse or before - uint256 userID = $._user_address_to_id[sender]; - - if (userID == 0) { - revert("The user is not authenticated"); - } - - // 4. Verify all the single responses, write proof results (under the userID key from the auth of the user), - // emit events (existing logic) - for (uint256 i = 0; i < singleResponses.length; i++) { - Response memory response = singleResponses[i]; - RequestData storage request = $._requests[response.requestId]; - - IRequestValidator.ResponseField[] memory signals = request.validator.verify( - response.proof, - request.params, - sender, - $._state - ); - - writeProofResults(response.requestId, userID, signals); - - if (response.metadata.length > 0) { - revert("Metadata not supported yet"); - } - // emit for all the responses - emit ResponseSubmitted(response.requestId, _msgSender()); - } - - // 5. Verify all the grouped responses, write proof results (under the userID key from the auth of the user), - // emit events (existing logic) - _verifyGroupedResponses(groupedResponses, userID, sender); - } - - function _verifyGroupedResponses( - GroupedResponses[] memory groupedResponses, - uint256 userID, - address sender - ) internal { - UniversalVerifierMultiQueryStorage storage $ = _getUniversalVerifierMultiQueryStorage(); - - for (uint256 i = 0; i < groupedResponses.length; i++) { - for (uint256 j = 0; j < groupedResponses[i].responses.length; j++) { - Response memory response = groupedResponses[i].responses[j]; - RequestData storage request = $._requests[response.requestId]; - - IRequestValidator.ResponseField[] memory signals = request.validator.verify( - response.proof, - request.params, - sender, - $._state - ); - - writeProofResults(response.requestId, userID, signals); - - if (response.metadata.length > 0) { - revert("Metadata not supported yet"); - } - - emit ResponseSubmitted(response.requestId, _msgSender()); - } - } - } - - /** - * @dev Writes proof results. - * @param requestId The request ID of the proof - * @param userID The userID of the proof - * @param responseFields The array of response fields of the proof - */ - function writeProofResults( - uint256 requestId, - uint256 userID, - IRequestValidator.ResponseField[] memory responseFields - ) public { - UniversalVerifierMultiQueryStorage storage $ = _getUniversalVerifierMultiQueryStorage(); - - Proof[] storage proofs = $._proofs[requestId][userID]; - // We only keep only 1 proof now without history. Prepared for the future if needed. - if (proofs.length == 0) { - proofs.push(); - } - for (uint256 i = 0; i < responseFields.length; i++) { - proofs[0].storageFields[responseFields[i].name] = responseFields[i].value; - } - - proofs[0].isVerified = true; - proofs[0].validatorVersion = $._requests[requestId].validator.version(); - proofs[0].blockTimestamp = block.timestamp; - } - - /** - * @dev Writes proof results. - * @param authType The auth type of the proof - * @param userID The userID of the proof - * @param responseFields The array of response fields of the proof - */ - function writeAuthProofResults( - string memory authType, - uint256 userID, - IAuthValidator.ResponseField[] memory responseFields - ) public { - UniversalVerifierMultiQueryStorage storage $ = _getUniversalVerifierMultiQueryStorage(); - - AuthProof[] storage proofs = $._authProofs[authType][userID]; - if (proofs.length == 0) { - proofs.push(); - } - for (uint256 i = 0; i < responseFields.length; i++) { - proofs[0].storageFields[responseFields[i].name] = responseFields[i].value; - } - - proofs[0].isVerified = true; - proofs[0].validatorVersion = $._authMethods[authType].validator.version(); - proofs[0].blockTimestamp = block.timestamp; - } - - /** - * @dev Sets an auth type - * @param authType The auth type to add - */ - function setAuthType( - AuthType calldata authType - ) public onlyOwner checkAuthTypeExistence(authType.authType, false) { - UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); - s._authTypes.push(authType.authType); - s._authMethods[authType.authType] = AuthTypeData({ - validator: authType.validator, - params: authType.params, - isActive: true - }); - - emit AuthTypeSet(authType.authType, address(authType.validator), authType.params); - } - - /** - * @dev Disables an auth type - * @param authType The auth type to disable - */ - function disableAuthType( - string calldata authType - ) public onlyOwner checkAuthTypeExistence(authType, true) { - UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); - s._authMethods[authType].isActive = false; - } - - /** - * @dev Enables an auth type - * @param authType The auth type to enable - */ - function enableAuthType( - string calldata authType - ) public onlyOwner checkAuthTypeExistence(authType, true) { - UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); - s._authMethods[authType].isActive = true; - } - - /** - * @dev Gets an auth type - * @param authType The Id of the auth type to get - * @return authMethod The auth type data - */ - function getAuthType( - string calldata authType - ) public view checkAuthTypeExistence(authType, true) returns (AuthTypeData memory authMethod) { - return _getUniversalVerifierMultiQueryStorage()._authMethods[authType]; - } - - /** - * @dev Gets response field value - * @param requestId Id of the request - * @param userID Id of the user - * @param responseFieldName Name of the response field to get - */ - function getResponseFieldValue( - uint256 requestId, - uint256 userID, - string memory responseFieldName - ) public view checkRequestExistence(requestId, true) returns (uint256) { - UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); - return s._proofs[requestId][userID][0].storageFields[responseFieldName]; - } - - /** - * @dev Gets response field value - * @param requestId Id of the request - * @param sender Address of the sender - * @param responseFieldName Name of the response field to get - */ - function getResponseFieldValueFromAddress( - uint256 requestId, - address sender, - string memory responseFieldName - ) public view checkRequestExistence(requestId, true) returns (uint256) { - UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); - uint256 userID = s._user_address_to_id[sender]; - return s._proofs[requestId][userID][0].storageFields[responseFieldName]; - } - - /** - * @dev Gets response field value - * @param authType Auth type of the proof response - * @param sender Address of the sender - * @param responseFieldName Name of the response field to get - */ - function getAuthResponseFieldValueFromAddress( - string memory authType, - address sender, - string memory responseFieldName - ) public view checkAuthTypeExistence(authType, true) returns (uint256) { - UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); - uint256 userID = s._user_address_to_id[sender]; - return s._authProofs[authType][userID][0].storageFields[responseFieldName]; - } - - function _checkLinkedResponseFields(uint256 queryId, uint256 userID) internal view { - UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); - - for (uint256 i = 0; i < s._queries[queryId].groupIds.length; i++) { - uint256 groupId = s._queries[queryId].groupIds[i]; - - // Check linkID in the same group or requests is the same - uint256 requestLinkID = getResponseFieldValue( - s._groupedRequests[groupId][0], - userID, - LINKED_PROOF_KEY - ); - for (uint256 j = 1; j < s._groupedRequests[groupId].length; j++) { - uint256 requestLinkIDToCompare = getResponseFieldValue( - s._groupedRequests[groupId][j], - userID, - LINKED_PROOF_KEY - ); - if (requestLinkID != requestLinkIDToCompare) { - revert LinkIDNotTheSameForGroupedRequests( - requestLinkID, - requestLinkIDToCompare - ); - } - } - } - } - - /** - * @dev Gets the status of the query verification - * @param queryId The ID of the query - * @param userAddress The address of the user - * @return status The status of the query. "True" if all requests are verified, "false" otherwise - */ - function getQueryStatus( - uint256 queryId, - address userAddress - ) - public - view - checkQueryExistence(queryId, true) - returns (AuthProofStatus[] memory, RequestProofStatus[] memory) - { - UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); - - // 1. Get the latest userId by the userAddress arg (in the mapping) - uint256 userID = s._user_address_to_id[userAddress]; - if (userID == 0) { - revert UserIDNotFound(userID); - } - - // 2. Check if all requests statuses are true for the userId - ( - AuthProofStatus[] memory authProofStatus, - RequestProofStatus[] memory requestProofStatus - ) = _getQueryStatus(queryId, userID); - - // 3. Check if all linked response fields are the same - _checkLinkedResponseFields(queryId, userID); - - return (authProofStatus, requestProofStatus); - } - - /** - * @dev Gets the status of the query verification - * @param queryId The ID of the query - * @param userAddress The address of the user - * @param userID The user id of the user - * @return status The status of the query. "True" if all requests are verified, "false" otherwise - */ - function getQueryStatus( - uint256 queryId, - address userAddress, - uint256 userID - ) - public - view - checkQueryExistence(queryId, true) - returns (AuthProofStatus[] memory, RequestProofStatus[] memory) - { - UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); - - // 1. Get the latest userId by the userAddress arg (in the mapping) - uint256 userIDFromAddress = s._user_address_to_id[userAddress]; - uint256 userIDSelected; - - if (userIDFromAddress != userID) { - address addressFromUserID = s._id_to_user_address[userID]; - if (addressFromUserID != userAddress) { - revert UserIDNotLinkedToAddress(userID, userAddress); - } - userIDSelected = s._user_address_to_id[addressFromUserID]; - } else { - userIDSelected = userID; - } - - // 2. Check if all requests statuses are true for the userId - ( - AuthProofStatus[] memory authProofStatus, - RequestProofStatus[] memory requestProofStatus - ) = _getQueryStatus(queryId, userIDSelected); - - // 3. Check if all linked response fields are the same - _checkLinkedResponseFields(queryId, userIDSelected); - - return (authProofStatus, requestProofStatus); - } - - function _getQueryStatus( - uint256 queryId, - uint256 userID - ) internal view returns (AuthProofStatus[] memory, RequestProofStatus[] memory) { - UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); - Query storage query = s._queries[queryId]; - - uint256 lengthGroupIds; - - if (query.groupIds.length > 0) { - for (uint256 i = 0; i < query.groupIds.length; i++) { - uint256 groupId = query.groupIds[i]; - lengthGroupIds += s._groupedRequests[groupId].length; - } - } - - AuthProofStatus[] memory authProofStatus = new AuthProofStatus[](s._authTypes.length); - RequestProofStatus[] memory requestProofStatus = new RequestProofStatus[]( - query.requestIds.length + lengthGroupIds - ); - - for (uint256 i = 0; i < s._authTypes.length; i++) { - string memory authType = s._authTypes[i]; - authProofStatus[i] = AuthProofStatus({ - authType: authType, - isVerified: s._authProofs[authType][userID][0].isVerified, - validatorVersion: s._authProofs[authType][userID][0].validatorVersion, - timestamp: s._authProofs[authType][userID][0].blockTimestamp - }); - } - - for (uint256 i = 0; i < query.requestIds.length; i++) { - uint256 requestId = query.requestIds[i]; - - requestProofStatus[i] = RequestProofStatus({ - requestId: requestId, - isVerified: s._proofs[requestId][userID][0].isVerified, - validatorVersion: s._proofs[requestId][userID][0].validatorVersion, - timestamp: s._proofs[requestId][userID][0].blockTimestamp - }); - } - - for (uint256 i = 0; i < query.groupIds.length; i++) { - uint256 groupId = query.groupIds[i]; - - for (uint256 j = 0; j < s._groupedRequests[groupId].length; j++) { - uint256 requestId = s._groupedRequests[groupId][j]; - - requestProofStatus[query.requestIds.length + j] = RequestProofStatus({ - requestId: requestId, - isVerified: s._proofs[requestId][userID][0].isVerified, - validatorVersion: s._proofs[requestId][userID][0].validatorVersion, - timestamp: s._proofs[requestId][userID][0].blockTimestamp - }); - } - } - - return (authProofStatus, requestProofStatus); - } - - /** - * @dev Checks if a user is authenticated - * @param userID The ID of the user - * @param userAddress The address of the user - * @return Whether the user is authenticated - */ - function isUserAuth(uint256 userID, address userAddress) public view returns (bool) { - UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); - return s._user_auth_timestamp[userID][userAddress] != 0; - } - - /** - * @dev Gets the timestamp of the authentication of a user - * @param userID The user id of the user - * @param userAddress The address of the user - * @return The user ID - */ - function userAuthTimestamp(uint256 userID, address userAddress) public view returns (uint256) { - if (isUserAuth(userID, userAddress)) { - UniversalVerifierMultiQueryStorage storage s = _getUniversalVerifierMultiQueryStorage(); - - return s._user_auth_timestamp[userID][userAddress]; - } else { - return 0; - } - } - - /** - * @dev Adds a validator to the whitelist - * @param validator The validator to add - */ - function addValidatorToWhitelist(IRequestValidator validator) public { - if (!IERC165(address(validator)).supportsInterface(type(IRequestValidator).interfaceId)) { - revert ValidatorNotSupportInterface(address(validator)); - } - - _getUniversalVerifierMultiQueryStorage()._validatorWhitelist[validator] = true; - } - - /** - * @dev Removes a validator from the whitelist - * @param validator The validator to remove - */ - function removeValidatorFromWhitelist(IRequestValidator validator) public { - _getUniversalVerifierMultiQueryStorage()._validatorWhitelist[validator] = false; - } - - /** - * @dev Checks if a validator is whitelisted - * @param validator The validator to check - * @return Whether the validator is whitelisted - */ - function isWhitelistedValidator( - IRequestValidator validator - ) public view virtual returns (bool) { - return _getUniversalVerifierMultiQueryStorage()._validatorWhitelist[validator]; - } -} diff --git a/contracts/verifiers/ValidatorWhitelist.sol b/contracts/verifiers/ValidatorWhitelist.sol index bdbf2ff9..8c985685 100644 --- a/contracts/verifiers/ValidatorWhitelist.sol +++ b/contracts/verifiers/ValidatorWhitelist.sol @@ -38,7 +38,7 @@ contract ValidatorWhitelist is Verifier { IVerifier.GroupedRequests[] calldata groupedRequests ) public virtual override { for (uint256 i = 0; i < singleRequests.length; i++) { - IRequestValidator validator = getRequest(singleRequests[i].requestId).validator; + IRequestValidator validator = singleRequests[i].validator; if (!isWhitelistedValidator(validator)) { revert ValidatorIsNotWhitelisted(address(validator)); } @@ -46,8 +46,7 @@ contract ValidatorWhitelist is Verifier { for (uint256 i = 0; i < groupedRequests.length; i++) { for (uint256 j = 0; j < groupedRequests[i].requests.length; j++) { - IRequestValidator validator = getRequest(groupedRequests[i].requests[j].requestId) - .validator; + IRequestValidator validator = groupedRequests[i].requests[j].validator; if (!isWhitelistedValidator(validator)) { revert ValidatorIsNotWhitelisted(address(validator)); } diff --git a/contracts/verifiers/Verifier.sol b/contracts/verifiers/Verifier.sol index 9f6d5cc7..1cc53895 100644 --- a/contracts/verifiers/Verifier.sol +++ b/contracts/verifiers/Verifier.sol @@ -59,19 +59,7 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { uint256 timestamp; } - /** - * @dev Query. Structure for query. - * @param queryId Query id. - * @param requestIds Request ids for this multi query (without groupId. Single requests). - * @param groupIds Group ids for this multi query (all the requests included in the group. Grouped requests). - * @param metadata Metadata for the query. Empty in first version. - */ - struct Query { - uint256 queryId; - uint256[] requestIds; - uint256[] groupIds; - bytes metadata; - } + /// @custom:storage-location erc7201:iden3.storage.Verifier struct VerifierStorage { @@ -105,15 +93,7 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { using VerifierLib for VerifierStorage; - /** - * @dev Event emitted upon adding a query - */ - event QuerySet(uint256 indexed queryId, uint256[] requestIds); - /** - * @dev Event emitted upon updating a query - */ - event QueryUpdate(uint256 indexed queryId, uint256[] requestIds); /** * @dev Modifier to check if the request exists @@ -326,16 +306,14 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { */ function setQuery( uint256 queryId, - Query calldata query - ) public checkQueryExistence(queryId, false) { + IVerifier.Query calldata query + ) public virtual checkQueryExistence(queryId, false) { VerifierStorage storage s = _getVerifierStorage(); s._queries[queryId] = query; s._queryIds.push(queryId); // checks for all the requests in this query _checkRequestsInQuery(queryId); - - emit QuerySet(queryId, query.requestIds); } /** @@ -343,7 +321,7 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { * @param queryId The ID of the multi query * @return query The query data */ - function getQuery(uint256 queryId) public view returns (Query memory query) { + function getQuery(uint256 queryId) public view returns (IVerifier.Query memory query) { return _getVerifierStorage()._queries[queryId]; } diff --git a/hardhat.config.ts b/hardhat.config.ts index 5246ba30..ef82cf2b 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -42,7 +42,7 @@ const config: HardhatUserConfig = { }, ], overrides: { - "contracts/verifiers/UniversalVerifierMultiQuery.sol": { + "contracts/verifiers/UniversalVerifier.sol": { version: "0.8.27", settings: { optimizer: { diff --git a/helpers/DeployHelper.ts b/helpers/DeployHelper.ts index 1ea17d87..e8356844 100644 --- a/helpers/DeployHelper.ts +++ b/helpers/DeployHelper.ts @@ -1011,93 +1011,6 @@ export class DeployHelper { return universalVerifier; } - async deployUniversalVerifierMultiQuery( - owner: SignerWithAddress | undefined, - stateAddr: string, - deployStrategy: "basic" | "create2" = "basic", - ): Promise { - if (!owner) { - owner = this.signers[0]; - } - const UniversalVerifierMultiQueryFactory = await ethers.getContractFactory( - contractsInfo.UNIVERSAL_VERIFIER_MULTIQUERY.name, - { - signer: owner, - }, - ); - const Create2AddressAnchorFactory = await ethers.getContractFactory( - contractsInfo.CREATE2_ADDRESS_ANCHOR.name, - ); - - let universalVerifierMultiQuery; - let create2AlreadyDeployed = false; - - if (deployStrategy === "create2") { - this.log("deploying with CREATE2 strategy..."); - - universalVerifierMultiQuery = await getUnifiedContract( - contractsInfo.UNIVERSAL_VERIFIER_MULTIQUERY.name, - ); - if (universalVerifierMultiQuery) { - let version; - try { - version = await universalVerifierMultiQuery.VERSION(); - } catch (e) { - create2AlreadyDeployed = true; - Logger.warning( - `Create2AnchorAddress implementation already deployed to TransparentUpgradeableProxy of ${contractsInfo.UNIVERSAL_VERIFIER_MULTIQUERY.name}.`, - ); - } - - if (version) { - Logger.warning( - `${contractsInfo.UNIVERSAL_VERIFIER.name} found already deployed to: ${await universalVerifierMultiQuery?.getAddress()}`, - ); - return universalVerifierMultiQuery; - } - } - - if (!create2AlreadyDeployed) { - // Deploying UniversalVerifier contract to predictable address but with dummy implementation - universalVerifierMultiQuery = ( - await ignition.deploy(UniversalVerifierMultiQueryProxyModule, { - strategy: deployStrategy, - }) - ).proxy; - await universalVerifierMultiQuery.waitForDeployment(); - } - // Upgrading UniversalVerifierMultiQuery contract to the first real implementation - // and force network files import, so creation, as they do not exist at the moment - const universalVerifierMultiQueryAddress = await universalVerifierMultiQuery.getAddress(); - await upgrades.forceImport(universalVerifierMultiQueryAddress, Create2AddressAnchorFactory); - universalVerifierMultiQuery = await upgrades.upgradeProxy( - universalVerifierMultiQuery, - UniversalVerifierMultiQueryFactory, - { - redeployImplementation: "always", - call: { - fn: "initialize", - args: [stateAddr, await owner.getAddress()], - }, - }, - ); - } else { - this.log("deploying with BASIC strategy..."); - - universalVerifierMultiQuery = await upgrades.deployProxy(UniversalVerifierMultiQueryFactory, [ - stateAddr, - await owner.getAddress(), - ]); - } - - await universalVerifierMultiQuery.waitForDeployment(); - Logger.success( - `${contractsInfo.UNIVERSAL_VERIFIER_MULTIQUERY.name} deployed to: ${await universalVerifierMultiQuery.getAddress()}`, - ); - - return universalVerifierMultiQuery; - } - async getDefaultIdType(): Promise<{ defaultIdType: string; chainId: number }> { const chainId = parseInt(await network.provider.send("eth_chainId"), 16); const defaultIdType = chainIdInfoMap.get(chainId)?.idType; diff --git a/helpers/constants.ts b/helpers/constants.ts index 0af3b957..afde6e08 100644 --- a/helpers/constants.ts +++ b/helpers/constants.ts @@ -123,25 +123,6 @@ export const contractsInfo = Object.freeze({ libraries: {}, }, }, - UNIVERSAL_VERIFIER_MULTIQUERY: { - name: "UniversalVerifierMultiQuery", - version: "1.0.0", - unifiedAddress: "", - create2Calldata: ethers.hexlify( - ethers.toUtf8Bytes("iden3.create2.UniversalVerifierMultiQuery"), - ), - verificationOpts: { - // For verifying the different contracts with proxy we need verification with different constructor arguments - constructorArgsImplementation: [], - constructorArgsProxy: [ - "0x56fF81aBB5cdaC478bF236db717e4976b2ff841e", - "0xae15d2023a76174a940cbb2b7f44012c728b9d74", - "0x6964656e332e637265617465322e556e6976657273616c5665726966696572", - ], - constructorArgsProxyAdmin: ["0xAe15d2023A76174a940cbb2b7F44012C728B9d74"], - libraries: {}, - }, - }, STATE: { name: "State", version: "2.6.1", diff --git a/ignition/modules/universalVerifier.ts b/ignition/modules/universalVerifier.ts index ee3e3154..c834058b 100644 --- a/ignition/modules/universalVerifier.ts +++ b/ignition/modules/universalVerifier.ts @@ -20,26 +20,3 @@ export const UniversalVerifierProxyModule = buildModule("UniversalVerifierProxyM const proxyAdmin = m.contractAt("ProxyAdmin", proxyAdminAddress); return { proxyAdmin, proxy }; }); - -export const UniversalVerifierMultiQueryProxyModule = buildModule( - "UniversalVerifierMultiQueryProxyModule", - (m) => { - const proxyAdminOwner = m.getAccount(0); - - // This contract is supposed to be deployed to the same address across many networks, - // so the first implementation address is a dummy contract that does nothing but accepts any calldata. - // Therefore, it is a mechanism to deploy TransparentUpgradeableProxy contract - // with constant constructor arguments, so predictable init bytecode and predictable CREATE2 address. - // Subsequent upgrades are supposed to switch this proxy to the real implementation. - - const proxy = m.contract("TransparentUpgradeableProxy", [ - contractsInfo.CREATE2_ADDRESS_ANCHOR.unifiedAddress, - proxyAdminOwner, - contractsInfo.UNIVERSAL_VERIFIER_MULTIQUERY.create2Calldata, - ]); - - const proxyAdminAddress = m.readEventArgument(proxy, "AdminChanged", "newAdmin"); - const proxyAdmin = m.contractAt("ProxyAdmin", proxyAdminAddress); - return { proxyAdmin, proxy }; - }, -); diff --git a/test/verifier/universal-verifier-multi-query.test.ts b/test/verifier/universal-verifier-multi-query.test.ts index 042d57e7..badbc0b6 100644 --- a/test/verifier/universal-verifier-multi-query.test.ts +++ b/test/verifier/universal-verifier-multi-query.test.ts @@ -137,9 +137,12 @@ describe("Universal Verifier Multi-query", function () { await state.setCrossChainProofValidator(crossChainProofValidatorStub); stateCrossChainStub = state; - verifier = await deployHelper.deployUniversalVerifierMultiQuery( + const verifierLib = await deployHelper.deployVerifierLib(); + + verifier = await deployHelper.deployUniversalVerifier( signer, await stateCrossChainStub.getAddress(), + await verifierLib.getAddress(), ); v3Validator = await deployHelper.deployValidatorStub("RequestValidatorV3Stub"); From fb2a5b438f5b40bdf4edcf5aae086d9a00fe6624 Mon Sep 17 00:00:00 2001 From: daveroga Date: Fri, 20 Dec 2024 13:33:17 +0100 Subject: [PATCH 42/69] update verifiers tests --- contracts/interfaces/IRequestValidator.sol | 7 - contracts/interfaces/IVerifier.sol | 13 +- .../RequestValidatorAuthV2Stub.sol | 6 +- .../test-helpers/RequestValidatorV2Stub.sol | 6 +- .../test-helpers/RequestValidatorV3Stub.sol | 5 - .../test-helpers/RequestValidatorV3_2Stub.sol | 5 - contracts/validators/AuthV2Validator.sol | 65 ++- .../CredentialAtomicQueryV2ValidatorBase.sol | 101 ++--- .../CredentialAtomicQueryV3Validator.sol | 137 +++--- .../CredentialAtomicQueryValidatorBase.sol | 40 +- contracts/validators/EthIdentityValidator.sol | 5 - contracts/verifiers/UniversalVerifier.sol | 11 +- contracts/verifiers/Verifier.sol | 53 ++- hardhat.config.ts | 9 + helpers/DeployHelper.ts | 9 +- helpers/constants.ts | 8 +- .../verifiers/embedded-verifier-upgrade.ts | 17 +- test/verifier/embedded-zkp-verifier.test.ts | 200 ++++----- .../universal-verifier-linked-proofs.test.ts | 79 ---- .../universal-verifier-submit-V2.test.ts | 216 --------- .../universal-verifier.events.test.ts | 54 ++- test/verifier/universal-verifier.test.ts | 419 ++++++++++++------ test/verifier/universal-verifier.v3.test.ts | 375 ++++++++++++---- 23 files changed, 937 insertions(+), 903 deletions(-) delete mode 100644 test/verifier/universal-verifier-linked-proofs.test.ts delete mode 100644 test/verifier/universal-verifier-submit-V2.test.ts diff --git a/contracts/interfaces/IRequestValidator.sol b/contracts/interfaces/IRequestValidator.sol index 4a1d15e1..70effc38 100644 --- a/contracts/interfaces/IRequestValidator.sol +++ b/contracts/interfaces/IRequestValidator.sol @@ -45,13 +45,6 @@ interface IRequestValidator { */ function getGroupID(bytes calldata params) external view returns (uint256); - /** - * @dev Get the hash of the group Id of the request query data. - * @param params Request query data of the credential to verify. - * @return Hash of the group Id of the request query data. - */ - function getGroupFieldHash(bytes calldata params) external view returns (bytes32); - /** * @dev Get the verifier ID of the request query data. * @param params Request query data of the credential to verify. diff --git a/contracts/interfaces/IVerifier.sol b/contracts/interfaces/IVerifier.sol index 16093183..d793f715 100644 --- a/contracts/interfaces/IVerifier.sol +++ b/contracts/interfaces/IVerifier.sol @@ -55,13 +55,11 @@ interface IVerifier { * @dev ProofStatus. Structure for proof status. * @param isVerified True if the proof is verified. * @param validatorVersion Version of the validator. - * @param blockNumber Block number of the proof. * @param blockTimestamp Block timestamp of the proof. */ struct ProofStatus { bool isVerified; string validatorVersion; - uint256 blockNumber; uint256 blockTimestamp; } @@ -238,4 +236,15 @@ interface IVerifier { * @return query The query data */ function getQuery(uint256 queryId) external view returns (IVerifier.Query memory query); + + /** + * @dev Get the proof status for the sender and request with requestId. + * @param sender Sender of the proof. + * @param requestId Request id of the proof. + * @return Proof status. + */ + function getProofStatus( + address sender, + uint256 requestId + ) external view returns (ProofStatus memory); } diff --git a/contracts/test-helpers/RequestValidatorAuthV2Stub.sol b/contracts/test-helpers/RequestValidatorAuthV2Stub.sol index a0330020..b8fa1919 100644 --- a/contracts/test-helpers/RequestValidatorAuthV2Stub.sol +++ b/contracts/test-helpers/RequestValidatorAuthV2Stub.sol @@ -34,11 +34,7 @@ contract RequestValidatorAuthV2Stub is IRequestValidator, ERC165 { } function getGroupID(bytes calldata) external pure override returns (uint256) { - revert("AuthV2 validator does not support groupId field"); - } - - function getGroupFieldHash(bytes calldata params) external pure override returns (bytes32) { - revert("AuthV2 validator does not support groupId field"); + return 0; } function getVerifierId(bytes calldata) external pure override returns (uint256) { diff --git a/contracts/test-helpers/RequestValidatorV2Stub.sol b/contracts/test-helpers/RequestValidatorV2Stub.sol index 605307c4..66b79081 100644 --- a/contracts/test-helpers/RequestValidatorV2Stub.sol +++ b/contracts/test-helpers/RequestValidatorV2Stub.sol @@ -36,11 +36,7 @@ contract RequestValidatorV2Stub is IRequestValidator, ERC165 { } function getGroupID(bytes calldata) external pure override returns (uint256) { - revert("V2 validator does not support groupId field"); - } - - function getGroupFieldHash(bytes calldata params) external pure override returns (bytes32) { - revert("V2 validator does not support groupId field"); + return 0; } function getVerifierId(bytes calldata) external pure override returns (uint256) { diff --git a/contracts/test-helpers/RequestValidatorV3Stub.sol b/contracts/test-helpers/RequestValidatorV3Stub.sol index 0d772810..e3f4c73c 100644 --- a/contracts/test-helpers/RequestValidatorV3Stub.sol +++ b/contracts/test-helpers/RequestValidatorV3Stub.sol @@ -61,11 +61,6 @@ contract RequestValidatorV3Stub is IRequestValidator, ERC165 { return credAtomicQuery.groupID; } - function getGroupFieldHash(bytes calldata params) external pure override returns (bytes32) { - // TODO: Implement hash function - return keccak256(params); - } - function getVerifierId(bytes calldata params) external pure override returns (uint256) { CredentialAtomicQueryV3 memory credAtomicQuery = abi.decode( params, diff --git a/contracts/test-helpers/RequestValidatorV3_2Stub.sol b/contracts/test-helpers/RequestValidatorV3_2Stub.sol index 9236a43b..13b3b7f2 100644 --- a/contracts/test-helpers/RequestValidatorV3_2Stub.sol +++ b/contracts/test-helpers/RequestValidatorV3_2Stub.sol @@ -61,11 +61,6 @@ contract RequestValidatorV3_2Stub is IRequestValidator, ERC165 { return credAtomicQuery.groupID; } - function getGroupFieldHash(bytes calldata params) external pure override returns (bytes32) { - // TODO: Implement hash function - return keccak256(params); - } - function getVerifierId(bytes calldata params) external pure override returns (uint256) { CredentialAtomicQueryV3 memory credAtomicQuery = abi.decode( params, diff --git a/contracts/validators/AuthV2Validator.sol b/contracts/validators/AuthV2Validator.sol index 56e1df7d..2ee2da37 100644 --- a/contracts/validators/AuthV2Validator.sol +++ b/contracts/validators/AuthV2Validator.sol @@ -4,7 +4,7 @@ pragma solidity 0.8.27; import {CredentialAtomicQueryValidatorBase} from "./CredentialAtomicQueryValidatorBase.sol"; import {IGroth16Verifier} from "../interfaces/IGroth16Verifier.sol"; import {GenesisUtils} from "../lib/GenesisUtils.sol"; -import {ICircuitValidator} from "../interfaces/ICircuitValidator.sol"; +import {IRequestValidator} from "../interfaces/IRequestValidator.sol"; import {IState} from "../interfaces/IState.sol"; /** @@ -65,62 +65,53 @@ contract AuthV2Validator is CredentialAtomicQueryValidatorBase { return pubSignals; } - /** - * @dev Verify the groth16 proof and check the request query data - * @param inputs Public inputs of the circuit. - * @param a πa element of the groth16 proof. - * @param b πb element of the groth16 proof. - * @param c πc element of the groth16 proof. - * @param data Request query data of the credential to verify. - * @param sender Sender of the proof. - * @return Array of key to public input index as result. + /** + * @dev Get the group ID of the request query data. + * @param params Request query data of the credential to verify. + * @return Group ID of the request query data. */ - function verify( - // solhint-disable-next-line no-unused-vars - uint256[] memory inputs, - // solhint-disable-next-line no-unused-vars - uint256[2] memory a, - // solhint-disable-next-line no-unused-vars - uint256[2][2] memory b, - // solhint-disable-next-line no-unused-vars - uint256[2] memory c, - // solhint-disable-next-line no-unused-vars - bytes calldata data, - // solhint-disable-next-line no-unused-vars - address sender - ) public view override returns (ICircuitValidator.KeyToInputIndex[] memory) { - revert("function not supported in this contract"); + function getGroupID(bytes calldata params) external pure override returns (uint256) { + return 0; } /** + * @dev Get the verifier ID from the request query data + * @param params Request query data of the credential to verify. + * @return Verifier ID + */ + function getVerifierId(bytes calldata params) external pure override returns (uint256) { + return 0; + } + + /** * @dev Verify the groth16 proof and check the request query data - * @param zkProof Proof packed as bytes to verify. + * @param proof Proof packed as bytes to verify. * @param data Request query data of the credential to verify. * @param sender Sender of the proof. - * @param stateContract State contract to get identities and gist states to check. + * @param state State contract to get identities and gist states to check. * @return Array of public signals as result. */ - function verifyV2( - bytes calldata zkProof, + function verify( + bytes calldata proof, // solhint-disable-next-line no-unused-vars bytes calldata data, address sender, - IState stateContract - ) public view override returns (ICircuitValidator.Signal[] memory) { + IState state + ) public view override returns (IRequestValidator.ResponseField[] memory) { ( uint256[] memory inputs, uint256[2] memory a, uint256[2][2] memory b, uint256[2] memory c - ) = abi.decode(zkProof, (uint256[], uint256[2], uint256[2][2], uint256[2])); - + ) = abi.decode(proof, (uint256[], uint256[2], uint256[2][2], uint256[2])); + PubSignals memory pubSignals = parsePubSignals(inputs); - _checkGistRoot(pubSignals.userID, pubSignals.gistRoot, stateContract); + _checkGistRoot(pubSignals.userID, pubSignals.gistRoot, state); _checkChallenge(pubSignals.challenge, sender); _verifyZKP(inputs, a, b, c); - ICircuitValidator.Signal[] memory signals = new ICircuitValidator.Signal[](1); - signals[0] = ICircuitValidator.Signal({name: "userID", value: pubSignals.userID}); - return signals; + IRequestValidator.ResponseField[] memory responseFields = new IRequestValidator.ResponseField[](1); + responseFields[0] = IRequestValidator.ResponseField({name: "userID", value: pubSignals.userID}); + return responseFields; } function _verifyZKP( diff --git a/contracts/validators/CredentialAtomicQueryV2ValidatorBase.sol b/contracts/validators/CredentialAtomicQueryV2ValidatorBase.sol index 1b150acf..779afe8d 100644 --- a/contracts/validators/CredentialAtomicQueryV2ValidatorBase.sol +++ b/contracts/validators/CredentialAtomicQueryV2ValidatorBase.sol @@ -3,7 +3,7 @@ pragma solidity 0.8.27; import {CredentialAtomicQueryValidatorBase} from "./CredentialAtomicQueryValidatorBase.sol"; import {IGroth16Verifier} from "../interfaces/IGroth16Verifier.sol"; -import {ICircuitValidator} from "../interfaces/ICircuitValidator.sol"; +import {IRequestValidator} from "../interfaces/IRequestValidator.sol"; import {IState} from "../interfaces/IState.sol"; /** @@ -59,50 +59,45 @@ abstract contract CredentialAtomicQueryV2ValidatorBase is CredentialAtomicQueryV /** * @dev Verify the groth16 proof and check the request query data - * @param inputs Public inputs of the circuit. - * @param a πa element of the groth16 proof. - * @param b πb element of the groth16 proof. - * @param c πc element of the groth16 proof. + * @param proof Proof packed as bytes to verify. * @param data Request query data of the credential to verify. * @param sender Sender of the proof. - * @return Array of key to public input index as result. - */ - function verify( - uint256[] memory inputs, - uint256[2] memory a, - uint256[2][2] memory b, - uint256[2] memory c, - bytes calldata data, - address sender - ) public view override returns (ICircuitValidator.KeyToInputIndex[] memory) { - _verifyMain(inputs, a, b, c, data, sender, IState(getStateAddress())); - - return _getSpecialInputIndexes(); - } - - /** - * @dev Verify the groth16 proof and check the request query data - * @param zkProof Proof packed as bytes to verify. - * @param data Request query data of the credential to verify. - * @param sender Sender of the proof. - * @param stateContract State contract to get identities and gist states to check. + * @param state State contract to get identities and gist states to check. * @return Array of public signals as result. */ - function verifyV2( - bytes calldata zkProof, + function verify( + bytes calldata proof, bytes calldata data, address sender, - IState stateContract - ) public view override returns (ICircuitValidator.Signal[] memory) { + IState state + ) public view override returns (IRequestValidator.ResponseField[] memory) { ( uint256[] memory inputs, uint256[2] memory a, uint256[2][2] memory b, uint256[2] memory c - ) = abi.decode(zkProof, (uint256[], uint256[2], uint256[2][2], uint256[2])); + ) = abi.decode(proof, (uint256[], uint256[2], uint256[2][2], uint256[2])); - PubSignals memory pubSignals = _verifyMain(inputs, a, b, c, data, sender, stateContract); - return _getSpecialSignals(pubSignals); + PubSignals memory pubSignals = _verifyMain(inputs, a, b, c, data, sender, state); + return _getResponseFields(pubSignals); + } + + /** + * @dev Get the group ID of the request query data. + * @param params Request query data of the credential to verify. + * @return Group ID of the request query data. + */ + function getGroupID(bytes calldata params) external pure override returns (uint256) { + return 0; + } + + /** + * @dev Get the verifier ID from the request query data + * @param params Request query data of the credential to verify. + * @return Verifier ID + */ + function getVerifierId(bytes calldata params) external pure override returns (uint256) { + return 0; } /** @@ -183,35 +178,23 @@ abstract contract CredentialAtomicQueryV2ValidatorBase is CredentialAtomicQueryV ); } - function _getSpecialSignals( + function _getResponseFields( PubSignals memory pubSignals - ) internal pure returns (ICircuitValidator.Signal[] memory) { - ICircuitValidator.Signal[] memory signals = new ICircuitValidator.Signal[](3); - signals[0] = ICircuitValidator.Signal({name: "userID", value: pubSignals.userID}); - signals[1] = ICircuitValidator.Signal({name: "timestamp", value: pubSignals.timestamp}); - signals[2] = ICircuitValidator.Signal({name: "issuerID", value: pubSignals.issuerID}); - return signals; - } - - function _getSpecialInputIndexes() - internal - view - returns (ICircuitValidator.KeyToInputIndex[] memory) - { - ICircuitValidator.KeyToInputIndex[] - memory keyToInputIndexes = new ICircuitValidator.KeyToInputIndex[](3); - keyToInputIndexes[0] = ICircuitValidator.KeyToInputIndex({ - key: "userID", - inputIndex: inputIndexOf("userID") + ) internal pure returns (IRequestValidator.ResponseField[] memory) { + IRequestValidator.ResponseField[] + memory responseFields = new IRequestValidator.ResponseField[](3); + responseFields[0] = IRequestValidator.ResponseField({ + name: "userID", + value: pubSignals.userID }); - keyToInputIndexes[1] = ICircuitValidator.KeyToInputIndex({ - key: "timestamp", - inputIndex: inputIndexOf("timestamp") + responseFields[1] = IRequestValidator.ResponseField({ + name: "timestamp", + value: pubSignals.timestamp }); - keyToInputIndexes[2] = ICircuitValidator.KeyToInputIndex({ - key: "issuerID", - inputIndex: inputIndexOf("issuerID") + responseFields[2] = IRequestValidator.ResponseField({ + name: "issuerID", + value: pubSignals.issuerID }); - return keyToInputIndexes; + return responseFields; } } diff --git a/contracts/validators/CredentialAtomicQueryV3Validator.sol b/contracts/validators/CredentialAtomicQueryV3Validator.sol index 7d002073..1f1905d5 100644 --- a/contracts/validators/CredentialAtomicQueryV3Validator.sol +++ b/contracts/validators/CredentialAtomicQueryV3Validator.sol @@ -4,7 +4,7 @@ pragma solidity 0.8.27; import {CredentialAtomicQueryValidatorBase} from "./CredentialAtomicQueryValidatorBase.sol"; import {IGroth16Verifier} from "../interfaces/IGroth16Verifier.sol"; import {GenesisUtils} from "../lib/GenesisUtils.sol"; -import {ICircuitValidator} from "../interfaces/ICircuitValidator.sol"; +import {IRequestValidator} from "../interfaces/IRequestValidator.sol"; import {IState} from "../interfaces/IState.sol"; /** @@ -116,47 +116,24 @@ contract CredentialAtomicQueryV3Validator is CredentialAtomicQueryValidatorBase /** * @dev Verify the groth16 proof and check the request query data - * @param inputs Public inputs of the circuit. - * @param a πa element of the groth16 proof. - * @param b πb element of the groth16 proof. - * @param c πc element of the groth16 proof. + * @param proof Proof packed as bytes to verify. * @param data Request query data of the credential to verify. * @param sender Sender of the proof. - * @return Array of key to public input index as result. - */ - function verify( - uint256[] memory inputs, - uint256[2] memory a, - uint256[2][2] memory b, - uint256[2] memory c, - bytes calldata data, - address sender - ) public view override returns (ICircuitValidator.KeyToInputIndex[] memory) { - (, bool hasSD) = _verifyMain(inputs, a, b, c, data, sender, IState(getStateAddress())); - - return _getSpecialInputIndexes(hasSD); - } - - /** - * @dev Verify the groth16 proof and check the request query data - * @param zkProof Proof packed as bytes to verify. - * @param data Request query data of the credential to verify. - * @param sender Sender of the proof. - * @param stateContract State contract to get identities and gist states to check. + * @param state State contract to get identities and gist states to check. * @return Array of public signals as result. */ - function verifyV2( - bytes calldata zkProof, + function verify( + bytes calldata proof, bytes calldata data, address sender, - IState stateContract - ) public view override returns (ICircuitValidator.Signal[] memory) { + IState state + ) public view override returns (IRequestValidator.ResponseField[] memory) { ( uint256[] memory inputs, uint256[2] memory a, uint256[2][2] memory b, uint256[2] memory c - ) = abi.decode(zkProof, (uint256[], uint256[2], uint256[2][2], uint256[2])); + ) = abi.decode(proof, (uint256[], uint256[2], uint256[2][2], uint256[2])); (PubSignals memory pubSignals, bool hasSD) = _verifyMain( inputs, @@ -165,9 +142,35 @@ contract CredentialAtomicQueryV3Validator is CredentialAtomicQueryValidatorBase c, data, sender, - stateContract + state ); - return _getSpecialSignals(pubSignals, hasSD); + return _getResponseFields(pubSignals, hasSD); + } + + /** + * @dev Get the group ID of the request query data. + * @param params Request query data of the credential to verify. + * @return Group ID of the request query data. + */ + function getGroupID(bytes calldata params) external pure override returns (uint256) { + CredentialAtomicQueryV3 memory credAtomicQuery = abi.decode( + params, + (CredentialAtomicQueryV3) + ); + return credAtomicQuery.groupID; + } + + /** + * @dev Get the verifier ID from the request query data + * @param params Request query data of the credential to verify. + * @return Verifier ID + */ + function getVerifierId(bytes calldata params) external pure override returns (uint256) { + CredentialAtomicQueryV3 memory credAtomicQuery = abi.decode( + params, + (CredentialAtomicQueryV3) + ); + return credAtomicQuery.verifierID; } /** @@ -272,64 +275,42 @@ contract CredentialAtomicQueryV3Validator is CredentialAtomicQueryValidatorBase ); } - function _getSpecialSignals( + function _getResponseFields( PubSignals memory pubSignals, bool hasSelectiveDisclosure - ) internal pure returns (ICircuitValidator.Signal[] memory) { - uint256 numSignals = hasSelectiveDisclosure ? 6 : 5; - ICircuitValidator.Signal[] memory signals = new ICircuitValidator.Signal[](numSignals); - - uint i = 0; - signals[i++] = ICircuitValidator.Signal({name: "userID", value: pubSignals.userID}); - signals[i++] = ICircuitValidator.Signal({name: "linkID", value: pubSignals.linkID}); - signals[i++] = ICircuitValidator.Signal({name: "nullifier", value: pubSignals.nullifier}); - if (hasSelectiveDisclosure) { - signals[i++] = ICircuitValidator.Signal({ - name: "operatorOutput", - value: pubSignals.operatorOutput - }); - } - signals[i++] = ICircuitValidator.Signal({name: "timestamp", value: pubSignals.timestamp}); - signals[i++] = ICircuitValidator.Signal({name: "issuerID", value: pubSignals.issuerID}); - - return signals; - } - - function _getSpecialInputIndexes( - bool hasSelectiveDisclosure - ) internal view returns (ICircuitValidator.KeyToInputIndex[] memory) { + ) internal pure returns (IRequestValidator.ResponseField[] memory) { uint256 numSignals = hasSelectiveDisclosure ? 6 : 5; - ICircuitValidator.KeyToInputIndex[] - memory keyToInputIndexes = new ICircuitValidator.KeyToInputIndex[](numSignals); + IRequestValidator.ResponseField[] + memory responseFields = new IRequestValidator.ResponseField[](numSignals); uint i = 0; - keyToInputIndexes[i++] = ICircuitValidator.KeyToInputIndex({ - key: "userID", - inputIndex: inputIndexOf("userID") + responseFields[i++] = IRequestValidator.ResponseField({ + name: "userID", + value: pubSignals.userID }); - keyToInputIndexes[i++] = ICircuitValidator.KeyToInputIndex({ - key: "linkID", - inputIndex: inputIndexOf("linkID") + responseFields[i++] = IRequestValidator.ResponseField({ + name: "linkID", + value: pubSignals.linkID }); - keyToInputIndexes[i++] = ICircuitValidator.KeyToInputIndex({ - key: "nullifier", - inputIndex: inputIndexOf("nullifier") + responseFields[i++] = IRequestValidator.ResponseField({ + name: "nullifier", + value: pubSignals.nullifier }); if (hasSelectiveDisclosure) { - keyToInputIndexes[i++] = ICircuitValidator.KeyToInputIndex({ - key: "operatorOutput", - inputIndex: inputIndexOf("operatorOutput") + responseFields[i++] = IRequestValidator.ResponseField({ + name: "operatorOutput", + value: pubSignals.operatorOutput }); } - keyToInputIndexes[i++] = ICircuitValidator.KeyToInputIndex({ - key: "timestamp", - inputIndex: inputIndexOf("timestamp") + responseFields[i++] = IRequestValidator.ResponseField({ + name: "timestamp", + value: pubSignals.timestamp }); - keyToInputIndexes[i++] = ICircuitValidator.KeyToInputIndex({ - key: "issuerID", - inputIndex: inputIndexOf("issuerID") + responseFields[i++] = IRequestValidator.ResponseField({ + name: "issuerID", + value: pubSignals.issuerID }); - return keyToInputIndexes; + return responseFields; } } diff --git a/contracts/validators/CredentialAtomicQueryValidatorBase.sol b/contracts/validators/CredentialAtomicQueryValidatorBase.sol index 46b7cfab..ca1e723a 100644 --- a/contracts/validators/CredentialAtomicQueryValidatorBase.sol +++ b/contracts/validators/CredentialAtomicQueryValidatorBase.sol @@ -4,7 +4,7 @@ pragma solidity 0.8.27; import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; import {GenesisUtils} from "../lib/GenesisUtils.sol"; -import {ICircuitValidator} from "../interfaces/ICircuitValidator.sol"; +import {IRequestValidator} from "../interfaces/IRequestValidator.sol"; import {IGroth16Verifier} from "../interfaces/IGroth16Verifier.sol"; import {IState} from "../interfaces/IState.sol"; import {PrimitiveTypeUtils} from "../lib/PrimitiveTypeUtils.sol"; @@ -14,7 +14,7 @@ import {PrimitiveTypeUtils} from "../lib/PrimitiveTypeUtils.sol"; */ abstract contract CredentialAtomicQueryValidatorBase is Ownable2StepUpgradeable, - ICircuitValidator, + IRequestValidator, ERC165 { /// @dev Main storage structure for the contract @@ -138,38 +138,20 @@ abstract contract CredentialAtomicQueryValidatorBase is } /** - * @dev Verify the groth16 proof and check the request query data - * @param inputs Public inputs of the circuit. - * @param a πa element of the groth16 proof. - * @param b πb element of the groth16 proof. - * @param c πc element of the groth16 proof. + * @dev Verify the proof with the supported method informed in the request query data + * packed as bytes and that the proof was generated by the sender. + * @param proof Proof packed as bytes to verify. * @param data Request query data of the credential to verify. * @param sender Sender of the proof. - * @return Array of key to public input index as result. + * @param state State contract to get identities and gist states to check. + * @return Array of response fields as result. */ function verify( - uint256[] memory inputs, - uint256[2] memory a, - uint256[2][2] memory b, - uint256[2] memory c, - bytes calldata data, - address sender - ) external view virtual returns (ICircuitValidator.KeyToInputIndex[] memory); - - /** - * @dev Verify the groth16 proof and check the request query data - * @param zkProof Proof packed as bytes to verify. - * @param data Request query data of the credential to verify. - * @param sender Sender of the proof. - * @param stateContract State contract to get identities and gist states to check. - * @return Array of public signals as result. - */ - function verifyV2( - bytes calldata zkProof, + bytes calldata proof, bytes calldata data, address sender, - IState stateContract - ) external view virtual returns (ICircuitValidator.Signal[] memory); + IState state + ) external view virtual returns (ResponseField[] memory); /** * @dev Get supported circuit ids @@ -210,7 +192,7 @@ abstract contract CredentialAtomicQueryValidatorBase is */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return - interfaceId == type(ICircuitValidator).interfaceId || + interfaceId == type(IRequestValidator).interfaceId || super.supportsInterface(interfaceId); } diff --git a/contracts/validators/EthIdentityValidator.sol b/contracts/validators/EthIdentityValidator.sol index 4f02c0e8..737c8ba0 100644 --- a/contracts/validators/EthIdentityValidator.sol +++ b/contracts/validators/EthIdentityValidator.sol @@ -105,11 +105,6 @@ contract EthIdentityValidator is Ownable2StepUpgradeable, IRequestValidator, ERC return 0; } - function getGroupFieldHash(bytes calldata params) external pure override returns (bytes32) { - // TODO: Implement hash function - return keccak256(params); - } - /** * @dev Get the verifier ID of the request query data. * @param params Request query data of the credential to verify. diff --git a/contracts/verifiers/UniversalVerifier.sol b/contracts/verifiers/UniversalVerifier.sol index fb18bae7..54a09474 100644 --- a/contracts/verifiers/UniversalVerifier.sol +++ b/contracts/verifiers/UniversalVerifier.sol @@ -106,8 +106,8 @@ contract UniversalVerifier is * @param groupedRequests The requests that are in a group */ function setRequests( - Request[] calldata singleRequests, - GroupedRequests[] calldata groupedRequests + IVerifier.Request[] calldata singleRequests, + IVerifier.GroupedRequests[] calldata groupedRequests ) public override(RequestOwnership, ValidatorWhitelist, Verifier) { super.setRequests(singleRequests, groupedRequests); @@ -136,14 +136,13 @@ contract UniversalVerifier is /** * @dev Updates a request - * @param requestId The ID of the request * @param request The request data */ - function updateRequest(uint256 requestId, IVerifier.Request calldata request) public onlyOwner { - super._updateRequest(requestId, request); + function updateRequest(IVerifier.Request calldata request) public onlyOwner { + super._updateRequest(request); emit RequestUpdate( - requestId, + request.requestId, _msgSender(), request.metadata, address(request.validator), diff --git a/contracts/verifiers/Verifier.sol b/contracts/verifiers/Verifier.sol index 1cc53895..e60e4b38 100644 --- a/contracts/verifiers/Verifier.sol +++ b/contracts/verifiers/Verifier.sol @@ -117,13 +117,15 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { modifier checkRequestGroupExistence(Request memory request, bool existence) { uint256 groupId = request.validator.getGroupID(request.params); - if (existence) { - if (!groupIdExists(groupId)) { - revert GroupIdNotFound(groupId); - } - } else { - if (groupIdExists(groupId)) { - revert GroupIdAlreadyExists(groupId); + if (groupId != 0) { + if (existence) { + if (!groupIdExists(groupId)) { + revert GroupIdNotFound(groupId); + } + } else { + if (groupIdExists(groupId)) { + revert GroupIdAlreadyExists(groupId); + } } } _; @@ -430,17 +432,15 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { /** * @dev Updates a request - * @param requestId The ID of the request * @param request The request data */ function _updateRequest( - uint256 requestId, IVerifier.Request calldata request - ) internal checkRequestExistence(requestId, true) { + ) internal checkRequestExistence(request.requestId, true) { VerifierStorage storage s = _getVerifierStorage(); uint256 verifierId = request.validator.getVerifierId(request.params); - s._requests[requestId] = RequestData({ + s._requests[request.requestId] = RequestData({ metadata: request.metadata, validator: request.validator, params: request.params, @@ -791,4 +791,35 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { function getRequestsCount() public view returns (uint256) { return _getVerifierStorage()._requestIds.length; } + + /** + * @dev Gets the address of the state contract linked to the verifier + * @return address State contract address + */ + function getStateAddress() public view virtual returns (address) { + return address(_getVerifierStorage()._state); + } + + /** + * @dev Checks the proof status for a given user and request ID + * @param sender The sender's address + * @param requestId The ID of the ZKP request + * @return The proof status structure + */ + function getProofStatus( + address sender, + uint256 requestId + ) public view checkRequestExistence(requestId, true) returns (IVerifier.ProofStatus memory) { + + VerifierStorage storage s = _getVerifierStorage(); + uint256 userID = s._user_address_to_id[sender]; + VerifierLib.Proof storage proof = s._proofs[requestId][userID][0]; + + return + IVerifier.ProofStatus( + proof.isVerified, + proof.validatorVersion, + proof.blockTimestamp + ); + } } diff --git a/hardhat.config.ts b/hardhat.config.ts index ef82cf2b..4c67dc28 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -51,6 +51,15 @@ const config: HardhatUserConfig = { }, }, }, + "contracts/test-helpers/EmbeddedVerifierWrapper.sol": { + version: "0.8.27", + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + }, + }, }, }, networks: { diff --git a/helpers/DeployHelper.ts b/helpers/DeployHelper.ts index e8356844..12f0232c 100644 --- a/helpers/DeployHelper.ts +++ b/helpers/DeployHelper.ts @@ -899,12 +899,12 @@ export class DeployHelper { return primitiveTypeUtilsWrapper; } - async deployEmbeddedZKPVerifierWrapper( + async deployEmbeddedVerifierWrapper( owner: SignerWithAddress | undefined, stateAddr: string, verifierLibAddr: string, ): Promise { - const Verifier = await ethers.getContractFactory("EmbeddedZKPVerifierWrapper", { + const Verifier = await ethers.getContractFactory(contractsInfo.EMBEDDED_VERIFIER_WRAPPER.name, { libraries: { VerifierLib: verifierLibAddr, }, @@ -914,7 +914,10 @@ export class DeployHelper { unsafeAllow: ["external-library-linking"], }); await verifier.waitForDeployment(); - console.log("EmbeddedZKPVerifierWrapper deployed to:", await verifier.getAddress()); + console.log( + `${contractsInfo.EMBEDDED_VERIFIER_WRAPPER.name} deployed to:`, + await verifier.getAddress(), + ); return verifier; } diff --git a/helpers/constants.ts b/helpers/constants.ts index afde6e08..957a6657 100644 --- a/helpers/constants.ts +++ b/helpers/constants.ts @@ -108,9 +108,9 @@ export const contractsInfo = Object.freeze({ }, UNIVERSAL_VERIFIER: { name: "UniversalVerifier", - version: "1.1.3", + version: "2.0.0", unifiedAddress: "0xfcc86A79fCb057A8e55C6B853dff9479C3cf607c", - create2Calldata: ethers.hexlify(ethers.toUtf8Bytes("iden3.create2.UniversalVerifier")), + create2Calldata: ethers.hexlify(ethers.toUtf8Bytes("iden3.create2.UniversalVerifier.v2")), verificationOpts: { // For verifying the different contracts with proxy we need verification with different constructor arguments constructorArgsImplementation: [], @@ -365,8 +365,8 @@ export const contractsInfo = Object.freeze({ libraries: {}, }, }, - EMBEDDED_ZKP_VERIFIER_WRAPPER: { - name: "EmbeddedZKPVerifierWrapper", + EMBEDDED_VERIFIER_WRAPPER: { + name: "EmbeddedVerifierWrapper", unifiedAddress: "", create2Calldata: "", }, diff --git a/scripts/upgrade/verifiers/embedded-verifier-upgrade.ts b/scripts/upgrade/verifiers/embedded-verifier-upgrade.ts index 526e07ec..f7ba72f8 100644 --- a/scripts/upgrade/verifiers/embedded-verifier-upgrade.ts +++ b/scripts/upgrade/verifiers/embedded-verifier-upgrade.ts @@ -16,11 +16,11 @@ const network = hre.network.name; const forceImport = false; async function main() { - // EmbeddedZKPVerifer is abstract contract + // EmbeddedVerifer is abstract contract // In real upgrade, you should use THE NAME as THE ADDRESS - // of your custom contract, which inherits EmbeddedZKPVerifer + // of your custom contract, which inherits EmbeddedVerifer let verifierContract = await ethers.getContractAt( - "", // EmbeddedZKPVerifierWrapper + "", // EmbeddedVerifierWrapper "", ); @@ -44,11 +44,14 @@ async function main() { const verifierLib = await deployerHelper.deployVerifierLib(); // **** Upgrade Embedded Verifier **** - const verifierFactory = await ethers.getContractFactory("EmbeddedZKPVerifierWrapper", { - libraries: { - VerifierLib: await verifierLib.getAddress(), + const verifierFactory = await ethers.getContractFactory( + contractsInfo.EMBEDDED_VERIFIER_WRAPPER.name, + { + libraries: { + VerifierLib: await verifierLib.getAddress(), + }, }, - }); + ); try { verifierContract = await upgrades.upgradeProxy( diff --git a/test/verifier/embedded-zkp-verifier.test.ts b/test/verifier/embedded-zkp-verifier.test.ts index 296673c5..d01c3cc8 100644 --- a/test/verifier/embedded-zkp-verifier.test.ts +++ b/test/verifier/embedded-zkp-verifier.test.ts @@ -10,8 +10,20 @@ import { CircuitId } from "@0xpolygonid/js-sdk"; import { loadFixture } from "@nomicfoundation/hardhat-toolbox/network-helpers"; describe("Embedded ZKP Verifier", function () { - let verifier: any, sig: any; + let verifier: any, sig: any, authV2Validator: any; let owner: Signer; + const authType = "authV2"; + + const storageFields = [ + { + name: "userID", + value: 1n, + }, + { + name: "issuerID", + value: 2n, + }, + ]; const query = { schema: BigInt("180410020913331409885634153623124536270"), @@ -36,21 +48,25 @@ describe("Embedded ZKP Verifier", function () { const verifierLib = await deployHelper.deployVerifierLib(); - verifier = await deployHelper.deployEmbeddedZKPVerifierWrapper( + verifier = await deployHelper.deployEmbeddedVerifierWrapper( owner, await state.getAddress(), await verifierLib.getAddress(), ); - const stub = await deployHelper.deployValidatorStub(); + const stub = await deployHelper.deployValidatorStub("RequestValidatorV2Stub"); sig = stub; + authV2Validator = await deployHelper.deployValidatorStub("AuthValidatorStub"); } - async function checkStorageFields(verifier: any, requestId: number) { - const fieldsToCheck = ["userID", "issuerID"]; - for (const field of fieldsToCheck) { - const value = await verifier.getProofStorageField(await owner.getAddress(), requestId, field); - expect(value).to.be.greaterThan(0n); + async function checkStorageFields(verifier: any, requestId: bigint, storageFields: any[]) { + for (const field of storageFields) { + const value = await verifier.getResponseFieldValueFromAddress( + requestId, + await owner.getAddress(), + field.name, + ); + expect(value).to.be.equal(field.value); } } @@ -59,62 +75,8 @@ describe("Embedded ZKP Verifier", function () { }); it("test submit response", async () => { - await verifier.setZKPRequest(0, { - metadata: "metadata", - validator: await sig.getAddress(), - data: packValidatorParams(query), - }); - - const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); - - const tx = await verifier.submitZKPResponse(0, inputs, pi_a, pi_b, pi_c); - const txRes = await tx.wait(); - await checkStorageFields(verifier, 0); - const receipt = await ethers.provider.getTransactionReceipt(txRes.hash); - - // 2 events are emitted - expect(receipt?.logs.length).to.equal(2); - - const interfaceEventBeforeProofSubmit = new ethers.Interface([ - "event BeforeProofSubmit(uint64 requestId, uint256[] inputs, address validator)", - ]); - const eventBeforeProofSubmit = interfaceEventBeforeProofSubmit.decodeEventLog( - "BeforeProofSubmit", - receipt?.logs[0].data || "", - receipt?.logs[0].topics, - ); - expect(eventBeforeProofSubmit[0]).to.equal(0); - expect(eventBeforeProofSubmit[1]).to.deep.equal(inputs.map((x) => BigInt(x))); - expect(eventBeforeProofSubmit[2]).to.equal(await sig.getAddress()); - - const interfaceEventAfterProofSubmit = new ethers.Interface([ - "event AfterProofSubmit(uint64 requestId, uint256[] inputs, address validator)", - ]); - const eventAfterProofSubmit = interfaceEventAfterProofSubmit.decodeEventLog( - "AfterProofSubmit", - receipt?.logs[1].data || "", - receipt?.logs[1].topics, - ); - expect(eventAfterProofSubmit[0]).to.equal(0); - expect(eventAfterProofSubmit[1]).to.deep.equal(inputs.map((x) => BigInt(x))); - expect(eventAfterProofSubmit[2]).to.equal(await sig.getAddress()); - - const ownerAddress = await owner.getAddress(); - const requestID = 0; - const { timestamp: txResTimestamp } = (await ethers.provider.getBlock( - txRes.blockNumber, - )) as Block; + const requestId = 0; - const isProofVerified = await verifier.isProofVerified(ownerAddress, requestID); - expect(isProofVerified).to.be.equal(true); - const proofStatus = await verifier.getProofStatus(ownerAddress, requestID); - expect(proofStatus.isVerified).to.be.equal(true); - expect(proofStatus.validatorVersion).to.be.equal("2.0.2-mock"); - expect(proofStatus.blockNumber).to.be.equal(txRes.blockNumber); - expect(proofStatus.blockTimestamp).to.be.equal(txResTimestamp); - }); - - it("test submit response v2", async () => { const globalStateMessage = { timestamp: BigInt(Math.floor(Date.now() / 1000)), idType: "0x01A1", @@ -136,15 +98,31 @@ describe("Embedded ZKP Verifier", function () { replacedAtTimestamp: 1724858009n, }; - await verifier.setZKPRequest(0, { - metadata: "metadata", - validator: await sig.getAddress(), - data: packValidatorParams(query), + const params = packValidatorParams(query); + + await verifier.setAuthType({ + authType: authType, + validator: await authV2Validator.getAddress(), + params: params, }); + await expect( + verifier.setRequests( + [ + { + requestId: requestId, + metadata: "metadata", + validator: await sig.getAddress(), + params: params, + }, + ], + [], + ), + ); + const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); - const zkProof = packZKProof(inputs, pi_a, pi_b, pi_c); + const proof = packZKProof(inputs, pi_a, pi_b, pi_c); const [signer] = await ethers.getSigners(); const crossChainProofs = packCrossChainProofs( @@ -156,48 +134,59 @@ describe("Embedded ZKP Verifier", function () { const metadatas = "0x"; - const tx = await verifier.submitZKPResponseV2( + const tx = await verifier.submitResponse( + [ + { + authType: authType, + proof, + }, + ], [ { - requestId: 0, - zkProof: zkProof, - data: metadatas, + requestId: requestId, + proof, + metadata: metadatas, }, ], + [], crossChainProofs, ); const txRes = await tx.wait(); - await checkStorageFields(verifier, 0); + await checkStorageFields(verifier, BigInt(0), storageFields); const receipt = await ethers.provider.getTransactionReceipt(txRes.hash); // 2 events are emitted expect(receipt?.logs.length).to.equal(2); - const interfaceEventBeforeProofSubmitV2 = new ethers.Interface([ - "event BeforeProofSubmitV2(tuple(uint64 requestId,bytes zkProof,bytes data)[])", + const interfaceEventBeforeProofSubmit = new ethers.Interface([ + "event BeforeProofSubmit(tuple(string authType,bytes proof)[],tuple(uint256 requestId,bytes proof,bytes metadata)[],tuple(uint256 groupId,tuple(uint256 requestId,bytes proof,bytes metadata)[])[])", ]); - const eventBeforeProofSubmitV2 = interfaceEventBeforeProofSubmitV2.decodeEventLog( - "BeforeProofSubmitV2", + const eventBeforeProofSubmit = interfaceEventBeforeProofSubmit.decodeEventLog( + "BeforeProofSubmit", receipt?.logs[0].data || "", receipt?.logs[0].topics, ); - expect(eventBeforeProofSubmitV2[0][0][0]).to.equal(0); - expect(eventBeforeProofSubmitV2[0][0][1]).to.deep.equal(zkProof); - expect(eventBeforeProofSubmitV2[0][0][2]).to.equal(metadatas); + expect(eventBeforeProofSubmit[0][0][0]).to.equal("authV2"); + expect(eventBeforeProofSubmit[0][0][1]).to.deep.equal(proof); + expect(eventBeforeProofSubmit[1][0][0]).to.equal(0); + expect(eventBeforeProofSubmit[1][0][1]).to.deep.equal(proof); + expect(eventBeforeProofSubmit[1][0][2]).to.equal(metadatas); - const interfaceEventAfterProofSubmitV2 = new ethers.Interface([ - "event AfterProofSubmitV2(tuple(uint64 requestId,bytes zkProof,bytes data)[])", + const interfaceEventAfterProofSubmit = new ethers.Interface([ + "event AfterProofSubmit(tuple(string authType,bytes proof)[],tuple(uint256 requestId,bytes proof,bytes metadata)[],tuple(uint256 groupId,tuple(uint256 requestId,bytes proof,bytes metadata)[])[])", ]); - const eventAfterProofSubmitV2 = interfaceEventAfterProofSubmitV2.decodeEventLog( - "AfterProofSubmitV2", + const eventAfterProofSubmit = interfaceEventAfterProofSubmit.decodeEventLog( + "AfterProofSubmit", receipt?.logs[1].data || "", receipt?.logs[1].topics, ); - expect(eventAfterProofSubmitV2[0][0][0]).to.equal(0); - expect(eventAfterProofSubmitV2[0][0][1]).to.deep.equal(zkProof); - expect(eventAfterProofSubmitV2[0][0][2]).to.equal(metadatas); + expect(eventAfterProofSubmit[0][0][0]).to.equal("authV2"); + expect(eventAfterProofSubmit[0][0][1]).to.deep.equal(proof); + expect(eventAfterProofSubmit[1][0][0]).to.equal(0); + expect(eventAfterProofSubmit[1][0][1]).to.deep.equal(proof); + expect(eventAfterProofSubmit[1][0][2]).to.equal(metadatas); const ownerAddress = await owner.getAddress(); const requestID = 0; @@ -209,29 +198,34 @@ describe("Embedded ZKP Verifier", function () { expect(isProofVerified).to.be.equal(true); const proofStatus = await verifier.getProofStatus(ownerAddress, requestID); expect(proofStatus.isVerified).to.be.equal(true); - expect(proofStatus.validatorVersion).to.be.equal("2.0.2-mock"); - expect(proofStatus.blockNumber).to.be.equal(txRes.blockNumber); + expect(proofStatus.validatorVersion).to.be.equal("1.0.0-mock"); expect(proofStatus.blockTimestamp).to.be.equal(txResTimestamp); }); - it("test getZKPRequest and request id exists", async () => { + it("test getRequest and request id exists", async () => { const requestsCount = 3; for (let i = 0; i < requestsCount; i++) { - await verifier.setZKPRequest(i, { - metadata: "metadataN" + i, - validator: await sig.getAddress(), - data: "0x00", - }); - const reqeustIdExists = await verifier.requestIdExists(i); - expect(reqeustIdExists).to.be.true; - const reqeustIdDoesntExists = await verifier.requestIdExists(i + 1); - expect(reqeustIdDoesntExists).to.be.false; - - const request = await verifier.getZKPRequest(i); + await verifier.setRequests( + [ + { + requestId: i, + metadata: "metadataN" + i, + validator: await sig.getAddress(), + params: "0x00", + }, + ], + [], + ); + const requestIdExists = await verifier.requestIdExists(i); + expect(requestIdExists).to.be.true; + const requestIdDoesntExists = await verifier.requestIdExists(i + 1); + expect(requestIdDoesntExists).to.be.false; + + const request = await verifier.getRequest(i); expect(request.metadata).to.be.equal("metadataN" + i); - await expect(verifier.getZKPRequest(i + 1)).to.be.rejectedWith("request id doesn't exist"); + await expect(verifier.getRequest(i + 1)).to.be.rejectedWith(`RequestIdNotFound(${i + 1})`); } - const count = await verifier.getZKPRequestsCount(); + const count = await verifier.getRequestsCount(); expect(count).to.be.equal(requestsCount); }); }); diff --git a/test/verifier/universal-verifier-linked-proofs.test.ts b/test/verifier/universal-verifier-linked-proofs.test.ts deleted file mode 100644 index 8edad40c..00000000 --- a/test/verifier/universal-verifier-linked-proofs.test.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { DeployHelper } from "../../helpers/DeployHelper"; -import { ethers } from "hardhat"; -import { packV3ValidatorParams } from "../utils/validator-pack-utils"; -import { prepareInputs, publishState } from "../utils/state-utils"; -import { expect } from "chai"; -import testData from "./linked-proofs-data.json"; -import { loadFixture } from "@nomicfoundation/hardhat-toolbox/network-helpers"; -import { TEN_YEARS } from "../../helpers/constants"; - -describe("Universal Verifier Linked proofs", function () { - let verifier: any, v3: any, state: any; - let signer, signer2; - let signerAddress: string; - let deployHelper: DeployHelper; - - async function deployContractsFixture() { - [signer, signer2] = await ethers.getSigners(); - signerAddress = await signer.getAddress(); - - deployHelper = await DeployHelper.initialize(null, true); - ({ state } = await deployHelper.deployStateWithLibraries(["0x0112"])); - - const verifierLib = await deployHelper.deployVerifierLib(); - - verifier = await deployHelper.deployUniversalVerifier( - signer, - await state.getAddress(), - await verifierLib.getAddress(), - ); - - const contracts = await deployHelper.deployValidatorContractsWithVerifiers( - "v3", - await state.getAddress(), - ); - v3 = contracts.validator; - await verifier.addValidatorToWhitelist(await v3.getAddress()); - await verifier.connect(); - - await publishState(state, testData.state as unknown as { [key: string]: string }); - await v3.setProofExpirationTimeout(TEN_YEARS); - for (let i = 0; i < testData.queryData.zkpRequests.length; i++) { - await verifier.setZKPRequest(100 + i, { - metadata: "linkedProofN" + i, - validator: await v3.getAddress(), - data: packV3ValidatorParams(testData.queryData.zkpRequests[i].request), - }); - } - - for (let i = 0; i < testData.queryData.zkpResponses.length; i++) { - const { inputs, pi_a, pi_b, pi_c } = prepareInputs(testData.queryData.zkpResponses[i]); - await verifier.submitZKPResponse(100 + i, inputs, pi_a, pi_b, pi_c); - } - } - - beforeEach(async () => { - await loadFixture(deployContractsFixture); - }); - - it("should linked proof validation pass", async () => { - expect(await verifier.verifyLinkedProofs(signerAddress, [101, 102])).not.to.throw; - expect(await verifier.verifyLinkedProofs(signerAddress, [100, 103])).not.to.throw; - }); - - it("should linked proof validation fail", async () => { - await expect(verifier.verifyLinkedProofs(signerAddress, [100, 101])).to.be.rejectedWith( - "LinkedProofError", - ); - await expect(verifier.verifyLinkedProofs(signerAddress, [102, 103])).to.be.rejectedWith( - "LinkedProofError", - ); - - await expect(verifier.verifyLinkedProofs(signerAddress, [102])).to.be.rejectedWith( - "Linked proof verification needs more than 1 request", - ); - await expect( - verifier.verifyLinkedProofs(await signer2.getAddress(), [101, 102]), - ).to.be.rejectedWith(`Can't find linkID for given request Ids and user address`); - }); -}); diff --git a/test/verifier/universal-verifier-submit-V2.test.ts b/test/verifier/universal-verifier-submit-V2.test.ts deleted file mode 100644 index dff258cf..00000000 --- a/test/verifier/universal-verifier-submit-V2.test.ts +++ /dev/null @@ -1,216 +0,0 @@ -import { expect } from "chai"; -import { DeployHelper } from "../../helpers/DeployHelper"; -import { ethers } from "hardhat"; -import { packValidatorParams } from "../utils/validator-pack-utils"; -import { prepareInputs } from "../utils/state-utils"; -import { Block, Contract } from "ethers"; -import proofJson from "../validators/sig/data/valid_sig_user_genesis.json"; -import { buildCrossChainProofs, packCrossChainProofs, packZKProof } from "../utils/packData"; -import { CircuitId } from "@0xpolygonid/js-sdk"; -import { loadFixture } from "@nomicfoundation/hardhat-toolbox/network-helpers"; - -describe("Universal Verifier V2 MTP & SIG validators", function () { - let verifier: any, sig: any; - let signer; - let signerAddress: string; - let deployHelper: DeployHelper; - let stateCrossChainStub, crossChainProofValidatorStub, validatorStub: Contract; - - const globalStateMessage = { - timestamp: BigInt(Math.floor(Date.now() / 1000)), - idType: "0x01A1", - root: 0n, - replacedAtTimestamp: 0n, - }; - - const identityStateMessage1 = { - timestamp: BigInt(Math.floor(Date.now() / 1000)), - id: 25530185136167283063987925153802803371825564143650291260157676786685420033n, - state: 4595702004868323299100310062178085028712435650290319955390778053863052230284n, - replacedAtTimestamp: 0n, - }; - - const identityStateUpdate2 = { - timestamp: BigInt(Math.floor(Date.now() / 1000)), - id: 25530185136167283063987925153802803371825564143650291260157676786685420033n, - state: 16775015541053109108201708100382933592407720757224325883910784163897594100403n, - replacedAtTimestamp: 1724858009n, - }; - - const query = { - schema: BigInt("180410020913331409885634153623124536270"), - claimPathKey: BigInt( - "8566939875427719562376598811066985304309117528846759529734201066483458512800", - ), - operator: 1n, - slotIndex: 0n, - value: [1420070400000000000n, ...new Array(63).fill("0").map((x) => BigInt(x))], - queryHash: BigInt( - "1496222740463292783938163206931059379817846775593932664024082849882751356658", - ), - circuitIds: [CircuitId.AtomicQuerySigV2OnChain], - claimPathNotExists: 0, - }; - - async function deployContractsFixture() { - [signer] = await ethers.getSigners(); - signerAddress = await signer.getAddress(); - - deployHelper = await DeployHelper.initialize(null, true); - crossChainProofValidatorStub = await deployHelper.deployCrossChainProofValidator(); - - const { state } = await deployHelper.deployStateWithLibraries(["0x01A1", "0x0102"]); - await state.setCrossChainProofValidator(crossChainProofValidatorStub); - stateCrossChainStub = state; - - const verifierLib = await deployHelper.deployVerifierLib(); - - verifier = await deployHelper.deployUniversalVerifier( - signer, - await stateCrossChainStub.getAddress(), - await verifierLib.getAddress(), - ); - - validatorStub = await deployHelper.deployValidatorStub(); - - sig = validatorStub; - await verifier.addValidatorToWhitelist(await sig.getAddress()); - await verifier.connect(); - } - - async function checkStorageFields(verifier: any, requestId: number) { - const fieldsToCheck = ["userID", "issuerID"]; - for (const field of fieldsToCheck) { - const value = await verifier.getProofStorageField( - await signer.getAddress(), - requestId, - field, - ); - expect(value).to.be.greaterThan(0n); - } - } - - beforeEach(async () => { - await loadFixture(deployContractsFixture); - }); - - it("Test submit response V2", async () => { - const requestId = 0; - const nonExistingRequestId = 1; - const data = packValidatorParams(query); - - await verifier.setZKPRequest(0, { - metadata: "metadata", - validator: await sig.getAddress(), - data: data, - }); - - const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); - - const zkProof = packZKProof(inputs, pi_a, pi_b, pi_c); - - const crossChainProofs = packCrossChainProofs( - await buildCrossChainProofs( - [globalStateMessage, identityStateMessage1, identityStateUpdate2], - signer, - ), - ); - - const metadatas = "0x"; - - const tx = await verifier.submitZKPResponseV2( - [ - { - requestId, - zkProof: zkProof, - data: metadatas, - }, - ], - crossChainProofs, - ); - - const txRes = await tx.wait(); - await checkStorageFields(verifier, requestId); - const filter = verifier.filters.ZKPResponseSubmitted; - - const events = await verifier.queryFilter(filter, -1); - expect(events[0].eventName).to.be.equal("ZKPResponseSubmitted"); - expect(events[0].args.requestId).to.be.equal(0); - expect(events[0].args.caller).to.be.equal(signerAddress); - - const { timestamp: txResTimestamp } = (await ethers.provider.getBlock( - txRes.blockNumber, - )) as Block; - - const status = await verifier.getProofStatus(signerAddress, requestId); - expect(status.isVerified).to.be.true; - expect(status.validatorVersion).to.be.equal("2.0.2-mock"); - expect(status.blockNumber).to.be.equal(txRes.blockNumber); - expect(status.blockTimestamp).to.be.equal(txResTimestamp); - - await expect(verifier.getProofStatus(signerAddress, nonExistingRequestId)).to.be.rejectedWith( - "request id doesn't exist", - ); - }); - - it("Test submit response V2 multi-request", async () => { - const requestIds = [0, 1, 2]; - const nonExistingRequestId = 3; - const data = packValidatorParams(query); - - for (const requestId of requestIds) { - await verifier.setZKPRequest(requestId, { - metadata: "metadata", - validator: await sig.getAddress(), - data: data, - }); - } - - const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); - - const zkProof = packZKProof(inputs, pi_a, pi_b, pi_c); - - const crossChainProofs = packCrossChainProofs( - await buildCrossChainProofs( - [globalStateMessage, identityStateMessage1, identityStateUpdate2], - signer, - ), - ); - - const metadatas = "0x"; - - const tx = await verifier.submitZKPResponseV2( - requestIds.map((requestId) => ({ - requestId, - zkProof: zkProof, - data: metadatas, - })), - crossChainProofs, - ); - - const txRes = await tx.wait(); - const filter = verifier.filters.ZKPResponseSubmitted; - - const events = await verifier.queryFilter(filter, -1); - expect(events[0].eventName).to.be.equal("ZKPResponseSubmitted"); - expect(events[0].args.requestId).to.be.equal(0); - expect(events[0].args.caller).to.be.equal(signerAddress); - - const { timestamp: txResTimestamp } = (await ethers.provider.getBlock( - txRes.blockNumber, - )) as Block; - - for (const requestId of requestIds) { - const status = await verifier.getProofStatus(signerAddress, requestId); - expect(status.isVerified).to.be.true; - expect(status.validatorVersion).to.be.equal("2.0.2-mock"); - expect(status.blockNumber).to.be.equal(txRes.blockNumber); - expect(status.blockTimestamp).to.be.equal(txResTimestamp); - await checkStorageFields(verifier, requestId); - } - - await expect(verifier.getProofStatus(signerAddress, nonExistingRequestId)).to.be.rejectedWith( - "request id doesn't exist", - ); - }); -}); diff --git a/test/verifier/universal-verifier.events.test.ts b/test/verifier/universal-verifier.events.test.ts index fa5441e9..d177e382 100644 --- a/test/verifier/universal-verifier.events.test.ts +++ b/test/verifier/universal-verifier.events.test.ts @@ -96,27 +96,36 @@ describe("Universal Verifier events", function () { await verifier.connect(); }); - it("Check ZKPRequestSet event", async () => { + it("Check RequestSet event", async () => { const requestsCount = 3; - const data = [ + const params = [ packValidatorParams(queries[0]), packValidatorParams(queries[1]), packValidatorParams(queries[2]), ]; for (let i = 0; i < requestsCount; i++) { - await verifier.setZKPRequest(i, { - metadata: "metadataN" + i, - validator: await sig.getAddress(), - data: data[i], - }); + await expect( + verifier.setRequests( + [ + { + requestId: i, + metadata: "metadata", + validator: await sig.getAddress(), + params: params[i], + }, + ], + [], + ), + ).to.emit(verifier, "RequestSet"); + console.log("RequestSet event emitted"); } - const filter = verifier.filters.ZKPRequestSet(null, null); + const filter = verifier.filters.RequestSet(null, null); const logs = await verifier.queryFilter(filter, 0, "latest"); const coder = AbiCoder.defaultAbiCoder(); logs.map((log, index) => { - const [decodedData] = coder.decode(encodedDataAbi as any, log.args.data); + const [decodedData] = coder.decode(encodedDataAbi as any, log.args.params); expect(decodedData.schema).to.equal(queries[index].schema); expect(decodedData.claimPathKey).to.equal(queries[index].claimPathKey); expect(decodedData.operator).to.equal(queries[index].operator); @@ -135,28 +144,35 @@ describe("Universal Verifier events", function () { }); }); - it("Check ZKPRequestUpdate event", async () => { + it("Check RequestUpdate event", async () => { const originalRequestData = packValidatorParams(queries[0]); const updatedRequestData = packValidatorParams(queries[1]); - await verifier.setZKPRequest(0, { - metadata: "metadataN0", - validator: await sig.getAddress(), - data: originalRequestData, - }); + await verifier.setRequests( + [ + { + requestId: 0, + metadata: "metadata0", + validator: await sig.getAddress(), + params: originalRequestData, + }, + ], + [], + ); - await verifier.updateZKPRequest(0, { + await verifier.updateRequest({ + requestId: 0, metadata: "metadataN1", validator: await sig.getAddress(), - data: updatedRequestData, + params: updatedRequestData, }); - const filter = verifier.filters.ZKPRequestUpdate(null, null); + const filter = verifier.filters.RequestUpdate(null, null); const logs = await verifier.queryFilter(filter, 0, "latest"); const coder = AbiCoder.defaultAbiCoder(); logs.map((log) => { - const [decodedData] = coder.decode(encodedDataAbi as any, log.args.data); + const [decodedData] = coder.decode(encodedDataAbi as any, log.args.params); expect(decodedData.schema).to.equal(queries[1].schema); expect(decodedData.claimPathKey).to.equal(queries[1].claimPathKey); expect(decodedData.operator).to.equal(queries[1].operator); diff --git a/test/verifier/universal-verifier.test.ts b/test/verifier/universal-verifier.test.ts index f31b1671..8980d2d2 100644 --- a/test/verifier/universal-verifier.test.ts +++ b/test/verifier/universal-verifier.test.ts @@ -7,12 +7,25 @@ import { Block } from "ethers"; import proofJson from "../validators/mtp/data/valid_mtp_user_genesis.json"; import { CircuitId } from "@0xpolygonid/js-sdk"; import { loadFixture } from "@nomicfoundation/hardhat-toolbox/network-helpers"; +import { packZKProof } from "../utils/packData"; describe("Universal Verifier MTP & SIG validators", function () { - let verifier: any, sigValidator: any, state: any; + let verifier: any, sigValidator: any, authV2Validator: any, state: any; let signer, signer2, signer3; let signerAddress: string; let deployHelper: DeployHelper; + const authType = "authV2"; + + const storageFields = [ + { + name: "userID", + value: 1n, + }, + { + name: "issuerID", + value: 2n, + }, + ]; const query = { schema: BigInt("180410020913331409885634153623124536270"), @@ -36,30 +49,38 @@ describe("Universal Verifier MTP & SIG validators", function () { const { state: stateContract } = await deployHelper.deployStateWithLibraries(["0x0112"]); const verifierLib = await deployHelper.deployVerifierLib(); + const sigV2Validator = await deployHelper.deployValidatorStub("RequestValidatorV2Stub"); + const universalVerifier: any = await deployHelper.deployUniversalVerifier( ethSigner, await stateContract.getAddress(), await verifierLib.getAddress(), ); - const stub = await deployHelper.deployValidatorStub(); - - const validator = stub; - await universalVerifier.addValidatorToWhitelist(await validator.getAddress()); + await universalVerifier.addValidatorToWhitelist(await sigV2Validator.getAddress()); await universalVerifier.connect(); - return { ethSigner, ethSigner2, ethSigner3, stateContract, universalVerifier, validator }; + const authV2Validator = await deployHelper.deployValidatorStub("AuthValidatorStub"); + + return { + ethSigner, + ethSigner2, + ethSigner3, + stateContract, + universalVerifier, + sigV2Validator, + authV2Validator, + }; } - async function checkStorageFields(verifier: any, requestId: number) { - const fieldsToCheck = ["userID", "issuerID"]; - for (const field of fieldsToCheck) { - const value = await verifier.getProofStorageField( - await signer.getAddress(), + async function checkStorageFields(verifier: any, requestId: bigint, storageFields: any[]) { + for (const field of storageFields) { + const value = await verifier.getResponseFieldValueFromAddress( requestId, - field, + await signer.getAddress(), + field.name, ); - expect(value).to.be.greaterThan(0n); + expect(value).to.be.equal(field.value); } } @@ -70,7 +91,8 @@ describe("Universal Verifier MTP & SIG validators", function () { ethSigner3: signer3, stateContract: state, universalVerifier: verifier, - validator: sigValidator, + sigV2Validator: sigValidator, + authV2Validator: authV2Validator, } = await loadFixture(deployContractsFixture)); signerAddress = await signer.getAddress(); }); @@ -86,50 +108,90 @@ describe("Universal Verifier MTP & SIG validators", function () { for (let i = 0; i < requestsCount; i++) { await expect( - verifier.setZKPRequest(i, { - metadata: "metadataN" + i, - validator: validatorAddr, - data: "0x0" + i, - }), + verifier.setRequests( + [ + { + requestId: i, + metadata: "metadataN" + i, + validator: validatorAddr, + params: "0x0" + i, + }, + ], + [], + ), ) - .to.emit(verifier, "ZKPRequestSet") + .to.emit(verifier, "RequestSet") .withArgs(i, signerAddress, "metadataN" + i, validatorAddr, "0x0" + i); - const request = await verifier.getZKPRequest(i); + const request = await verifier.getRequest(i); expect(request.metadata).to.be.equal("metadataN" + i); expect(request.validator).to.be.equal(validatorAddr); - expect(request.data).to.be.equal("0x0" + i); + expect(request.params).to.be.equal("0x0" + i); const requestIdExists = await verifier.requestIdExists(i); expect(requestIdExists).to.be.true; const requestIdDoesntExists = await verifier.requestIdExists(i + 1); expect(requestIdDoesntExists).to.be.false; - await expect(verifier.getZKPRequest(i + 1)).to.be.rejectedWith("request id doesn't exist"); + await expect(verifier.getRequest(i + 1)).to.be.rejectedWith(`RequestIdNotFound(${i + 1})`); } - const count = await verifier.getZKPRequestsCount(); + const count = await verifier.getRequestsCount(); expect(count).to.be.equal(requestsCount); }); it("Test submit response", async () => { const requestId = 0; const nonExistingRequestId = 1; - const data = packValidatorParams(query); + const params = packValidatorParams(query); + + verifier.setRequests( + [ + { + requestId: 0, + metadata: "metadata", + validator: await sigValidator.getAddress(), + params: params, + }, + ], + [], + ); - await verifier.setZKPRequest(0, { - metadata: "metadata", - validator: await sigValidator.getAddress(), - data: data, + await verifier.setAuthType({ + authType: authType, + validator: await authV2Validator.getAddress(), + params: params, }); const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); - const tx = await verifier.submitZKPResponse(0, inputs, pi_a, pi_b, pi_c); + const proof = packZKProof(inputs, pi_a, pi_b, pi_c); + + const crossChainProofs = "0x"; + const metadatas = "0x"; + + const tx = await verifier.submitResponse( + [ + { + authType: authType, + proof, + }, + ], + [ + { + requestId: 0, + proof, + metadata: metadatas, + }, + ], + [], + crossChainProofs, + ); + const txRes = await tx.wait(); - await checkStorageFields(verifier, requestId); - const filter = verifier.filters.ZKPResponseSubmitted; + await checkStorageFields(verifier, BigInt(requestId), storageFields); + const filter = verifier.filters.ResponseSubmitted; const events = await verifier.queryFilter(filter, -1); - expect(events[0].eventName).to.be.equal("ZKPResponseSubmitted"); + expect(events[0].eventName).to.be.equal("ResponseSubmitted"); expect(events[0].args.requestId).to.be.equal(0); expect(events[0].args.caller).to.be.equal(signerAddress); @@ -137,25 +199,13 @@ describe("Universal Verifier MTP & SIG validators", function () { txRes.blockNumber, )) as Block; - await expect( - verifier.verifyZKPResponse( - 0, - inputs, - pi_a, - pi_b, - pi_c, - "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", - ), - ).not.to.be.rejected; - const status = await verifier.getProofStatus(signerAddress, requestId); expect(status.isVerified).to.be.true; - expect(status.validatorVersion).to.be.equal("2.0.2-mock"); - expect(status.blockNumber).to.be.equal(txRes.blockNumber); + expect(status.validatorVersion).to.be.equal("1.0.0-mock"); expect(status.blockTimestamp).to.be.equal(txResTimestamp); await expect(verifier.getProofStatus(signerAddress, nonExistingRequestId)).to.be.rejectedWith( - "request id doesn't exist", + `RequestIdNotFound(${nonExistingRequestId})`, ); }); @@ -169,13 +219,20 @@ describe("Universal Verifier MTP & SIG validators", function () { const someSignerAddress = await someSigner.getAddress(); await expect(verifier.getRequestOwner(requestId)).to.be.rejectedWith( - "request id doesn't exist", + `RequestIdNotFound(${requestId})`, + ); + + await verifier.connect(requestOwner).setRequests( + [ + { + requestId: requestId, + metadata: "metadata", + validator: await sigValidator.getAddress(), + params: packValidatorParams(query), + }, + ], + [], ); - await verifier.connect(requestOwner).setZKPRequest(requestId, { - metadata: "metadata", - validator: await sigValidator.getAddress(), - data: packValidatorParams(query), - }); expect(await verifier.getRequestOwner(requestId)).to.be.equal(requestOwnerAddr); await expect( @@ -192,11 +249,11 @@ describe("Universal Verifier MTP & SIG validators", function () { expect(await verifier.getRequestOwner(requestId)).to.be.equal(requestOwnerAddr); await expect(verifier.getRequestOwner(nonExistentRequestId)).to.be.rejectedWith( - "request id doesn't exist", + `RequestIdNotFound(${nonExistentRequestId})`, ); await expect( verifier.setRequestOwner(nonExistentRequestId, someSignerAddress), - ).to.be.rejectedWith("request id doesn't exist"); + ).to.be.rejectedWith(`RequestIdNotFound(${nonExistentRequestId})`); }); it("Check disable/enable functionality", async () => { @@ -206,57 +263,99 @@ describe("Universal Verifier MTP & SIG validators", function () { const requestId = 0; const nonExistentRequestId = 1; - await expect(verifier.isZKPRequestEnabled(requestId)).to.be.rejectedWith( - "request id doesn't exist", + const params = packValidatorParams(query); + + await expect(verifier.isRequestEnabled(requestId)).to.be.rejectedWith( + `RequestIdNotFound(${requestId})`, ); - await verifier.connect(requestOwner).setZKPRequest(requestId, { - metadata: "metadata", - validator: await sigValidator.getAddress(), - data: packValidatorParams(query), - }); - expect(await verifier.isZKPRequestEnabled(requestId)).to.be.true; + await verifier.connect(requestOwner).setRequests( + [ + { + requestId: requestId, + metadata: "metadata", + validator: await sigValidator.getAddress(), + params: params, + }, + ], + [], + ); - await expect(verifier.connect(someSigner).disableZKPRequest(requestId)).to.be.rejectedWith( + expect(await verifier.isRequestEnabled(requestId)).to.be.true; + + await expect(verifier.connect(someSigner).disableRequest(requestId)).to.be.rejectedWith( "Not an owner or request owner", ); - expect(await verifier.isZKPRequestEnabled(requestId)).to.be.true; + expect(await verifier.isRequestEnabled(requestId)).to.be.true; - await verifier.connect(owner).disableZKPRequest(requestId); - expect(await verifier.isZKPRequestEnabled(requestId)).to.be.false; + await verifier.connect(owner).disableRequest(requestId); + expect(await verifier.isRequestEnabled(requestId)).to.be.false; - await expect(verifier.connect(someSigner).enableZKPRequest(requestId)).to.be.rejectedWith( + await expect(verifier.connect(someSigner).enableRequest(requestId)).to.be.rejectedWith( "Not an owner or request owner", ); - await verifier.connect(requestOwner).enableZKPRequest(requestId); - expect(await verifier.isZKPRequestEnabled(requestId)).to.be.true; + await verifier.connect(requestOwner).enableRequest(requestId); + expect(await verifier.isRequestEnabled(requestId)).to.be.true; - const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); - await verifier.submitZKPResponse(0, inputs, pi_a, pi_b, pi_c); + await verifier.setAuthType({ + authType: authType, + validator: await authV2Validator.getAddress(), + params: params, + }); - await verifier.connect(requestOwner).disableZKPRequest(requestId); - await expect(verifier.submitZKPResponse(0, inputs, pi_a, pi_b, pi_c)).to.be.rejectedWith( - "Request is disabled", + const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); + const proof = packZKProof(inputs, pi_a, pi_b, pi_c); + + const crossChainProofs = "0x"; + const metadatas = "0x"; + + await verifier.submitResponse( + [ + { + authType: authType, + proof, + }, + ], + [ + { + requestId: requestId, + proof, + metadata: metadatas, + }, + ], + [], + crossChainProofs, ); + + await verifier.connect(requestOwner).disableRequest(requestId); await expect( - verifier.verifyZKPResponse( - 0, - inputs, - pi_a, - pi_b, - pi_c, - "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + verifier.submitResponse( + [ + { + authType: authType, + proof, + }, + ], + [ + { + requestId: requestId, + proof, + metadata: metadatas, + }, + ], + [], + crossChainProofs, ), - ).to.be.rejectedWith("Request is disabled"); + ).to.be.rejectedWith(`RequestIsDisabled(${requestId})`); - await expect(verifier.isZKPRequestEnabled(nonExistentRequestId)).to.be.rejectedWith( - "request id doesn't exist", + await expect(verifier.isRequestEnabled(nonExistentRequestId)).to.be.rejectedWith( + `RequestIdNotFound(${nonExistentRequestId})`, ); - await expect(verifier.disableZKPRequest(nonExistentRequestId)).to.be.rejectedWith( - "request id doesn't exist", + await expect(verifier.disableRequest(nonExistentRequestId)).to.be.rejectedWith( + `RequestIdNotFound(${nonExistentRequestId})`, ); - await expect(verifier.enableZKPRequest(nonExistentRequestId)).to.be.rejectedWith( - "request id doesn't exist", + await expect(verifier.enableRequest(nonExistentRequestId)).to.be.rejectedWith( + `RequestIdNotFound(${nonExistentRequestId})`, ); }); @@ -271,15 +370,19 @@ describe("Universal Verifier MTP & SIG validators", function () { ); const mtpValAddr = await mtp.getAddress(); expect(await verifier.isWhitelistedValidator(mtpValAddr)).to.be.false; - await expect( - verifier.setZKPRequest(requestId, { - metadata: "metadata", - validator: mtpValAddr, - data: "0x00", - }), - ).to.be.rejectedWith("Validator is not whitelisted"); - + verifier.setRequests( + [ + { + requestId: requestId, + metadata: "metadata", + validator: mtpValAddr, + params: "0x00", + }, + ], + [], + ), + ).to.be.rejectedWith(`ValidatorIsNotWhitelisted("${mtpValAddr}")`); await expect(verifier.connect(someAddress).addValidatorToWhitelist(mtpValAddr)) .to.be.revertedWithCustomError(verifier, "OwnableUnauthorizedAccount") .withArgs(someAddress); @@ -289,84 +392,128 @@ describe("Universal Verifier MTP & SIG validators", function () { expect(await verifier.isWhitelistedValidator(mtpValAddr)).to.be.true; await expect( - verifier.setZKPRequest(requestId, { - metadata: "metadata", - validator: mtpValAddr, - data: "0x00", - }), + verifier.setRequests( + [ + { + requestId: requestId, + metadata: "metadata", + validator: mtpValAddr, + params: "0x00", + }, + ], + [], + ), ).not.to.be.rejected; // can't whitelist validator, which does not support ICircuitValidator interface await expect(verifier.addValidatorToWhitelist(someAddress)).to.be.rejected; await expect( - verifier.setZKPRequest(otherRequestId, { - metadata: "metadata", - validator: someAddress, - data: "0x00", - }), - ).to.be.rejectedWith("Validator is not whitelisted"); + verifier.setRequests( + [ + { + requestId: otherRequestId, + metadata: "metadata", + validator: someAddress, + params: "0x00", + }, + ], + [], + ), + ).to.be.rejectedWith(`ValidatorIsNotWhitelisted("${await someAddress.getAddress()}")`); await verifier.removeValidatorFromWhitelist(mtpValAddr); + await verifier.setAuthType({ + authType: authType, + validator: await authV2Validator.getAddress(), + params: "0x00", + }); + + const proof = packZKProof( + [], + ["0", "0"], + [ + ["0", "0"], + ["0", "0"], + ], + ["0", "0"], + ); + const crossChainProofs = "0x"; + const metadatas = "0x"; await expect( - verifier.submitZKPResponse( - requestId, - [], - [0, 0], + verifier.submitResponse( + [ + { + authType: authType, + proof, + }, + ], [ - [0, 0], - [0, 0], + { + requestId: requestId, + proof, + metadata: metadatas, + }, ], - [0, 0], + [], + crossChainProofs, ), - ).to.be.rejectedWith("Validator is not whitelisted"); + ).to.be.rejectedWith(`ValidatorIsNotWhitelisted("${mtpValAddr}")`); }); - it("Check updateZKPRequest", async () => { + it("Check updateRequest", async () => { const owner = signer; const requestOwner = signer2; const requestId = 0; - const data = packValidatorParams(query); - - await verifier.connect(requestOwner).setZKPRequest(requestId, { - metadata: "metadata", - validator: await sigValidator.getAddress(), - data: data, - }); + const params = packValidatorParams(query); + + await verifier.connect(requestOwner).setRequests( + [ + { + requestId: requestId, + metadata: "metadata", + validator: await sigValidator.getAddress(), + params: params, + }, + ], + [], + ); - let request = await verifier.getZKPRequest(requestId); + let request = await verifier.getRequest(requestId); expect(request.metadata).to.be.equal("metadata"); - await expect( - verifier.connect(requestOwner).updateZKPRequest(requestId, { + verifier.connect(requestOwner).updateRequest({ + requestId, metadata: "metadata", validator: await sigValidator.getAddress(), - data: data, + params: params, }), ).to.be.revertedWithCustomError(verifier, "OwnableUnauthorizedAccount"); - await verifier.connect(owner).updateZKPRequest(requestId, { + await verifier.connect(owner).updateRequest({ + requestId, metadata: "metadata2", validator: await sigValidator.getAddress(), - data: data, + params: params, }); - request = await verifier.getZKPRequest(requestId); + request = await verifier.getRequest(requestId); expect(request.metadata).to.be.equal("metadata2"); }); - it("updateZKPRequest - not existed request", async () => { + it("updateRequest - not existed request", async () => { const owner = signer; const requestId = 0; - const data = packValidatorParams(query); + const params = packValidatorParams(query); await expect( - verifier.connect(owner).updateZKPRequest(requestId, { + verifier.connect(owner).updateRequest({ + requestId, metadata: "metadata", validator: await sigValidator.getAddress(), - data: data, + params, }), - ).to.be.rejectedWith("equest id doesn't exis"); + ).to.be.rejectedWith(`RequestIdNotFound(${requestId})`); }); }); diff --git a/test/verifier/universal-verifier.v3.test.ts b/test/verifier/universal-verifier.v3.test.ts index 08c840c5..c3f47cfe 100644 --- a/test/verifier/universal-verifier.v3.test.ts +++ b/test/verifier/universal-verifier.v3.test.ts @@ -32,10 +32,12 @@ const storageFields = [ ]; describe("Universal Verifier V3 validator", function () { - let verifier: any, v3Validator: any, state: any; + let verifier: any, v3Validator: any, authV2Validator: any, state: any; let signer, signer2; let deployHelper: DeployHelper; + const authType = "authV2"; + const value = ["20010101", ...new Array(63).fill("0")]; const schema = "267831521922558027206082390043321796944"; @@ -83,29 +85,39 @@ describe("Universal Verifier V3 validator", function () { "v3", await stateContract.getAddress(), ); - const validator = contracts.validator; + const v3Validator = contracts.validator; const universalVerifier: any = await deployHelper.deployUniversalVerifier( signer, await stateContract.getAddress(), await verifierLib.getAddress(), ); - await universalVerifier.addValidatorToWhitelist(await validator.getAddress()); + await universalVerifier.addValidatorToWhitelist(await v3Validator.getAddress()); await universalVerifier.connect(); - return { stateContract, validator, universalVerifier }; + const authV2Validator = await deployHelper.deployValidatorStub("AuthValidatorStub"); + + return { stateContract, v3Validator, authV2Validator, universalVerifier }; }; async function deployContractsFixture() { const [ethSigner, ethSigner2] = await ethers.getSigners(); - const { stateContract, validator, universalVerifier } = await initializeState(); - return { ethSigner, ethSigner2, stateContract, universalVerifier, validator }; + const { stateContract, v3Validator, authV2Validator, universalVerifier } = + await initializeState(); + return { + ethSigner, + ethSigner2, + stateContract, + universalVerifier, + v3Validator, + authV2Validator, + }; } - async function checkStorageFields(verifier: any, requestId: number, storageFields: any[]) { + async function checkStorageFields(verifier: any, requestId: bigint, storageFields: any[]) { for (const field of storageFields) { - const value = await verifier.getProofStorageField( - await signer.getAddress(), + const value = await verifier.getResponseFieldValueFromAddress( requestId, + await signer.getAddress(), field.name, ); expect(value).to.be.equal(field.value); @@ -117,88 +129,151 @@ describe("Universal Verifier V3 validator", function () { ethSigner: signer, ethSigner2: signer2, stateContract: state, - validator: v3Validator, + v3Validator: v3Validator, + authV2Validator: authV2Validator, universalVerifier: verifier, } = await loadFixture(deployContractsFixture)); await v3Validator.setProofExpirationTimeout(TEN_YEARS); }); it("Test submit response", async () => { - await publishState(state, stateTransition1 as any); - const data = packV3ValidatorParams(query); const requestId = 32; - await verifier.setZKPRequest(requestId, { - metadata: "metadata", - validator: await v3Validator.getAddress(), - data: data, - }); - await v3Validator.setProofExpirationTimeout(TEN_YEARS); - const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); - - await verifier.verifyZKPResponse( - requestId, - inputs, - pi_a, - pi_b, - pi_c, - "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + await publishState(state, stateTransition1 as any); + const params = packV3ValidatorParams(query); + await verifier.setRequests( + [ + { + requestId: requestId, + metadata: "metadata", + validator: await v3Validator.getAddress(), + params: params, + }, + ], + [], ); - await expect(verifier.submitZKPResponse(requestId, inputs, pi_a, pi_b, pi_c)).not.to.be - .rejected; - - await checkStorageFields(verifier, requestId, storageFields); - }); + const requestStored = await verifier.getRequest(requestId); + // check if the request is stored correctly checking metadata and validator + expect(requestStored.metadata).to.be.equal("metadata"); + expect(requestStored.validator).to.be.equal(await v3Validator.getAddress()); + expect(requestStored.params).to.be.equal(params); + expect(requestStored.creator).to.be.equal(await signer.getAddress()); + expect(requestStored.verifierId).to.be.equal(verifierId); + expect(requestStored.isVerifierAuthenticated).to.be.equal(false); + + await verifier.setAuthType({ + authType: authType, + validator: await authV2Validator.getAddress(), + params: params, + }); + const authRequestStored = await verifier.getAuthType(authType); + expect(authRequestStored.validator).to.be.equal(await authV2Validator.getAddress()); + expect(authRequestStored.params).to.be.equal(params); + expect(authRequestStored.isActive).to.be.equal(true); - it("Test submit response V2", async () => { - const requestId = 32; + await v3Validator.setProofExpirationTimeout(TEN_YEARS); const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); - const zkProof = packZKProof(inputs, pi_a, pi_b, pi_c); + const proof = packZKProof(inputs, pi_a, pi_b, pi_c); const crossChainProofs = "0x"; const metadatas = "0x"; await expect( - verifier.submitZKPResponseV2( + verifier.submitResponse( + [ + { + authType: authType, + proof, + }, + ], [ { requestId, - zkProof: zkProof, - data: metadatas, + proof, + metadata: metadatas, }, ], + [], crossChainProofs, ), ).not.to.be.rejected; - await checkStorageFields(verifier, requestId, storageFields); + await checkStorageFields(verifier, BigInt(requestId), storageFields); }); it("Test submit response fails with UserID does not correspond to the sender", async () => { const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); const requestId = 32; + + const proof = packZKProof(inputs, pi_a, pi_b, pi_c); + + const crossChainProofs = "0x"; + const metadatas = "0x"; + await expect( - verifier.connect(signer2).submitZKPResponse(requestId, inputs, pi_a, pi_b, pi_c), + verifier.connect(signer2).submitResponse( + [ + { + authType: authType, + proof, + }, + ], + [ + { + requestId, + proof, + metadata: metadatas, + }, + ], + [], + crossChainProofs, + ), ).to.be.rejectedWith("UserID does not correspond to the sender"); }); it("Test submit response fails with Issuer is not on the Allowed Issuers list", async () => { - const data = packV3ValidatorParams(query, ["1"]); + const params = packV3ValidatorParams(query, ["1"]); const requestId = 33; - await verifier.setZKPRequest(requestId, { - metadata: "metadata", - validator: await v3Validator.getAddress(), - data: data, - }); + await verifier.setRequests( + [ + { + requestId: requestId, + metadata: "metadata", + validator: await v3Validator.getAddress(), + params: params, + }, + ], + [], + ); const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); + const proof = packZKProof(inputs, pi_a, pi_b, pi_c); + + const crossChainProofs = "0x"; + const metadatas = "0x"; await expect( - verifier.connect(signer).submitZKPResponse(requestId, inputs, pi_a, pi_b, pi_c), + verifier.connect(signer).submitResponse( + [ + { + authType: authType, + proof, + }, + ], + [ + { + requestId, + proof, + metadata: metadatas, + }, + ], + [], + crossChainProofs, + ), ).to.be.rejectedWith("Issuer is not on the Allowed Issuers list"); }); @@ -208,17 +283,43 @@ describe("Universal Verifier V3 validator", function () { }; query2.groupID = 0; const requestId = 34; - const data = packV3ValidatorParams(query2); - await verifier.setZKPRequest(requestId, { - metadata: "metadata", - validator: await v3Validator.getAddress(), - data: data, - }); + const params = packV3ValidatorParams(query2); + await verifier.setRequests( + [ + { + requestId: requestId, + metadata: "metadata", + validator: await v3Validator.getAddress(), + params: params, + }, + ], + [], + ); const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); + const proof = packZKProof(inputs, pi_a, pi_b, pi_c); + + const crossChainProofs = "0x"; + const metadatas = "0x"; await expect( - verifier.connect(signer).submitZKPResponse(requestId, inputs, pi_a, pi_b, pi_c), + verifier.connect(signer).submitResponse( + [ + { + authType: authType, + proof, + }, + ], + [ + { + requestId, + proof, + metadata: metadatas, + }, + ], + [], + crossChainProofs, + ), ).to.be.rejectedWith("Invalid Link ID pub signal"); }); @@ -228,17 +329,43 @@ describe("Universal Verifier V3 validator", function () { }; query2.proofType = 2; const requestId = 35; - const data = packV3ValidatorParams(query2); - await verifier.setZKPRequest(requestId, { - metadata: "metadata", - validator: await v3Validator.getAddress(), - data: data, - }); + const params = packV3ValidatorParams(query2); + await verifier.setRequests( + [ + { + requestId: requestId, + metadata: "metadata", + validator: await v3Validator.getAddress(), + params: params, + }, + ], + [], + ); const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); + const proof = packZKProof(inputs, pi_a, pi_b, pi_c); + + const crossChainProofs = "0x"; + const metadatas = "0x"; await expect( - verifier.connect(signer).submitZKPResponse(requestId, inputs, pi_a, pi_b, pi_c), + verifier.connect(signer).submitResponse( + [ + { + authType: authType, + proof, + }, + ], + [ + { + requestId, + proof, + metadata: metadatas, + }, + ], + [], + crossChainProofs, + ), ).to.be.rejectedWith("Proof type should match the requested one in query"); }); @@ -248,17 +375,43 @@ describe("Universal Verifier V3 validator", function () { }; query2.nullifierSessionID = "2"; const requestId = 36; - const data = packV3ValidatorParams(query2); - await verifier.setZKPRequest(requestId, { - metadata: "metadata", - validator: await v3Validator.getAddress(), - data: data, - }); + const params = packV3ValidatorParams(query2); + await verifier.setRequests( + [ + { + requestId: requestId, + metadata: "metadata", + validator: await v3Validator.getAddress(), + params: params, + }, + ], + [], + ); const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); + const proof = packZKProof(inputs, pi_a, pi_b, pi_c); + + const crossChainProofs = "0x"; + const metadatas = "0x"; await expect( - verifier.connect(signer).submitZKPResponse(requestId, inputs, pi_a, pi_b, pi_c), + verifier.connect(signer).submitResponse( + [ + { + authType: authType, + proof, + }, + ], + [ + { + requestId, + proof, + metadata: metadatas, + }, + ], + [], + crossChainProofs, + ), ).to.be.rejectedWith("Invalid nullify pub signal"); }); @@ -268,17 +421,43 @@ describe("Universal Verifier V3 validator", function () { }; query2.queryHash = BigInt(0); const requestId = 37; - const data = packV3ValidatorParams(query2); - await verifier.setZKPRequest(requestId, { - metadata: "metadata", - validator: await v3Validator.getAddress(), - data: data, - }); + const params = packV3ValidatorParams(query2); + await verifier.setRequests( + [ + { + requestId: requestId, + metadata: "metadata", + validator: await v3Validator.getAddress(), + params: params, + }, + ], + [], + ); const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); + const proof = packZKProof(inputs, pi_a, pi_b, pi_c); + + const crossChainProofs = "0x"; + const metadatas = "0x"; await expect( - verifier.connect(signer).submitZKPResponse(requestId, inputs, pi_a, pi_b, pi_c), + verifier.connect(signer).submitResponse( + [ + { + authType: authType, + proof, + }, + ], + [ + { + requestId, + proof, + metadata: metadatas, + }, + ], + [], + crossChainProofs, + ), ).to.be.rejectedWith("Query hash does not match the requested one"); }); @@ -287,7 +466,7 @@ describe("Universal Verifier V3 validator", function () { ethSigner: signer, ethSigner2: signer2, stateContract: state, - validator: v3Validator, + v3Validator: v3Validator, universalVerifier: verifier, } = await loadFixture(deployContractsFixture)); @@ -295,18 +474,50 @@ describe("Universal Verifier V3 validator", function () { await publishState(state, stateTransition12 as any); await publishState(state, stateTransition13 as any); - const data = packV3ValidatorParams(query); + const params = packV3ValidatorParams(query); const requestId = 37; - await verifier.setZKPRequest(requestId, { - metadata: "metadata", - validator: await v3Validator.getAddress(), - data: data, + await verifier.setRequests( + [ + { + requestId: requestId, + metadata: "metadata", + validator: await v3Validator.getAddress(), + params: params, + }, + ], + [], + ); + + await verifier.setAuthType({ + authType: authType, + validator: await authV2Validator.getAddress(), + params: params, }); const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); + const proof = packZKProof(inputs, pi_a, pi_b, pi_c); + + const crossChainProofs = "0x"; + const metadatas = "0x"; await expect( - verifier.connect(signer).submitZKPResponse(requestId, inputs, pi_a, pi_b, pi_c), + verifier.connect(signer).submitResponse( + [ + { + authType: authType, + proof, + }, + ], + [ + { + requestId, + proof, + metadata: metadatas, + }, + ], + [], + crossChainProofs, + ), ).to.be.rejectedWith("Generated proof is outdated"); }); }); From faa2d0b55cd492ddfb4d2e78414fdd8eb3e631e2 Mon Sep 17 00:00:00 2001 From: daveroga Date: Fri, 20 Dec 2024 13:44:32 +0100 Subject: [PATCH 43/69] fix validators tests and solhint --- contracts/lib/VerifierLib.sol | 2 +- .../RequestValidatorAuthV2Stub.sol | 2 +- contracts/validators/AuthV2Validator.sol | 14 +++++---- contracts/verifiers/Verifier.sol | 27 ++++++++--------- test/validators/mtp/index.ts | 26 +++-------------- test/validators/sig/index.ts | 26 +++-------------- test/validators/v3/index.ts | 29 +++---------------- 7 files changed, 35 insertions(+), 91 deletions(-) diff --git a/contracts/lib/VerifierLib.sol b/contracts/lib/VerifierLib.sol index cac98051..20af4992 100644 --- a/contracts/lib/VerifierLib.sol +++ b/contracts/lib/VerifierLib.sol @@ -38,7 +38,7 @@ library VerifierLib { // (see https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#storage-gaps) uint256[45] __gap; } - + /** * @dev Writes proof results. * @param requestId The request ID of the proof diff --git a/contracts/test-helpers/RequestValidatorAuthV2Stub.sol b/contracts/test-helpers/RequestValidatorAuthV2Stub.sol index b8fa1919..51820f44 100644 --- a/contracts/test-helpers/RequestValidatorAuthV2Stub.sol +++ b/contracts/test-helpers/RequestValidatorAuthV2Stub.sol @@ -34,7 +34,7 @@ contract RequestValidatorAuthV2Stub is IRequestValidator, ERC165 { } function getGroupID(bytes calldata) external pure override returns (uint256) { - return 0; + return 0; } function getVerifierId(bytes calldata) external pure override returns (uint256) { diff --git a/contracts/validators/AuthV2Validator.sol b/contracts/validators/AuthV2Validator.sol index 2ee2da37..3ca40325 100644 --- a/contracts/validators/AuthV2Validator.sol +++ b/contracts/validators/AuthV2Validator.sol @@ -65,7 +65,7 @@ contract AuthV2Validator is CredentialAtomicQueryValidatorBase { return pubSignals; } - /** + /** * @dev Get the group ID of the request query data. * @param params Request query data of the credential to verify. * @return Group ID of the request query data. @@ -83,7 +83,7 @@ contract AuthV2Validator is CredentialAtomicQueryValidatorBase { return 0; } - /** + /** * @dev Verify the groth16 proof and check the request query data * @param proof Proof packed as bytes to verify. * @param data Request query data of the credential to verify. @@ -104,13 +104,17 @@ contract AuthV2Validator is CredentialAtomicQueryValidatorBase { uint256[2][2] memory b, uint256[2] memory c ) = abi.decode(proof, (uint256[], uint256[2], uint256[2][2], uint256[2])); - + PubSignals memory pubSignals = parsePubSignals(inputs); _checkGistRoot(pubSignals.userID, pubSignals.gistRoot, state); _checkChallenge(pubSignals.challenge, sender); _verifyZKP(inputs, a, b, c); - IRequestValidator.ResponseField[] memory responseFields = new IRequestValidator.ResponseField[](1); - responseFields[0] = IRequestValidator.ResponseField({name: "userID", value: pubSignals.userID}); + IRequestValidator.ResponseField[] + memory responseFields = new IRequestValidator.ResponseField[](1); + responseFields[0] = IRequestValidator.ResponseField({ + name: "userID", + value: pubSignals.userID + }); return responseFields; } diff --git a/contracts/verifiers/Verifier.sol b/contracts/verifiers/Verifier.sol index e60e4b38..df24b605 100644 --- a/contracts/verifiers/Verifier.sol +++ b/contracts/verifiers/Verifier.sol @@ -59,8 +59,6 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { uint256 timestamp; } - - /// @custom:storage-location erc7201:iden3.storage.Verifier struct VerifierStorage { // Information about requests @@ -93,8 +91,6 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { using VerifierLib for VerifierStorage; - - /** * @dev Modifier to check if the request exists */ @@ -684,7 +680,11 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { function _getQueryStatus( uint256 queryId, uint256 userID - ) internal view returns (IVerifier.AuthProofStatus[] memory, IVerifier.RequestProofStatus[] memory) { + ) + internal + view + returns (IVerifier.AuthProofStatus[] memory, IVerifier.RequestProofStatus[] memory) + { VerifierStorage storage s = _getVerifierStorage(); Query storage query = s._queries[queryId]; @@ -697,10 +697,13 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { } } - IVerifier.AuthProofStatus[] memory authProofStatus = new IVerifier.AuthProofStatus[](s._authTypes.length); - IVerifier.RequestProofStatus[] memory requestProofStatus = new IVerifier.RequestProofStatus[]( - query.requestIds.length + lengthGroupIds + IVerifier.AuthProofStatus[] memory authProofStatus = new IVerifier.AuthProofStatus[]( + s._authTypes.length ); + IVerifier.RequestProofStatus[] + memory requestProofStatus = new IVerifier.RequestProofStatus[]( + query.requestIds.length + lengthGroupIds + ); for (uint256 i = 0; i < s._authTypes.length; i++) { string memory authType = s._authTypes[i]; @@ -783,7 +786,6 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { } } - /** * @dev Get the requests count. * @return Requests count. @@ -810,16 +812,11 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { address sender, uint256 requestId ) public view checkRequestExistence(requestId, true) returns (IVerifier.ProofStatus memory) { - VerifierStorage storage s = _getVerifierStorage(); uint256 userID = s._user_address_to_id[sender]; VerifierLib.Proof storage proof = s._proofs[requestId][userID][0]; return - IVerifier.ProofStatus( - proof.isVerified, - proof.validatorVersion, - proof.blockTimestamp - ); + IVerifier.ProofStatus(proof.isVerified, proof.validatorVersion, proof.blockTimestamp); } } diff --git a/test/validators/mtp/index.ts b/test/validators/mtp/index.ts index 4e341f8e..14626c89 100644 --- a/test/validators/mtp/index.ts +++ b/test/validators/mtp/index.ts @@ -206,34 +206,16 @@ describe("Atomic MTP Validator", function () { const data = packValidatorParams(query, test.allowedIssuers); // Check verify function - if (test.errorMessage) { - await expect( - mtpValidator.verify(inputs, pi_a, pi_b, pi_c, data, senderAddress), - ).to.be.rejectedWith(test.errorMessage); - } else if (test.errorMessage === "") { - await expect(mtpValidator.verify(inputs, pi_a, pi_b, pi_c, data, senderAddress)).to.be - .reverted; - } else { - const signals = await mtpValidator.verify(inputs, pi_a, pi_b, pi_c, data, senderAddress); - const signalValues: any[] = []; - // Replace index with value to check instead of signal index - for (let i = 0; i < signals.length; i++) { - signalValues.push([signals[i][0], inputs[signals[i][1]]]); - } - checkSignals(signalValues, test.signalValues); - } - - // Check verifyV2 function const zkProof = packZKProof(inputs, pi_a, pi_b, pi_c); if (test.errorMessage) { await expect( - mtpValidator.verifyV2(zkProof, data, senderAddress, await state.getAddress()), + mtpValidator.verify(zkProof, data, senderAddress, await state.getAddress()), ).to.be.rejectedWith(test.errorMessage); } else if (test.errorMessage === "") { - await expect(mtpValidator.verifyV2(zkProof, data, senderAddress, await state.getAddress())) - .to.be.reverted; + await expect(mtpValidator.verify(zkProof, data, senderAddress, await state.getAddress())).to + .be.reverted; } else { - const signals = await mtpValidator.verifyV2( + const signals = await mtpValidator.verify( zkProof, data, senderAddress, diff --git a/test/validators/sig/index.ts b/test/validators/sig/index.ts index 08d83bdf..d21adc3e 100644 --- a/test/validators/sig/index.ts +++ b/test/validators/sig/index.ts @@ -202,34 +202,16 @@ describe("Atomic Sig Validator", function () { const data = packValidatorParams(query, test.allowedIssuers); // Check verify function - if (test.errorMessage) { - await expect( - sigValidator.verify(inputs, pi_a, pi_b, pi_c, data, senderAddress), - ).to.be.rejectedWith(test.errorMessage); - } else if (test.errorMessage === "") { - await expect(sigValidator.verify(inputs, pi_a, pi_b, pi_c, data, senderAddress)).to.be - .reverted; - } else { - const signals = await sigValidator.verify(inputs, pi_a, pi_b, pi_c, data, senderAddress); - const signalValues: any[] = []; - // Replace index with value to check instead of signal index - for (let i = 0; i < signals.length; i++) { - signalValues.push([signals[i][0], inputs[signals[i][1]]]); - } - checkSignals(signalValues, test.signalValues); - } - - // Check verifyV2 function const zkProof = packZKProof(inputs, pi_a, pi_b, pi_c); if (test.errorMessage) { await expect( - sigValidator.verifyV2(zkProof, data, senderAddress, await state.getAddress()), + sigValidator.verify(zkProof, data, senderAddress, await state.getAddress()), ).to.be.rejectedWith(test.errorMessage); } else if (test.errorMessage === "") { - await expect(sigValidator.verifyV2(zkProof, data, senderAddress, await state.getAddress())) - .to.be.reverted; + await expect(sigValidator.verify(zkProof, data, senderAddress, await state.getAddress())).to + .be.reverted; } else { - const signals = await sigValidator.verifyV2( + const signals = await sigValidator.verify( zkProof, data, senderAddress, diff --git a/test/validators/v3/index.ts b/test/validators/v3/index.ts index 1d7f39a2..ef7683a7 100644 --- a/test/validators/v3/index.ts +++ b/test/validators/v3/index.ts @@ -594,37 +594,16 @@ describe("Atomic V3 Validator", function () { const data = packV3ValidatorParams(query, test.allowedIssuers); // Check verify function - if (test.errorMessage) { - await expect( - v3validator.verify(inputs, pi_a, pi_b, pi_c, data, test.sender), - ).to.be.rejectedWith(test.errorMessage); - } else if (test.errorMessage === "") { - await expect(v3validator.verify(inputs, pi_a, pi_b, pi_c, data, test.sender)).to.be - .reverted; - } else { - const signals = await v3validator.verify(inputs, pi_a, pi_b, pi_c, data, test.sender); - - const signalValues: any[] = []; - // Replace index with value to check instead of signal index - for (let i = 0; i < signals.length; i++) { - signalValues.push([signals[i][0], inputs[signals[i][1]]]); - } - - // Check if the number signals are correct. "operatorOutput" for selective disclosure is optional - checkSignals(signalValues, test.signalValues); - } - - // Check verifyV2 function const zkProof = packZKProof(inputs, pi_a, pi_b, pi_c); if (test.errorMessage) { await expect( - v3validator.verifyV2(zkProof, data, test.sender, await state.getAddress()), + v3validator.verify(zkProof, data, test.sender, await state.getAddress()), ).to.be.rejectedWith(test.errorMessage); } else if (test.errorMessage === "") { - await expect(v3validator.verifyV2(zkProof, data, test.sender, await state.getAddress())).to - .be.reverted; + await expect(v3validator.verify(zkProof, data, test.sender, await state.getAddress())).to.be + .reverted; } else { - const signals = await v3validator.verifyV2( + const signals = await v3validator.verify( zkProof, data, test.sender, From 6336ad57fdb1cdedaabc070d13078d2eae14b677 Mon Sep 17 00:00:00 2001 From: daveroga Date: Mon, 30 Dec 2024 08:46:43 +0100 Subject: [PATCH 44/69] partial changes review --- contracts/interfaces/IAuthValidator.sol | 4 +- contracts/interfaces/IRequestValidator.sol | 25 ++++++------ contracts/interfaces/IVerifier.sol | 3 +- contracts/lib/VerifierLib.sol | 3 +- .../RequestValidatorAuthV2Stub.sol | 8 +--- .../test-helpers/RequestValidatorV2Stub.sol | 8 +--- .../test-helpers/RequestValidatorV3Stub.sol | 14 ++----- .../test-helpers/RequestValidatorV3_2Stub.sol | 12 +----- contracts/validators/AuthV2Validator.sol | 18 +-------- .../CredentialAtomicQueryV2ValidatorBase.sol | 18 +-------- .../CredentialAtomicQueryV3Validator.sol | 24 ++---------- contracts/validators/EthIdentityValidator.sol | 15 ++----- contracts/verifiers/UniversalVerifier.sol | 8 ++-- contracts/verifiers/Verifier.sol | 39 +++++++++---------- .../universal-verifier-multi-query.test.ts | 6 +-- 15 files changed, 62 insertions(+), 143 deletions(-) diff --git a/contracts/interfaces/IAuthValidator.sol b/contracts/interfaces/IAuthValidator.sol index 8578b07f..b69b94be 100644 --- a/contracts/interfaces/IAuthValidator.sol +++ b/contracts/interfaces/IAuthValidator.sol @@ -26,14 +26,14 @@ interface IAuthValidator { * @dev Verify the proof with the supported method informed in the auth query data * packed as bytes and that the proof was generated by the sender. * @param proof Proof packed as bytes to verify. - * @param data Request query data of the credential to verify. + * @param params Request query data of the credential to verify. * @param sender Sender of the proof. * @param state State contract to get identities and gist states to check. * @return Array of response fields as result. */ function verify( bytes calldata proof, - bytes calldata data, + bytes calldata params, address sender, IState state ) external returns (ResponseField[] memory); diff --git a/contracts/interfaces/IRequestValidator.sol b/contracts/interfaces/IRequestValidator.sol index 70effc38..a7292f71 100644 --- a/contracts/interfaces/IRequestValidator.sol +++ b/contracts/interfaces/IRequestValidator.sol @@ -17,6 +17,16 @@ interface IRequestValidator { uint256 value; } + /** + * @dev RequestParams. Information about request params from request query data. + * @param groupID Group ID of the request query params + * @param verifierID Verifier ID of the request query params + */ + struct RequestParams { + uint256 groupID; + uint256 verifierID; + } + /** * @dev Get version of the contract */ @@ -26,29 +36,22 @@ interface IRequestValidator { * @dev Verify the proof with the supported method informed in the request query data * packed as bytes and that the proof was generated by the sender. * @param proof Proof packed as bytes to verify. - * @param data Request query data of the credential to verify. + * @param params Request query data of the credential to verify. * @param sender Sender of the proof. * @param state State contract to get identities and gist states to check. * @return Array of response fields as result. */ function verify( bytes calldata proof, - bytes calldata data, + bytes calldata params, address sender, IState state ) external returns (ResponseField[] memory); /** - * @dev Get the group ID of the request query data. + * @dev Get the request params of the request query data. * @param params Request query data of the credential to verify. * @return Group ID of the request query data. */ - function getGroupID(bytes calldata params) external view returns (uint256); - - /** - * @dev Get the verifier ID of the request query data. - * @param params Request query data of the credential to verify. - * @return Verifier ID encoded in the request query data. - */ - function getVerifierId(bytes calldata params) external view returns (uint256); + function getRequestParams(bytes calldata params) external view returns (RequestParams memory); } diff --git a/contracts/interfaces/IVerifier.sol b/contracts/interfaces/IVerifier.sol index c5fafe71..0b60623a 100644 --- a/contracts/interfaces/IVerifier.sol +++ b/contracts/interfaces/IVerifier.sol @@ -240,10 +240,9 @@ interface IVerifier { /** * @dev Sets a query - * @param queryId The ID of the query * @param query The query data */ - function setQuery(uint256 queryId, Query calldata query) external; + function setQuery(Query calldata query) external; /** * @dev Gets a specific multi query by ID diff --git a/contracts/lib/VerifierLib.sol b/contracts/lib/VerifierLib.sol index 20af4992..1b291077 100644 --- a/contracts/lib/VerifierLib.sol +++ b/contracts/lib/VerifierLib.sol @@ -20,10 +20,9 @@ library VerifierLib { // TODO: discuss if we need this field // uint256 blockNumber; uint256 blockTimestamp; - mapping(string key => bytes) metadata; // This empty reserved space is put in place to allow future versions // (see https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#storage-gaps) - uint256[45] __gap; + uint256[46] __gap; } /** diff --git a/contracts/test-helpers/RequestValidatorAuthV2Stub.sol b/contracts/test-helpers/RequestValidatorAuthV2Stub.sol index 51820f44..64ad9e69 100644 --- a/contracts/test-helpers/RequestValidatorAuthV2Stub.sol +++ b/contracts/test-helpers/RequestValidatorAuthV2Stub.sol @@ -33,11 +33,7 @@ contract RequestValidatorAuthV2Stub is IRequestValidator, ERC165 { return signals; } - function getGroupID(bytes calldata) external pure override returns (uint256) { - return 0; - } - - function getVerifierId(bytes calldata) external pure override returns (uint256) { - return 0; + function getRequestParams(bytes calldata) external pure override returns (IRequestValidator.RequestParams memory) { + return IRequestValidator.RequestParams({ groupID: 0, verifierID: 0 }); } } diff --git a/contracts/test-helpers/RequestValidatorV2Stub.sol b/contracts/test-helpers/RequestValidatorV2Stub.sol index 66b79081..549f1077 100644 --- a/contracts/test-helpers/RequestValidatorV2Stub.sol +++ b/contracts/test-helpers/RequestValidatorV2Stub.sol @@ -35,11 +35,7 @@ contract RequestValidatorV2Stub is IRequestValidator, ERC165 { return signals; } - function getGroupID(bytes calldata) external pure override returns (uint256) { - return 0; - } - - function getVerifierId(bytes calldata) external pure override returns (uint256) { - return 0; + function getRequestParams(bytes calldata) external pure override returns (IRequestValidator.RequestParams memory) { + return IRequestValidator.RequestParams({ groupID: 0, verifierID: 0 }); } } diff --git a/contracts/test-helpers/RequestValidatorV3Stub.sol b/contracts/test-helpers/RequestValidatorV3Stub.sol index e3f4c73c..71048ba7 100644 --- a/contracts/test-helpers/RequestValidatorV3Stub.sol +++ b/contracts/test-helpers/RequestValidatorV3Stub.sol @@ -53,19 +53,11 @@ contract RequestValidatorV3Stub is IRequestValidator, ERC165 { return signals; } - function getGroupID(bytes calldata params) external pure override returns (uint256) { + function getRequestParams(bytes calldata params) external pure override returns (IRequestValidator.RequestParams memory) { CredentialAtomicQueryV3 memory credAtomicQuery = abi.decode( params, (CredentialAtomicQueryV3) ); - return credAtomicQuery.groupID; - } - - function getVerifierId(bytes calldata params) external pure override returns (uint256) { - CredentialAtomicQueryV3 memory credAtomicQuery = abi.decode( - params, - (CredentialAtomicQueryV3) - ); - return credAtomicQuery.verifierID; - } + return IRequestValidator.RequestParams({ groupID: credAtomicQuery.groupID, verifierID: credAtomicQuery.verifierID }); + } } diff --git a/contracts/test-helpers/RequestValidatorV3_2Stub.sol b/contracts/test-helpers/RequestValidatorV3_2Stub.sol index 13b3b7f2..4140e913 100644 --- a/contracts/test-helpers/RequestValidatorV3_2Stub.sol +++ b/contracts/test-helpers/RequestValidatorV3_2Stub.sol @@ -53,19 +53,11 @@ contract RequestValidatorV3_2Stub is IRequestValidator, ERC165 { return signals; } - function getGroupID(bytes calldata params) external pure override returns (uint256) { + function getRequestParams(bytes calldata params) external pure override returns (IRequestValidator.RequestParams memory) { CredentialAtomicQueryV3 memory credAtomicQuery = abi.decode( params, (CredentialAtomicQueryV3) ); - return credAtomicQuery.groupID; - } - - function getVerifierId(bytes calldata params) external pure override returns (uint256) { - CredentialAtomicQueryV3 memory credAtomicQuery = abi.decode( - params, - (CredentialAtomicQueryV3) - ); - return credAtomicQuery.verifierID; + return IRequestValidator.RequestParams({ groupID: credAtomicQuery.groupID, verifierID: credAtomicQuery.verifierID }); } } diff --git a/contracts/validators/AuthV2Validator.sol b/contracts/validators/AuthV2Validator.sol index 3ca40325..0fef4671 100644 --- a/contracts/validators/AuthV2Validator.sol +++ b/contracts/validators/AuthV2Validator.sol @@ -65,22 +65,8 @@ contract AuthV2Validator is CredentialAtomicQueryValidatorBase { return pubSignals; } - /** - * @dev Get the group ID of the request query data. - * @param params Request query data of the credential to verify. - * @return Group ID of the request query data. - */ - function getGroupID(bytes calldata params) external pure override returns (uint256) { - return 0; - } - - /** - * @dev Get the verifier ID from the request query data - * @param params Request query data of the credential to verify. - * @return Verifier ID - */ - function getVerifierId(bytes calldata params) external pure override returns (uint256) { - return 0; + function getRequestParams(bytes calldata) external pure override returns (IRequestValidator.RequestParams memory) { + return IRequestValidator.RequestParams({ groupID: 0, verifierID: 0 }); } /** diff --git a/contracts/validators/CredentialAtomicQueryV2ValidatorBase.sol b/contracts/validators/CredentialAtomicQueryV2ValidatorBase.sol index 779afe8d..800f69dd 100644 --- a/contracts/validators/CredentialAtomicQueryV2ValidatorBase.sol +++ b/contracts/validators/CredentialAtomicQueryV2ValidatorBase.sol @@ -82,22 +82,8 @@ abstract contract CredentialAtomicQueryV2ValidatorBase is CredentialAtomicQueryV return _getResponseFields(pubSignals); } - /** - * @dev Get the group ID of the request query data. - * @param params Request query data of the credential to verify. - * @return Group ID of the request query data. - */ - function getGroupID(bytes calldata params) external pure override returns (uint256) { - return 0; - } - - /** - * @dev Get the verifier ID from the request query data - * @param params Request query data of the credential to verify. - * @return Verifier ID - */ - function getVerifierId(bytes calldata params) external pure override returns (uint256) { - return 0; + function getRequestParams(bytes calldata) external pure override returns (IRequestValidator.RequestParams memory) { + return IRequestValidator.RequestParams({ groupID: 0, verifierID: 0 }); } /** diff --git a/contracts/validators/CredentialAtomicQueryV3Validator.sol b/contracts/validators/CredentialAtomicQueryV3Validator.sol index 1f1905d5..6dac4c13 100644 --- a/contracts/validators/CredentialAtomicQueryV3Validator.sol +++ b/contracts/validators/CredentialAtomicQueryV3Validator.sol @@ -147,32 +147,14 @@ contract CredentialAtomicQueryV3Validator is CredentialAtomicQueryValidatorBase return _getResponseFields(pubSignals, hasSD); } - /** - * @dev Get the group ID of the request query data. - * @param params Request query data of the credential to verify. - * @return Group ID of the request query data. - */ - function getGroupID(bytes calldata params) external pure override returns (uint256) { + function getRequestParams(bytes calldata params) external pure override returns (IRequestValidator.RequestParams memory) { CredentialAtomicQueryV3 memory credAtomicQuery = abi.decode( params, (CredentialAtomicQueryV3) ); - return credAtomicQuery.groupID; + return IRequestValidator.RequestParams({ groupID: credAtomicQuery.groupID, verifierID: credAtomicQuery.verifierID }); } - - /** - * @dev Get the verifier ID from the request query data - * @param params Request query data of the credential to verify. - * @return Verifier ID - */ - function getVerifierId(bytes calldata params) external pure override returns (uint256) { - CredentialAtomicQueryV3 memory credAtomicQuery = abi.decode( - params, - (CredentialAtomicQueryV3) - ); - return credAtomicQuery.verifierID; - } - + /** * @dev Verify the groth16 proof and check the request query data * @param inputs Public inputs of the circuit. diff --git a/contracts/validators/EthIdentityValidator.sol b/contracts/validators/EthIdentityValidator.sol index 737c8ba0..a79a483d 100644 --- a/contracts/validators/EthIdentityValidator.sol +++ b/contracts/validators/EthIdentityValidator.sol @@ -100,17 +100,8 @@ contract EthIdentityValidator is Ownable2StepUpgradeable, IRequestValidator, ERC require(calcId == id, "Sender is not owner of the ethereum identity"); } - function getGroupID(bytes calldata) external pure override returns (uint256) { - // TODO: Implement group ID - return 0; - } - - /** - * @dev Get the verifier ID of the request query data. - * @param params Request query data of the credential to verify. - * @return Verifier ID encoded in the request query data. - */ - function getVerifierId(bytes calldata params) external view returns (uint256) { - return 0; + function getRequestParams(bytes calldata) external pure override returns (IRequestValidator.RequestParams memory) { + return IRequestValidator.RequestParams({ groupID: 0, verifierID: 0 }); } + } diff --git a/contracts/verifiers/UniversalVerifier.sol b/contracts/verifiers/UniversalVerifier.sol index 97a9abe3..65407786 100644 --- a/contracts/verifiers/UniversalVerifier.sol +++ b/contracts/verifiers/UniversalVerifier.sol @@ -152,15 +152,13 @@ contract UniversalVerifier is /** * @dev Sets a query - * @param queryId The ID of the query * @param query The query data */ function setQuery( - uint256 queryId, Query calldata query - ) public override checkQueryExistence(queryId, false) { - super.setQuery(queryId, query); - emit QuerySet(queryId, query.requestIds); + ) public override checkQueryExistence(query.queryId, false) { + super.setQuery(query); + emit QuerySet(query.queryId, query.requestIds); } /** diff --git a/contracts/verifiers/Verifier.sol b/contracts/verifiers/Verifier.sol index 3eb71f3d..58f3fdef 100644 --- a/contracts/verifiers/Verifier.sol +++ b/contracts/verifiers/Verifier.sol @@ -97,16 +97,16 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { * @dev Modifier to check if the request exists */ modifier checkRequestGroupExistence(Request memory request, bool existence) { - uint256 groupId = request.validator.getGroupID(request.params); + IRequestValidator.RequestParams memory requestParams = request.validator.getRequestParams(request.params); - if (groupId != 0) { + if (requestParams.groupID != 0) { if (existence) { - if (!groupIdExists(groupId)) { - revert GroupIdNotFound(groupId); + if (!groupIdExists(requestParams.groupID)) { + revert GroupIdNotFound(requestParams.groupID); } } else { - if (groupIdExists(groupId)) { - revert GroupIdAlreadyExists(groupId); + if (groupIdExists(requestParams.groupID)) { + revert GroupIdAlreadyExists(requestParams.groupID); } } } @@ -220,14 +220,14 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { Request calldata request ) internal checkRequestExistence(request.requestId, false) { VerifierStorage storage s = _getVerifierStorage(); - uint256 verifierId = request.validator.getVerifierId(request.params); + IRequestValidator.RequestParams memory requestParams = request.validator.getRequestParams(request.params); s._requests[request.requestId] = IVerifier.RequestData({ metadata: request.metadata, validator: request.validator, params: request.params, creator: _msgSender(), - verifierId: verifierId + verifierId: requestParams.verifierID }); s._requestIds.push(request.requestId); } @@ -285,19 +285,17 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { /** * @dev Sets a query - * @param queryId The ID of the query * @param query The query data */ function setQuery( - uint256 queryId, IVerifier.Query calldata query - ) public virtual checkQueryExistence(queryId, false) { + ) public virtual checkQueryExistence(query.queryId, false) { VerifierStorage storage s = _getVerifierStorage(); - s._queries[queryId] = query; - s._queryIds.push(queryId); + s._queries[query.queryId] = query; + s._queryIds.push(query.queryId); // checks for all the requests in this query - _checkRequestsInQuery(queryId); + _checkRequestsInQuery(query.queryId); } /** @@ -316,10 +314,11 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { // check that all the single requests doesn't have group for (uint256 i = 0; i < requestIds.length; i++) { + IRequestValidator.RequestParams memory requestParams = s._requests[requestIds[i]].validator.getRequestParams( + s._requests[requestIds[i]].params + ); if ( - s._requests[requestIds[i]].validator.getGroupID( - s._requests[requestIds[i]].params - ) != 0 + requestParams.groupID != 0 ) { revert RequestIsAlreadyGrouped(requestIds[i]); } @@ -420,14 +419,14 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { IVerifier.Request calldata request ) internal checkRequestExistence(request.requestId, true) { VerifierStorage storage s = _getVerifierStorage(); - uint256 verifierId = request.validator.getVerifierId(request.params); - + IRequestValidator.RequestParams memory requestParams = request.validator.getRequestParams(request.params); + s._requests[request.requestId] = IVerifier.RequestData({ metadata: request.metadata, validator: request.validator, params: request.params, creator: _msgSender(), - verifierId: verifierId + verifierId: requestParams.verifierID }); } diff --git a/test/verifier/universal-verifier-multi-query.test.ts b/test/verifier/universal-verifier-multi-query.test.ts index badbc0b6..6c315f7c 100644 --- a/test/verifier/universal-verifier-multi-query.test.ts +++ b/test/verifier/universal-verifier-multi-query.test.ts @@ -227,7 +227,7 @@ describe("Universal Verifier Multi-query", function () { groupIds: [], metadata: "0x", }; - const txSetQuery = await verifier.setQuery(queryId, query); + const txSetQuery = await verifier.setQuery(query); await txSetQuery.wait(); const queryStored = await verifier.getQuery(queryId); expect(queryStored.queryId).to.be.equal(queryId); @@ -359,7 +359,7 @@ describe("Universal Verifier Multi-query", function () { groupIds: [groupId], metadata: "0x", }; - const txSetQuery = await verifier.setQuery(queryId, query); + const txSetQuery = await verifier.setQuery(query); await txSetQuery.wait(); const queryStored = await verifier.getQuery(queryId); @@ -472,7 +472,7 @@ describe("Universal Verifier Multi-query", function () { groupIds: [groupId], metadata: "0x", }; - const txSetQuery = await verifier.setQuery(queryId, query); + const txSetQuery = await verifier.setQuery(query); await txSetQuery.wait(); const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); From 31aa30b985071250081e784aaf647287475f5d4c Mon Sep 17 00:00:00 2001 From: daveroga Date: Tue, 7 Jan 2025 08:45:01 +0100 Subject: [PATCH 45/69] override _setRequest to avoid loops and fix solhint --- contracts/interfaces/IRequestValidator.sol | 2 +- .../RequestValidatorAuthV2Stub.sol | 6 ++- .../test-helpers/RequestValidatorV2Stub.sol | 6 ++- .../test-helpers/RequestValidatorV3Stub.sol | 12 +++-- .../test-helpers/RequestValidatorV3_2Stub.sol | 10 +++- contracts/validators/AuthV2Validator.sol | 6 ++- .../CredentialAtomicQueryV2ValidatorBase.sol | 6 ++- .../CredentialAtomicQueryV3Validator.sol | 12 +++-- contracts/validators/EthIdentityValidator.sol | 7 +-- contracts/verifiers/EmbeddedVerifier.sol | 12 ----- contracts/verifiers/RequestOwnership.sol | 26 ++-------- contracts/verifiers/UniversalVerifier.sol | 47 +++++-------------- contracts/verifiers/ValidatorWhitelist.sol | 43 +++++------------ contracts/verifiers/Verifier.sol | 30 +++++++----- test/verifier/universal-verifier.test.ts | 3 +- 15 files changed, 96 insertions(+), 132 deletions(-) diff --git a/contracts/interfaces/IRequestValidator.sol b/contracts/interfaces/IRequestValidator.sol index a7292f71..6313843d 100644 --- a/contracts/interfaces/IRequestValidator.sol +++ b/contracts/interfaces/IRequestValidator.sol @@ -18,7 +18,7 @@ interface IRequestValidator { } /** - * @dev RequestParams. Information about request params from request query data. + * @dev RequestParams. Information about request params from request query data. * @param groupID Group ID of the request query params * @param verifierID Verifier ID of the request query params */ diff --git a/contracts/test-helpers/RequestValidatorAuthV2Stub.sol b/contracts/test-helpers/RequestValidatorAuthV2Stub.sol index 64ad9e69..036b5b9e 100644 --- a/contracts/test-helpers/RequestValidatorAuthV2Stub.sol +++ b/contracts/test-helpers/RequestValidatorAuthV2Stub.sol @@ -33,7 +33,9 @@ contract RequestValidatorAuthV2Stub is IRequestValidator, ERC165 { return signals; } - function getRequestParams(bytes calldata) external pure override returns (IRequestValidator.RequestParams memory) { - return IRequestValidator.RequestParams({ groupID: 0, verifierID: 0 }); + function getRequestParams( + bytes calldata + ) external pure override returns (IRequestValidator.RequestParams memory) { + return IRequestValidator.RequestParams({groupID: 0, verifierID: 0}); } } diff --git a/contracts/test-helpers/RequestValidatorV2Stub.sol b/contracts/test-helpers/RequestValidatorV2Stub.sol index 549f1077..bebb9a95 100644 --- a/contracts/test-helpers/RequestValidatorV2Stub.sol +++ b/contracts/test-helpers/RequestValidatorV2Stub.sol @@ -35,7 +35,9 @@ contract RequestValidatorV2Stub is IRequestValidator, ERC165 { return signals; } - function getRequestParams(bytes calldata) external pure override returns (IRequestValidator.RequestParams memory) { - return IRequestValidator.RequestParams({ groupID: 0, verifierID: 0 }); + function getRequestParams( + bytes calldata + ) external pure override returns (IRequestValidator.RequestParams memory) { + return IRequestValidator.RequestParams({groupID: 0, verifierID: 0}); } } diff --git a/contracts/test-helpers/RequestValidatorV3Stub.sol b/contracts/test-helpers/RequestValidatorV3Stub.sol index 71048ba7..b791723d 100644 --- a/contracts/test-helpers/RequestValidatorV3Stub.sol +++ b/contracts/test-helpers/RequestValidatorV3Stub.sol @@ -53,11 +53,17 @@ contract RequestValidatorV3Stub is IRequestValidator, ERC165 { return signals; } - function getRequestParams(bytes calldata params) external pure override returns (IRequestValidator.RequestParams memory) { + function getRequestParams( + bytes calldata params + ) external pure override returns (IRequestValidator.RequestParams memory) { CredentialAtomicQueryV3 memory credAtomicQuery = abi.decode( params, (CredentialAtomicQueryV3) ); - return IRequestValidator.RequestParams({ groupID: credAtomicQuery.groupID, verifierID: credAtomicQuery.verifierID }); - } + return + IRequestValidator.RequestParams({ + groupID: credAtomicQuery.groupID, + verifierID: credAtomicQuery.verifierID + }); + } } diff --git a/contracts/test-helpers/RequestValidatorV3_2Stub.sol b/contracts/test-helpers/RequestValidatorV3_2Stub.sol index 4140e913..65a9646e 100644 --- a/contracts/test-helpers/RequestValidatorV3_2Stub.sol +++ b/contracts/test-helpers/RequestValidatorV3_2Stub.sol @@ -53,11 +53,17 @@ contract RequestValidatorV3_2Stub is IRequestValidator, ERC165 { return signals; } - function getRequestParams(bytes calldata params) external pure override returns (IRequestValidator.RequestParams memory) { + function getRequestParams( + bytes calldata params + ) external pure override returns (IRequestValidator.RequestParams memory) { CredentialAtomicQueryV3 memory credAtomicQuery = abi.decode( params, (CredentialAtomicQueryV3) ); - return IRequestValidator.RequestParams({ groupID: credAtomicQuery.groupID, verifierID: credAtomicQuery.verifierID }); + return + IRequestValidator.RequestParams({ + groupID: credAtomicQuery.groupID, + verifierID: credAtomicQuery.verifierID + }); } } diff --git a/contracts/validators/AuthV2Validator.sol b/contracts/validators/AuthV2Validator.sol index 0fef4671..8a4963c7 100644 --- a/contracts/validators/AuthV2Validator.sol +++ b/contracts/validators/AuthV2Validator.sol @@ -65,8 +65,10 @@ contract AuthV2Validator is CredentialAtomicQueryValidatorBase { return pubSignals; } - function getRequestParams(bytes calldata) external pure override returns (IRequestValidator.RequestParams memory) { - return IRequestValidator.RequestParams({ groupID: 0, verifierID: 0 }); + function getRequestParams( + bytes calldata + ) external pure override returns (IRequestValidator.RequestParams memory) { + return IRequestValidator.RequestParams({groupID: 0, verifierID: 0}); } /** diff --git a/contracts/validators/CredentialAtomicQueryV2ValidatorBase.sol b/contracts/validators/CredentialAtomicQueryV2ValidatorBase.sol index 800f69dd..07ae71a3 100644 --- a/contracts/validators/CredentialAtomicQueryV2ValidatorBase.sol +++ b/contracts/validators/CredentialAtomicQueryV2ValidatorBase.sol @@ -82,8 +82,10 @@ abstract contract CredentialAtomicQueryV2ValidatorBase is CredentialAtomicQueryV return _getResponseFields(pubSignals); } - function getRequestParams(bytes calldata) external pure override returns (IRequestValidator.RequestParams memory) { - return IRequestValidator.RequestParams({ groupID: 0, verifierID: 0 }); + function getRequestParams( + bytes calldata + ) external pure override returns (IRequestValidator.RequestParams memory) { + return IRequestValidator.RequestParams({groupID: 0, verifierID: 0}); } /** diff --git a/contracts/validators/CredentialAtomicQueryV3Validator.sol b/contracts/validators/CredentialAtomicQueryV3Validator.sol index 6dac4c13..3792ff3f 100644 --- a/contracts/validators/CredentialAtomicQueryV3Validator.sol +++ b/contracts/validators/CredentialAtomicQueryV3Validator.sol @@ -147,14 +147,20 @@ contract CredentialAtomicQueryV3Validator is CredentialAtomicQueryValidatorBase return _getResponseFields(pubSignals, hasSD); } - function getRequestParams(bytes calldata params) external pure override returns (IRequestValidator.RequestParams memory) { + function getRequestParams( + bytes calldata params + ) external pure override returns (IRequestValidator.RequestParams memory) { CredentialAtomicQueryV3 memory credAtomicQuery = abi.decode( params, (CredentialAtomicQueryV3) ); - return IRequestValidator.RequestParams({ groupID: credAtomicQuery.groupID, verifierID: credAtomicQuery.verifierID }); + return + IRequestValidator.RequestParams({ + groupID: credAtomicQuery.groupID, + verifierID: credAtomicQuery.verifierID + }); } - + /** * @dev Verify the groth16 proof and check the request query data * @param inputs Public inputs of the circuit. diff --git a/contracts/validators/EthIdentityValidator.sol b/contracts/validators/EthIdentityValidator.sol index a79a483d..e45962ee 100644 --- a/contracts/validators/EthIdentityValidator.sol +++ b/contracts/validators/EthIdentityValidator.sol @@ -100,8 +100,9 @@ contract EthIdentityValidator is Ownable2StepUpgradeable, IRequestValidator, ERC require(calcId == id, "Sender is not owner of the ethereum identity"); } - function getRequestParams(bytes calldata) external pure override returns (IRequestValidator.RequestParams memory) { - return IRequestValidator.RequestParams({ groupID: 0, verifierID: 0 }); + function getRequestParams( + bytes calldata + ) external pure override returns (IRequestValidator.RequestParams memory) { + return IRequestValidator.RequestParams({groupID: 0, verifierID: 0}); } - } diff --git a/contracts/verifiers/EmbeddedVerifier.sol b/contracts/verifiers/EmbeddedVerifier.sol index 209d34dd..df1cefdf 100644 --- a/contracts/verifiers/EmbeddedVerifier.sol +++ b/contracts/verifiers/EmbeddedVerifier.sol @@ -25,18 +25,6 @@ abstract contract EmbeddedVerifier is Ownable2StepUpgradeable, Verifier { _setState(state); } - /** - * @dev Sets different requests - * @param singleRequests The requests that are not in any group - * @param groupedRequests The requests that are in a group - */ - function setRequests( - Request[] calldata singleRequests, - GroupedRequests[] calldata groupedRequests - ) public virtual override onlyOwner { - super.setRequests(singleRequests, groupedRequests); - } - /** * @dev Submits an array of responses and updates proofs status * @param authResponses The list of auth responses including auth type and proof diff --git a/contracts/verifiers/RequestOwnership.sol b/contracts/verifiers/RequestOwnership.sol index b2fb345d..6baac983 100644 --- a/contracts/verifiers/RequestOwnership.sol +++ b/contracts/verifiers/RequestOwnership.sol @@ -24,27 +24,6 @@ abstract contract RequestOwnership is Verifier { } } - /** - * @dev Sets different requests - * @param singleRequests The requests that are not in any group - * @param groupedRequests The requests that are in a group - */ - function setRequests( - IVerifier.Request[] calldata singleRequests, - IVerifier.GroupedRequests[] calldata groupedRequests - ) public virtual override { - super.setRequests(singleRequests, groupedRequests); - for (uint256 i = 0; i < singleRequests.length; i++) { - _setRequestOwner(singleRequests[i].requestId, _msgSender()); - } - - for (uint256 i = 0; i < groupedRequests.length; i++) { - for (uint256 j = 0; j < groupedRequests[i].requests.length; j++) { - _setRequestOwner(groupedRequests[i].requests[j].requestId, _msgSender()); - } - } - } - /** * @dev Get a request owner address * @param requestId The ID of a request @@ -56,6 +35,11 @@ abstract contract RequestOwnership is Verifier { return _getRequestOwnershipStorage()._requestOwners[requestId]; } + function _setRequest(Request calldata request) internal virtual override { + super._setRequest(request); + _setRequestOwner(request.requestId, _msgSender()); + } + function _setRequestOwner( uint256 requestId, address requestOwner diff --git a/contracts/verifiers/UniversalVerifier.sol b/contracts/verifiers/UniversalVerifier.sol index 65407786..340442ed 100644 --- a/contracts/verifiers/UniversalVerifier.sol +++ b/contracts/verifiers/UniversalVerifier.sol @@ -100,40 +100,6 @@ contract UniversalVerifier is emit AuthTypeSet(authType.authType, address(authType.validator), authType.params); } - /** - * @dev Sets different requests - * @param singleRequests The requests that are not in any group - * @param groupedRequests The requests that are in a group - */ - function setRequests( - IVerifier.Request[] calldata singleRequests, - IVerifier.GroupedRequests[] calldata groupedRequests - ) public override(RequestOwnership, ValidatorWhitelist, Verifier) { - super.setRequests(singleRequests, groupedRequests); - - for (uint256 i = 0; i < singleRequests.length; i++) { - emit RequestSet( - singleRequests[i].requestId, - _msgSender(), - singleRequests[i].metadata, - address(singleRequests[i].validator), - singleRequests[i].params - ); - } - - for (uint256 i = 0; i < groupedRequests.length; i++) { - for (uint256 j = 0; j < groupedRequests[i].requests.length; j++) { - emit RequestSet( - groupedRequests[i].requests[j].requestId, - _msgSender(), - groupedRequests[i].requests[j].metadata, - address(groupedRequests[i].requests[j].validator), - groupedRequests[i].requests[j].params - ); - } - } - } - /** * @dev Updates a request * @param request The request data @@ -253,4 +219,17 @@ contract UniversalVerifier is { return super._getRequestIfCanBeVerified(requestId); } + + function _setRequest( + Request calldata request + ) internal virtual override(RequestOwnership, ValidatorWhitelist, Verifier) { + super._setRequest(request); + emit RequestSet( + request.requestId, + _msgSender(), + request.metadata, + address(request.validator), + request.params + ); + } } diff --git a/contracts/verifiers/ValidatorWhitelist.sol b/contracts/verifiers/ValidatorWhitelist.sol index 72e48668..c6405827 100644 --- a/contracts/verifiers/ValidatorWhitelist.sol +++ b/contracts/verifiers/ValidatorWhitelist.sol @@ -7,6 +7,7 @@ import {Verifier} from "./Verifier.sol"; import {IVerifier} from "../interfaces/IVerifier.sol"; error ValidatorIsNotWhitelisted(address validator); +error ValidatorNotSupportInterface(address validator); contract ValidatorWhitelist is Verifier { /// @custom:storage-location erc7201:iden3.storage.ValidatorWhitelist @@ -36,33 +37,6 @@ contract ValidatorWhitelist is Verifier { } } - /** - * @dev Sets different requests - * @param singleRequests The requests that are not in any group - * @param groupedRequests The requests that are in a group - */ - function setRequests( - IVerifier.Request[] calldata singleRequests, - IVerifier.GroupedRequests[] calldata groupedRequests - ) public virtual override { - for (uint256 i = 0; i < singleRequests.length; i++) { - IRequestValidator validator = singleRequests[i].validator; - if (!isWhitelistedValidator(validator)) { - revert ValidatorIsNotWhitelisted(address(validator)); - } - } - - for (uint256 i = 0; i < groupedRequests.length; i++) { - for (uint256 j = 0; j < groupedRequests[i].requests.length; j++) { - IRequestValidator validator = groupedRequests[i].requests[j].validator; - if (!isWhitelistedValidator(validator)) { - revert ValidatorIsNotWhitelisted(address(validator)); - } - } - } - super.setRequests(singleRequests, groupedRequests); - } - /** * @dev Checks if validator is whitelisted * @param validator The validator address @@ -75,10 +49,9 @@ contract ValidatorWhitelist is Verifier { } function _addValidatorToWhitelist(IRequestValidator validator) internal { - require( - IERC165(address(validator)).supportsInterface(type(IRequestValidator).interfaceId), - "Validator doesn't support relevant interface" - ); + if (!IERC165(address(validator)).supportsInterface(type(IRequestValidator).interfaceId)) { + revert ValidatorNotSupportInterface(address(validator)); + } _getValidatorWhitelistStorage()._validatorWhitelist[validator] = true; } @@ -99,4 +72,12 @@ contract ValidatorWhitelist is Verifier { { return super._getRequestIfCanBeVerified(requestId); } + + function _setRequest(Request calldata request) internal virtual override { + IRequestValidator validator = request.validator; + if (!isWhitelistedValidator(validator)) { + revert ValidatorIsNotWhitelisted(address(validator)); + } + super._setRequest(request); + } } diff --git a/contracts/verifiers/Verifier.sol b/contracts/verifiers/Verifier.sol index 58f3fdef..f36f30d4 100644 --- a/contracts/verifiers/Verifier.sol +++ b/contracts/verifiers/Verifier.sol @@ -23,7 +23,6 @@ error AuthResponsesExactlyOneRequired(); error LinkIDNotTheSameForGroupedRequests(uint256 requestLinkID, uint256 requestLinkIDToCompare); error UserIDNotFound(uint256 userID); error UserIDNotLinkedToAddress(uint256 userID, address userAddress); -error ValidatorNotSupportInterface(address validator); abstract contract Verifier is IVerifier, ContextUpgradeable { /// @dev Key to retrieve the linkID from the proof storage @@ -97,7 +96,9 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { * @dev Modifier to check if the request exists */ modifier checkRequestGroupExistence(Request memory request, bool existence) { - IRequestValidator.RequestParams memory requestParams = request.validator.getRequestParams(request.params); + IRequestValidator.RequestParams memory requestParams = request.validator.getRequestParams( + request.params + ); if (requestParams.groupID != 0) { if (existence) { @@ -218,9 +219,11 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { function _setRequest( Request calldata request - ) internal checkRequestExistence(request.requestId, false) { + ) internal virtual checkRequestExistence(request.requestId, false) { VerifierStorage storage s = _getVerifierStorage(); - IRequestValidator.RequestParams memory requestParams = request.validator.getRequestParams(request.params); + IRequestValidator.RequestParams memory requestParams = request.validator.getRequestParams( + request.params + ); s._requests[request.requestId] = IVerifier.RequestData({ metadata: request.metadata, @@ -240,7 +243,7 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { function setRequests( Request[] calldata singleRequests, GroupedRequests[] calldata groupedRequests - ) public virtual { + ) public { VerifierStorage storage s = _getVerifierStorage(); for (uint256 i = 0; i < singleRequests.length; i++) { @@ -314,12 +317,11 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { // check that all the single requests doesn't have group for (uint256 i = 0; i < requestIds.length; i++) { - IRequestValidator.RequestParams memory requestParams = s._requests[requestIds[i]].validator.getRequestParams( - s._requests[requestIds[i]].params - ); - if ( - requestParams.groupID != 0 - ) { + IRequestValidator.RequestParams memory requestParams = s + ._requests[requestIds[i]] + .validator + .getRequestParams(s._requests[requestIds[i]].params); + if (requestParams.groupID != 0) { revert RequestIsAlreadyGrouped(requestIds[i]); } } @@ -419,8 +421,10 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { IVerifier.Request calldata request ) internal checkRequestExistence(request.requestId, true) { VerifierStorage storage s = _getVerifierStorage(); - IRequestValidator.RequestParams memory requestParams = request.validator.getRequestParams(request.params); - + IRequestValidator.RequestParams memory requestParams = request.validator.getRequestParams( + request.params + ); + s._requests[request.requestId] = IVerifier.RequestData({ metadata: request.metadata, validator: request.validator, diff --git a/test/verifier/universal-verifier.test.ts b/test/verifier/universal-verifier.test.ts index 8980d2d2..63fcdf63 100644 --- a/test/verifier/universal-verifier.test.ts +++ b/test/verifier/universal-verifier.test.ts @@ -408,6 +408,7 @@ describe("Universal Verifier MTP & SIG validators", function () { // can't whitelist validator, which does not support ICircuitValidator interface await expect(verifier.addValidatorToWhitelist(someAddress)).to.be.rejected; + // not a validator with proper interface and even not supporting IERC165 interface to check it await expect( verifier.setRequests( [ @@ -420,7 +421,7 @@ describe("Universal Verifier MTP & SIG validators", function () { ], [], ), - ).to.be.rejectedWith(`ValidatorIsNotWhitelisted("${await someAddress.getAddress()}")`); + ).to.be.rejectedWith(`function returned an unexpected amount of data`); await verifier.removeValidatorFromWhitelist(mtpValAddr); await verifier.setAuthType({ From a30dbfd21201df2c48dd1f9941666481ce95b525 Mon Sep 17 00:00:00 2001 From: daveroga Date: Tue, 7 Jan 2025 08:57:07 +0100 Subject: [PATCH 46/69] remove embedded zkp verifier --- contracts/verifiers/ZKPVerifier.sol | 10 - helpers/DeployHelper.ts | 22 -- .../maintenance/checkEmbeddedZKPVerifier.ts | 70 ------ test/verifier/embedded-zkp-verifier.test.ts | 231 ------------------ 4 files changed, 333 deletions(-) delete mode 100644 contracts/verifiers/ZKPVerifier.sol delete mode 100644 scripts/maintenance/checkEmbeddedZKPVerifier.ts delete mode 100644 test/verifier/embedded-zkp-verifier.test.ts diff --git a/contracts/verifiers/ZKPVerifier.sol b/contracts/verifiers/ZKPVerifier.sol deleted file mode 100644 index 97c1647c..00000000 --- a/contracts/verifiers/ZKPVerifier.sol +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.27; - -import {EmbeddedVerifier} from "./EmbeddedVerifier.sol"; - -/** - * @dev The ZKPVerifier is deprecated and will be removed in the future major versions - * Please use EmbeddedVerifier instead - */ -contract ZKPVerifier is EmbeddedVerifier {} diff --git a/helpers/DeployHelper.ts b/helpers/DeployHelper.ts index 12f0232c..d968b98c 100644 --- a/helpers/DeployHelper.ts +++ b/helpers/DeployHelper.ts @@ -899,28 +899,6 @@ export class DeployHelper { return primitiveTypeUtilsWrapper; } - async deployEmbeddedVerifierWrapper( - owner: SignerWithAddress | undefined, - stateAddr: string, - verifierLibAddr: string, - ): Promise { - const Verifier = await ethers.getContractFactory(contractsInfo.EMBEDDED_VERIFIER_WRAPPER.name, { - libraries: { - VerifierLib: verifierLibAddr, - }, - }); - // const zkpVerifier = await ZKPVerifier.deploy(await owner.getAddress()); - const verifier = await upgrades.deployProxy(Verifier, [await owner.getAddress(), stateAddr], { - unsafeAllow: ["external-library-linking"], - }); - await verifier.waitForDeployment(); - console.log( - `${contractsInfo.EMBEDDED_VERIFIER_WRAPPER.name} deployed to:`, - await verifier.getAddress(), - ); - return verifier; - } - async deployUniversalVerifier( owner: SignerWithAddress | undefined, stateAddr: string, diff --git a/scripts/maintenance/checkEmbeddedZKPVerifier.ts b/scripts/maintenance/checkEmbeddedZKPVerifier.ts deleted file mode 100644 index 599ec4e7..00000000 --- a/scripts/maintenance/checkEmbeddedZKPVerifier.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { getStateContractAddress, Logger } from "../../helpers/helperUtils"; -import { contractsInfo } from "../../helpers/constants"; -import hre, { ethers } from "hardhat"; -import { - setZKPRequest_KYCAgeCredential, - submitZKPResponses_KYCAgeCredential, -} from "../upgrade/verifiers/helpers/testVerifier"; -import { Contract } from "ethers"; - -// Replace these addresses with the ones you want to test -const embeddedZKPVerifierAddress = ""; -const validatorSigV2Address = ""; -const validatorMTPV2Address = ""; -const validatorV3Address = ""; - -async function testVerification(verifier: Contract) { - const requestId_V3 = 7254189; - await setZKPRequest_KYCAgeCredential(requestId_V3, verifier, validatorV3Address, "v3"); - await submitZKPResponses_KYCAgeCredential(requestId_V3, verifier, "v3", { - stateContractAddress: getStateContractAddress(), - verifierContractAddress: await verifier.getAddress(), - checkSubmitZKResponseV2: false, - }); - - const requestId_SigV2 = 7254190; - await setZKPRequest_KYCAgeCredential(requestId_SigV2, verifier, validatorSigV2Address, "sigV2"); - await submitZKPResponses_KYCAgeCredential(requestId_SigV2, verifier, "sigV2", { - stateContractAddress: getStateContractAddress(), - verifierContractAddress: await verifier.getAddress(), - checkSubmitZKResponseV2: false, - }); - - const requestId_MTPV2 = 7254191; - await setZKPRequest_KYCAgeCredential(requestId_MTPV2, verifier, validatorMTPV2Address, "mtpV2"); - await submitZKPResponses_KYCAgeCredential(requestId_MTPV2, verifier, "mtpV2", { - stateContractAddress: getStateContractAddress(), - verifierContractAddress: await verifier.getAddress(), - checkSubmitZKResponseV2: false, - }); -} - -async function main() { - console.log( - `\nChecking EmbeddedZKPVerifier verification on ${hre.network.name} with address ${embeddedZKPVerifierAddress}...`, - ); - - const embeddedZKPVerifier = await ethers.getContractAt( - contractsInfo.EMBEDDED_ZKP_VERIFIER_WRAPPER.name, - embeddedZKPVerifierAddress, - ); - - try { - await testVerification(embeddedZKPVerifier); - Logger.success( - `${hre.network.name} embedded ZKP Verifier onchain ${embeddedZKPVerifierAddress} verified`, - ); - } catch (error) { - console.error(error); - Logger.error( - `${hre.network.name} embedded ZKP Verifier onchain ${embeddedZKPVerifierAddress} not verified`, - ); - } -} - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); diff --git a/test/verifier/embedded-zkp-verifier.test.ts b/test/verifier/embedded-zkp-verifier.test.ts deleted file mode 100644 index d01c3cc8..00000000 --- a/test/verifier/embedded-zkp-verifier.test.ts +++ /dev/null @@ -1,231 +0,0 @@ -import { expect } from "chai"; -import { DeployHelper } from "../../helpers/DeployHelper"; -import { ethers } from "hardhat"; -import { packValidatorParams } from "../utils/validator-pack-utils"; -import { prepareInputs } from "../utils/state-utils"; -import { Block, Signer } from "ethers"; -import { buildCrossChainProofs, packCrossChainProofs, packZKProof } from "../utils/packData"; -import proofJson from "../validators/sig/data/valid_sig_user_genesis.json"; -import { CircuitId } from "@0xpolygonid/js-sdk"; -import { loadFixture } from "@nomicfoundation/hardhat-toolbox/network-helpers"; - -describe("Embedded ZKP Verifier", function () { - let verifier: any, sig: any, authV2Validator: any; - let owner: Signer; - const authType = "authV2"; - - const storageFields = [ - { - name: "userID", - value: 1n, - }, - { - name: "issuerID", - value: 2n, - }, - ]; - - const query = { - schema: BigInt("180410020913331409885634153623124536270"), - claimPathKey: BigInt( - "8566939875427719562376598811066985304309117528846759529734201066483458512800", - ), - operator: 1n, - slotIndex: 0n, - value: [1420070400000000000n, ...new Array(63).fill("0").map((x) => BigInt(x))], - queryHash: BigInt( - "1496222740463292783938163206931059379817846775593932664024082849882751356658", - ), - circuitIds: [CircuitId.AtomicQuerySigV2OnChain], - claimPathNotExists: 0, - }; - - async function deployContractsFixture() { - const deployHelper = await DeployHelper.initialize(null, true); - [owner] = await ethers.getSigners(); - - const { state } = await deployHelper.deployStateWithLibraries(["0x0112"]); - - const verifierLib = await deployHelper.deployVerifierLib(); - - verifier = await deployHelper.deployEmbeddedVerifierWrapper( - owner, - await state.getAddress(), - await verifierLib.getAddress(), - ); - - const stub = await deployHelper.deployValidatorStub("RequestValidatorV2Stub"); - sig = stub; - authV2Validator = await deployHelper.deployValidatorStub("AuthValidatorStub"); - } - - async function checkStorageFields(verifier: any, requestId: bigint, storageFields: any[]) { - for (const field of storageFields) { - const value = await verifier.getResponseFieldValueFromAddress( - requestId, - await owner.getAddress(), - field.name, - ); - expect(value).to.be.equal(field.value); - } - } - - beforeEach(async () => { - await loadFixture(deployContractsFixture); - }); - - it("test submit response", async () => { - const requestId = 0; - - const globalStateMessage = { - timestamp: BigInt(Math.floor(Date.now() / 1000)), - idType: "0x01A1", - root: 0n, - replacedAtTimestamp: 0n, - }; - - const identityStateMessage1 = { - timestamp: BigInt(Math.floor(Date.now() / 1000)), - id: 25530185136167283063987925153802803371825564143650291260157676786685420033n, - state: 4595702004868323299100310062178085028712435650290319955390778053863052230284n, - replacedAtTimestamp: 0n, - }; - - const identityStateUpdate2 = { - timestamp: BigInt(Math.floor(Date.now() / 1000)), - id: 25530185136167283063987925153802803371825564143650291260157676786685420033n, - state: 16775015541053109108201708100382933592407720757224325883910784163897594100403n, - replacedAtTimestamp: 1724858009n, - }; - - const params = packValidatorParams(query); - - await verifier.setAuthType({ - authType: authType, - validator: await authV2Validator.getAddress(), - params: params, - }); - - await expect( - verifier.setRequests( - [ - { - requestId: requestId, - metadata: "metadata", - validator: await sig.getAddress(), - params: params, - }, - ], - [], - ), - ); - - const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); - - const proof = packZKProof(inputs, pi_a, pi_b, pi_c); - const [signer] = await ethers.getSigners(); - - const crossChainProofs = packCrossChainProofs( - await buildCrossChainProofs( - [globalStateMessage, identityStateMessage1, identityStateUpdate2], - signer, - ), - ); - - const metadatas = "0x"; - - const tx = await verifier.submitResponse( - [ - { - authType: authType, - proof, - }, - ], - [ - { - requestId: requestId, - proof, - metadata: metadatas, - }, - ], - [], - crossChainProofs, - ); - - const txRes = await tx.wait(); - await checkStorageFields(verifier, BigInt(0), storageFields); - - const receipt = await ethers.provider.getTransactionReceipt(txRes.hash); - - // 2 events are emitted - expect(receipt?.logs.length).to.equal(2); - - const interfaceEventBeforeProofSubmit = new ethers.Interface([ - "event BeforeProofSubmit(tuple(string authType,bytes proof)[],tuple(uint256 requestId,bytes proof,bytes metadata)[],tuple(uint256 groupId,tuple(uint256 requestId,bytes proof,bytes metadata)[])[])", - ]); - const eventBeforeProofSubmit = interfaceEventBeforeProofSubmit.decodeEventLog( - "BeforeProofSubmit", - receipt?.logs[0].data || "", - receipt?.logs[0].topics, - ); - expect(eventBeforeProofSubmit[0][0][0]).to.equal("authV2"); - expect(eventBeforeProofSubmit[0][0][1]).to.deep.equal(proof); - expect(eventBeforeProofSubmit[1][0][0]).to.equal(0); - expect(eventBeforeProofSubmit[1][0][1]).to.deep.equal(proof); - expect(eventBeforeProofSubmit[1][0][2]).to.equal(metadatas); - - const interfaceEventAfterProofSubmit = new ethers.Interface([ - "event AfterProofSubmit(tuple(string authType,bytes proof)[],tuple(uint256 requestId,bytes proof,bytes metadata)[],tuple(uint256 groupId,tuple(uint256 requestId,bytes proof,bytes metadata)[])[])", - ]); - const eventAfterProofSubmit = interfaceEventAfterProofSubmit.decodeEventLog( - "AfterProofSubmit", - receipt?.logs[1].data || "", - receipt?.logs[1].topics, - ); - expect(eventAfterProofSubmit[0][0][0]).to.equal("authV2"); - expect(eventAfterProofSubmit[0][0][1]).to.deep.equal(proof); - expect(eventAfterProofSubmit[1][0][0]).to.equal(0); - expect(eventAfterProofSubmit[1][0][1]).to.deep.equal(proof); - expect(eventAfterProofSubmit[1][0][2]).to.equal(metadatas); - - const ownerAddress = await owner.getAddress(); - const requestID = 0; - const { timestamp: txResTimestamp } = (await ethers.provider.getBlock( - txRes.blockNumber, - )) as Block; - - const isProofVerified = await verifier.isProofVerified(ownerAddress, requestID); - expect(isProofVerified).to.be.equal(true); - const proofStatus = await verifier.getProofStatus(ownerAddress, requestID); - expect(proofStatus.isVerified).to.be.equal(true); - expect(proofStatus.validatorVersion).to.be.equal("1.0.0-mock"); - expect(proofStatus.blockTimestamp).to.be.equal(txResTimestamp); - }); - - it("test getRequest and request id exists", async () => { - const requestsCount = 3; - for (let i = 0; i < requestsCount; i++) { - await verifier.setRequests( - [ - { - requestId: i, - metadata: "metadataN" + i, - validator: await sig.getAddress(), - params: "0x00", - }, - ], - [], - ); - const requestIdExists = await verifier.requestIdExists(i); - expect(requestIdExists).to.be.true; - const requestIdDoesntExists = await verifier.requestIdExists(i + 1); - expect(requestIdDoesntExists).to.be.false; - - const request = await verifier.getRequest(i); - expect(request.metadata).to.be.equal("metadataN" + i); - await expect(verifier.getRequest(i + 1)).to.be.rejectedWith(`RequestIdNotFound(${i + 1})`); - } - const count = await verifier.getRequestsCount(); - expect(count).to.be.equal(requestsCount); - }); -}); From c6077ebb8726b82bda0a35f3b1b010e5bee4e031 Mon Sep 17 00:00:00 2001 From: daveroga Date: Wed, 8 Jan 2025 14:17:02 +0100 Subject: [PATCH 47/69] update Query to MultiRequest --- contracts/interfaces/IVerifier.sol | 38 +++--- contracts/verifiers/UniversalVerifier.sol | 22 +-- contracts/verifiers/Verifier.sol | 126 +++++++++--------- .../universal-verifier-multi-query.test.ts | 73 +++++----- 4 files changed, 129 insertions(+), 130 deletions(-) diff --git a/contracts/interfaces/IVerifier.sol b/contracts/interfaces/IVerifier.sol index 0b60623a..a0ec107f 100644 --- a/contracts/interfaces/IVerifier.sol +++ b/contracts/interfaces/IVerifier.sol @@ -143,14 +143,14 @@ interface IVerifier { } /** - * @dev Query. Structure for query. - * @param queryId Query id. - * @param requestIds Request ids for this multi query (without groupId. Single requests). - * @param groupIds Group ids for this multi query (all the requests included in the group. Grouped requests). - * @param metadata Metadata for the query. Empty in first version. + * @dev MultiRequest. Structure for multiRequest. + * @param multiRequestId MultiRequest id. + * @param requestIds Request ids for this multi multiRequest (without groupId. Single requests). + * @param groupIds Group ids for this multi multiRequest (all the requests included in the group. Grouped requests). + * @param metadata Metadata for the multiRequest. Empty in first version. */ - struct Query { - uint256 queryId; + struct MultiRequest { + uint256 multiRequestId; uint256[] requestIds; uint256[] groupIds; bytes metadata; @@ -202,13 +202,13 @@ interface IVerifier { function requestIdExists(uint256 requestId) external view returns (bool); /** - * @dev Gets the status of the query verification - * @param queryId The ID of the query + * @dev Gets the status of the multiRequest verification + * @param multiRequestId The ID of the MultiRequest * @param userAddress The address of the user - * @return status The status of the query. "True" if all requests are verified, "false" otherwise + * @return status The status of the MultiRequest. "True" if all requests are verified, "false" otherwise */ - function getQueryStatus( - uint256 queryId, + function getMultiRequestStatus( + uint256 multiRequestId, address userAddress ) external view returns (AuthProofStatus[] memory, RequestProofStatus[] memory); @@ -239,17 +239,17 @@ interface IVerifier { function setAuthType(AuthType calldata authType) external; /** - * @dev Sets a query - * @param query The query data + * @dev Sets a multiRequest + * @param multiRequest The multiRequest data */ - function setQuery(Query calldata query) external; + function setMultiRequest(MultiRequest calldata multiRequest) external; /** - * @dev Gets a specific multi query by ID - * @param queryId The ID of the multi query - * @return query The query data + * @dev Gets a specific multiRequest by ID + * @param multiRequestId The ID of the multiRequest + * @return multiRequest The multiRequest data */ - function getQuery(uint256 queryId) external view returns (IVerifier.Query memory query); + function getMultiRequest(uint256 multiRequestId) external view returns (MultiRequest memory multiRequest); /** * @dev Get the proof status for the sender and request with requestId. diff --git a/contracts/verifiers/UniversalVerifier.sol b/contracts/verifiers/UniversalVerifier.sol index 340442ed..7ffe0404 100644 --- a/contracts/verifiers/UniversalVerifier.sol +++ b/contracts/verifiers/UniversalVerifier.sol @@ -61,14 +61,14 @@ contract UniversalVerifier is ); /** - * @dev Event emitted upon adding a query + * @dev Event emitted upon adding a multiRequest */ - event QuerySet(uint256 indexed queryId, uint256[] requestIds); + event MultiRequestSet(uint256 indexed multiRequestId, uint256[] requestIds); /** - * @dev Event emitted upon updating a query + * @dev Event emitted upon updating a multiRequest */ - event QueryUpdate(uint256 indexed queryId, uint256[] requestIds); + event MultiRequestUpdate(uint256 indexed multiRequestId, uint256[] requestIds); /// @dev Modifier to check if the caller is the contract Owner or ZKP Request Owner modifier onlyOwnerOrRequestOwner(uint256 requestId) { @@ -117,14 +117,14 @@ contract UniversalVerifier is } /** - * @dev Sets a query - * @param query The query data + * @dev Sets a multiRequest + * @param multiRequest The multiRequest data */ - function setQuery( - Query calldata query - ) public override checkQueryExistence(query.queryId, false) { - super.setQuery(query); - emit QuerySet(query.queryId, query.requestIds); + function setMultiRequest( + IVerifier.MultiRequest calldata multiRequest + ) public override checkMultiRequestExistence(multiRequest.multiRequestId, false) { + super.setMultiRequest(multiRequest); + emit MultiRequestSet(multiRequest.multiRequestId, multiRequest.requestIds); } /** diff --git a/contracts/verifiers/Verifier.sol b/contracts/verifiers/Verifier.sol index f36f30d4..9947597a 100644 --- a/contracts/verifiers/Verifier.sol +++ b/contracts/verifiers/Verifier.sol @@ -13,8 +13,8 @@ error RequestIdNotFound(uint256 requestId); error RequestAlreadyExists(uint256 requestId); error GroupIdNotFound(uint256 groupId); error GroupIdAlreadyExists(uint256 groupId); -error QueryIdNotFound(uint256 queryId); -error QueryIdAlreadyExists(uint256 queryId); +error MultiRequestIdNotFound(uint256 multiRequestId); +error MultiRequestIdAlreadyExists(uint256 multiRequestId); error AuthTypeNotFound(string authType); error AuthTypeAlreadyExists(string authType); error ValidatorNotWhitelisted(address validator); @@ -54,9 +54,9 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { mapping(uint256 groupId => uint256[] requestIds) _groupedRequests; uint256[] _groupIds; IState _state; - // Information about queries - mapping(uint256 queryId => Query) _queries; - uint256[] _queryIds; + // Information about multiRequests + mapping(uint256 multiRequestId => IVerifier.MultiRequest) _multiRequests; + uint256[] _multiRequestIds; // Information linked between users and their addresses mapping(address userAddress => uint256 userID) _user_address_to_id; mapping(uint256 userID => address userAddress) _id_to_user_address; @@ -115,16 +115,16 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { } /** - * @dev Modifier to check if the query exists + * @dev Modifier to check if the multiRequest exists */ - modifier checkQueryExistence(uint256 queryId, bool existence) { + modifier checkMultiRequestExistence(uint256 multiRequestId, bool existence) { if (existence) { - if (!queryIdExists(queryId)) { - revert QueryIdNotFound(queryId); + if (!multiRequestIdExists(multiRequestId)) { + revert MultiRequestIdNotFound(multiRequestId); } } else { - if (queryIdExists(queryId)) { - revert QueryIdAlreadyExists(queryId); + if (multiRequestIdExists(multiRequestId)) { + revert MultiRequestIdAlreadyExists(multiRequestId); } } _; @@ -186,12 +186,12 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { } /** - * @dev Checks if a query ID exists - * @param queryId The ID of the query - * @return Whether the query ID exists + * @dev Checks if a multiRequest ID exists + * @param multiRequestId The ID of the multiRequest + * @return Whether the multiRequest ID exists */ - function queryIdExists(uint256 queryId) public view returns (bool) { - return _getVerifierStorage()._queries[queryId].queryId == queryId; + function multiRequestIdExists(uint256 multiRequestId) public view returns (bool) { + return _getVerifierStorage()._multiRequests[multiRequestId].multiRequestId == multiRequestId; } /** @@ -287,33 +287,33 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { } /** - * @dev Sets a query - * @param query The query data + * @dev Sets a multiRequest + * @param multiRequest The multiRequest data */ - function setQuery( - IVerifier.Query calldata query - ) public virtual checkQueryExistence(query.queryId, false) { + function setMultiRequest( + IVerifier.MultiRequest calldata multiRequest + ) public virtual checkMultiRequestExistence(multiRequest.multiRequestId, false) { VerifierStorage storage s = _getVerifierStorage(); - s._queries[query.queryId] = query; - s._queryIds.push(query.queryId); + s._multiRequests[multiRequest.multiRequestId] = multiRequest; + s._multiRequestIds.push(multiRequest.multiRequestId); - // checks for all the requests in this query - _checkRequestsInQuery(query.queryId); + // checks for all the requests in this multiRequest + _checkRequestsInMultiRequest(multiRequest.multiRequestId); } /** - * @dev Gets a specific multi query by ID - * @param queryId The ID of the multi query - * @return query The query data + * @dev Gets a specific multiRequest by ID + * @param multiRequestId The ID of the multiRequest + * @return multiRequest The multiRequest data */ - function getQuery(uint256 queryId) public view returns (IVerifier.Query memory query) { - return _getVerifierStorage()._queries[queryId]; + function getMultiRequest(uint256 multiRequestId) public view returns (IVerifier.MultiRequest memory multiRequest) { + return _getVerifierStorage()._multiRequests[multiRequestId]; } - function _checkRequestsInQuery(uint256 queryId) internal view { + function _checkRequestsInMultiRequest(uint256 multiRequestId) internal view { VerifierStorage storage s = _getVerifierStorage(); - uint256[] memory requestIds = s._queries[queryId].requestIds; + uint256[] memory requestIds = s._multiRequests[multiRequestId].requestIds; // check that all the single requests doesn't have group for (uint256 i = 0; i < requestIds.length; i++) { @@ -558,11 +558,11 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { return s._authProofs[authType][userID][0].storageFields[responseFieldName]; } - function _checkLinkedResponseFields(uint256 queryId, uint256 userID) internal view { + function _checkLinkedResponseFields(uint256 multiRequestId, uint256 userID) internal view { VerifierStorage storage s = _getVerifierStorage(); - for (uint256 i = 0; i < s._queries[queryId].groupIds.length; i++) { - uint256 groupId = s._queries[queryId].groupIds[i]; + for (uint256 i = 0; i < s._multiRequests[multiRequestId].groupIds.length; i++) { + uint256 groupId = s._multiRequests[multiRequestId].groupIds[i]; // Check linkID in the same group or requests is the same uint256 requestLinkID = getResponseFieldValue( @@ -587,18 +587,18 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { } /** - * @dev Gets the status of the query verification - * @param queryId The ID of the query + * @dev Gets the status of the multiRequest verification + * @param multiRequestId The ID of the multiRequest * @param userAddress The address of the user - * @return status The status of the query. "True" if all requests are verified, "false" otherwise + * @return status The status of the multiRequest. "True" if all requests are verified, "false" otherwise */ - function getQueryStatus( - uint256 queryId, + function getMultiRequestStatus( + uint256 multiRequestId, address userAddress ) public view - checkQueryExistence(queryId, true) + checkMultiRequestExistence(multiRequestId, true) returns (IVerifier.AuthProofStatus[] memory, IVerifier.RequestProofStatus[] memory) { VerifierStorage storage s = _getVerifierStorage(); @@ -613,29 +613,29 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { ( IVerifier.AuthProofStatus[] memory authProofStatus, IVerifier.RequestProofStatus[] memory requestProofStatus - ) = _getQueryStatus(queryId, userID); + ) = _getMultiRequestStatus(multiRequestId, userID); // 3. Check if all linked response fields are the same - _checkLinkedResponseFields(queryId, userID); + _checkLinkedResponseFields(multiRequestId, userID); return (authProofStatus, requestProofStatus); } /** - * @dev Gets the status of the query verification - * @param queryId The ID of the query + * @dev Gets the status of the multiRequest verification + * @param multiRequestId The ID of the multiRequest * @param userAddress The address of the user * @param userID The user id of the user - * @return status The status of the query. "True" if all requests are verified, "false" otherwise + * @return status The status of the multiRequest. "True" if all requests are verified, "false" otherwise */ - function getQueryStatus( - uint256 queryId, + function getMultiRequestStatus( + uint256 multiRequestId, address userAddress, uint256 userID ) public view - checkQueryExistence(queryId, true) + checkMultiRequestExistence(multiRequestId, true) returns (IVerifier.AuthProofStatus[] memory, IVerifier.RequestProofStatus[] memory) { VerifierStorage storage s = _getVerifierStorage(); @@ -658,16 +658,16 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { ( IVerifier.AuthProofStatus[] memory authProofStatus, IVerifier.RequestProofStatus[] memory requestProofStatus - ) = _getQueryStatus(queryId, userIDSelected); + ) = _getMultiRequestStatus(multiRequestId, userIDSelected); // 3. Check if all linked response fields are the same - _checkLinkedResponseFields(queryId, userIDSelected); + _checkLinkedResponseFields(multiRequestId, userIDSelected); return (authProofStatus, requestProofStatus); } - function _getQueryStatus( - uint256 queryId, + function _getMultiRequestStatus( + uint256 multiRequestId, uint256 userID ) internal @@ -675,13 +675,13 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { returns (IVerifier.AuthProofStatus[] memory, IVerifier.RequestProofStatus[] memory) { VerifierStorage storage s = _getVerifierStorage(); - Query storage query = s._queries[queryId]; + IVerifier.MultiRequest storage multiRequest = s._multiRequests[multiRequestId]; uint256 lengthGroupIds; - if (query.groupIds.length > 0) { - for (uint256 i = 0; i < query.groupIds.length; i++) { - uint256 groupId = query.groupIds[i]; + if (multiRequest.groupIds.length > 0) { + for (uint256 i = 0; i < multiRequest.groupIds.length; i++) { + uint256 groupId = multiRequest.groupIds[i]; lengthGroupIds += s._groupedRequests[groupId].length; } } @@ -691,7 +691,7 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { ); IVerifier.RequestProofStatus[] memory requestProofStatus = new IVerifier.RequestProofStatus[]( - query.requestIds.length + lengthGroupIds + multiRequest.requestIds.length + lengthGroupIds ); for (uint256 i = 0; i < s._authTypes.length; i++) { @@ -704,8 +704,8 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { }); } - for (uint256 i = 0; i < query.requestIds.length; i++) { - uint256 requestId = query.requestIds[i]; + for (uint256 i = 0; i < multiRequest.requestIds.length; i++) { + uint256 requestId = multiRequest.requestIds[i]; requestProofStatus[i] = IVerifier.RequestProofStatus({ requestId: requestId, @@ -715,13 +715,13 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { }); } - for (uint256 i = 0; i < query.groupIds.length; i++) { - uint256 groupId = query.groupIds[i]; + for (uint256 i = 0; i < multiRequest.groupIds.length; i++) { + uint256 groupId = multiRequest.groupIds[i]; for (uint256 j = 0; j < s._groupedRequests[groupId].length; j++) { uint256 requestId = s._groupedRequests[groupId][j]; - requestProofStatus[query.requestIds.length + j] = IVerifier.RequestProofStatus({ + requestProofStatus[multiRequest.requestIds.length + j] = IVerifier.RequestProofStatus({ requestId: requestId, isVerified: s._proofs[requestId][userID][0].isVerified, validatorVersion: s._proofs[requestId][userID][0].validatorVersion, diff --git a/test/verifier/universal-verifier-multi-query.test.ts b/test/verifier/universal-verifier-multi-query.test.ts index 6c315f7c..0c26d3f8 100644 --- a/test/verifier/universal-verifier-multi-query.test.ts +++ b/test/verifier/universal-verifier-multi-query.test.ts @@ -9,9 +9,8 @@ import { buildCrossChainProofs, packCrossChainProofs, packZKProof } from "../uti import { CircuitId } from "@0xpolygonid/js-sdk"; import { loadFixture } from "@nomicfoundation/hardhat-toolbox/network-helpers"; import { calculateQueryHashV3 } from "../utils/query-hash-utils"; -import { request } from "http"; -describe("Universal Verifier Multi-query", function () { +describe("Universal Verifier Multi-request", function () { let verifier: any, v3Validator: any, authV2Validator: any, v3_2Validator: any; let signer; let signerAddress: string; @@ -180,10 +179,10 @@ describe("Universal Verifier Multi-query", function () { await loadFixture(deployContractsFixture); }); - it("Test submit response multiquery without groupID", async () => { + it("Test submit response multi-request without groupID", async () => { const requestId = 1; - const queryId = 1; - const nonExistingQueryId = 5; + const multiRequestId = 1; + const nonExistingMultiRequestId = 5; const userId = 1; const userId2 = 2; const authType = "authV2"; @@ -221,17 +220,17 @@ describe("Universal Verifier Multi-query", function () { expect(authRequestStored.params).to.be.equal(params); expect(authRequestStored.isActive).to.be.equal(true); - const query = { - queryId, + const multiRequest = { + multiRequestId, requestIds: [requestId], groupIds: [], metadata: "0x", }; - const txSetQuery = await verifier.setQuery(query); - await txSetQuery.wait(); - const queryStored = await verifier.getQuery(queryId); - expect(queryStored.queryId).to.be.equal(queryId); - expect(queryStored.requestIds).to.be.deep.equal(query.requestIds); + const txSetMultiRequest = await verifier.setMultiRequest(multiRequest); + await txSetMultiRequest.wait(); + const multiRequestStored = await verifier.getMultiRequest(multiRequestId); + expect(multiRequestStored.multiRequestId).to.be.equal(multiRequestId); + expect(multiRequestStored.requestIds).to.be.deep.equal(multiRequest.requestIds); const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); @@ -282,11 +281,11 @@ describe("Universal Verifier Multi-query", function () { expect(events[0].args.requestId).to.be.equal(requestId); expect(events[0].args.caller).to.be.equal(signerAddress); - await expect(verifier.getQueryStatus(nonExistingQueryId, signerAddress)).to.be.rejectedWith( - `QueryIdNotFound(${nonExistingQueryId})`, - ); + await expect( + verifier.getMultiRequestStatus(nonExistingMultiRequestId, signerAddress), + ).to.be.rejectedWith(`MultiRequestIdNotFound(${nonExistingMultiRequestId})`); - const status = await verifier.getQueryStatus(queryId, signerAddress); + const status = await verifier.getMultiRequestStatus(multiRequestId, signerAddress); expect(status[0][0].authType).to.be.equal(authType); expect(status[0][0].isVerified).to.be.equal(true); // auth type isVerified expect(status[1][0].requestId).to.be.equal(requestId); @@ -298,13 +297,13 @@ describe("Universal Verifier Multi-query", function () { expect(requestStored.isVerifierAuthenticated).to.be.equal(true); }); - it("Test submit response multiquery with same groupID and linkID", async () => { + it("Test submit response multi-request with same groupID and linkID", async () => { const requestId2 = 2; const requestId3 = 3; const groupId = 1; const authType = "authV2"; - const queryId = 1; - const nonExistingQueryId = 5; + const multiRequestId = 1; + const nonExistingMultiRequestId = 5; const userId = 1; const authParams = "0x"; const paramsRequest2 = packV3ValidatorParams(requestQuery2); @@ -353,18 +352,18 @@ describe("Universal Verifier Multi-query", function () { expect(authRequestStored.params).to.be.equal(authParams); expect(authRequestStored.isActive).to.be.equal(true); - const query = { - queryId, + const multiRequest = { + multiRequestId, requestIds: [], groupIds: [groupId], metadata: "0x", }; - const txSetQuery = await verifier.setQuery(query); - await txSetQuery.wait(); + const txSetMultiRequest = await verifier.setMultiRequest(multiRequest); + await txSetMultiRequest.wait(); - const queryStored = await verifier.getQuery(queryId); - expect(queryStored[0]).to.be.equal(queryId); - expect(queryStored[1]).to.be.deep.equal(query.requestIds); + const multiRequestStored = await verifier.getMultiRequest(multiRequestId); + expect(multiRequestStored[0]).to.be.equal(multiRequestId); + expect(multiRequestStored[1]).to.be.deep.equal(multiRequest.requestIds); const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); @@ -414,10 +413,10 @@ describe("Universal Verifier Multi-query", function () { expect(events[0].args.requestId).to.be.equal(requestId2); expect(events[0].args.caller).to.be.equal(signerAddress); - await expect(verifier.getQueryStatus(nonExistingQueryId, signerAddress)).to.be.rejectedWith( - `QueryIdNotFound(${nonExistingQueryId})`, - ); - const status = await verifier.getQueryStatus(queryId, signerAddress); + await expect( + verifier.getMultiRequestStatus(nonExistingMultiRequestId, signerAddress), + ).to.be.rejectedWith(`MultiRequestIdNotFound(${nonExistingMultiRequestId})`); + const status = await verifier.getMultiRequestStatus(multiRequestId, signerAddress); expect(status[0][0].authType).to.be.equal(authType); expect(status[0][0].isVerified).to.be.equal(true); // auth type isVerified expect(status[1][0].requestId).to.be.equal(requestId2); @@ -426,12 +425,12 @@ describe("Universal Verifier Multi-query", function () { expect(status[1][1].isVerified).to.be.equal(true); // requestId3 isVerified }); - it("Test submit response multiquery with same groupID and different linkID", async () => { + it("Test submit response multi-request with same groupID and different linkID", async () => { const requestId2 = 2; const requestId3 = 3; const groupId = 1; const authType = "authV2"; - const queryId = 1; + const multiRequestId = 1; const authParams = "0x"; const paramsRequest2 = packV3ValidatorParams(requestQuery2); const paramsRequest3 = packV3ValidatorParams(requestQuery3); @@ -466,14 +465,14 @@ describe("Universal Verifier Multi-query", function () { params: authParams, }); - const query = { - queryId, + const multiRequest = { + multiRequestId, requestIds: [], groupIds: [groupId], metadata: "0x", }; - const txSetQuery = await verifier.setQuery(query); - await txSetQuery.wait(); + const txSetMultiRequest = await verifier.setMultiRequest(multiRequest); + await txSetMultiRequest.wait(); const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); @@ -508,7 +507,7 @@ describe("Universal Verifier Multi-query", function () { ); await tx.wait(); - await expect(verifier.getQueryStatus(queryId, signerAddress)).to.be.rejectedWith( + await expect(verifier.getMultiRequestStatus(multiRequestId, signerAddress)).to.be.rejectedWith( "LinkIDNotTheSameForGroupedRequests(3, 4)", ); }); From d31f2ca20ce8c9c52edbb93467c1b636cf46f52d Mon Sep 17 00:00:00 2001 From: daveroga Date: Wed, 8 Jan 2025 14:38:10 +0100 Subject: [PATCH 48/69] update submitResponse --- contracts/interfaces/IVerifier.sol | 10 +- .../test-helpers/EmbeddedVerifierWrapper.sol | 24 ++-- contracts/verifiers/EmbeddedVerifier.sol | 36 +++--- contracts/verifiers/UniversalVerifier.sol | 26 ++--- contracts/verifiers/Verifier.sol | 63 ++-------- .../universal-verifier-multi-query.test.ts | 51 +++----- .../universal-verifier-submit-V2.test.ts | 110 +++++++----------- test/verifier/universal-verifier.test.ts | 44 +++---- test/verifier/universal-verifier.v3.test.ts | 88 +++++--------- 9 files changed, 152 insertions(+), 300 deletions(-) diff --git a/contracts/interfaces/IVerifier.sol b/contracts/interfaces/IVerifier.sol index a0ec107f..8717365a 100644 --- a/contracts/interfaces/IVerifier.sol +++ b/contracts/interfaces/IVerifier.sol @@ -158,16 +158,14 @@ interface IVerifier { /** * @dev Submits an array of responses and updates proofs status - * @param authResponses The list of auth responses including auth type and proof - * @param singleResponses The list of responses including request ID, proof and metadata for single requests - * @param groupedResponses The list of responses including request ID, proof and metadata for grouped requests + * @param authResponse Auth response including auth type and proof + * @param responses The list of responses including request ID, proof and metadata for requests * @param crossChainProofs The list of cross chain proofs from universal resolver (oracle). This * includes identities and global states. */ function submitResponse( - AuthResponse[] memory authResponses, - Response[] memory singleResponses, - GroupedResponses[] memory groupedResponses, + AuthResponse memory authResponse, + Response[] memory responses, bytes memory crossChainProofs ) external; diff --git a/contracts/test-helpers/EmbeddedVerifierWrapper.sol b/contracts/test-helpers/EmbeddedVerifierWrapper.sol index 01652ad9..64413e50 100644 --- a/contracts/test-helpers/EmbeddedVerifierWrapper.sol +++ b/contracts/test-helpers/EmbeddedVerifierWrapper.sol @@ -8,14 +8,12 @@ import {IVerifier} from "../interfaces/IVerifier.sol"; contract EmbeddedVerifierWrapper is EmbeddedVerifier { event BeforeProofSubmit( - AuthResponse[] authResponses, - Response[] singleResponses, - GroupedResponses[] groupedResponses + AuthResponse authResponse, + Response[] responses ); event AfterProofSubmit( - AuthResponse[] authResponses, - Response[] singleResponses, - GroupedResponses[] groupedResponses + AuthResponse authResponse, + Response[] responses ); function initialize(address initialOwner, IState state) public initializer { @@ -23,18 +21,16 @@ contract EmbeddedVerifierWrapper is EmbeddedVerifier { } function _beforeProofSubmit( - AuthResponse[] memory authResponses, - Response[] memory singleResponses, - GroupedResponses[] memory groupedResponses + AuthResponse memory authResponse, + Response[] memory responses ) internal override { - emit BeforeProofSubmit(authResponses, singleResponses, groupedResponses); + emit BeforeProofSubmit(authResponse, responses); } function _afterProofSubmit( - AuthResponse[] memory authResponses, - Response[] memory singleResponses, - GroupedResponses[] memory groupedResponses + AuthResponse memory authResponse, + Response[] memory responses ) internal override { - emit AfterProofSubmit(authResponses, singleResponses, groupedResponses); + emit AfterProofSubmit(authResponse, responses); } } diff --git a/contracts/verifiers/EmbeddedVerifier.sol b/contracts/verifiers/EmbeddedVerifier.sol index df1cefdf..0f6c245b 100644 --- a/contracts/verifiers/EmbeddedVerifier.sol +++ b/contracts/verifiers/EmbeddedVerifier.sol @@ -27,44 +27,38 @@ abstract contract EmbeddedVerifier is Ownable2StepUpgradeable, Verifier { /** * @dev Submits an array of responses and updates proofs status - * @param authResponses The list of auth responses including auth type and proof - * @param singleResponses The list of responses including request ID, proof and metadata for single requests - * @param groupedResponses The list of responses including request ID, proof and metadata for grouped requests + * @param authResponse Auth response including auth type and proof + * @param responses The list of responses including request ID, proof and metadata for requests * @param crossChainProofs The list of cross chain proofs from universal resolver (oracle). This * includes identities and global states. */ function submitResponse( - AuthResponse[] memory authResponses, - Response[] memory singleResponses, - GroupedResponses[] memory groupedResponses, + AuthResponse memory authResponse, + Response[] memory responses, bytes memory crossChainProofs ) public virtual override { - _beforeProofSubmit(authResponses, singleResponses, groupedResponses); - super.submitResponse(authResponses, singleResponses, groupedResponses, crossChainProofs); - _afterProofSubmit(authResponses, singleResponses, groupedResponses); + _beforeProofSubmit(authResponse, responses); + super.submitResponse(authResponse, responses, crossChainProofs); + _afterProofSubmit(authResponse, responses); } /** * @dev Hook that is called before any proof response submit - * @param authResponses The list of auth responses including auth type and proof - * @param singleResponses The list of responses including request ID, proof and metadata for single requests - * @param groupedResponses The list of responses including request ID, proof and metadata for grouped requests + * @param authResponse Auth response including auth type and proof + * @param responses The list of responses including request ID, proof and metadata for requests */ function _beforeProofSubmit( - AuthResponse[] memory authResponses, - Response[] memory singleResponses, - GroupedResponses[] memory groupedResponses + AuthResponse memory authResponse, + Response[] memory responses ) internal virtual {} /** * @dev Hook that is called after any proof response submit - * @param authResponses The list of auth responses including auth type and proof - * @param singleResponses The list of responses including request ID, proof and metadata for single requests - * @param groupedResponses The list of responses including request ID, proof and metadata for grouped requests + * @param authResponse The list of auth responses including auth type and proof + * @param responses The list of responses including request ID, proof and metadata for requests */ function _afterProofSubmit( - AuthResponse[] memory authResponses, - Response[] memory singleResponses, - GroupedResponses[] memory groupedResponses + AuthResponse memory authResponse, + Response[] memory responses ) internal virtual {} } diff --git a/contracts/verifiers/UniversalVerifier.sol b/contracts/verifiers/UniversalVerifier.sol index 7ffe0404..c7a31605 100644 --- a/contracts/verifiers/UniversalVerifier.sol +++ b/contracts/verifiers/UniversalVerifier.sol @@ -129,31 +129,21 @@ contract UniversalVerifier is /** * @dev Submits an array of responses and updates proofs status - * @param authResponses The list of auth responses including auth type and proof - * @param singleResponses The list of responses including request ID, proof and metadata for single requests - * @param groupedResponses The list of responses including request ID, proof and metadata for grouped requests + * @param authResponse Auth responses including auth type and proof + * @param responses The list of responses including request ID, proof and metadata for requests * @param crossChainProofs The list of cross chain proofs from universal resolver (oracle). This * includes identities and global states. */ function submitResponse( - AuthResponse[] memory authResponses, - Response[] memory singleResponses, - GroupedResponses[] memory groupedResponses, + AuthResponse memory authResponse, + Response[] memory responses, bytes memory crossChainProofs ) public override { - super.submitResponse(authResponses, singleResponses, groupedResponses, crossChainProofs); - for (uint256 i = 0; i < authResponses.length; i++) { - emit AuthResponseSubmitted(authResponses[i].authType, _msgSender()); - } - - for (uint256 i = 0; i < singleResponses.length; i++) { - emit ResponseSubmitted(singleResponses[i].requestId, _msgSender()); - } + super.submitResponse(authResponse, responses, crossChainProofs); + emit AuthResponseSubmitted(authResponse.authType, _msgSender()); - for (uint256 i = 0; i < groupedResponses.length; i++) { - for (uint256 j = 0; j < groupedResponses[i].responses.length; j++) { - emit ResponseSubmitted(groupedResponses[i].responses[j].requestId, _msgSender()); - } + for (uint256 i = 0; i < responses.length; i++) { + emit ResponseSubmitted(responses[i].requestId, _msgSender()); } } diff --git a/contracts/verifiers/Verifier.sol b/contracts/verifiers/Verifier.sol index 9947597a..2b1a9888 100644 --- a/contracts/verifiers/Verifier.sol +++ b/contracts/verifiers/Verifier.sol @@ -19,7 +19,6 @@ error AuthTypeNotFound(string authType); error AuthTypeAlreadyExists(string authType); error ValidatorNotWhitelisted(address validator); error RequestIsAlreadyGrouped(uint256 requestId); -error AuthResponsesExactlyOneRequired(); error LinkIDNotTheSameForGroupedRequests(uint256 requestLinkID, uint256 requestLinkIDToCompare); error UserIDNotFound(uint256 userID); error UserIDNotLinkedToAddress(uint256 userID, address userAddress); @@ -329,16 +328,14 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { /** * @dev Submits an array of responses and updates proofs status - * @param authResponses The list of auth responses including auth type and proof - * @param singleResponses The list of responses including request ID, proof and metadata for single requests - * @param groupedResponses The list of responses including request ID, proof and metadata for grouped requests + * @param authResponse Auth response including auth type and proof + * @param responses The list of responses including request ID, proof and metadata for requests * @param crossChainProofs The list of cross chain proofs from universal resolver (oracle). This * includes identities and global states. */ function submitResponse( - AuthResponse[] memory authResponses, - Response[] memory singleResponses, - GroupedResponses[] memory groupedResponses, + AuthResponse memory authResponse, + Response[] memory responses, bytes memory crossChainProofs ) public virtual { VerifierStorage storage $ = _getVerifierStorage(); @@ -349,17 +346,11 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { // TODO: Get userID from responses that has userID informed (LinkedMultiquery doesn't have userID) - // 2. Process auth response first - if (authResponses.length != 1) { - // TODO: Check if it's already authenticated or it's an ethereum identity - revert AuthResponsesExactlyOneRequired(); - } - uint256 userIDFromReponse; - AuthTypeData storage authTypeData = $._authMethods[authResponses[0].authType]; + AuthTypeData storage authTypeData = $._authMethods[authResponse.authType]; // Authenticate user IAuthValidator.ResponseField[] memory authSignals = authTypeData.validator.verify( - authResponses[0].proof, + authResponse.proof, authTypeData.params, sender, $._state @@ -374,24 +365,24 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { // For some reason the auth request doesn't return the userID in the response if (userIDFromReponse != 0) { - $.writeAuthProofResults(authResponses[0].authType, userIDFromReponse, authSignals); + $.writeAuthProofResults(authResponse.authType, userIDFromReponse, authSignals); // Link userID and user address $._user_address_to_id[sender] = userIDFromReponse; $._id_to_user_address[userIDFromReponse] = sender; $._user_auth_timestamp[userIDFromReponse][sender] = block.timestamp; } - // 3. Get userID from latest auth response processed in this submitResponse or before + // 2. Get userID from latest auth response processed in this submitResponse or before uint256 userID = $._user_address_to_id[sender]; if (userID == 0) { revert("The user is not authenticated"); } - // 4. Verify all the single responses, write proof results (under the userID key from the auth of the user), + // 3. Verify all the responses, write proof results (under the userID key from the auth of the user), // emit events (existing logic) - for (uint256 i = 0; i < singleResponses.length; i++) { - IVerifier.Response memory response = singleResponses[i]; + for (uint256 i = 0; i < responses.length; i++) { + IVerifier.Response memory response = responses[i]; IVerifier.RequestData storage request = _getRequestIfCanBeVerified(response.requestId); IRequestValidator.ResponseField[] memory signals = request.validator.verify( @@ -407,10 +398,6 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { revert("Metadata not supported yet"); } } - - // 5. Verify all the grouped responses, write proof results (under the userID key from the auth of the user), - // emit events (existing logic) - _verifyGroupedResponses(groupedResponses, userID, sender); } /** @@ -434,34 +421,6 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { }); } - function _verifyGroupedResponses( - IVerifier.GroupedResponses[] memory groupedResponses, - uint256 userID, - address sender - ) internal { - VerifierStorage storage $ = _getVerifierStorage(); - - for (uint256 i = 0; i < groupedResponses.length; i++) { - for (uint256 j = 0; j < groupedResponses[i].responses.length; j++) { - IVerifier.Response memory response = groupedResponses[i].responses[j]; - IVerifier.RequestData storage request = $._requests[response.requestId]; - - IRequestValidator.ResponseField[] memory signals = request.validator.verify( - response.proof, - request.params, - sender, - $._state - ); - - $.writeProofResults(response.requestId, userID, signals); - - if (response.metadata.length > 0) { - revert("Metadata not supported yet"); - } - } - } - } - /** * @dev Sets an auth type * @param authType The auth type to add diff --git a/test/verifier/universal-verifier-multi-query.test.ts b/test/verifier/universal-verifier-multi-query.test.ts index 0c26d3f8..02e39c0d 100644 --- a/test/verifier/universal-verifier-multi-query.test.ts +++ b/test/verifier/universal-verifier-multi-query.test.ts @@ -246,12 +246,10 @@ describe("Universal Verifier Multi-request", function () { const metadatas = "0x"; const tx = await verifier.submitResponse( - [ - { - authType: authType, - proof, - }, - ], + { + authType: authType, + proof, + }, [ { requestId, @@ -259,7 +257,6 @@ describe("Universal Verifier Multi-request", function () { metadata: metadatas, }, ], - [], crossChainProofs, ); @@ -379,21 +376,13 @@ describe("Universal Verifier Multi-request", function () { const metadatas = "0x"; const tx = await verifier.submitResponse( + { + authType: authType, + proof, + }, [ - { - authType: authType, - proof, - }, - ], - [], - [ - { - groupId: groupId, - responses: [ - { requestId: requestId2, proof, metadata: metadatas }, - { requestId: requestId3, proof, metadata: metadatas }, - ], - }, + { requestId: requestId2, proof, metadata: metadatas }, + { requestId: requestId3, proof, metadata: metadatas }, ], crossChainProofs, ); @@ -487,21 +476,13 @@ describe("Universal Verifier Multi-request", function () { const metadatas = "0x"; const tx = await verifier.submitResponse( + { + authType: authType, + proof, + }, [ - { - authType: authType, - proof, - }, - ], - [], - [ - { - groupId: groupId, - responses: [ - { requestId: requestId2, proof, metadata: metadatas }, - { requestId: requestId3, proof, metadata: metadatas }, - ], - }, + { requestId: requestId2, proof, metadata: metadatas }, + { requestId: requestId3, proof, metadata: metadatas }, ], crossChainProofs, ); diff --git a/test/verifier/universal-verifier-submit-V2.test.ts b/test/verifier/universal-verifier-submit-V2.test.ts index 23ddf575..8699c293 100644 --- a/test/verifier/universal-verifier-submit-V2.test.ts +++ b/test/verifier/universal-verifier-submit-V2.test.ts @@ -170,14 +170,11 @@ describe("Universal Verifier submitResponse SigV2 validators", function () { const crossChainProofs = "0x"; const tx = await verifier.submitResponse( - [ - { - authType: authType, - proof: singleProof[0].proof, - }, - ], + { + authType: authType, + proof: singleProof[0].proof, + }, singleProof, - [], crossChainProofs, ); @@ -205,14 +202,11 @@ describe("Universal Verifier submitResponse SigV2 validators", function () { const requestIdsMulti = requestIds.slice(1, 3); const txMulti = await verifier.submitResponse( - [ - { - authType: authType, - proof: singleProof[0].proof, - }, - ], + { + authType: authType, + proof: singleProof[0].proof, + }, multiProof, - [], crossChainProofs, ); @@ -240,14 +234,11 @@ describe("Universal Verifier submitResponse SigV2 validators", function () { await verifier.disableRequest(0); await expect( verifier.submitResponse( - [ - { - authType: authType, - proof: singleProof[0].proof, - }, - ], + { + authType: authType, + proof: singleProof[0].proof, + }, singleProof, - [], crossChainProofs, ), ).to.be.rejectedWith(`RequestIsDisabled(${singleProof[0].requestId})`); @@ -255,14 +246,11 @@ describe("Universal Verifier submitResponse SigV2 validators", function () { await verifier.disableRequest(1); await expect( verifier.submitResponse( - [ - { - authType: authType, - proof: singleProof[0].proof, - }, - ], + { + authType: authType, + proof: singleProof[0].proof, + }, multiProof, - [], crossChainProofs, ), ).to.be.rejectedWith(`RequestIsDisabled(${multiProof[0].requestId})`); @@ -270,14 +258,11 @@ describe("Universal Verifier submitResponse SigV2 validators", function () { await verifier.enableRequest(0); await expect( verifier.submitResponse( - [ - { - authType: authType, - proof: singleProof[0].proof, - }, - ], + { + authType: authType, + proof: singleProof[0].proof, + }, singleProof, - [], crossChainProofs, ), ).not.to.be.rejected; @@ -285,14 +270,11 @@ describe("Universal Verifier submitResponse SigV2 validators", function () { await verifier.enableRequest(1); await expect( verifier.submitResponse( - [ - { - authType: authType, - proof: singleProof[0].proof, - }, - ], + { + authType: authType, + proof: singleProof[0].proof, + }, multiProof, - [], crossChainProofs, ), ).not.to.be.rejected; @@ -302,27 +284,21 @@ describe("Universal Verifier submitResponse SigV2 validators", function () { await verifier.removeValidatorFromWhitelist(await sig.getAddress()); await expect( verifier.submitResponse( - [ - { - authType: authType, - proof: singleProof[0].proof, - }, - ], + { + authType: authType, + proof: singleProof[0].proof, + }, singleProof, - [], crossChainProofs, ), ).to.be.rejectedWith(`ValidatorIsNotWhitelisted("${await sig.getAddress()}")`); await expect( verifier.submitResponse( - [ - { - authType: authType, - proof: singleProof[0].proof, - }, - ], + { + authType: authType, + proof: singleProof[0].proof, + }, multiProof, - [], crossChainProofs, ), ).to.be.rejectedWith(`ValidatorIsNotWhitelisted("${await sig.getAddress()}")`); @@ -330,27 +306,21 @@ describe("Universal Verifier submitResponse SigV2 validators", function () { await verifier.addValidatorToWhitelist(await sig.getAddress()); await expect( verifier.submitResponse( - [ - { - authType: authType, - proof: singleProof[0].proof, - }, - ], + { + authType: authType, + proof: singleProof[0].proof, + }, singleProof, - [], crossChainProofs, ), ).not.to.be.rejected; await expect( verifier.submitResponse( - [ - { - authType: authType, - proof: singleProof[0].proof, - }, - ], + { + authType: authType, + proof: singleProof[0].proof, + }, multiProof, - [], crossChainProofs, ), ).not.to.be.rejected; diff --git a/test/verifier/universal-verifier.test.ts b/test/verifier/universal-verifier.test.ts index 63fcdf63..002df4a9 100644 --- a/test/verifier/universal-verifier.test.ts +++ b/test/verifier/universal-verifier.test.ts @@ -169,12 +169,10 @@ describe("Universal Verifier MTP & SIG validators", function () { const metadatas = "0x"; const tx = await verifier.submitResponse( - [ - { - authType: authType, - proof, - }, - ], + { + authType: authType, + proof, + }, [ { requestId: 0, @@ -182,7 +180,6 @@ describe("Universal Verifier MTP & SIG validators", function () { metadata: metadatas, }, ], - [], crossChainProofs, ); @@ -310,12 +307,10 @@ describe("Universal Verifier MTP & SIG validators", function () { const metadatas = "0x"; await verifier.submitResponse( - [ - { - authType: authType, - proof, - }, - ], + { + authType: authType, + proof, + }, [ { requestId: requestId, @@ -323,19 +318,16 @@ describe("Universal Verifier MTP & SIG validators", function () { metadata: metadatas, }, ], - [], crossChainProofs, ); await verifier.connect(requestOwner).disableRequest(requestId); await expect( verifier.submitResponse( - [ - { - authType: authType, - proof, - }, - ], + { + authType: authType, + proof, + }, [ { requestId: requestId, @@ -343,7 +335,6 @@ describe("Universal Verifier MTP & SIG validators", function () { metadata: metadatas, }, ], - [], crossChainProofs, ), ).to.be.rejectedWith(`RequestIsDisabled(${requestId})`); @@ -444,12 +435,10 @@ describe("Universal Verifier MTP & SIG validators", function () { const metadatas = "0x"; await expect( verifier.submitResponse( - [ - { - authType: authType, - proof, - }, - ], + { + authType: authType, + proof, + }, [ { requestId: requestId, @@ -457,7 +446,6 @@ describe("Universal Verifier MTP & SIG validators", function () { metadata: metadatas, }, ], - [], crossChainProofs, ), ).to.be.rejectedWith(`ValidatorIsNotWhitelisted("${mtpValAddr}")`); diff --git a/test/verifier/universal-verifier.v3.test.ts b/test/verifier/universal-verifier.v3.test.ts index c3f47cfe..75e5d531 100644 --- a/test/verifier/universal-verifier.v3.test.ts +++ b/test/verifier/universal-verifier.v3.test.ts @@ -184,12 +184,10 @@ describe("Universal Verifier V3 validator", function () { await expect( verifier.submitResponse( - [ - { - authType: authType, - proof, - }, - ], + { + authType: authType, + proof, + }, [ { requestId, @@ -197,7 +195,6 @@ describe("Universal Verifier V3 validator", function () { metadata: metadatas, }, ], - [], crossChainProofs, ), ).not.to.be.rejected; @@ -216,12 +213,10 @@ describe("Universal Verifier V3 validator", function () { await expect( verifier.connect(signer2).submitResponse( - [ - { - authType: authType, - proof, - }, - ], + { + authType: authType, + proof, + }, [ { requestId, @@ -229,7 +224,6 @@ describe("Universal Verifier V3 validator", function () { metadata: metadatas, }, ], - [], crossChainProofs, ), ).to.be.rejectedWith("UserID does not correspond to the sender"); @@ -258,12 +252,10 @@ describe("Universal Verifier V3 validator", function () { await expect( verifier.connect(signer).submitResponse( - [ - { - authType: authType, - proof, - }, - ], + { + authType: authType, + proof, + }, [ { requestId, @@ -271,7 +263,6 @@ describe("Universal Verifier V3 validator", function () { metadata: metadatas, }, ], - [], crossChainProofs, ), ).to.be.rejectedWith("Issuer is not on the Allowed Issuers list"); @@ -304,12 +295,10 @@ describe("Universal Verifier V3 validator", function () { await expect( verifier.connect(signer).submitResponse( - [ - { - authType: authType, - proof, - }, - ], + { + authType: authType, + proof, + }, [ { requestId, @@ -317,7 +306,6 @@ describe("Universal Verifier V3 validator", function () { metadata: metadatas, }, ], - [], crossChainProofs, ), ).to.be.rejectedWith("Invalid Link ID pub signal"); @@ -350,12 +338,10 @@ describe("Universal Verifier V3 validator", function () { await expect( verifier.connect(signer).submitResponse( - [ - { - authType: authType, - proof, - }, - ], + { + authType: authType, + proof, + }, [ { requestId, @@ -363,7 +349,6 @@ describe("Universal Verifier V3 validator", function () { metadata: metadatas, }, ], - [], crossChainProofs, ), ).to.be.rejectedWith("Proof type should match the requested one in query"); @@ -396,12 +381,10 @@ describe("Universal Verifier V3 validator", function () { await expect( verifier.connect(signer).submitResponse( - [ - { - authType: authType, - proof, - }, - ], + { + authType: authType, + proof, + }, [ { requestId, @@ -409,7 +392,6 @@ describe("Universal Verifier V3 validator", function () { metadata: metadatas, }, ], - [], crossChainProofs, ), ).to.be.rejectedWith("Invalid nullify pub signal"); @@ -442,12 +424,10 @@ describe("Universal Verifier V3 validator", function () { await expect( verifier.connect(signer).submitResponse( - [ - { - authType: authType, - proof, - }, - ], + { + authType: authType, + proof, + }, [ { requestId, @@ -455,7 +435,6 @@ describe("Universal Verifier V3 validator", function () { metadata: metadatas, }, ], - [], crossChainProofs, ), ).to.be.rejectedWith("Query hash does not match the requested one"); @@ -502,12 +481,10 @@ describe("Universal Verifier V3 validator", function () { await expect( verifier.connect(signer).submitResponse( - [ - { - authType: authType, - proof, - }, - ], + { + authType: authType, + proof, + }, [ { requestId, @@ -515,7 +492,6 @@ describe("Universal Verifier V3 validator", function () { metadata: metadatas, }, ], - [], crossChainProofs, ), ).to.be.rejectedWith("Generated proof is outdated"); From 7ad4411c3685b2ed9e9420ef1ad87d6cda01dcc3 Mon Sep 17 00:00:00 2001 From: daveroga Date: Wed, 8 Jan 2025 14:39:13 +0100 Subject: [PATCH 49/69] fix solhint --- contracts/interfaces/IVerifier.sol | 4 +++- .../test-helpers/EmbeddedVerifierWrapper.sol | 10 ++-------- contracts/verifiers/Verifier.sol | 20 +++++++++++-------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/contracts/interfaces/IVerifier.sol b/contracts/interfaces/IVerifier.sol index 8717365a..26d02994 100644 --- a/contracts/interfaces/IVerifier.sol +++ b/contracts/interfaces/IVerifier.sol @@ -247,7 +247,9 @@ interface IVerifier { * @param multiRequestId The ID of the multiRequest * @return multiRequest The multiRequest data */ - function getMultiRequest(uint256 multiRequestId) external view returns (MultiRequest memory multiRequest); + function getMultiRequest( + uint256 multiRequestId + ) external view returns (MultiRequest memory multiRequest); /** * @dev Get the proof status for the sender and request with requestId. diff --git a/contracts/test-helpers/EmbeddedVerifierWrapper.sol b/contracts/test-helpers/EmbeddedVerifierWrapper.sol index 64413e50..c20fb81c 100644 --- a/contracts/test-helpers/EmbeddedVerifierWrapper.sol +++ b/contracts/test-helpers/EmbeddedVerifierWrapper.sol @@ -7,14 +7,8 @@ import {IRequestValidator} from "../interfaces/IRequestValidator.sol"; import {IVerifier} from "../interfaces/IVerifier.sol"; contract EmbeddedVerifierWrapper is EmbeddedVerifier { - event BeforeProofSubmit( - AuthResponse authResponse, - Response[] responses - ); - event AfterProofSubmit( - AuthResponse authResponse, - Response[] responses - ); + event BeforeProofSubmit(AuthResponse authResponse, Response[] responses); + event AfterProofSubmit(AuthResponse authResponse, Response[] responses); function initialize(address initialOwner, IState state) public initializer { super.__EmbeddedVerifier_init(initialOwner, state); diff --git a/contracts/verifiers/Verifier.sol b/contracts/verifiers/Verifier.sol index 2b1a9888..d88667cb 100644 --- a/contracts/verifiers/Verifier.sol +++ b/contracts/verifiers/Verifier.sol @@ -190,7 +190,8 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { * @return Whether the multiRequest ID exists */ function multiRequestIdExists(uint256 multiRequestId) public view returns (bool) { - return _getVerifierStorage()._multiRequests[multiRequestId].multiRequestId == multiRequestId; + return + _getVerifierStorage()._multiRequests[multiRequestId].multiRequestId == multiRequestId; } /** @@ -305,7 +306,9 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { * @param multiRequestId The ID of the multiRequest * @return multiRequest The multiRequest data */ - function getMultiRequest(uint256 multiRequestId) public view returns (IVerifier.MultiRequest memory multiRequest) { + function getMultiRequest( + uint256 multiRequestId + ) public view returns (IVerifier.MultiRequest memory multiRequest) { return _getVerifierStorage()._multiRequests[multiRequestId]; } @@ -680,12 +683,13 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { for (uint256 j = 0; j < s._groupedRequests[groupId].length; j++) { uint256 requestId = s._groupedRequests[groupId][j]; - requestProofStatus[multiRequest.requestIds.length + j] = IVerifier.RequestProofStatus({ - requestId: requestId, - isVerified: s._proofs[requestId][userID][0].isVerified, - validatorVersion: s._proofs[requestId][userID][0].validatorVersion, - timestamp: s._proofs[requestId][userID][0].blockTimestamp - }); + requestProofStatus[multiRequest.requestIds.length + j] = IVerifier + .RequestProofStatus({ + requestId: requestId, + isVerified: s._proofs[requestId][userID][0].isVerified, + validatorVersion: s._proofs[requestId][userID][0].validatorVersion, + timestamp: s._proofs[requestId][userID][0].blockTimestamp + }); } } From 7005a64444cdd533b7143c7f223fcb4cda3514e5 Mon Sep 17 00:00:00 2001 From: daveroga Date: Wed, 8 Jan 2025 18:09:36 +0100 Subject: [PATCH 50/69] address centered model instead of userID and some fixes --- contracts/interfaces/IVerifier.sol | 30 +-- contracts/lib/VerifierLib.sol | 31 +-- contracts/verifiers/Verifier.sol | 237 +++++------------- .../universal-verifier-multi-query.test.ts | 138 ++++------ .../universal-verifier-submit-V2.test.ts | 21 +- .../universal-verifier.events.test.ts | 38 ++- test/verifier/universal-verifier.test.ts | 154 +++++------- test/verifier/universal-verifier.v3.test.ts | 143 +++++------ 8 files changed, 266 insertions(+), 526 deletions(-) diff --git a/contracts/interfaces/IVerifier.sol b/contracts/interfaces/IVerifier.sol index 26d02994..7ffcad54 100644 --- a/contracts/interfaces/IVerifier.sol +++ b/contracts/interfaces/IVerifier.sol @@ -45,7 +45,6 @@ interface IVerifier { * @param params Parameters data of the request. * @param creator Creator of the request. * @param verifierId Verifier id. - * @param isVerifierAuthenticated True if the verifier is authenticated. */ struct RequestInfo { uint256 requestId; @@ -54,10 +53,9 @@ interface IVerifier { bytes params; address creator; uint256 verifierId; - bool isVerifierAuthenticated; } /** - * @dev AuthProofStatus. Structure for auth proof status. + * @dev GroupedRequests. Structure for auth proof status. * @param groupId Group id of the requests. * @param requests Requests of the group. */ @@ -128,20 +126,6 @@ interface IVerifier { bytes params; } - /** - * @dev AuthProofStatus. Structure for auth proof status. - * @param authType Auth type of the auth proof. - * @param isVerified True if the proof is verified. - * @param validatorVersion Version of the validator. - * @param timestamp Timestamp of the proof. - */ - struct AuthProofStatus { - string authType; - bool isVerified; - string validatorVersion; - uint256 timestamp; - } - /** * @dev MultiRequest. Structure for multiRequest. * @param multiRequestId MultiRequest id. @@ -171,12 +155,10 @@ interface IVerifier { /** * @dev Sets different requests - * @param singleRequests The requests that are not in any group - * @param groupedRequests The requests that are in a group + * @param requests List of requests */ function setRequests( - Request[] calldata singleRequests, - GroupedRequests[] calldata groupedRequests + Request[] calldata requests ) external; /** @@ -208,17 +190,17 @@ interface IVerifier { function getMultiRequestStatus( uint256 multiRequestId, address userAddress - ) external view returns (AuthProofStatus[] memory, RequestProofStatus[] memory); + ) external view returns (RequestProofStatus[] memory); /** * @dev Gets proof storage response field value * @param requestId Id of the request - * @param userID Id of the user + * @param sender Address of the user * @param responseFieldName Name of the proof storage response field to get */ function getResponseFieldValue( uint256 requestId, - uint256 userID, + address sender, string memory responseFieldName ) external view returns (uint256); diff --git a/contracts/lib/VerifierLib.sol b/contracts/lib/VerifierLib.sol index 1b291077..63815d7e 100644 --- a/contracts/lib/VerifierLib.sol +++ b/contracts/lib/VerifierLib.sol @@ -41,16 +41,16 @@ library VerifierLib { /** * @dev Writes proof results. * @param requestId The request ID of the proof - * @param userID The userID of the proof + * @param sender The address of the sender of the proof * @param responseFields The array of response fields of the proof */ function writeProofResults( Verifier.VerifierStorage storage self, uint256 requestId, - uint256 userID, + address sender, IRequestValidator.ResponseField[] memory responseFields ) public { - Proof[] storage proofs = self._proofs[requestId][userID]; + Proof[] storage proofs = self._proofs[requestId][sender]; // We only keep only 1 proof now without history. Prepared for the future if needed. if (proofs.length == 0) { proofs.push(); @@ -63,29 +63,4 @@ library VerifierLib { proofs[0].validatorVersion = self._requests[requestId].validator.version(); proofs[0].blockTimestamp = block.timestamp; } - - /** - * @dev Writes proof results. - * @param authType The auth type of the proof - * @param userID The userID of the proof - * @param responseFields The array of response fields of the proof - */ - function writeAuthProofResults( - Verifier.VerifierStorage storage self, - string memory authType, - uint256 userID, - IAuthValidator.ResponseField[] memory responseFields - ) public { - AuthProof[] storage proofs = self._authProofs[authType][userID]; - if (proofs.length == 0) { - proofs.push(); - } - for (uint256 i = 0; i < responseFields.length; i++) { - proofs[0].storageFields[responseFields[i].name] = responseFields[i].value; - } - - proofs[0].isVerified = true; - proofs[0].validatorVersion = self._authMethods[authType].validator.version(); - proofs[0].blockTimestamp = block.timestamp; - } } diff --git a/contracts/verifiers/Verifier.sol b/contracts/verifiers/Verifier.sol index d88667cb..9cd21bcd 100644 --- a/contracts/verifiers/Verifier.sol +++ b/contracts/verifiers/Verifier.sol @@ -47,7 +47,7 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { struct VerifierStorage { // Information about requests // solhint-disable-next-line - mapping(uint256 requestId => mapping(uint256 userID => VerifierLib.Proof[])) _proofs; + mapping(uint256 requestId => mapping(address sender => VerifierLib.Proof[])) _proofs; mapping(uint256 requestId => IVerifier.RequestData) _requests; uint256[] _requestIds; mapping(uint256 groupId => uint256[] requestIds) _groupedRequests; @@ -56,16 +56,11 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { // Information about multiRequests mapping(uint256 multiRequestId => IVerifier.MultiRequest) _multiRequests; uint256[] _multiRequestIds; - // Information linked between users and their addresses - mapping(address userAddress => uint256 userID) _user_address_to_id; - mapping(uint256 userID => address userAddress) _id_to_user_address; - mapping(uint256 userID => mapping(address userAddress => uint256 timestamp)) _user_auth_timestamp; // Whitelisted validators mapping(IRequestValidator => bool isApproved) _validatorWhitelist; // Information about auth types and validators string[] _authTypes; mapping(string authType => AuthTypeData) _authMethods; - mapping(string authType => mapping(uint256 userID => VerifierLib.AuthProof[])) _authProofs; } // solhint-disable-next-line @@ -179,9 +174,7 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { * @return Whether the group ID exists */ function groupIdExists(uint256 groupId) public view returns (bool) { - return - _getVerifierStorage()._groupIds.length > 0 && - _getVerifierStorage()._groupIds[groupId] != 0; + return _getVerifierStorage()._groupedRequests[groupId].length != 0; } /** @@ -237,29 +230,34 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { /** * @dev Sets different requests - * @param singleRequests The requests that are not in any group - * @param groupedRequests The requests that are in a group + * @param requests The list of requests */ - function setRequests( - Request[] calldata singleRequests, - GroupedRequests[] calldata groupedRequests - ) public { + function setRequests(Request[] calldata requests) public { VerifierStorage storage s = _getVerifierStorage(); - for (uint256 i = 0; i < singleRequests.length; i++) { - _setRequestWithChecks(singleRequests[i]); - } - for (uint256 i = 0; i < groupedRequests.length; i++) { - if (groupIdExists(groupedRequests[i].groupId)) { + // 1. Check first that groupIds don't exist + for (uint256 i = 0; i < requests.length; i++) { + uint256 groupID = requests[i].validator.getRequestParams(requests[i].params).groupID; + if (groupID != 0 && groupIdExists(groupID)) { revert("Group ID already exists"); } - s._groupIds.push(groupedRequests[i].groupId); + } - for (uint256 j = 0; j < groupedRequests[i].requests.length; j++) { - _setRequest(groupedRequests[i].requests[j]); - s._groupedRequests[groupedRequests[i].groupId].push( - groupedRequests[i].requests[j].requestId - ); + // 2. Set requests checking groups + for (uint256 i = 0; i < requests.length; i++) { + uint256 groupID = requests[i].validator.getRequestParams(requests[i].params).groupID; + + // request without group + if (groupID == 0) { + _setRequestWithChecks(requests[i]); + } else { + // request with group + if (!groupIdExists(groupID)) { + s._groupIds.push(groupID); + } + + _setRequest(requests[i]); + s._groupedRequests[groupID].push(requests[i].requestId); } } } @@ -275,14 +273,13 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { VerifierStorage storage $ = _getVerifierStorage(); IVerifier.RequestData storage rd = $._requests[requestId]; return - RequestInfo({ + IVerifier.RequestInfo({ requestId: requestId, metadata: rd.metadata, validator: rd.validator, params: rd.params, creator: rd.creator, - verifierId: rd.verifierId, - isVerifierAuthenticated: $._user_auth_timestamp[rd.verifierId][rd.creator] != 0 + verifierId: rd.verifierId }); } @@ -366,19 +363,7 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { } } - // For some reason the auth request doesn't return the userID in the response - if (userIDFromReponse != 0) { - $.writeAuthProofResults(authResponse.authType, userIDFromReponse, authSignals); - // Link userID and user address - $._user_address_to_id[sender] = userIDFromReponse; - $._id_to_user_address[userIDFromReponse] = sender; - $._user_auth_timestamp[userIDFromReponse][sender] = block.timestamp; - } - - // 2. Get userID from latest auth response processed in this submitResponse or before - uint256 userID = $._user_address_to_id[sender]; - - if (userID == 0) { + if (userIDFromReponse == 0) { revert("The user is not authenticated"); } @@ -395,7 +380,7 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { $._state ); - $.writeProofResults(response.requestId, userID, signals); + $.writeProofResults(response.requestId, sender, signals); if (response.metadata.length > 0) { revert("Metadata not supported yet"); @@ -476,51 +461,19 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { /** * @dev Gets response field value * @param requestId Id of the request - * @param userID Id of the user + * @param sender Address of the user * @param responseFieldName Name of the response field to get */ function getResponseFieldValue( - uint256 requestId, - uint256 userID, - string memory responseFieldName - ) public view checkRequestExistence(requestId, true) returns (uint256) { - VerifierStorage storage s = _getVerifierStorage(); - return s._proofs[requestId][userID][0].storageFields[responseFieldName]; - } - - /** - * @dev Gets response field value - * @param requestId Id of the request - * @param sender Address of the sender - * @param responseFieldName Name of the response field to get - */ - function getResponseFieldValueFromAddress( uint256 requestId, address sender, string memory responseFieldName ) public view checkRequestExistence(requestId, true) returns (uint256) { VerifierStorage storage s = _getVerifierStorage(); - uint256 userID = s._user_address_to_id[sender]; - return s._proofs[requestId][userID][0].storageFields[responseFieldName]; - } - - /** - * @dev Gets response field value - * @param authType Auth type of the proof response - * @param sender Address of the sender - * @param responseFieldName Name of the response field to get - */ - function getAuthResponseFieldValueFromAddress( - string memory authType, - address sender, - string memory responseFieldName - ) public view checkAuthTypeExistence(authType, true) returns (uint256) { - VerifierStorage storage s = _getVerifierStorage(); - uint256 userID = s._user_address_to_id[sender]; - return s._authProofs[authType][userID][0].storageFields[responseFieldName]; + return s._proofs[requestId][sender][0].storageFields[responseFieldName]; } - function _checkLinkedResponseFields(uint256 multiRequestId, uint256 userID) internal view { + function _checkLinkedResponseFields(uint256 multiRequestId, address sender) internal view { VerifierStorage storage s = _getVerifierStorage(); for (uint256 i = 0; i < s._multiRequests[multiRequestId].groupIds.length; i++) { @@ -529,13 +482,13 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { // Check linkID in the same group or requests is the same uint256 requestLinkID = getResponseFieldValue( s._groupedRequests[groupId][0], - userID, + sender, LINKED_PROOF_KEY ); for (uint256 j = 1; j < s._groupedRequests[groupId].length; j++) { uint256 requestLinkIDToCompare = getResponseFieldValue( s._groupedRequests[groupId][j], - userID, + sender, LINKED_PROOF_KEY ); if (requestLinkID != requestLinkIDToCompare) { @@ -561,26 +514,20 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { public view checkMultiRequestExistence(multiRequestId, true) - returns (IVerifier.AuthProofStatus[] memory, IVerifier.RequestProofStatus[] memory) + returns (IVerifier.RequestProofStatus[] memory) { VerifierStorage storage s = _getVerifierStorage(); - // 1. Get the latest userId by the userAddress arg (in the mapping) - uint256 userID = s._user_address_to_id[userAddress]; - if (userID == 0) { - revert UserIDNotFound(userID); - } - - // 2. Check if all requests statuses are true for the userId - ( - IVerifier.AuthProofStatus[] memory authProofStatus, - IVerifier.RequestProofStatus[] memory requestProofStatus - ) = _getMultiRequestStatus(multiRequestId, userID); + // 1. Check if all requests statuses are true for the userAddress + IVerifier.RequestProofStatus[] memory requestProofStatus = _getMultiRequestStatus( + multiRequestId, + userAddress + ); - // 3. Check if all linked response fields are the same - _checkLinkedResponseFields(multiRequestId, userID); + // 2. Check if all linked response fields are the same + _checkLinkedResponseFields(multiRequestId, userAddress); - return (authProofStatus, requestProofStatus); + return requestProofStatus; } /** @@ -598,44 +545,26 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { public view checkMultiRequestExistence(multiRequestId, true) - returns (IVerifier.AuthProofStatus[] memory, IVerifier.RequestProofStatus[] memory) + returns (IVerifier.RequestProofStatus[] memory) { VerifierStorage storage s = _getVerifierStorage(); - // 1. Get the latest userId by the userAddress arg (in the mapping) - uint256 userIDFromAddress = s._user_address_to_id[userAddress]; - uint256 userIDSelected; - - if (userIDFromAddress != userID) { - address addressFromUserID = s._id_to_user_address[userID]; - if (addressFromUserID != userAddress) { - revert UserIDNotLinkedToAddress(userID, userAddress); - } - userIDSelected = s._user_address_to_id[addressFromUserID]; - } else { - userIDSelected = userID; - } - - // 2. Check if all requests statuses are true for the userId - ( - IVerifier.AuthProofStatus[] memory authProofStatus, - IVerifier.RequestProofStatus[] memory requestProofStatus - ) = _getMultiRequestStatus(multiRequestId, userIDSelected); + // 1. Check if all requests statuses are true for the userId + IVerifier.RequestProofStatus[] memory requestProofStatus = _getMultiRequestStatus( + multiRequestId, + userAddress + ); - // 3. Check if all linked response fields are the same - _checkLinkedResponseFields(multiRequestId, userIDSelected); + // 2. Check if all linked response fields are the same + _checkLinkedResponseFields(multiRequestId, userAddress); - return (authProofStatus, requestProofStatus); + return requestProofStatus; } function _getMultiRequestStatus( uint256 multiRequestId, - uint256 userID - ) - internal - view - returns (IVerifier.AuthProofStatus[] memory, IVerifier.RequestProofStatus[] memory) - { + address userAddress + ) internal view returns (IVerifier.RequestProofStatus[] memory) { VerifierStorage storage s = _getVerifierStorage(); IVerifier.MultiRequest storage multiRequest = s._multiRequests[multiRequestId]; @@ -648,32 +577,19 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { } } - IVerifier.AuthProofStatus[] memory authProofStatus = new IVerifier.AuthProofStatus[]( - s._authTypes.length - ); IVerifier.RequestProofStatus[] memory requestProofStatus = new IVerifier.RequestProofStatus[]( multiRequest.requestIds.length + lengthGroupIds ); - for (uint256 i = 0; i < s._authTypes.length; i++) { - string memory authType = s._authTypes[i]; - authProofStatus[i] = IVerifier.AuthProofStatus({ - authType: authType, - isVerified: s._authProofs[authType][userID][0].isVerified, - validatorVersion: s._authProofs[authType][userID][0].validatorVersion, - timestamp: s._authProofs[authType][userID][0].blockTimestamp - }); - } - for (uint256 i = 0; i < multiRequest.requestIds.length; i++) { uint256 requestId = multiRequest.requestIds[i]; requestProofStatus[i] = IVerifier.RequestProofStatus({ requestId: requestId, - isVerified: s._proofs[requestId][userID][0].isVerified, - validatorVersion: s._proofs[requestId][userID][0].validatorVersion, - timestamp: s._proofs[requestId][userID][0].blockTimestamp + isVerified: s._proofs[requestId][userAddress][0].isVerified, + validatorVersion: s._proofs[requestId][userAddress][0].validatorVersion, + timestamp: s._proofs[requestId][userAddress][0].blockTimestamp }); } @@ -686,14 +602,14 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { requestProofStatus[multiRequest.requestIds.length + j] = IVerifier .RequestProofStatus({ requestId: requestId, - isVerified: s._proofs[requestId][userID][0].isVerified, - validatorVersion: s._proofs[requestId][userID][0].validatorVersion, - timestamp: s._proofs[requestId][userID][0].blockTimestamp + isVerified: s._proofs[requestId][userAddress][0].isVerified, + validatorVersion: s._proofs[requestId][userAddress][0].validatorVersion, + timestamp: s._proofs[requestId][userAddress][0].blockTimestamp }); } } - return (authProofStatus, requestProofStatus); + return requestProofStatus; } /** @@ -707,35 +623,7 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { uint256 requestId ) public view checkRequestExistence(requestId, true) returns (bool) { VerifierStorage storage s = _getVerifierStorage(); - uint256 userID = s._user_address_to_id[sender]; - return s._proofs[requestId][userID][0].isVerified; - } - - /** - * @dev Checks if a user is authenticated - * @param userID The ID of the user - * @param userAddress The address of the user - * @return Whether the user is authenticated - */ - function isUserAuth(uint256 userID, address userAddress) public view returns (bool) { - VerifierStorage storage s = _getVerifierStorage(); - return s._user_auth_timestamp[userID][userAddress] != 0; - } - - /** - * @dev Gets the timestamp of the authentication of a user - * @param userID The user id of the user - * @param userAddress The address of the user - * @return The user ID - */ - function userAuthTimestamp(uint256 userID, address userAddress) public view returns (uint256) { - if (isUserAuth(userID, userAddress)) { - VerifierStorage storage s = _getVerifierStorage(); - - return s._user_auth_timestamp[userID][userAddress]; - } else { - return 0; - } + return s._proofs[requestId][sender][0].isVerified; } /** @@ -765,8 +653,7 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { uint256 requestId ) public view checkRequestExistence(requestId, true) returns (IVerifier.ProofStatus memory) { VerifierStorage storage s = _getVerifierStorage(); - uint256 userID = s._user_address_to_id[sender]; - VerifierLib.Proof storage proof = s._proofs[requestId][userID][0]; + VerifierLib.Proof storage proof = s._proofs[requestId][sender][0]; return IVerifier.ProofStatus(proof.isVerified, proof.validatorVersion, proof.blockTimestamp); diff --git a/test/verifier/universal-verifier-multi-query.test.ts b/test/verifier/universal-verifier-multi-query.test.ts index 02e39c0d..9390fb16 100644 --- a/test/verifier/universal-verifier-multi-query.test.ts +++ b/test/verifier/universal-verifier-multi-query.test.ts @@ -155,7 +155,7 @@ describe("Universal Verifier Multi-request", function () { async function checkStorageFields(verifier: any, requestId: bigint, storageFields: any[]) { for (const field of storageFields) { - const value = await verifier.getResponseFieldValueFromAddress( + const value = await verifier.getResponseFieldValue( requestId, await signer.getAddress(), field.name, @@ -164,17 +164,6 @@ describe("Universal Verifier Multi-request", function () { } } - async function checkAuthStorageFields(verifier: any, authType: string, storageFields: any[]) { - for (const field of storageFields) { - const value = await verifier.getAuthResponseFieldValueFromAddress( - authType, - await signer.getAddress(), - field.name, - ); - expect(value).to.be.equal(field.value); - } - } - beforeEach(async () => { await loadFixture(deployContractsFixture); }); @@ -183,22 +172,17 @@ describe("Universal Verifier Multi-request", function () { const requestId = 1; const multiRequestId = 1; const nonExistingMultiRequestId = 5; - const userId = 1; - const userId2 = 2; const authType = "authV2"; const params = packV3ValidatorParams(requestQuery1); - const txSetRequests = await verifier.setRequests( - [ - { - requestId: requestId, - metadata: "metadata", - validator: await v3Validator.getAddress(), - params: params, - }, - ], - [], - ); + const txSetRequests = await verifier.setRequests([ + { + requestId: requestId, + metadata: "metadata", + validator: await v3Validator.getAddress(), + params: params, + }, + ]); await txSetRequests.wait(); let requestStored = await verifier.getRequest(requestId); @@ -208,7 +192,6 @@ describe("Universal Verifier Multi-request", function () { expect(requestStored.params).to.be.equal(params); expect(requestStored.creator).to.be.equal(await signer.getAddress()); expect(requestStored.verifierId).to.be.equal(verifierId); - expect(requestStored.isVerifierAuthenticated).to.be.equal(false); await verifier.setAuthType({ authType: authType, @@ -262,17 +245,9 @@ describe("Universal Verifier Multi-request", function () { await tx.wait(); - await checkAuthStorageFields(verifier, authType, authStorageFields); await checkStorageFields(verifier, BigInt(requestId), storageFields); - const isUserAuth = await verifier.isUserAuth(userId, await signer.getAddress()); - expect(isUserAuth).to.be.equal(true); - - const isUserAuth2 = await verifier.isUserAuth(userId2, await signer.getAddress()); - expect(isUserAuth2).to.be.equal(false); - const filter = verifier.filters.ResponseSubmitted; - const events = await verifier.queryFilter(filter, -1); expect(events[0].eventName).to.be.equal("ResponseSubmitted"); expect(events[0].args.requestId).to.be.equal(requestId); @@ -283,15 +258,12 @@ describe("Universal Verifier Multi-request", function () { ).to.be.rejectedWith(`MultiRequestIdNotFound(${nonExistingMultiRequestId})`); const status = await verifier.getMultiRequestStatus(multiRequestId, signerAddress); - expect(status[0][0].authType).to.be.equal(authType); - expect(status[0][0].isVerified).to.be.equal(true); // auth type isVerified - expect(status[1][0].requestId).to.be.equal(requestId); - expect(status[1][0].isVerified).to.be.equal(true); // request isVerified + expect(status[0].requestId).to.be.equal(requestId); + expect(status[0].isVerified).to.be.equal(true); // request isVerified requestStored = await verifier.getRequest(requestId); // check if validator is authenticated // TODO reorg the tests to decouple validator from user - expect(requestStored.isVerifierAuthenticated).to.be.equal(true); }); it("Test submit response multi-request with same groupID and linkID", async () => { @@ -301,33 +273,24 @@ describe("Universal Verifier Multi-request", function () { const authType = "authV2"; const multiRequestId = 1; const nonExistingMultiRequestId = 5; - const userId = 1; const authParams = "0x"; const paramsRequest2 = packV3ValidatorParams(requestQuery2); const paramsRequest3 = packV3ValidatorParams(requestQuery3); - const txSetRequests = await verifier.setRequests( - [], - [ - { - groupId: groupId, - requests: [ - { - requestId: requestId2, - metadata: "metadata", - validator: await v3Validator.getAddress(), - params: paramsRequest2, - }, - { - requestId: requestId3, - metadata: "metadata", - validator: await v3Validator.getAddress(), - params: paramsRequest3, - }, - ], - }, - ], - ); + const txSetRequests = await verifier.setRequests([ + { + requestId: requestId2, + metadata: "metadata", + validator: await v3Validator.getAddress(), + params: paramsRequest2, + }, + { + requestId: requestId3, + metadata: "metadata", + validator: await v3Validator.getAddress(), + params: paramsRequest3, + }, + ]); await txSetRequests.wait(); const requestStored = await verifier.getRequest(requestId2); @@ -337,7 +300,6 @@ describe("Universal Verifier Multi-request", function () { expect(requestStored.params).to.be.equal(paramsRequest2); expect(requestStored.creator).to.be.equal(await signer.getAddress()); expect(requestStored.verifierId).to.be.equal(verifierId); - expect(requestStored.isVerifierAuthenticated).to.be.equal(false); await verifier.setAuthType({ authType: authType, @@ -388,15 +350,10 @@ describe("Universal Verifier Multi-request", function () { ); await tx.wait(); - await checkAuthStorageFields(verifier, authType, authStorageFields); await checkStorageFields(verifier, BigInt(requestId2), storageFields); await checkStorageFields(verifier, BigInt(requestId3), storageFields); - const isUserAuth = await verifier.isUserAuth(userId, await signer.getAddress()); - expect(isUserAuth).to.be.equal(true); - const filter = verifier.filters.ResponseSubmitted; - const events = await verifier.queryFilter(filter, -1); expect(events[0].eventName).to.be.equal("ResponseSubmitted"); expect(events[0].args.requestId).to.be.equal(requestId2); @@ -406,12 +363,9 @@ describe("Universal Verifier Multi-request", function () { verifier.getMultiRequestStatus(nonExistingMultiRequestId, signerAddress), ).to.be.rejectedWith(`MultiRequestIdNotFound(${nonExistingMultiRequestId})`); const status = await verifier.getMultiRequestStatus(multiRequestId, signerAddress); - expect(status[0][0].authType).to.be.equal(authType); - expect(status[0][0].isVerified).to.be.equal(true); // auth type isVerified - expect(status[1][0].requestId).to.be.equal(requestId2); - expect(status[1][0].isVerified).to.be.equal(true); // requestId2 isVerified - expect(status[1][1].requestId).to.be.equal(requestId3); - expect(status[1][1].isVerified).to.be.equal(true); // requestId3 isVerified + expect(status[0].isVerified).to.be.equal(true); // requestId2 isVerified + expect(status[1].requestId).to.be.equal(requestId3); + expect(status[1].isVerified).to.be.equal(true); // requestId3 isVerified }); it("Test submit response multi-request with same groupID and different linkID", async () => { @@ -424,28 +378,20 @@ describe("Universal Verifier Multi-request", function () { const paramsRequest2 = packV3ValidatorParams(requestQuery2); const paramsRequest3 = packV3ValidatorParams(requestQuery3); - const txSetRequests = await verifier.setRequests( - [], - [ - { - groupId: groupId, - requests: [ - { - requestId: requestId2, - metadata: "metadata", - validator: await v3Validator.getAddress(), - params: paramsRequest2, - }, - { - requestId: requestId3, - metadata: "metadata", - validator: await v3_2Validator.getAddress(), - params: paramsRequest3, - }, - ], - }, - ], - ); + const txSetRequests = await verifier.setRequests([ + { + requestId: requestId2, + metadata: "metadata", + validator: await v3Validator.getAddress(), + params: paramsRequest2, + }, + { + requestId: requestId3, + metadata: "metadata", + validator: await v3_2Validator.getAddress(), + params: paramsRequest3, + }, + ]); await txSetRequests.wait(); await verifier.setAuthType({ diff --git a/test/verifier/universal-verifier-submit-V2.test.ts b/test/verifier/universal-verifier-submit-V2.test.ts index 8699c293..cca9d850 100644 --- a/test/verifier/universal-verifier-submit-V2.test.ts +++ b/test/verifier/universal-verifier-submit-V2.test.ts @@ -118,17 +118,14 @@ describe("Universal Verifier submitResponse SigV2 validators", function () { }); for (const requestId of requestIds) { - await verifier.setRequests( - [ - { - requestId: requestId, - metadata: "metadata", - validator: await sig.getAddress(), - params: params, - }, - ], - [], - ); + await verifier.setRequests([ + { + requestId: requestId, + metadata: "metadata", + validator: await sig.getAddress(), + params: params, + }, + ]); } } @@ -145,7 +142,7 @@ describe("Universal Verifier submitResponse SigV2 validators", function () { async function checkStorageFields(verifier: any, requestId: bigint, storageFields: any[]) { for (const field of storageFields) { - const value = await verifier.getResponseFieldValueFromAddress( + const value = await verifier.getResponseFieldValue( requestId, await signer.getAddress(), field.name, diff --git a/test/verifier/universal-verifier.events.test.ts b/test/verifier/universal-verifier.events.test.ts index d177e382..6852ff6d 100644 --- a/test/verifier/universal-verifier.events.test.ts +++ b/test/verifier/universal-verifier.events.test.ts @@ -106,17 +106,14 @@ describe("Universal Verifier events", function () { for (let i = 0; i < requestsCount; i++) { await expect( - verifier.setRequests( - [ - { - requestId: i, - metadata: "metadata", - validator: await sig.getAddress(), - params: params[i], - }, - ], - [], - ), + verifier.setRequests([ + { + requestId: i, + metadata: "metadata", + validator: await sig.getAddress(), + params: params[i], + }, + ]), ).to.emit(verifier, "RequestSet"); console.log("RequestSet event emitted"); } @@ -148,17 +145,14 @@ describe("Universal Verifier events", function () { const originalRequestData = packValidatorParams(queries[0]); const updatedRequestData = packValidatorParams(queries[1]); - await verifier.setRequests( - [ - { - requestId: 0, - metadata: "metadata0", - validator: await sig.getAddress(), - params: originalRequestData, - }, - ], - [], - ); + await verifier.setRequests([ + { + requestId: 0, + metadata: "metadata0", + validator: await sig.getAddress(), + params: originalRequestData, + }, + ]); await verifier.updateRequest({ requestId: 0, diff --git a/test/verifier/universal-verifier.test.ts b/test/verifier/universal-verifier.test.ts index 002df4a9..dbfa8252 100644 --- a/test/verifier/universal-verifier.test.ts +++ b/test/verifier/universal-verifier.test.ts @@ -75,7 +75,7 @@ describe("Universal Verifier MTP & SIG validators", function () { async function checkStorageFields(verifier: any, requestId: bigint, storageFields: any[]) { for (const field of storageFields) { - const value = await verifier.getResponseFieldValueFromAddress( + const value = await verifier.getResponseFieldValue( requestId, await signer.getAddress(), field.name, @@ -108,17 +108,14 @@ describe("Universal Verifier MTP & SIG validators", function () { for (let i = 0; i < requestsCount; i++) { await expect( - verifier.setRequests( - [ - { - requestId: i, - metadata: "metadataN" + i, - validator: validatorAddr, - params: "0x0" + i, - }, - ], - [], - ), + verifier.setRequests([ + { + requestId: i, + metadata: "metadataN" + i, + validator: validatorAddr, + params: "0x0" + i, + }, + ]), ) .to.emit(verifier, "RequestSet") .withArgs(i, signerAddress, "metadataN" + i, validatorAddr, "0x0" + i); @@ -144,17 +141,14 @@ describe("Universal Verifier MTP & SIG validators", function () { const nonExistingRequestId = 1; const params = packValidatorParams(query); - verifier.setRequests( - [ - { - requestId: 0, - metadata: "metadata", - validator: await sigValidator.getAddress(), - params: params, - }, - ], - [], - ); + verifier.setRequests([ + { + requestId: 0, + metadata: "metadata", + validator: await sigValidator.getAddress(), + params: params, + }, + ]); await verifier.setAuthType({ authType: authType, @@ -219,17 +213,14 @@ describe("Universal Verifier MTP & SIG validators", function () { `RequestIdNotFound(${requestId})`, ); - await verifier.connect(requestOwner).setRequests( - [ - { - requestId: requestId, - metadata: "metadata", - validator: await sigValidator.getAddress(), - params: packValidatorParams(query), - }, - ], - [], - ); + await verifier.connect(requestOwner).setRequests([ + { + requestId: requestId, + metadata: "metadata", + validator: await sigValidator.getAddress(), + params: packValidatorParams(query), + }, + ]); expect(await verifier.getRequestOwner(requestId)).to.be.equal(requestOwnerAddr); await expect( @@ -266,17 +257,14 @@ describe("Universal Verifier MTP & SIG validators", function () { `RequestIdNotFound(${requestId})`, ); - await verifier.connect(requestOwner).setRequests( - [ - { - requestId: requestId, - metadata: "metadata", - validator: await sigValidator.getAddress(), - params: params, - }, - ], - [], - ); + await verifier.connect(requestOwner).setRequests([ + { + requestId: requestId, + metadata: "metadata", + validator: await sigValidator.getAddress(), + params: params, + }, + ]); expect(await verifier.isRequestEnabled(requestId)).to.be.true; @@ -362,17 +350,14 @@ describe("Universal Verifier MTP & SIG validators", function () { const mtpValAddr = await mtp.getAddress(); expect(await verifier.isWhitelistedValidator(mtpValAddr)).to.be.false; await expect( - verifier.setRequests( - [ - { - requestId: requestId, - metadata: "metadata", - validator: mtpValAddr, - params: "0x00", - }, - ], - [], - ), + verifier.setRequests([ + { + requestId: requestId, + metadata: "metadata", + validator: mtpValAddr, + params: "0x00", + }, + ]), ).to.be.rejectedWith(`ValidatorIsNotWhitelisted("${mtpValAddr}")`); await expect(verifier.connect(someAddress).addValidatorToWhitelist(mtpValAddr)) .to.be.revertedWithCustomError(verifier, "OwnableUnauthorizedAccount") @@ -383,17 +368,14 @@ describe("Universal Verifier MTP & SIG validators", function () { expect(await verifier.isWhitelistedValidator(mtpValAddr)).to.be.true; await expect( - verifier.setRequests( - [ - { - requestId: requestId, - metadata: "metadata", - validator: mtpValAddr, - params: "0x00", - }, - ], - [], - ), + verifier.setRequests([ + { + requestId: requestId, + metadata: "metadata", + validator: mtpValAddr, + params: "0x00", + }, + ]), ).not.to.be.rejected; // can't whitelist validator, which does not support ICircuitValidator interface @@ -401,17 +383,14 @@ describe("Universal Verifier MTP & SIG validators", function () { // not a validator with proper interface and even not supporting IERC165 interface to check it await expect( - verifier.setRequests( - [ - { - requestId: otherRequestId, - metadata: "metadata", - validator: someAddress, - params: "0x00", - }, - ], - [], - ), + verifier.setRequests([ + { + requestId: otherRequestId, + metadata: "metadata", + validator: someAddress, + params: "0x00", + }, + ]), ).to.be.rejectedWith(`function returned an unexpected amount of data`); await verifier.removeValidatorFromWhitelist(mtpValAddr); @@ -457,17 +436,14 @@ describe("Universal Verifier MTP & SIG validators", function () { const requestId = 0; const params = packValidatorParams(query); - await verifier.connect(requestOwner).setRequests( - [ - { - requestId: requestId, - metadata: "metadata", - validator: await sigValidator.getAddress(), - params: params, - }, - ], - [], - ); + await verifier.connect(requestOwner).setRequests([ + { + requestId: requestId, + metadata: "metadata", + validator: await sigValidator.getAddress(), + params: params, + }, + ]); let request = await verifier.getRequest(requestId); expect(request.metadata).to.be.equal("metadata"); diff --git a/test/verifier/universal-verifier.v3.test.ts b/test/verifier/universal-verifier.v3.test.ts index 75e5d531..68b50600 100644 --- a/test/verifier/universal-verifier.v3.test.ts +++ b/test/verifier/universal-verifier.v3.test.ts @@ -115,7 +115,7 @@ describe("Universal Verifier V3 validator", function () { async function checkStorageFields(verifier: any, requestId: bigint, storageFields: any[]) { for (const field of storageFields) { - const value = await verifier.getResponseFieldValueFromAddress( + const value = await verifier.getResponseFieldValue( requestId, await signer.getAddress(), field.name, @@ -141,17 +141,14 @@ describe("Universal Verifier V3 validator", function () { await publishState(state, stateTransition1 as any); const params = packV3ValidatorParams(query); - await verifier.setRequests( - [ - { - requestId: requestId, - metadata: "metadata", - validator: await v3Validator.getAddress(), - params: params, - }, - ], - [], - ); + await verifier.setRequests([ + { + requestId: requestId, + metadata: "metadata", + validator: await v3Validator.getAddress(), + params: params, + }, + ]); const requestStored = await verifier.getRequest(requestId); // check if the request is stored correctly checking metadata and validator @@ -160,7 +157,6 @@ describe("Universal Verifier V3 validator", function () { expect(requestStored.params).to.be.equal(params); expect(requestStored.creator).to.be.equal(await signer.getAddress()); expect(requestStored.verifierId).to.be.equal(verifierId); - expect(requestStored.isVerifierAuthenticated).to.be.equal(false); await verifier.setAuthType({ authType: authType, @@ -230,20 +226,19 @@ describe("Universal Verifier V3 validator", function () { }); it("Test submit response fails with Issuer is not on the Allowed Issuers list", async () => { - const params = packV3ValidatorParams(query, ["1"]); + const query2 = { ...query }; + query2.groupID = 2; + const params = packV3ValidatorParams(query2, ["1"]); const requestId = 33; - await verifier.setRequests( - [ - { - requestId: requestId, - metadata: "metadata", - validator: await v3Validator.getAddress(), - params: params, - }, - ], - [], - ); + await verifier.setRequests([ + { + requestId: requestId, + metadata: "metadata", + validator: await v3Validator.getAddress(), + params: params, + }, + ]); const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); const proof = packZKProof(inputs, pi_a, pi_b, pi_c); @@ -275,17 +270,14 @@ describe("Universal Verifier V3 validator", function () { query2.groupID = 0; const requestId = 34; const params = packV3ValidatorParams(query2); - await verifier.setRequests( - [ - { - requestId: requestId, - metadata: "metadata", - validator: await v3Validator.getAddress(), - params: params, - }, - ], - [], - ); + await verifier.setRequests([ + { + requestId: requestId, + metadata: "metadata", + validator: await v3Validator.getAddress(), + params: params, + }, + ]); const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); const proof = packZKProof(inputs, pi_a, pi_b, pi_c); @@ -316,19 +308,17 @@ describe("Universal Verifier V3 validator", function () { ...query, }; query2.proofType = 2; + query2.groupID = 3; const requestId = 35; const params = packV3ValidatorParams(query2); - await verifier.setRequests( - [ - { - requestId: requestId, - metadata: "metadata", - validator: await v3Validator.getAddress(), - params: params, - }, - ], - [], - ); + await verifier.setRequests([ + { + requestId: requestId, + metadata: "metadata", + validator: await v3Validator.getAddress(), + params: params, + }, + ]); const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); const proof = packZKProof(inputs, pi_a, pi_b, pi_c); @@ -359,19 +349,17 @@ describe("Universal Verifier V3 validator", function () { ...query, }; query2.nullifierSessionID = "2"; + query2.groupID = 4; const requestId = 36; const params = packV3ValidatorParams(query2); - await verifier.setRequests( - [ - { - requestId: requestId, - metadata: "metadata", - validator: await v3Validator.getAddress(), - params: params, - }, - ], - [], - ); + await verifier.setRequests([ + { + requestId: requestId, + metadata: "metadata", + validator: await v3Validator.getAddress(), + params: params, + }, + ]); const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); const proof = packZKProof(inputs, pi_a, pi_b, pi_c); @@ -402,19 +390,17 @@ describe("Universal Verifier V3 validator", function () { ...query, }; query2.queryHash = BigInt(0); + query2.groupID = 5; const requestId = 37; const params = packV3ValidatorParams(query2); - await verifier.setRequests( - [ - { - requestId: requestId, - metadata: "metadata", - validator: await v3Validator.getAddress(), - params: params, - }, - ], - [], - ); + await verifier.setRequests([ + { + requestId: requestId, + metadata: "metadata", + validator: await v3Validator.getAddress(), + params: params, + }, + ]); const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); const proof = packZKProof(inputs, pi_a, pi_b, pi_c); @@ -455,17 +441,14 @@ describe("Universal Verifier V3 validator", function () { const params = packV3ValidatorParams(query); const requestId = 37; - await verifier.setRequests( - [ - { - requestId: requestId, - metadata: "metadata", - validator: await v3Validator.getAddress(), - params: params, - }, - ], - [], - ); + await verifier.setRequests([ + { + requestId: requestId, + metadata: "metadata", + validator: await v3Validator.getAddress(), + params: params, + }, + ]); await verifier.setAuthType({ authType: authType, From 85804dda3c85ccfbe22fcf9b64d4efb6406483b3 Mon Sep 17 00:00:00 2001 From: daveroga Date: Thu, 9 Jan 2025 07:11:24 +0100 Subject: [PATCH 51/69] fix solhint and custom errors --- contracts/interfaces/IVerifier.sol | 4 +--- contracts/verifiers/Verifier.sol | 8 +++++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/contracts/interfaces/IVerifier.sol b/contracts/interfaces/IVerifier.sol index 7ffcad54..27011d32 100644 --- a/contracts/interfaces/IVerifier.sol +++ b/contracts/interfaces/IVerifier.sol @@ -157,9 +157,7 @@ interface IVerifier { * @dev Sets different requests * @param requests List of requests */ - function setRequests( - Request[] calldata requests - ) external; + function setRequests(Request[] calldata requests) external; /** * @dev Gets a specific request by ID diff --git a/contracts/verifiers/Verifier.sol b/contracts/verifiers/Verifier.sol index 9cd21bcd..eec2c96b 100644 --- a/contracts/verifiers/Verifier.sol +++ b/contracts/verifiers/Verifier.sol @@ -22,6 +22,8 @@ error RequestIsAlreadyGrouped(uint256 requestId); error LinkIDNotTheSameForGroupedRequests(uint256 requestLinkID, uint256 requestLinkIDToCompare); error UserIDNotFound(uint256 userID); error UserIDNotLinkedToAddress(uint256 userID, address userAddress); +error UserNotAuthenticated(); +error MetadataNotSupportedYet(); abstract contract Verifier is IVerifier, ContextUpgradeable { /// @dev Key to retrieve the linkID from the proof storage @@ -239,7 +241,7 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { for (uint256 i = 0; i < requests.length; i++) { uint256 groupID = requests[i].validator.getRequestParams(requests[i].params).groupID; if (groupID != 0 && groupIdExists(groupID)) { - revert("Group ID already exists"); + revert GroupIdAlreadyExists(groupID); } } @@ -364,7 +366,7 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { } if (userIDFromReponse == 0) { - revert("The user is not authenticated"); + revert UserNotAuthenticated(); } // 3. Verify all the responses, write proof results (under the userID key from the auth of the user), @@ -383,7 +385,7 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { $.writeProofResults(response.requestId, sender, signals); if (response.metadata.length > 0) { - revert("Metadata not supported yet"); + revert MetadataNotSupportedYet(); } } } From 1d49a4e495e1486a2d733057db6c69330338379d Mon Sep 17 00:00:00 2001 From: daveroga Date: Thu, 9 Jan 2025 14:20:26 +0100 Subject: [PATCH 52/69] updates from review --- contracts/interfaces/IAuthValidator.sol | 14 +-- contracts/interfaces/IVerifier.sol | 37 +++--- contracts/test-helpers/AuthValidatorStub.sol | 7 +- contracts/validators/EthIdentityValidator.sol | 16 +-- contracts/verifiers/Verifier.sol | 114 ++++++++++-------- .../universal-verifier-multi-query.test.ts | 13 ++ .../universal-verifier-submit-V2.test.ts | 10 +- test/verifier/universal-verifier.test.ts | 6 +- 8 files changed, 110 insertions(+), 107 deletions(-) diff --git a/contracts/interfaces/IAuthValidator.sol b/contracts/interfaces/IAuthValidator.sol index b69b94be..3be885f3 100644 --- a/contracts/interfaces/IAuthValidator.sol +++ b/contracts/interfaces/IAuthValidator.sol @@ -7,16 +7,6 @@ import {IState} from "./IState.sol"; * @dev IAuthValidator. Interface for verification of auth data. */ interface IAuthValidator { - /** - * @dev ResponseField. Information about response fields from verification. Used in verify function. - * @param name Name of the response field - * @param value Value of the response field - */ - struct ResponseField { - string name; - uint256 value; - } - /** * @dev Get version of the contract */ @@ -29,12 +19,12 @@ interface IAuthValidator { * @param params Request query data of the credential to verify. * @param sender Sender of the proof. * @param state State contract to get identities and gist states to check. - * @return Array of response fields as result. + * @return userID User Id for the auth proof verified. */ function verify( bytes calldata proof, bytes calldata params, address sender, IState state - ) external returns (ResponseField[] memory); + ) external returns (uint256 userID); } diff --git a/contracts/interfaces/IVerifier.sol b/contracts/interfaces/IVerifier.sol index 27011d32..0c923cc0 100644 --- a/contracts/interfaces/IVerifier.sol +++ b/contracts/interfaces/IVerifier.sol @@ -64,18 +64,6 @@ interface IVerifier { Request[] requests; } - /** - * @dev ProofStatus. Structure for proof status. - * @param isVerified True if the proof is verified. - * @param validatorVersion Version of the validator. - * @param blockTimestamp Block timestamp of the proof. - */ - struct ProofStatus { - bool isVerified; - string validatorVersion; - uint256 blockTimestamp; - } - /** * @dev Response. Structure for response. * @param requestId Request id of the request. @@ -107,13 +95,13 @@ interface IVerifier { } /** - * @dev RequestProofStatus. Structure for request proof status. + * @dev RequestStatus. Structure for request proof status. * @param requestId Request id of the proof. * @param isVerified True if the proof is verified. * @param validatorVersion Version of the validator. * @param timestamp Timestamp of the proof. */ - struct RequestProofStatus { + struct RequestStatus { uint256 requestId; bool isVerified; string validatorVersion; @@ -188,7 +176,18 @@ interface IVerifier { function getMultiRequestStatus( uint256 multiRequestId, address userAddress - ) external view returns (RequestProofStatus[] memory); + ) external view returns (RequestStatus[] memory); + + /** + * @dev Checks if the proofs from a Multirequest submitted for a given sender and request ID are verified + * @param multiRequestId The ID of the MultiRequest + * @param userAddress The address of the user + * @return Wether the multiRequest is verified. + */ + function isMultiRequestVerified( + uint256 multiRequestId, + address userAddress + ) external view returns (bool); /** * @dev Gets proof storage response field value @@ -203,12 +202,12 @@ interface IVerifier { ) external view returns (uint256); /** - * @dev Get if proof is verified for the sender and request with requestId. + * @dev Checks if a proof from a request submitted for a given sender and request ID is verified * @param sender Sender of the proof. * @param requestId Request id of the Request to verify. * @return True if proof is verified for the sender and request id. */ - function isProofVerified(address sender, uint256 requestId) external view returns (bool); + function isRequestVerified(address sender, uint256 requestId) external view returns (bool); /** * @dev Sets an auth type @@ -237,8 +236,8 @@ interface IVerifier { * @param requestId Request id of the proof. * @return Proof status. */ - function getProofStatus( + function getRequestStatus( address sender, uint256 requestId - ) external view returns (ProofStatus memory); + ) external view returns (RequestStatus memory); } diff --git a/contracts/test-helpers/AuthValidatorStub.sol b/contracts/test-helpers/AuthValidatorStub.sol index e56f8aa8..ec7509f0 100644 --- a/contracts/test-helpers/AuthValidatorStub.sol +++ b/contracts/test-helpers/AuthValidatorStub.sol @@ -25,10 +25,7 @@ contract AuthValidatorStub is IAuthValidator, ERC165 { bytes calldata, address, IState - ) external pure override returns (IAuthValidator.ResponseField[] memory) { - IAuthValidator.ResponseField[] memory signals = new IAuthValidator.ResponseField[](1); - signals[0].name = "userID"; - signals[0].value = 1; - return signals; + ) external pure override returns (uint256 userID) { + return 1; } } diff --git a/contracts/validators/EthIdentityValidator.sol b/contracts/validators/EthIdentityValidator.sol index e45962ee..5aac1cf4 100644 --- a/contracts/validators/EthIdentityValidator.sol +++ b/contracts/validators/EthIdentityValidator.sol @@ -5,13 +5,13 @@ import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/acces import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; import {IGroth16Verifier} from "../interfaces/IGroth16Verifier.sol"; import {GenesisUtils} from "../lib/GenesisUtils.sol"; -import {IRequestValidator} from "../interfaces/IRequestValidator.sol"; +import {IAuthValidator} from "../interfaces/IAuthValidator.sol"; import {IState} from "../interfaces/IState.sol"; /** * @dev EthIdentityValidator validator */ -contract EthIdentityValidator is Ownable2StepUpgradeable, IRequestValidator, ERC165 { +contract EthIdentityValidator is Ownable2StepUpgradeable, IAuthValidator, ERC165 { struct PubSignals { uint256 userID; } @@ -81,13 +81,11 @@ contract EthIdentityValidator is Ownable2StepUpgradeable, IRequestValidator, ERC bytes calldata data, address sender, IState stateContract - ) public view override returns (IRequestValidator.ResponseField[] memory) { + ) public view override returns (uint256) { uint256 userID = abi.decode(proof, (uint256)); _verifyEthIdentity(userID, sender); - IRequestValidator.ResponseField[] memory signals = new IRequestValidator.ResponseField[](1); - signals[0] = IRequestValidator.ResponseField({name: "userID", value: userID}); - return signals; + return userID; } function _getState() internal view returns (IState) { @@ -99,10 +97,4 @@ contract EthIdentityValidator is Ownable2StepUpgradeable, IRequestValidator, ERC uint256 calcId = GenesisUtils.calcIdFromEthAddress(idType, sender); require(calcId == id, "Sender is not owner of the ethereum identity"); } - - function getRequestParams( - bytes calldata - ) external pure override returns (IRequestValidator.RequestParams memory) { - return IRequestValidator.RequestParams({groupID: 0, verifierID: 0}); - } } diff --git a/contracts/verifiers/Verifier.sol b/contracts/verifiers/Verifier.sol index eec2c96b..ef453862 100644 --- a/contracts/verifiers/Verifier.sol +++ b/contracts/verifiers/Verifier.sol @@ -351,20 +351,13 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { uint256 userIDFromReponse; AuthTypeData storage authTypeData = $._authMethods[authResponse.authType]; // Authenticate user - IAuthValidator.ResponseField[] memory authSignals = authTypeData.validator.verify( + userIDFromReponse = authTypeData.validator.verify( authResponse.proof, authTypeData.params, sender, $._state ); - for (uint256 j = 0; j < authSignals.length; j++) { - if (keccak256(bytes(authSignals[j].name)) == keccak256(bytes("userID"))) { - userIDFromReponse = authSignals[j].value; - break; - } - } - if (userIDFromReponse == 0) { revert UserNotAuthenticated(); } @@ -516,12 +509,10 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { public view checkMultiRequestExistence(multiRequestId, true) - returns (IVerifier.RequestProofStatus[] memory) + returns (IVerifier.RequestStatus[] memory) { - VerifierStorage storage s = _getVerifierStorage(); - // 1. Check if all requests statuses are true for the userAddress - IVerifier.RequestProofStatus[] memory requestProofStatus = _getMultiRequestStatus( + IVerifier.RequestStatus[] memory requestStatus = _getMultiRequestStatus( multiRequestId, userAddress ); @@ -529,44 +520,32 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { // 2. Check if all linked response fields are the same _checkLinkedResponseFields(multiRequestId, userAddress); - return requestProofStatus; + return requestStatus; } /** - * @dev Gets the status of the multiRequest verification + * @dev Checks if the proofs from a Multirequest submitted for a given sender and request ID are verified * @param multiRequestId The ID of the multiRequest * @param userAddress The address of the user - * @param userID The user id of the user * @return status The status of the multiRequest. "True" if all requests are verified, "false" otherwise */ - function getMultiRequestStatus( + function isMultiRequestVerified( uint256 multiRequestId, - address userAddress, - uint256 userID - ) - public - view - checkMultiRequestExistence(multiRequestId, true) - returns (IVerifier.RequestProofStatus[] memory) - { - VerifierStorage storage s = _getVerifierStorage(); - - // 1. Check if all requests statuses are true for the userId - IVerifier.RequestProofStatus[] memory requestProofStatus = _getMultiRequestStatus( - multiRequestId, - userAddress - ); + address userAddress + ) public view checkMultiRequestExistence(multiRequestId, true) returns (bool) { + // 1. Check if all requests are verified for the userAddress + bool verified = _isMultiRequestVerified(multiRequestId, userAddress); // 2. Check if all linked response fields are the same _checkLinkedResponseFields(multiRequestId, userAddress); - return requestProofStatus; + return verified; } function _getMultiRequestStatus( uint256 multiRequestId, address userAddress - ) internal view returns (IVerifier.RequestProofStatus[] memory) { + ) internal view returns (IVerifier.RequestStatus[] memory) { VerifierStorage storage s = _getVerifierStorage(); IVerifier.MultiRequest storage multiRequest = s._multiRequests[multiRequestId]; @@ -579,15 +558,14 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { } } - IVerifier.RequestProofStatus[] - memory requestProofStatus = new IVerifier.RequestProofStatus[]( - multiRequest.requestIds.length + lengthGroupIds - ); + IVerifier.RequestStatus[] memory requestStatus = new IVerifier.RequestStatus[]( + multiRequest.requestIds.length + lengthGroupIds + ); for (uint256 i = 0; i < multiRequest.requestIds.length; i++) { uint256 requestId = multiRequest.requestIds[i]; - requestProofStatus[i] = IVerifier.RequestProofStatus({ + requestStatus[i] = IVerifier.RequestStatus({ requestId: requestId, isVerified: s._proofs[requestId][userAddress][0].isVerified, validatorVersion: s._proofs[requestId][userAddress][0].validatorVersion, @@ -601,26 +579,55 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { for (uint256 j = 0; j < s._groupedRequests[groupId].length; j++) { uint256 requestId = s._groupedRequests[groupId][j]; - requestProofStatus[multiRequest.requestIds.length + j] = IVerifier - .RequestProofStatus({ - requestId: requestId, - isVerified: s._proofs[requestId][userAddress][0].isVerified, - validatorVersion: s._proofs[requestId][userAddress][0].validatorVersion, - timestamp: s._proofs[requestId][userAddress][0].blockTimestamp - }); + requestStatus[multiRequest.requestIds.length + j] = IVerifier.RequestStatus({ + requestId: requestId, + isVerified: s._proofs[requestId][userAddress][0].isVerified, + validatorVersion: s._proofs[requestId][userAddress][0].validatorVersion, + timestamp: s._proofs[requestId][userAddress][0].blockTimestamp + }); + } + } + + return requestStatus; + } + + function _isMultiRequestVerified( + uint256 multiRequestId, + address userAddress + ) internal view returns (bool) { + VerifierStorage storage s = _getVerifierStorage(); + IVerifier.MultiRequest storage multiRequest = s._multiRequests[multiRequestId]; + + for (uint256 i = 0; i < multiRequest.requestIds.length; i++) { + uint256 requestId = multiRequest.requestIds[i]; + + if (!s._proofs[requestId][userAddress][0].isVerified) { + return false; + } + } + + for (uint256 i = 0; i < multiRequest.groupIds.length; i++) { + uint256 groupId = multiRequest.groupIds[i]; + + for (uint256 j = 0; j < s._groupedRequests[groupId].length; j++) { + uint256 requestId = s._groupedRequests[groupId][j]; + + if (!s._proofs[requestId][userAddress][0].isVerified) { + return false; + } } } - return requestProofStatus; + return true; } /** - * @dev Checks if a proof submitted for a given sender and request ID is verified + * @dev Checks if a proof from a request submitted for a given sender and request ID is verified * @param sender The sender's address * @param requestId The ID of the request * @return True if proof is verified */ - function isProofVerified( + function isRequestVerified( address sender, uint256 requestId ) public view checkRequestExistence(requestId, true) returns (bool) { @@ -650,15 +657,20 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { * @param requestId The ID of the ZKP request * @return The proof status structure */ - function getProofStatus( + function getRequestStatus( address sender, uint256 requestId - ) public view checkRequestExistence(requestId, true) returns (IVerifier.ProofStatus memory) { + ) public view checkRequestExistence(requestId, true) returns (IVerifier.RequestStatus memory) { VerifierStorage storage s = _getVerifierStorage(); VerifierLib.Proof storage proof = s._proofs[requestId][sender][0]; return - IVerifier.ProofStatus(proof.isVerified, proof.validatorVersion, proof.blockTimestamp); + IVerifier.RequestStatus( + requestId, + proof.isVerified, + proof.validatorVersion, + proof.blockTimestamp + ); } function _getRequestIfCanBeVerified( diff --git a/test/verifier/universal-verifier-multi-query.test.ts b/test/verifier/universal-verifier-multi-query.test.ts index 9390fb16..20686f55 100644 --- a/test/verifier/universal-verifier-multi-query.test.ts +++ b/test/verifier/universal-verifier-multi-query.test.ts @@ -257,6 +257,12 @@ describe("Universal Verifier Multi-request", function () { verifier.getMultiRequestStatus(nonExistingMultiRequestId, signerAddress), ).to.be.rejectedWith(`MultiRequestIdNotFound(${nonExistingMultiRequestId})`); + const isMultiRequestVerified = await verifier.isMultiRequestVerified( + multiRequestId, + signerAddress, + ); + expect(isMultiRequestVerified).to.be.equal(true); + const status = await verifier.getMultiRequestStatus(multiRequestId, signerAddress); expect(status[0].requestId).to.be.equal(requestId); expect(status[0].isVerified).to.be.equal(true); // request isVerified @@ -362,6 +368,13 @@ describe("Universal Verifier Multi-request", function () { await expect( verifier.getMultiRequestStatus(nonExistingMultiRequestId, signerAddress), ).to.be.rejectedWith(`MultiRequestIdNotFound(${nonExistingMultiRequestId})`); + + const isMultiRequestVerified = await verifier.isMultiRequestVerified( + multiRequestId, + signerAddress, + ); + expect(isMultiRequestVerified).to.be.equal(true); + const status = await verifier.getMultiRequestStatus(multiRequestId, signerAddress); expect(status[0].isVerified).to.be.equal(true); // requestId2 isVerified expect(status[1].requestId).to.be.equal(requestId3); diff --git a/test/verifier/universal-verifier-submit-V2.test.ts b/test/verifier/universal-verifier-submit-V2.test.ts index cca9d850..7fd48535 100644 --- a/test/verifier/universal-verifier-submit-V2.test.ts +++ b/test/verifier/universal-verifier-submit-V2.test.ts @@ -188,12 +188,12 @@ describe("Universal Verifier submitResponse SigV2 validators", function () { txRes.blockNumber, )) as Block; - const status = await verifier.getProofStatus(signerAddress, requestId); + const status = await verifier.getRequestStatus(signerAddress, requestId); expect(status.isVerified).to.be.true; expect(status.validatorVersion).to.be.equal("1.0.0-mock"); - expect(status.blockTimestamp).to.be.equal(txResTimestamp); + expect(status.timestamp).to.be.equal(txResTimestamp); - await expect(verifier.getProofStatus(signerAddress, nonExistingRequestId)).to.be.rejectedWith( + await expect(verifier.getRequestStatus(signerAddress, nonExistingRequestId)).to.be.rejectedWith( `RequestIdNotFound(${nonExistingRequestId})`, ); @@ -219,10 +219,10 @@ describe("Universal Verifier submitResponse SigV2 validators", function () { )) as Block; for (const requestId of requestIdsMulti) { - const status = await verifier.getProofStatus(signerAddress, requestId); + const status = await verifier.getRequestStatus(signerAddress, requestId); expect(status.isVerified).to.be.true; expect(status.validatorVersion).to.be.equal("1.0.0-mock"); - expect(status.blockTimestamp).to.be.equal(txResTimestampMuti); + expect(status.timestamp).to.be.equal(txResTimestampMuti); await checkStorageFields(verifier, BigInt(requestId), storageFields); } }); diff --git a/test/verifier/universal-verifier.test.ts b/test/verifier/universal-verifier.test.ts index dbfa8252..685e7b90 100644 --- a/test/verifier/universal-verifier.test.ts +++ b/test/verifier/universal-verifier.test.ts @@ -190,12 +190,12 @@ describe("Universal Verifier MTP & SIG validators", function () { txRes.blockNumber, )) as Block; - const status = await verifier.getProofStatus(signerAddress, requestId); + const status = await verifier.getRequestStatus(signerAddress, requestId); expect(status.isVerified).to.be.true; expect(status.validatorVersion).to.be.equal("1.0.0-mock"); - expect(status.blockTimestamp).to.be.equal(txResTimestamp); + expect(status.timestamp).to.be.equal(txResTimestamp); - await expect(verifier.getProofStatus(signerAddress, nonExistingRequestId)).to.be.rejectedWith( + await expect(verifier.getRequestStatus(signerAddress, nonExistingRequestId)).to.be.rejectedWith( `RequestIdNotFound(${nonExistingRequestId})`, ); }); From 4a9e98814119053ce147b1ea69153723b58d1f45 Mon Sep 17 00:00:00 2001 From: daveroga Date: Thu, 9 Jan 2025 17:08:11 +0100 Subject: [PATCH 53/69] check grouped requests 2 requests at least --- contracts/verifiers/Verifier.sol | 38 +++++++++++++++++---- test/verifier/universal-verifier.v3.test.ts | 36 ++++++++++++++++++- 2 files changed, 67 insertions(+), 7 deletions(-) diff --git a/contracts/verifiers/Verifier.sol b/contracts/verifiers/Verifier.sol index ef453862..679e3c32 100644 --- a/contracts/verifiers/Verifier.sol +++ b/contracts/verifiers/Verifier.sol @@ -24,6 +24,7 @@ error UserIDNotFound(uint256 userID); error UserIDNotLinkedToAddress(uint256 userID, address userAddress); error UserNotAuthenticated(); error MetadataNotSupportedYet(); +error GroupMustHaveAtLeastTwoRequests(uint256 groupID); abstract contract Verifier is IVerifier, ContextUpgradeable { /// @dev Key to retrieve the linkID from the proof storage @@ -58,8 +59,6 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { // Information about multiRequests mapping(uint256 multiRequestId => IVerifier.MultiRequest) _multiRequests; uint256[] _multiRequestIds; - // Whitelisted validators - mapping(IRequestValidator => bool isApproved) _validatorWhitelist; // Information about auth types and validators string[] _authTypes; mapping(string authType => AuthTypeData) _authMethods; @@ -234,14 +233,41 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { * @dev Sets different requests * @param requests The list of requests */ - function setRequests(Request[] calldata requests) public { + function setRequests(IVerifier.Request[] calldata requests) public { VerifierStorage storage s = _getVerifierStorage(); - // 1. Check first that groupIds don't exist + uint256 newGroupsCount = 0; + uint256[] memory newGroupsGroupID = new uint256[](requests.length); + uint256[] memory newGroupsRequestCount = new uint256[](requests.length); + + // 1. Check first that groupIds don't exist and keep the number of requests per group for (uint256 i = 0; i < requests.length; i++) { uint256 groupID = requests[i].validator.getRequestParams(requests[i].params).groupID; - if (groupID != 0 && groupIdExists(groupID)) { - revert GroupIdAlreadyExists(groupID); + + if (groupID != 0) { + if (groupIdExists(groupID)) { + revert GroupIdAlreadyExists(groupID); + } + + bool existingGroupID = false; + for (uint256 j = 0; j < newGroupsCount; j++) { + if (newGroupsGroupID[j] == groupID) { + newGroupsRequestCount[j]++; + existingGroupID = true; + break; + } + } + if (!existingGroupID) { + newGroupsGroupID[newGroupsCount] = groupID; + newGroupsRequestCount[newGroupsCount]++; + newGroupsCount++; + } + } + } + + for (uint256 i = 0; i < newGroupsCount; i++) { + if (newGroupsRequestCount[i] < 2) { + revert GroupMustHaveAtLeastTwoRequests(newGroupsGroupID[i]); } } diff --git a/test/verifier/universal-verifier.v3.test.ts b/test/verifier/universal-verifier.v3.test.ts index 68b50600..4c1b01f0 100644 --- a/test/verifier/universal-verifier.v3.test.ts +++ b/test/verifier/universal-verifier.v3.test.ts @@ -148,6 +148,12 @@ describe("Universal Verifier V3 validator", function () { validator: await v3Validator.getAddress(), params: params, }, + { + requestId: requestId + 10, + metadata: "metadata", + validator: await v3Validator.getAddress(), + params: params, + }, ]); const requestStored = await verifier.getRequest(requestId); @@ -238,6 +244,12 @@ describe("Universal Verifier V3 validator", function () { validator: await v3Validator.getAddress(), params: params, }, + { + requestId: requestId + 10, + metadata: "metadata", + validator: await v3Validator.getAddress(), + params: params, + }, ]); const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); const proof = packZKProof(inputs, pi_a, pi_b, pi_c); @@ -318,6 +330,12 @@ describe("Universal Verifier V3 validator", function () { validator: await v3Validator.getAddress(), params: params, }, + { + requestId: requestId + 10, + metadata: "metadata", + validator: await v3Validator.getAddress(), + params: params, + }, ]); const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); @@ -359,6 +377,12 @@ describe("Universal Verifier V3 validator", function () { validator: await v3Validator.getAddress(), params: params, }, + { + requestId: requestId + 10, + metadata: "metadata", + validator: await v3Validator.getAddress(), + params: params, + }, ]); const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); @@ -400,6 +424,12 @@ describe("Universal Verifier V3 validator", function () { validator: await v3Validator.getAddress(), params: params, }, + { + requestId: requestId + 10, + metadata: "metadata", + validator: await v3Validator.getAddress(), + params: params, + }, ]); const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proofJson); @@ -439,7 +469,11 @@ describe("Universal Verifier V3 validator", function () { await publishState(state, stateTransition12 as any); await publishState(state, stateTransition13 as any); - const params = packV3ValidatorParams(query); + const query2 = { + ...query, + }; + query2.groupID = 0; + const params = packV3ValidatorParams(query2); const requestId = 37; await verifier.setRequests([ { From 81fdf8358f6fe7679cc4ddaf1e10a58944e310ec Mon Sep 17 00:00:00 2001 From: daveroga Date: Thu, 9 Jan 2025 17:31:45 +0100 Subject: [PATCH 54/69] fix error cyclomatic-complexity from solhint --- contracts/verifiers/Verifier.sol | 50 +++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/contracts/verifiers/Verifier.sol b/contracts/verifiers/Verifier.sol index 679e3c32..6ea8ed1b 100644 --- a/contracts/verifiers/Verifier.sol +++ b/contracts/verifiers/Verifier.sol @@ -249,27 +249,23 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { revert GroupIdAlreadyExists(groupID); } - bool existingGroupID = false; - for (uint256 j = 0; j < newGroupsCount; j++) { - if (newGroupsGroupID[j] == groupID) { - newGroupsRequestCount[j]++; - existingGroupID = true; - break; - } - } - if (!existingGroupID) { + (bool exists, uint256 groupIDIndex) = _getGroupIDIndex( + groupID, + newGroupsGroupID, + newGroupsCount + ); + + if (!exists) { newGroupsGroupID[newGroupsCount] = groupID; newGroupsRequestCount[newGroupsCount]++; newGroupsCount++; + } else { + newGroupsRequestCount[groupIDIndex]++; } } } - for (uint256 i = 0; i < newGroupsCount; i++) { - if (newGroupsRequestCount[i] < 2) { - revert GroupMustHaveAtLeastTwoRequests(newGroupsGroupID[i]); - } - } + _checkGroupsRequestsCount(newGroupsGroupID, newGroupsRequestCount, newGroupsCount); // 2. Set requests checking groups for (uint256 i = 0; i < requests.length; i++) { @@ -290,6 +286,32 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { } } + function _getGroupIDIndex( + uint256 groupID, + uint256[] memory groupList, + uint256 listCount + ) internal pure returns (bool, uint256) { + for (uint256 j = 0; j < listCount; j++) { + if (groupList[j] == groupID) { + return (true, j); + } + } + + return (false, 0); + } + + function _checkGroupsRequestsCount( + uint256[] memory groupList, + uint256[] memory groupRequestsList, + uint256 groupsCount + ) internal pure { + for (uint256 i = 0; i < groupsCount; i++) { + if (groupRequestsList[i] < 2) { + revert GroupMustHaveAtLeastTwoRequests(groupList[i]); + } + } + } + /** * @dev Gets a specific request by ID * @param requestId The ID of the request From 034de2c068cdc6a44d988dab611c06a5766464b8 Mon Sep 17 00:00:00 2001 From: daveroga Date: Fri, 10 Jan 2025 11:36:03 +0100 Subject: [PATCH 55/69] move _state in storage --- contracts/verifiers/Verifier.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/verifiers/Verifier.sol b/contracts/verifiers/Verifier.sol index 6ea8ed1b..3018b761 100644 --- a/contracts/verifiers/Verifier.sol +++ b/contracts/verifiers/Verifier.sol @@ -53,9 +53,9 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { mapping(uint256 requestId => mapping(address sender => VerifierLib.Proof[])) _proofs; mapping(uint256 requestId => IVerifier.RequestData) _requests; uint256[] _requestIds; + IState _state; mapping(uint256 groupId => uint256[] requestIds) _groupedRequests; uint256[] _groupIds; - IState _state; // Information about multiRequests mapping(uint256 multiRequestId => IVerifier.MultiRequest) _multiRequests; uint256[] _multiRequestIds; From 10eb73dc27b7056ad6ad39776363e41bfb931a13 Mon Sep 17 00:00:00 2001 From: daveroga Date: Fri, 10 Jan 2025 14:38:49 +0100 Subject: [PATCH 56/69] check link responses with bool --- contracts/verifiers/Verifier.sol | 41 ++++++++++++++----- .../universal-verifier-multi-query.test.ts | 2 +- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/contracts/verifiers/Verifier.sol b/contracts/verifiers/Verifier.sol index 3018b761..a27584ce 100644 --- a/contracts/verifiers/Verifier.sol +++ b/contracts/verifiers/Verifier.sol @@ -19,7 +19,7 @@ error AuthTypeNotFound(string authType); error AuthTypeAlreadyExists(string authType); error ValidatorNotWhitelisted(address validator); error RequestIsAlreadyGrouped(uint256 requestId); -error LinkIDNotTheSameForGroupedRequests(uint256 requestLinkID, uint256 requestLinkIDToCompare); +error LinkIDNotTheSameForGroupedRequests(); error UserIDNotFound(uint256 userID); error UserIDNotLinkedToAddress(uint256 userID, address userAddress); error UserNotAuthenticated(); @@ -513,10 +513,16 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { string memory responseFieldName ) public view checkRequestExistence(requestId, true) returns (uint256) { VerifierStorage storage s = _getVerifierStorage(); - return s._proofs[requestId][sender][0].storageFields[responseFieldName]; + return + s._proofs[requestId][sender][s._proofs[requestId][sender].length - 1].storageFields[ + responseFieldName + ]; } - function _checkLinkedResponseFields(uint256 multiRequestId, address sender) internal view { + function _checkLinkedResponseFields( + uint256 multiRequestId, + address sender + ) internal view returns (bool) { VerifierStorage storage s = _getVerifierStorage(); for (uint256 i = 0; i < s._multiRequests[multiRequestId].groupIds.length; i++) { @@ -535,13 +541,12 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { LINKED_PROOF_KEY ); if (requestLinkID != requestLinkIDToCompare) { - revert LinkIDNotTheSameForGroupedRequests( - requestLinkID, - requestLinkIDToCompare - ); + return false; } } } + + return true; } /** @@ -566,7 +571,14 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { ); // 2. Check if all linked response fields are the same - _checkLinkedResponseFields(multiRequestId, userAddress); + bool linkedResponsesOK = _checkLinkedResponseFields( + multiRequestId, + userAddress + ); + + if (!linkedResponsesOK) { + revert LinkIDNotTheSameForGroupedRequests(); + } return requestStatus; } @@ -584,9 +596,18 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { // 1. Check if all requests are verified for the userAddress bool verified = _isMultiRequestVerified(multiRequestId, userAddress); - // 2. Check if all linked response fields are the same - _checkLinkedResponseFields(multiRequestId, userAddress); + if (verified) { + // 2. Check if all linked response fields are the same + bool linkedResponsesOK = _checkLinkedResponseFields( + multiRequestId, + userAddress + ); + if (!linkedResponsesOK) { + verified = false; + } + } + return verified; } diff --git a/test/verifier/universal-verifier-multi-query.test.ts b/test/verifier/universal-verifier-multi-query.test.ts index 20686f55..cd8d538c 100644 --- a/test/verifier/universal-verifier-multi-query.test.ts +++ b/test/verifier/universal-verifier-multi-query.test.ts @@ -448,7 +448,7 @@ describe("Universal Verifier Multi-request", function () { await tx.wait(); await expect(verifier.getMultiRequestStatus(multiRequestId, signerAddress)).to.be.rejectedWith( - "LinkIDNotTheSameForGroupedRequests(3, 4)", + "LinkIDNotTheSameForGroupedRequests()", ); }); }); From ca1f04f1442258f13f7ec9d3905c4af9c005b4d8 Mon Sep 17 00:00:00 2001 From: daveroga Date: Fri, 10 Jan 2025 14:39:33 +0100 Subject: [PATCH 57/69] fix solhint --- contracts/verifiers/Verifier.sol | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/contracts/verifiers/Verifier.sol b/contracts/verifiers/Verifier.sol index a27584ce..c2247a05 100644 --- a/contracts/verifiers/Verifier.sol +++ b/contracts/verifiers/Verifier.sol @@ -571,10 +571,7 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { ); // 2. Check if all linked response fields are the same - bool linkedResponsesOK = _checkLinkedResponseFields( - multiRequestId, - userAddress - ); + bool linkedResponsesOK = _checkLinkedResponseFields(multiRequestId, userAddress); if (!linkedResponsesOK) { revert LinkIDNotTheSameForGroupedRequests(); @@ -598,16 +595,13 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { if (verified) { // 2. Check if all linked response fields are the same - bool linkedResponsesOK = _checkLinkedResponseFields( - multiRequestId, - userAddress - ); + bool linkedResponsesOK = _checkLinkedResponseFields(multiRequestId, userAddress); if (!linkedResponsesOK) { verified = false; } } - + return verified; } From 188bb13090f078a3e1a73df300b81d9de443c9fa Mon Sep 17 00:00:00 2001 From: daveroga Date: Fri, 10 Jan 2025 15:52:31 +0100 Subject: [PATCH 58/69] remove history in proofs from requests --- contracts/lib/VerifierLib.sol | 13 +++++-------- contracts/verifiers/Verifier.sol | 27 ++++++++++++--------------- 2 files changed, 17 insertions(+), 23 deletions(-) diff --git a/contracts/lib/VerifierLib.sol b/contracts/lib/VerifierLib.sol index 63815d7e..2e39ad5c 100644 --- a/contracts/lib/VerifierLib.sol +++ b/contracts/lib/VerifierLib.sol @@ -50,17 +50,14 @@ library VerifierLib { address sender, IRequestValidator.ResponseField[] memory responseFields ) public { - Proof[] storage proofs = self._proofs[requestId][sender]; + Proof storage proof = self._proofs[requestId][sender]; // We only keep only 1 proof now without history. Prepared for the future if needed. - if (proofs.length == 0) { - proofs.push(); - } for (uint256 i = 0; i < responseFields.length; i++) { - proofs[0].storageFields[responseFields[i].name] = responseFields[i].value; + proof.storageFields[responseFields[i].name] = responseFields[i].value; } - proofs[0].isVerified = true; - proofs[0].validatorVersion = self._requests[requestId].validator.version(); - proofs[0].blockTimestamp = block.timestamp; + proof.isVerified = true; + proof.validatorVersion = self._requests[requestId].validator.version(); + proof.blockTimestamp = block.timestamp; } } diff --git a/contracts/verifiers/Verifier.sol b/contracts/verifiers/Verifier.sol index c2247a05..4c6e2ff7 100644 --- a/contracts/verifiers/Verifier.sol +++ b/contracts/verifiers/Verifier.sol @@ -50,7 +50,7 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { struct VerifierStorage { // Information about requests // solhint-disable-next-line - mapping(uint256 requestId => mapping(address sender => VerifierLib.Proof[])) _proofs; + mapping(uint256 requestId => mapping(address sender => VerifierLib.Proof)) _proofs; mapping(uint256 requestId => IVerifier.RequestData) _requests; uint256[] _requestIds; IState _state; @@ -513,10 +513,7 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { string memory responseFieldName ) public view checkRequestExistence(requestId, true) returns (uint256) { VerifierStorage storage s = _getVerifierStorage(); - return - s._proofs[requestId][sender][s._proofs[requestId][sender].length - 1].storageFields[ - responseFieldName - ]; + return s._proofs[requestId][sender].storageFields[responseFieldName]; } function _checkLinkedResponseFields( @@ -630,9 +627,9 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { requestStatus[i] = IVerifier.RequestStatus({ requestId: requestId, - isVerified: s._proofs[requestId][userAddress][0].isVerified, - validatorVersion: s._proofs[requestId][userAddress][0].validatorVersion, - timestamp: s._proofs[requestId][userAddress][0].blockTimestamp + isVerified: s._proofs[requestId][userAddress].isVerified, + validatorVersion: s._proofs[requestId][userAddress].validatorVersion, + timestamp: s._proofs[requestId][userAddress].blockTimestamp }); } @@ -644,9 +641,9 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { requestStatus[multiRequest.requestIds.length + j] = IVerifier.RequestStatus({ requestId: requestId, - isVerified: s._proofs[requestId][userAddress][0].isVerified, - validatorVersion: s._proofs[requestId][userAddress][0].validatorVersion, - timestamp: s._proofs[requestId][userAddress][0].blockTimestamp + isVerified: s._proofs[requestId][userAddress].isVerified, + validatorVersion: s._proofs[requestId][userAddress].validatorVersion, + timestamp: s._proofs[requestId][userAddress].blockTimestamp }); } } @@ -664,7 +661,7 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { for (uint256 i = 0; i < multiRequest.requestIds.length; i++) { uint256 requestId = multiRequest.requestIds[i]; - if (!s._proofs[requestId][userAddress][0].isVerified) { + if (!s._proofs[requestId][userAddress].isVerified) { return false; } } @@ -675,7 +672,7 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { for (uint256 j = 0; j < s._groupedRequests[groupId].length; j++) { uint256 requestId = s._groupedRequests[groupId][j]; - if (!s._proofs[requestId][userAddress][0].isVerified) { + if (!s._proofs[requestId][userAddress].isVerified) { return false; } } @@ -695,7 +692,7 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { uint256 requestId ) public view checkRequestExistence(requestId, true) returns (bool) { VerifierStorage storage s = _getVerifierStorage(); - return s._proofs[requestId][sender][0].isVerified; + return s._proofs[requestId][sender].isVerified; } /** @@ -725,7 +722,7 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { uint256 requestId ) public view checkRequestExistence(requestId, true) returns (IVerifier.RequestStatus memory) { VerifierStorage storage s = _getVerifierStorage(); - VerifierLib.Proof storage proof = s._proofs[requestId][sender][0]; + VerifierLib.Proof storage proof = s._proofs[requestId][sender]; return IVerifier.RequestStatus( From ffe4e6e44ea1f5db39dcfbabc0a0b3d73e07e786 Mon Sep 17 00:00:00 2001 From: daveroga Date: Fri, 10 Jan 2025 17:21:24 +0100 Subject: [PATCH 59/69] check nullifierSessionID on request creation --- contracts/interfaces/IRequestValidator.sol | 4 +- .../RequestValidatorAuthV2Stub.sol | 2 +- .../test-helpers/RequestValidatorV2Stub.sol | 2 +- .../test-helpers/RequestValidatorV3Stub.sol | 3 +- .../test-helpers/RequestValidatorV3_2Stub.sol | 3 +- contracts/validators/AuthV2Validator.sol | 2 +- .../CredentialAtomicQueryV2ValidatorBase.sol | 2 +- .../CredentialAtomicQueryV3Validator.sol | 8 ++- contracts/verifiers/Verifier.sol | 15 +++++- test/verifier/universal-verifier.v3.test.ts | 50 ++++++++++++++++++- 10 files changed, 79 insertions(+), 12 deletions(-) diff --git a/contracts/interfaces/IRequestValidator.sol b/contracts/interfaces/IRequestValidator.sol index 6313843d..9fd0c1f6 100644 --- a/contracts/interfaces/IRequestValidator.sol +++ b/contracts/interfaces/IRequestValidator.sol @@ -21,10 +21,12 @@ interface IRequestValidator { * @dev RequestParams. Information about request params from request query data. * @param groupID Group ID of the request query params * @param verifierID Verifier ID of the request query params + * @param nullifierSessionID NullifierSessionID of the request query params */ struct RequestParams { uint256 groupID; uint256 verifierID; + uint256 nullifierSessionID; } /** @@ -51,7 +53,7 @@ interface IRequestValidator { /** * @dev Get the request params of the request query data. * @param params Request query data of the credential to verify. - * @return Group ID of the request query data. + * @return RequestParams of the request query data. */ function getRequestParams(bytes calldata params) external view returns (RequestParams memory); } diff --git a/contracts/test-helpers/RequestValidatorAuthV2Stub.sol b/contracts/test-helpers/RequestValidatorAuthV2Stub.sol index 036b5b9e..0d03be66 100644 --- a/contracts/test-helpers/RequestValidatorAuthV2Stub.sol +++ b/contracts/test-helpers/RequestValidatorAuthV2Stub.sol @@ -36,6 +36,6 @@ contract RequestValidatorAuthV2Stub is IRequestValidator, ERC165 { function getRequestParams( bytes calldata ) external pure override returns (IRequestValidator.RequestParams memory) { - return IRequestValidator.RequestParams({groupID: 0, verifierID: 0}); + return IRequestValidator.RequestParams({groupID: 0, verifierID: 0, nullifierSessionID: 0}); } } diff --git a/contracts/test-helpers/RequestValidatorV2Stub.sol b/contracts/test-helpers/RequestValidatorV2Stub.sol index bebb9a95..f7938dd3 100644 --- a/contracts/test-helpers/RequestValidatorV2Stub.sol +++ b/contracts/test-helpers/RequestValidatorV2Stub.sol @@ -38,6 +38,6 @@ contract RequestValidatorV2Stub is IRequestValidator, ERC165 { function getRequestParams( bytes calldata ) external pure override returns (IRequestValidator.RequestParams memory) { - return IRequestValidator.RequestParams({groupID: 0, verifierID: 0}); + return IRequestValidator.RequestParams({groupID: 0, verifierID: 0, nullifierSessionID: 0}); } } diff --git a/contracts/test-helpers/RequestValidatorV3Stub.sol b/contracts/test-helpers/RequestValidatorV3Stub.sol index b791723d..9b50f67b 100644 --- a/contracts/test-helpers/RequestValidatorV3Stub.sol +++ b/contracts/test-helpers/RequestValidatorV3Stub.sol @@ -63,7 +63,8 @@ contract RequestValidatorV3Stub is IRequestValidator, ERC165 { return IRequestValidator.RequestParams({ groupID: credAtomicQuery.groupID, - verifierID: credAtomicQuery.verifierID + verifierID: credAtomicQuery.verifierID, + nullifierSessionID: credAtomicQuery.nullifierSessionID }); } } diff --git a/contracts/test-helpers/RequestValidatorV3_2Stub.sol b/contracts/test-helpers/RequestValidatorV3_2Stub.sol index 65a9646e..94a6d8ce 100644 --- a/contracts/test-helpers/RequestValidatorV3_2Stub.sol +++ b/contracts/test-helpers/RequestValidatorV3_2Stub.sol @@ -63,7 +63,8 @@ contract RequestValidatorV3_2Stub is IRequestValidator, ERC165 { return IRequestValidator.RequestParams({ groupID: credAtomicQuery.groupID, - verifierID: credAtomicQuery.verifierID + verifierID: credAtomicQuery.verifierID, + nullifierSessionID: credAtomicQuery.nullifierSessionID }); } } diff --git a/contracts/validators/AuthV2Validator.sol b/contracts/validators/AuthV2Validator.sol index 8a4963c7..5428906c 100644 --- a/contracts/validators/AuthV2Validator.sol +++ b/contracts/validators/AuthV2Validator.sol @@ -68,7 +68,7 @@ contract AuthV2Validator is CredentialAtomicQueryValidatorBase { function getRequestParams( bytes calldata ) external pure override returns (IRequestValidator.RequestParams memory) { - return IRequestValidator.RequestParams({groupID: 0, verifierID: 0}); + return IRequestValidator.RequestParams({groupID: 0, verifierID: 0, nullifierSessionID: 0}); } /** diff --git a/contracts/validators/CredentialAtomicQueryV2ValidatorBase.sol b/contracts/validators/CredentialAtomicQueryV2ValidatorBase.sol index 07ae71a3..3bc58deb 100644 --- a/contracts/validators/CredentialAtomicQueryV2ValidatorBase.sol +++ b/contracts/validators/CredentialAtomicQueryV2ValidatorBase.sol @@ -85,7 +85,7 @@ abstract contract CredentialAtomicQueryV2ValidatorBase is CredentialAtomicQueryV function getRequestParams( bytes calldata ) external pure override returns (IRequestValidator.RequestParams memory) { - return IRequestValidator.RequestParams({groupID: 0, verifierID: 0}); + return IRequestValidator.RequestParams({groupID: 0, verifierID: 0, nullifierSessionID: 0}); } /** diff --git a/contracts/validators/CredentialAtomicQueryV3Validator.sol b/contracts/validators/CredentialAtomicQueryV3Validator.sol index 3792ff3f..e5c8835d 100644 --- a/contracts/validators/CredentialAtomicQueryV3Validator.sol +++ b/contracts/validators/CredentialAtomicQueryV3Validator.sol @@ -7,6 +7,8 @@ import {GenesisUtils} from "../lib/GenesisUtils.sol"; import {IRequestValidator} from "../interfaces/IRequestValidator.sol"; import {IState} from "../interfaces/IState.sol"; +error VerifierIDNotSet(); + /** * @dev CredentialAtomicQueryV3 validator */ @@ -154,10 +156,14 @@ contract CredentialAtomicQueryV3Validator is CredentialAtomicQueryValidatorBase params, (CredentialAtomicQueryV3) ); + + if (credAtomicQuery.verifierID == 0) revert VerifierIDNotSet(); + return IRequestValidator.RequestParams({ groupID: credAtomicQuery.groupID, - verifierID: credAtomicQuery.verifierID + verifierID: credAtomicQuery.verifierID, + nullifierSessionID: credAtomicQuery.nullifierSessionID }); } diff --git a/contracts/verifiers/Verifier.sol b/contracts/verifiers/Verifier.sol index 4c6e2ff7..213640f7 100644 --- a/contracts/verifiers/Verifier.sol +++ b/contracts/verifiers/Verifier.sol @@ -25,6 +25,7 @@ error UserIDNotLinkedToAddress(uint256 userID, address userAddress); error UserNotAuthenticated(); error MetadataNotSupportedYet(); error GroupMustHaveAtLeastTwoRequests(uint256 groupID); +error NullifierSessionIDAlreadyExists(uint256 nullifierSessionID); abstract contract Verifier is IVerifier, ContextUpgradeable { /// @dev Key to retrieve the linkID from the proof storage @@ -62,6 +63,7 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { // Information about auth types and validators string[] _authTypes; mapping(string authType => AuthTypeData) _authMethods; + mapping(uint256 nullifierSessionID => uint256 requestId) _nullifierSessionIDs; } // solhint-disable-next-line @@ -240,9 +242,10 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { uint256[] memory newGroupsGroupID = new uint256[](requests.length); uint256[] memory newGroupsRequestCount = new uint256[](requests.length); - // 1. Check first that groupIds don't exist and keep the number of requests per group + // 1. Check first that groupIds don't exist and keep the number of requests per group. for (uint256 i = 0; i < requests.length; i++) { uint256 groupID = requests[i].validator.getRequestParams(requests[i].params).groupID; + if (groupID != 0) { if (groupIdExists(groupID)) { @@ -267,8 +270,16 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { _checkGroupsRequestsCount(newGroupsGroupID, newGroupsRequestCount, newGroupsCount); - // 2. Set requests checking groups + // 2. Set requests checking groups and nullifierSessionID uniqueness for (uint256 i = 0; i < requests.length; i++) { + uint256 nullifierSessionID = requests[i].validator.getRequestParams(requests[i].params).nullifierSessionID; + if (nullifierSessionID != 0) { + if (s._nullifierSessionIDs[nullifierSessionID] != 0) { + revert NullifierSessionIDAlreadyExists(nullifierSessionID); + } + s._nullifierSessionIDs[nullifierSessionID] = requests[i].requestId; + } + uint256 groupID = requests[i].validator.getRequestParams(requests[i].params).groupID; // request without group diff --git a/test/verifier/universal-verifier.v3.test.ts b/test/verifier/universal-verifier.v3.test.ts index 4c1b01f0..49910328 100644 --- a/test/verifier/universal-verifier.v3.test.ts +++ b/test/verifier/universal-verifier.v3.test.ts @@ -370,6 +370,15 @@ describe("Universal Verifier V3 validator", function () { query2.groupID = 4; const requestId = 36; const params = packV3ValidatorParams(query2); + + const query3 = { + ...query, + }; + query3.nullifierSessionID = "3"; + query3.groupID = 4; + const requestId2 = 46; + const params2 = packV3ValidatorParams(query3); + await verifier.setRequests([ { requestId: requestId, @@ -378,10 +387,10 @@ describe("Universal Verifier V3 validator", function () { params: params, }, { - requestId: requestId + 10, + requestId: requestId2, metadata: "metadata", validator: await v3Validator.getAddress(), - params: params, + params: params2, }, ]); @@ -513,4 +522,41 @@ describe("Universal Verifier V3 validator", function () { ), ).to.be.rejectedWith("Generated proof is outdated"); }); + + it("Test set request fails with NullifierSessionID already exists", async () => { + const query2 = { + ...query, + }; + query2.groupID = 0; + query2.nullifierSessionID = "1"; + const requestId = 38; + const params = packV3ValidatorParams(query2); + await verifier.setRequests([ + { + requestId: requestId, + metadata: "metadata", + validator: await v3Validator.getAddress(), + params: params, + }, + ]); + + const query3 = { + ...query, + }; + query3.groupID = 0; + query3.nullifierSessionID = "1"; + const requestId2 = 39; + const params2 = packV3ValidatorParams(query3); + + await expect( + verifier.setRequests([ + { + requestId: requestId2, + metadata: "metadata", + validator: await v3Validator.getAddress(), + params: params2, + }, + ]), + ).to.be.rejectedWith(`NullifierSessionIDAlreadyExists(${query2.nullifierSessionID})`); + }); }); From f6071310724220f8e36b0606dbbc38e6b6078d13 Mon Sep 17 00:00:00 2001 From: daveroga Date: Fri, 10 Jan 2025 20:29:17 +0100 Subject: [PATCH 60/69] fix cyclomatic-complexity in solhint --- contracts/verifiers/Verifier.sol | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/contracts/verifiers/Verifier.sol b/contracts/verifiers/Verifier.sol index 213640f7..05a68c25 100644 --- a/contracts/verifiers/Verifier.sol +++ b/contracts/verifiers/Verifier.sol @@ -245,7 +245,6 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { // 1. Check first that groupIds don't exist and keep the number of requests per group. for (uint256 i = 0; i < requests.length; i++) { uint256 groupID = requests[i].validator.getRequestParams(requests[i].params).groupID; - if (groupID != 0) { if (groupIdExists(groupID)) { @@ -272,13 +271,7 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { // 2. Set requests checking groups and nullifierSessionID uniqueness for (uint256 i = 0; i < requests.length; i++) { - uint256 nullifierSessionID = requests[i].validator.getRequestParams(requests[i].params).nullifierSessionID; - if (nullifierSessionID != 0) { - if (s._nullifierSessionIDs[nullifierSessionID] != 0) { - revert NullifierSessionIDAlreadyExists(nullifierSessionID); - } - s._nullifierSessionIDs[nullifierSessionID] = requests[i].requestId; - } + _checkNullifierSessionIdUniqueness(requests[i]); uint256 groupID = requests[i].validator.getRequestParams(requests[i].params).groupID; @@ -297,6 +290,20 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { } } + function _checkNullifierSessionIdUniqueness(IVerifier.Request calldata request) internal { + VerifierStorage storage s = _getVerifierStorage(); + uint256 nullifierSessionID = request + .validator + .getRequestParams(request.params) + .nullifierSessionID; + if (nullifierSessionID != 0) { + if (s._nullifierSessionIDs[nullifierSessionID] != 0) { + revert NullifierSessionIDAlreadyExists(nullifierSessionID); + } + s._nullifierSessionIDs[nullifierSessionID] = request.requestId; + } + } + function _getGroupIDIndex( uint256 groupID, uint256[] memory groupList, From 848e6099cb1947801f4210f40dd49caae6970e9d Mon Sep 17 00:00:00 2001 From: daveroga Date: Mon, 13 Jan 2025 11:12:45 +0100 Subject: [PATCH 61/69] check verifierID in requests for V3 --- contracts/verifiers/UniversalVerifier.sol | 7 + contracts/verifiers/Verifier.sol | 34 +++++ .../universal-verifier-multi-query.test.ts | 125 +++++++++--------- test/verifier/universal-verifier.v3.test.ts | 33 +++++ 4 files changed, 140 insertions(+), 59 deletions(-) diff --git a/contracts/verifiers/UniversalVerifier.sol b/contracts/verifiers/UniversalVerifier.sol index c7a31605..02bc88b0 100644 --- a/contracts/verifiers/UniversalVerifier.sol +++ b/contracts/verifiers/UniversalVerifier.sol @@ -154,6 +154,13 @@ contract UniversalVerifier is _setState(state); } + /** + * @dev Sets the verifier ID + */ + function setVerifierID(uint256 verifierID) public onlyOwner { + _setVerifierID(verifierID); + } + /** * @dev Sets the request owner address * @param requestId The ID of the request diff --git a/contracts/verifiers/Verifier.sol b/contracts/verifiers/Verifier.sol index 05a68c25..e6f2b73e 100644 --- a/contracts/verifiers/Verifier.sol +++ b/contracts/verifiers/Verifier.sol @@ -8,6 +8,7 @@ import {IAuthValidator} from "../interfaces/IAuthValidator.sol"; import {IState} from "../interfaces/IState.sol"; import {VerifierLib} from "../lib/VerifierLib.sol"; import {IVerifier} from "../interfaces/IVerifier.sol"; +import {GenesisUtils} from "../lib/GenesisUtils.sol"; error RequestIdNotFound(uint256 requestId); error RequestAlreadyExists(uint256 requestId); @@ -26,6 +27,7 @@ error UserNotAuthenticated(); error MetadataNotSupportedYet(); error GroupMustHaveAtLeastTwoRequests(uint256 groupID); error NullifierSessionIDAlreadyExists(uint256 nullifierSessionID); +error VerifierIDIsNotValid(uint256 requestVerifierID, uint256 expectedVerifierID); abstract contract Verifier is IVerifier, ContextUpgradeable { /// @dev Key to retrieve the linkID from the proof storage @@ -64,6 +66,8 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { string[] _authTypes; mapping(string authType => AuthTypeData) _authMethods; mapping(uint256 nullifierSessionID => uint256 requestId) _nullifierSessionIDs; + // verifierID to check in requests + uint256 _verifierID; } // solhint-disable-next-line @@ -153,6 +157,11 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { function __Verifier_init_unchained(IState state) internal onlyInitializing { _setState(state); + // initial calculation of verifierID from contract address and default id type from State contract + VerifierStorage storage s = _getVerifierStorage(); + bytes2 idType = s._state.getDefaultIdType(); + uint256 calculatedVerifierID = GenesisUtils.calcIdFromEthAddress(idType, address(this)); + _setVerifierID(calculatedVerifierID); } function _getVerifierStorage() private pure returns (VerifierStorage storage $) { @@ -161,6 +170,11 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { } } + function _setVerifierID(uint256 verifierID) internal { + VerifierStorage storage s = _getVerifierStorage(); + s._verifierID = verifierID; + } + /** * @dev Checks if a request ID exists * @param requestId The ID of the request @@ -272,6 +286,7 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { // 2. Set requests checking groups and nullifierSessionID uniqueness for (uint256 i = 0; i < requests.length; i++) { _checkNullifierSessionIdUniqueness(requests[i]); + _checkVerifierID(requests[i]); uint256 groupID = requests[i].validator.getRequestParams(requests[i].params).groupID; @@ -290,6 +305,17 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { } } + function _checkVerifierID(IVerifier.Request calldata request) internal view { + VerifierStorage storage s = _getVerifierStorage(); + uint256 requestVerifierID = request.validator.getRequestParams(request.params).verifierID; + + if (requestVerifierID != 0) { + if (requestVerifierID != s._verifierID) { + revert VerifierIDIsNotValid(requestVerifierID, s._verifierID); + } + } + } + function _checkNullifierSessionIdUniqueness(IVerifier.Request calldata request) internal { VerifierStorage storage s = _getVerifierStorage(); uint256 nullifierSessionID = request @@ -729,6 +755,14 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { return address(_getVerifierStorage()._state); } + /** + * @dev Gets the verifierID of the verifier contract + * @return uint256 verifierID of the verifier contract + */ + function getVerifierID() public view virtual returns (uint256) { + return _getVerifierStorage()._verifierID; + } + /** * @dev Checks the proof status for a given user and request ID * @param sender The sender's address diff --git a/test/verifier/universal-verifier-multi-query.test.ts b/test/verifier/universal-verifier-multi-query.test.ts index cd8d538c..b8550b27 100644 --- a/test/verifier/universal-verifier-multi-query.test.ts +++ b/test/verifier/universal-verifier-multi-query.test.ts @@ -47,65 +47,11 @@ describe("Universal Verifier Multi-request", function () { "20376033832371109177683048456014525905119173674985843915445634726167450989630"; const [merklized, isRevocationChecked, valueArrSize] = [1, 1, 1]; const nullifierSessionId = "0"; - const verifierId = "1"; - const queryHash = calculateQueryHashV3( - value, - schema, - slotIndex, - operator, - claimPathKey, - valueArrSize, - merklized, - isRevocationChecked, - verifierId, - nullifierSessionId, - ); - - const requestQuery1 = { - schema, - claimPathKey, - operator, - slotIndex, - value, - // we can use the same offchain circuit id because now an auth request is used for authentication - circuitIds: [CircuitId.AuthV2], - skipClaimRevocationCheck: false, - queryHash, - groupID: 0, - nullifierSessionID: nullifierSessionId, // for ethereum based user - proofType: 1, // 1 for BJJ - verifierID: verifierId, - }; - - const requestQuery2 = { - schema, - claimPathKey, - operator, - slotIndex, - value, - circuitIds: [CircuitId.AtomicQueryV3], - skipClaimRevocationCheck: false, - queryHash, - groupID: 1, - nullifierSessionID: nullifierSessionId, // for ethereum based user - proofType: 1, // 1 for BJJ - verifierID: verifierId, - }; - - const requestQuery3 = { - schema, - claimPathKey, - operator, - slotIndex, - value, - circuitIds: [CircuitId.AtomicQueryV3], - skipClaimRevocationCheck: false, - queryHash, - groupID: 1, - nullifierSessionID: nullifierSessionId, // for ethereum based user - proofType: 1, // 1 for BJJ - verifierID: verifierId, - }; + let verifierId; + let queryHash: any; + let requestQuery1: any; + let requestQuery2: any; + let requestQuery3: any; const storageFields = [ { @@ -144,6 +90,67 @@ describe("Universal Verifier Multi-request", function () { await verifierLib.getAddress(), ); + verifierId = await verifier.getVerifierID(); + + queryHash = calculateQueryHashV3( + value, + schema, + slotIndex, + operator, + claimPathKey, + valueArrSize, + merklized, + isRevocationChecked, + verifierId, + nullifierSessionId, + ); + + requestQuery1 = { + schema, + claimPathKey, + operator, + slotIndex, + value, + // we can use the same offchain circuit id because now an auth request is used for authentication + circuitIds: [CircuitId.AuthV2], + skipClaimRevocationCheck: false, + queryHash, + groupID: 0, + nullifierSessionID: nullifierSessionId, // for ethereum based user + proofType: 1, // 1 for BJJ + verifierID: verifierId, + }; + + requestQuery2 = { + schema, + claimPathKey, + operator, + slotIndex, + value, + circuitIds: [CircuitId.AtomicQueryV3], + skipClaimRevocationCheck: false, + queryHash, + groupID: 1, + nullifierSessionID: nullifierSessionId, // for ethereum based user + proofType: 1, // 1 for BJJ + verifierID: verifierId, + }; + + requestQuery3 = { + schema, + claimPathKey, + operator, + slotIndex, + value, + circuitIds: [CircuitId.AtomicQueryV3], + skipClaimRevocationCheck: false, + queryHash, + groupID: 1, + nullifierSessionID: nullifierSessionId, // for ethereum based user + proofType: 1, // 1 for BJJ + verifierID: verifierId, + }; + v3Validator = await deployHelper.deployValidatorStub("RequestValidatorV3Stub"); v3_2Validator = await deployHelper.deployValidatorStub("RequestValidatorV3_2Stub"); authV2Validator = await deployHelper.deployValidatorStub("AuthValidatorStub"); diff --git a/test/verifier/universal-verifier.v3.test.ts b/test/verifier/universal-verifier.v3.test.ts index 49910328..3a17126d 100644 --- a/test/verifier/universal-verifier.v3.test.ts +++ b/test/verifier/universal-verifier.v3.test.ts @@ -91,6 +91,8 @@ describe("Universal Verifier V3 validator", function () { await stateContract.getAddress(), await verifierLib.getAddress(), ); + // set the verifierID of the proof for the test + await universalVerifier.setVerifierID(verifierId); await universalVerifier.addValidatorToWhitelist(await v3Validator.getAddress()); await universalVerifier.connect(); @@ -559,4 +561,35 @@ describe("Universal Verifier V3 validator", function () { ]), ).to.be.rejectedWith(`NullifierSessionIDAlreadyExists(${query2.nullifierSessionID})`); }); + + it("Test set request fails with VerifierIDIsNotValid", async () => { + ({ + ethSigner: signer, + ethSigner2: signer2, + stateContract: state, + v3Validator: v3Validator, + universalVerifier: verifier, + } = await loadFixture(deployContractsFixture)); + + await verifier.setVerifierID(1); + + const query2 = { + ...query, + }; + query2.groupID = 0; + const params = packV3ValidatorParams(query2); + const requestId = 40; + await expect( + verifier.setRequests([ + { + requestId: requestId, + metadata: "metadata", + validator: await v3Validator.getAddress(), + params: params, + }, + ]), + ).to.be.rejectedWith( + `VerifierIDIsNotValid(21929109382993718606847853573861987353620810345503358891473103689157378049, ${await verifier.getVerifierID()})`, + ); + }); }); From 4bac7da44da5ce336f8bc5d50d2a1b0673d1d412 Mon Sep 17 00:00:00 2001 From: daveroga Date: Tue, 14 Jan 2025 18:02:08 +0100 Subject: [PATCH 62/69] add test for authv2 validator --- contracts/interfaces/IAuthValidator.sol | 4 +- contracts/test-helpers/AuthValidatorStub.sol | 3 +- .../validators/AuthV2Validator_forAuth.sol | 193 ++++++++++++++++++ contracts/validators/EthIdentityValidator.sol | 3 +- contracts/verifiers/Verifier.sol | 6 +- helpers/DeployHelper.ts | 48 ++++- helpers/constants.ts | 16 ++ ignition/modules/authV2ValidatorForAuth.ts | 25 +++ test/validators/authv2/index.ts | 139 +++++++++++++ 9 files changed, 431 insertions(+), 6 deletions(-) create mode 100644 contracts/validators/AuthV2Validator_forAuth.sol create mode 100644 ignition/modules/authV2ValidatorForAuth.ts create mode 100644 test/validators/authv2/index.ts diff --git a/contracts/interfaces/IAuthValidator.sol b/contracts/interfaces/IAuthValidator.sol index 3be885f3..4edacc08 100644 --- a/contracts/interfaces/IAuthValidator.sol +++ b/contracts/interfaces/IAuthValidator.sol @@ -19,12 +19,14 @@ interface IAuthValidator { * @param params Request query data of the credential to verify. * @param sender Sender of the proof. * @param state State contract to get identities and gist states to check. + * @param expectedNonce Expected nonce hash calculated to check * @return userID User Id for the auth proof verified. */ function verify( bytes calldata proof, bytes calldata params, address sender, - IState state + IState state, + bytes32 expectedNonce ) external returns (uint256 userID); } diff --git a/contracts/test-helpers/AuthValidatorStub.sol b/contracts/test-helpers/AuthValidatorStub.sol index ec7509f0..8ca0aa2a 100644 --- a/contracts/test-helpers/AuthValidatorStub.sol +++ b/contracts/test-helpers/AuthValidatorStub.sol @@ -24,7 +24,8 @@ contract AuthValidatorStub is IAuthValidator, ERC165 { bytes calldata, bytes calldata, address, - IState + IState, + bytes32 ) external pure override returns (uint256 userID) { return 1; } diff --git a/contracts/validators/AuthV2Validator_forAuth.sol b/contracts/validators/AuthV2Validator_forAuth.sol new file mode 100644 index 00000000..8105470b --- /dev/null +++ b/contracts/validators/AuthV2Validator_forAuth.sol @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.27; + +import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; +import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; +import {IGroth16Verifier} from "../interfaces/IGroth16Verifier.sol"; +import {GenesisUtils} from "../lib/GenesisUtils.sol"; +import {IAuthValidator} from "../interfaces/IAuthValidator.sol"; +import {IState} from "../interfaces/IState.sol"; + +error VerifierAddressShouldNotBeZero(); +error ProofIsNotValid(); +error GistRootIsExpired(); +error ChallengeIsInvalid(); + +/** + * @dev AuthV2Validator validator + */ +contract AuthV2Validator_forAuth is Ownable2StepUpgradeable, IAuthValidator, ERC165 { + struct PubSignals { + uint256 userID; + uint256 challenge; + uint256 gistRoot; + } + + /** + * @dev Version of contract + */ + string public constant VERSION = "1.0.0"; + + string internal constant CIRCUIT_ID = "authV2"; + + /// @dev Main storage structure for the contract + /// @custom:storage-location iden3.storage.AuthV2Validator + struct AuthV2ValidatorStorage { + mapping(string => IGroth16Verifier) _circuitIdToVerifier; + string[] _supportedCircuitIds; + IState state; + uint256 revocationStateExpirationTimeout; + uint256 proofExpirationTimeout; + uint256 gistRootExpirationTimeout; + mapping(string => uint256) _inputNameToIndex; + } + + // keccak256(abi.encode(uint256(keccak256("iden3.storage.AuthV2Validator")) - 1)) + // & ~bytes32(uint256(0xff)); + bytes32 private constant AuthV2ValidatorStorageLocation = + 0x5212d71c1540b1d75013e45246a2b44f2ee9363a102ea02fac1792932b691600; + + /** + * @dev Initialize the contract + * @param _verifierContractAddr Address of the verifier contract + * @param _stateContractAddr Address of the state contract + * @param owner Owner of the contract + */ + function initialize( + address _verifierContractAddr, + address _stateContractAddr, + address owner + ) public initializer { + _initDefaultStateVariables(_stateContractAddr, _verifierContractAddr, CIRCUIT_ID, owner); + } + + /// @dev Get the main storage using assembly to ensure specific storage location + function _getAuthV2ValidatorStorage() private pure returns (AuthV2ValidatorStorage storage $) { + assembly { + $.slot := AuthV2ValidatorStorageLocation + } + } + + function _initDefaultStateVariables( + address _stateContractAddr, + address _verifierContractAddr, + string memory circuitId, + address owner + ) internal { + AuthV2ValidatorStorage storage s = _getAuthV2ValidatorStorage(); + + s.revocationStateExpirationTimeout = 1 hours; + s.proofExpirationTimeout = 1 hours; + s.gistRootExpirationTimeout = 1 hours; + s._supportedCircuitIds = [circuitId]; + s._circuitIdToVerifier[circuitId] = IGroth16Verifier(_verifierContractAddr); + s.state = IState(_stateContractAddr); + __Ownable_init(owner); + } + + /** + * @dev Get the version of the contract + * @return Version of the contract + */ + function version() public pure override returns (string memory) { + return VERSION; + } + + /** + * @dev Parse the public signals + * @param inputs Array of public inputs + * @return Parsed public signals + */ + function parsePubSignals(uint256[] memory inputs) public pure returns (PubSignals memory) { + PubSignals memory pubSignals = PubSignals({ + userID: inputs[0], + challenge: inputs[1], + gistRoot: inputs[2] + }); + + return pubSignals; + } + + /** + * @dev Verify the groth16 proof and check the request query data + * @param proof Proof packed as bytes to verify. + * @param data Request query data of the credential to verify. + * @param sender Sender of the proof. + * @param state State contract to get identities and gist states to check. + * @param expectedNonce Expected nonce hash calculated to check + * @return userID user ID of public signals as result. + */ + function verify( + bytes calldata proof, + // solhint-disable-next-line no-unused-vars + bytes calldata data, + address sender, + IState state, + bytes32 expectedNonce + ) public view override returns (uint256 userID) { + ( + uint256[] memory inputs, + uint256[2] memory a, + uint256[2][2] memory b, + uint256[2] memory c + ) = abi.decode(proof, (uint256[], uint256[2], uint256[2][2], uint256[2])); + + PubSignals memory pubSignals = parsePubSignals(inputs); + _checkGistRoot(pubSignals.userID, pubSignals.gistRoot, state); + _checkChallenge(pubSignals.challenge, expectedNonce); // expectedNonce + _verifyZKP(inputs, a, b, c); + return pubSignals.userID; + } + + function _checkGistRoot(uint256 _id, uint256 _gistRoot, IState _stateContract) internal view { + AuthV2ValidatorStorage storage $ = _getAuthV2ValidatorStorage(); + bytes2 idType = GenesisUtils.getIdType(_id); + uint256 replacedAt = _stateContract.getGistRootReplacedAt(idType, _gistRoot); + + if (replacedAt != 0 && block.timestamp > $.gistRootExpirationTimeout + replacedAt) { + revert GistRootIsExpired(); + } + } + + function _checkChallenge(uint256 challenge, bytes32 expectedNonce) internal pure { + if (challenge != uint256(expectedNonce)) { + revert ChallengeIsInvalid(); + } + } + + /** + * @dev Get the verifier by circuit id + * @param circuitId Circuit id + * @return The verifier + */ + function getVerifierByCircuitId( + string memory circuitId + ) public view virtual returns (IGroth16Verifier) { + return _getAuthV2ValidatorStorage()._circuitIdToVerifier[circuitId]; + } + + function _verifyZKP( + uint256[] memory inputs, + uint256[2] memory a, + uint256[2][2] memory b, + uint256[2] memory c + ) internal view { + IGroth16Verifier verifier = getVerifierByCircuitId(CIRCUIT_ID); + if (verifier == IGroth16Verifier(address(0))) { + revert VerifierAddressShouldNotBeZero(); + } + + // verify that zkp is valid + if (!verifier.verify(a, b, c, inputs)) { + revert ProofIsNotValid(); + } + } + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + return + interfaceId == type(IAuthValidator).interfaceId || super.supportsInterface(interfaceId); + } +} diff --git a/contracts/validators/EthIdentityValidator.sol b/contracts/validators/EthIdentityValidator.sol index 5aac1cf4..6a32cdd9 100644 --- a/contracts/validators/EthIdentityValidator.sol +++ b/contracts/validators/EthIdentityValidator.sol @@ -80,7 +80,8 @@ contract EthIdentityValidator is Ownable2StepUpgradeable, IAuthValidator, ERC165 // solhint-disable-next-line no-unused-vars bytes calldata data, address sender, - IState stateContract + IState stateContract, + bytes32 expectedNonce ) public view override returns (uint256) { uint256 userID = abi.decode(proof, (uint256)); diff --git a/contracts/verifiers/Verifier.sol b/contracts/verifiers/Verifier.sol index e6f2b73e..c4f1f824 100644 --- a/contracts/verifiers/Verifier.sol +++ b/contracts/verifiers/Verifier.sol @@ -442,12 +442,16 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { uint256 userIDFromReponse; AuthTypeData storage authTypeData = $._authMethods[authResponse.authType]; + + bytes32 expectedNonce = keccak256(abi.encode(sender, responses)); + // Authenticate user userIDFromReponse = authTypeData.validator.verify( authResponse.proof, authTypeData.params, sender, - $._state + $._state, + expectedNonce ); if (userIDFromReponse == 0) { diff --git a/helpers/DeployHelper.ts b/helpers/DeployHelper.ts index d968b98c..fcf82180 100644 --- a/helpers/DeployHelper.ts +++ b/helpers/DeployHelper.ts @@ -23,11 +23,12 @@ import { waitNotToInterfereWithHardhatIgnition, } from "./helperUtils"; import { MCPaymentProxyModule } from "../ignition/modules/mcPayment"; +import { AuthV2ValidatorForAuthProxyModule } from "../ignition/modules/authV2ValidatorForAuth"; const SMT_MAX_DEPTH = 64; export type Groth16VerifierType = "mtpV2" | "sigV2" | "v3" | "authV2"; -export type ValidatorType = "mtpV2" | "sigV2" | "v3" | "authV2"; +export type ValidatorType = "mtpV2" | "sigV2" | "v3" | "authV2" | "authV2_forAuth"; export class DeployHelper { constructor( @@ -556,6 +557,28 @@ export class DeployHelper { return g16Verifier; } + getGroth16VerifierTypeFromValidatorType(validatorType: ValidatorType): Groth16VerifierType { + let groth16VerifierType; + switch (validatorType) { + case "mtpV2": + groth16VerifierType = "mtpV2"; + break; + case "sigV2": + groth16VerifierType = "sigV2"; + break; + case "v3": + groth16VerifierType = "v3"; + break; + case "authV2": + groth16VerifierType = "authV2"; + break; + case "authV2_forAuth": + groth16VerifierType = "authV2"; + break; + } + return groth16VerifierType; + } + getGroth16VerifierWrapperName(groth16VerifierType: Groth16VerifierType): string { let g16VerifierContractWrapperName; switch (groth16VerifierType) { @@ -640,6 +663,7 @@ export class DeployHelper { validatorType: ValidatorType, stateAddress: string, deployStrategy: "basic" | "create2" = "basic", + groth16VerifierWrapperAddress?: string, ): Promise<{ state: any; groth16VerifierWrapper: any; @@ -649,6 +673,7 @@ export class DeployHelper { validatorType, stateAddress, deployStrategy, + groth16VerifierWrapperAddress, ); const state = await ethers.getContractAt("State", stateAddress); @@ -663,6 +688,7 @@ export class DeployHelper { validatorType: ValidatorType, stateAddress: string, deployStrategy: "basic" | "create2" = "basic", + groth16VerifierWrapperAddress?: string, ): Promise<{ state: any; validator: any; @@ -684,6 +710,9 @@ export class DeployHelper { case "authV2": validatorContractName = "AuthV2Validator"; break; + case "authV2_forAuth": + validatorContractName = "AuthV2Validator_forAuth"; + break; } let validator; @@ -704,6 +733,9 @@ export class DeployHelper { case "authV2": validatorModule = AuthV2ValidatorProxyModule; break; + case "authV2_forAuth": + validatorContractName = AuthV2ValidatorForAuthProxyModule; + break; } await waitNotToInterfereWithHardhatIgnition(undefined); @@ -733,7 +765,19 @@ export class DeployHelper { } } - const groth16VerifierWrapper = await this.deployGroth16VerifierWrapper(validatorType); + let groth16VerifierWrapper; + if (!groth16VerifierWrapperAddress) { + groth16VerifierWrapper = await this.deployGroth16VerifierWrapper( + this.getGroth16VerifierTypeFromValidatorType(validatorType), + ); + } else { + groth16VerifierWrapper = await ethers.getContractAt( + this.getGroth16VerifierWrapperName( + this.getGroth16VerifierTypeFromValidatorType(validatorType), + ), + groth16VerifierWrapperAddress, + ); + } const ValidatorFactory = await ethers.getContractFactory(validatorContractName); const Create2AddressAnchorFactory = await ethers.getContractFactory( diff --git a/helpers/constants.ts b/helpers/constants.ts index 28ae61b3..e06a3065 100644 --- a/helpers/constants.ts +++ b/helpers/constants.ts @@ -209,6 +209,22 @@ export const contractsInfo = Object.freeze({ libraries: {}, }, }, + VALIDATOR_AUTH_V2_FOR_AUTH: { + name: "AuthV2Validator_forAuth", + version: "1.0.0", + unifiedAddress: "", + create2Calldata: ethers.hexlify(ethers.toUtf8Bytes("iden3.create2.AuthV2Validator_forAuth")), + verificationOpts: { + constructorArgsImplementation: [], + constructorArgsProxy: [ + "0x56fF81aBB5cdaC478bF236db717e4976b2ff841e", + "0xae15d2023a76174a940cbb2b7f44012c728b9d74", + "0x6964656e332e637265617465322e556e6976657273616c5665726966696572", + ], + constructorArgsProxyAdmin: ["0xAe15d2023A76174a940cbb2b7F44012C728B9d74"], + libraries: {}, + }, + }, IDENTITY_TREE_STORE: { name: "IdentityTreeStore", version: "1.1.0", diff --git a/ignition/modules/authV2ValidatorForAuth.ts b/ignition/modules/authV2ValidatorForAuth.ts new file mode 100644 index 00000000..0e7b0f08 --- /dev/null +++ b/ignition/modules/authV2ValidatorForAuth.ts @@ -0,0 +1,25 @@ +import { buildModule } from "@nomicfoundation/hardhat-ignition/modules"; +import { contractsInfo } from "../../helpers/constants"; + +export const AuthV2ValidatorForAuthProxyModule = buildModule( + "AuthV2ValidatorForAuthProxyModule", + (m) => { + const proxyAdminOwner = m.getAccount(0); + + // This contract is supposed to be deployed to the same address across many networks, + // so the first implementation address is a dummy contract that does nothing but accepts any calldata. + // Therefore, it is a mechanism to deploy TransparentUpgradeableProxy contract + // with constant constructor arguments, so predictable init bytecode and predictable CREATE2 address. + // Subsequent upgrades are supposed to switch this proxy to the real implementation. + + const proxy = m.contract("TransparentUpgradeableProxy", [ + contractsInfo.CREATE2_ADDRESS_ANCHOR.unifiedAddress, + proxyAdminOwner, + contractsInfo.VALIDATOR_AUTH_V2_FOR_AUTH.create2Calldata, + ]); + + const proxyAdminAddress = m.readEventArgument(proxy, "AdminChanged", "newAdmin"); + const proxyAdmin = m.contractAt("ProxyAdmin", proxyAdminAddress); + return { proxyAdmin, proxy }; + }, +); diff --git a/test/validators/authv2/index.ts b/test/validators/authv2/index.ts new file mode 100644 index 00000000..b25644e0 --- /dev/null +++ b/test/validators/authv2/index.ts @@ -0,0 +1,139 @@ +import { expect } from "chai"; +import { prepareInputs, publishState } from "../../utils/state-utils"; +import { DeployHelper } from "../../../helpers/DeployHelper"; +import { loadFixture } from "@nomicfoundation/hardhat-toolbox/network-helpers"; +import { packZKProof } from "../../utils/packData"; +import { time } from "@nomicfoundation/hardhat-network-helpers"; + +const testCases: any[] = [ + { + name: "Validate AuthV2", + sender: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + stateTransitions: [ + require("../common-data/issuer_from_genesis_state_to_first_transition_v3.json"), + ], + userID: 23273167900576580892722615617815475823351560716009055944677723144398443009n, + }, + { + name: "Validation of challenge failed", + sender: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + challenge: "0x00", + stateTransitions: [ + require("../common-data/issuer_from_genesis_state_to_first_transition_v3.json"), + ], + userID: 23273167900576580892722615617815475823351560716009055944677723144398443009n, + errorMessage: "ChallengeIsInvalid()", + }, + { + name: "Validation of Gist root not found", + sender: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + challenge: "0x00", + stateTransitions: [ + require("../common-data/issuer_from_genesis_state_to_first_transition_v3.json"), + ], + userID: 23273167900576580892722615617815475823351560716009055944677723144398443009n, + gistRoot: 2n, + errorMessage: "GIST root entry not found", + }, +]; + +describe("Auth V2 Validator", function () { + let state: any, authV2validator; + + async function deployContractsFixture() { + const deployHelper = await DeployHelper.initialize(null, true); + + const { state: stateContract } = await deployHelper.deployStateWithLibraries(["0x0212"]); + + const verifierStub = await deployHelper.deployGroth16VerifierValidatorStub(); + + const contracts = await deployHelper.deployValidatorContractsWithVerifiers( + "authV2_forAuth", + await stateContract.getAddress(), + "basic", + await verifierStub.getAddress(), + ); + const validator = contracts.validator; + + return { + stateContract, + validator, + }; + } + + beforeEach(async () => { + ({ stateContract: state, validator: authV2validator } = + await loadFixture(deployContractsFixture)); + }); + + for (const test of testCases) { + it(test.name, async function () { + this.timeout(50000); + + for (let i = 0; i < test.stateTransitions.length; i++) { + if (test.stateTransitionDelayMs) { + await time.increase(test.stateTransitionDelayMs); + } + await publishState(state, test.stateTransitions[i]); + } + + const challenge = + test.challenge || "0x0000000000000000000000000000000000000000000000000000000000000001"; + + const proof = { + pub_signals: [test.userID, challenge, test.gistRoot || "0"], + proof: { + pi_a: ["0", "0", "0"], + pi_b: [ + ["0", "0"], + ["0", "0"], + ["0", "0"], + ], + pi_c: ["0", "0", "0"], + protocol: "groth16", + curve: "bn128", + }, + }; + + const { inputs, pi_a, pi_b, pi_c } = prepareInputs(proof); + + const data = "0x00"; + + // Check verify function + const zkProof = packZKProof(inputs, pi_a, pi_b, pi_c); + const expectedNonce = "0x0000000000000000000000000000000000000000000000000000000000000001"; + + if (test.errorMessage) { + await expect( + authV2validator.verify( + zkProof, + data, + test.sender, + await state.getAddress(), + expectedNonce, + ), + ).to.be.rejectedWith(test.errorMessage); + } else if (test.errorMessage === "") { + await expect( + authV2validator.verify( + zkProof, + data, + test.sender, + await state.getAddress(), + expectedNonce, + ), + ).to.be.reverted; + } else { + const userID = await authV2validator.verify( + zkProof, + data, + test.sender, + await state.getAddress(), + expectedNonce, + ); + + expect(userID).to.be.equal(test.userID); + } + }); + } +}); From 1e9c474d2b4ea611ea4b1db8aa9bdaa80e9eceba Mon Sep 17 00:00:00 2001 From: Andriian Chestnykh Date: Wed, 15 Jan 2025 07:23:48 +0000 Subject: [PATCH 63/69] Feature/pid 2720 implement linked multi query validator (#328) * WIP * WIP 2: LinkedMultiQueryValidator * Fix compilation * Fix compilation after merge * First working test * Fix after merge * New Verifier tests * fix ownable upgradeable like the other validator contracts * fix solhint compiler version >=0.7.0 <0.9.0 does not satisfy the >=0.8.4 semver requirement --------- Co-authored-by: daveroga --- .../Groth16VerifierLinkedMultiQuery10.sol | 378 ++++++++++++++++++ .../groth16-verifiers/Groth16VerifierMTP.sol | 2 +- .../groth16-verifiers/Groth16VerifierSig.sol | 2 +- .../Groth16VerifierStateTransition.sol | 2 +- .../groth16-verifiers/Groth16VerifierV3.sol | 2 +- .../test-helpers/RequestValidatorStub.sol | 56 +++ .../test-helpers/VerifierTestWrapper.sol | 11 + .../validators/LinkedMultiQueryValidator.sol | 171 ++++++++ contracts/verifiers/Verifier.sol | 2 +- hardhat.config.ts | 9 + test/utils/validator-pack-utils.ts | 29 ++ .../linked-multi-query.test.ts | 66 +++ test/verifier/verifer.test.ts | 189 +++++++++ 13 files changed, 914 insertions(+), 5 deletions(-) create mode 100644 contracts/lib/groth16-verifiers/Groth16VerifierLinkedMultiQuery10.sol create mode 100644 contracts/test-helpers/RequestValidatorStub.sol create mode 100644 contracts/test-helpers/VerifierTestWrapper.sol create mode 100644 contracts/validators/LinkedMultiQueryValidator.sol create mode 100644 test/validators/linked-multi-query/linked-multi-query.test.ts create mode 100644 test/verifier/verifer.test.ts diff --git a/contracts/lib/groth16-verifiers/Groth16VerifierLinkedMultiQuery10.sol b/contracts/lib/groth16-verifiers/Groth16VerifierLinkedMultiQuery10.sol new file mode 100644 index 00000000..618fec2a --- /dev/null +++ b/contracts/lib/groth16-verifiers/Groth16VerifierLinkedMultiQuery10.sol @@ -0,0 +1,378 @@ +// SPDX-License-Identifier: GPL-3.0 +/* + Copyright 2021 0KIMS association. + + This file is generated with [snarkJS](https://github.com/iden3/snarkjs). + + snarkJS is a free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + snarkJS is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU General Public License + along with snarkJS. If not, see . +*/ + +pragma solidity >=0.8.4 <0.9.0; + +contract Groth16VerifierLinkedMultiQuery10 { + // Scalar field size + uint256 constant r = + 21888242871839275222246405745257275088548364400416034343698204186575808495617; + // Base field size + uint256 constant q = + 21888242871839275222246405745257275088696311157297823662689037894645226208583; + + // Verification Key data + uint256 constant alphax = + 20491192805390485299153009773594534940189261866228447918068658471970481763042; + uint256 constant alphay = + 9383485363053290200918347156157836566562967994039712273449902621266178545958; + uint256 constant betax1 = + 4252822878758300859123897981450591353533073413197771768651442665752259397132; + uint256 constant betax2 = + 6375614351688725206403948262868962793625744043794305715222011528459656738731; + uint256 constant betay1 = + 21847035105528745403288232691147584728191162732299865338377159692350059136679; + uint256 constant betay2 = + 10505242626370262277552901082094356697409835680220590971873171140371331206856; + uint256 constant gammax1 = + 11559732032986387107991004021392285783925812861821192530917403151452391805634; + uint256 constant gammax2 = + 10857046999023057135944570762232829481370756359578518086990519993285655852781; + uint256 constant gammay1 = + 4082367875863433681332203403145435568316851327593401208105741076214120093531; + uint256 constant gammay2 = + 8495653923123431417604973247489272438418190587263600148770280649306958101930; + uint256 constant deltax1 = + 21291812099108226492090526531856517811536779320610647433368462710569987629667; + uint256 constant deltax2 = + 7422706200222071207612539875145077133720724588635722111352933730752292151748; + uint256 constant deltay1 = + 4989595936857753080590763475667045909669577855455429661996541896488894355086; + uint256 constant deltay2 = + 7804873025069228507657362942988295674141728266159164738606678724879837766865; + + uint256 constant IC0x = + 13015581866057939615044081583045919131345394140954806467311037651824926809137; + uint256 constant IC0y = + 19199257274325478520723856677156665582913726904171496384388386979514146830385; + + uint256 constant IC1x = + 10836696331615958613375912191598585473596944014351349205151216670472007047104; + uint256 constant IC1y = + 18151565789544339802944925826820745913919794940894597097945581067223456503290; + + uint256 constant IC2x = + 2521454347017845179812204837744809576676502631733513603735836135568584172517; + uint256 constant IC2y = + 3501399375712891534416956015871266687993382179223425820927444909719773784000; + + uint256 constant IC3x = + 12902759004757033464484224633223316629259640877759091071934246018491120158014; + uint256 constant IC3y = + 10530595221003018740166770887238121536641717782983700434860957393537377462688; + + uint256 constant IC4x = + 14396108820288481954520230152903593378784954979791566628290313535465222312673; + uint256 constant IC4y = + 3784848455654095039400974766873749327058883956069664158606371742679004481262; + + uint256 constant IC5x = + 7306155850433441454520730583879250450768280776704357773574132692617582253779; + uint256 constant IC5y = + 12455376969135234446294310531099544581755547805028939854891661001898814277398; + + uint256 constant IC6x = + 11045807683719739644808142033270177544020089289619098035168588942815058484418; + uint256 constant IC6y = + 13092458821751534997862164199468151201172454812384435257314927054359497548919; + + uint256 constant IC7x = + 3945155015672178941829711782116513676998120686639456211803723484217806236983; + uint256 constant IC7y = + 17000849064520210669364248028590910378150860582962698015157060837710488469296; + + uint256 constant IC8x = + 3371159865674642649301678639163412357452907069109259029303253765825005981296; + uint256 constant IC8y = + 17978892392839452667480179479711336569955118316034726238482987691834590035299; + + uint256 constant IC9x = + 11542257334672837016873113655269938882192710424347800558282401911743012652878; + uint256 constant IC9y = + 12907194003694380463359872849585227119158194436988229150165768636934702126758; + + uint256 constant IC10x = + 10924487014413455809857087289047835940102808812220354481726612874603682190090; + uint256 constant IC10y = + 7831687065967848887670012871107845702198797882152208670133118946896871226058; + + uint256 constant IC11x = + 7434324710583028490423228486594320155747502346191849950845882352672633711682; + uint256 constant IC11y = + 20237565512054319455974247061341945536694894321183696504602960667650315929240; + + uint256 constant IC12x = + 18538725364743735638088040944559875499513412657296085409993286885226847288061; + uint256 constant IC12y = + 9582409580986559864080535927664970006439905143280044223756970487389536717215; + + uint256 constant IC13x = + 20264513570341389963378417977695934692604282552694130252994774928809623697364; + uint256 constant IC13y = + 14533726145537386199589432995532247040447926239858992245580191210278119127004; + + uint256 constant IC14x = + 8476417330972823302591981075156886824420339451306440343277486706738356438208; + uint256 constant IC14y = + 6260247764163090750873453137463697081422194897573167939251123384332056849696; + + uint256 constant IC15x = + 9232610854787193722189606473710397066228883352893024292590345065129445621563; + uint256 constant IC15y = + 384290290273240264153101053284951304021711308740663001318785794084360007577; + + uint256 constant IC16x = + 3491961004949745137370385087237392443750482573691263397701684161046110198184; + uint256 constant IC16y = + 1568780763134063166674421584506781975264602795939028226979106023977248085290; + + uint256 constant IC17x = + 12444732225942273896337031259862393370698023086285228914834280452014112643161; + uint256 constant IC17y = + 13010200024976169996641848839004968930199576297459478124394712223188568536531; + + uint256 constant IC18x = + 5485347718930494855998707838674976743642710691132458017258215937084085538847; + uint256 constant IC18y = + 10378472084668591994591260053208762501011439718776689746311337213589094600271; + + uint256 constant IC19x = + 16281851437925747606447202619966856801129306067276497311975142310490498270139; + uint256 constant IC19y = + 14346286064901915767631774726603470883429271385136028031111962221938307746929; + + uint256 constant IC20x = + 5384449404453968001026035016043669667029614231397778442819787400026310102928; + uint256 constant IC20y = + 6769642349046094562869967845055001956030603469432686907060688372618181464002; + + uint256 constant IC21x = + 5860343174900754573398191252758066025871079545674098835379774290764368632050; + uint256 constant IC21y = + 6829739009799169739175478771283057030411977569254623439372350123075044870223; + + uint256 constant IC22x = + 20671793756419652415555536775628683454807378137828500470382577112168552141631; + uint256 constant IC22y = + 13803157598239242950536679861122859248896733922534199075709866438331899522705; + + // Memory data + uint16 constant pVk = 0; + uint16 constant pPairing = 128; + + uint16 constant pLastMem = 896; + + function verifyProof( + uint[2] calldata _pA, + uint[2][2] calldata _pB, + uint[2] calldata _pC, + uint[22] calldata _pubSignals + ) public view returns (bool) { + assembly { + function checkField(v) { + if iszero(lt(v, q)) { + mstore(0, 0) + return(0, 0x20) + } + } + + // G1 function to multiply a G1 value(x,y) to value in an address + function g1_mulAccC(pR, x, y, s) { + let success + let mIn := mload(0x40) + mstore(mIn, x) + mstore(add(mIn, 32), y) + mstore(add(mIn, 64), s) + + success := staticcall(sub(gas(), 2000), 7, mIn, 96, mIn, 64) + + if iszero(success) { + mstore(0, 0) + return(0, 0x20) + } + + mstore(add(mIn, 64), mload(pR)) + mstore(add(mIn, 96), mload(add(pR, 32))) + + success := staticcall(sub(gas(), 2000), 6, mIn, 128, pR, 64) + + if iszero(success) { + mstore(0, 0) + return(0, 0x20) + } + } + + function checkPairing(pA, pB, pC, pubSignals, pMem) -> isOk { + let _pPairing := add(pMem, pPairing) + let _pVk := add(pMem, pVk) + + mstore(_pVk, IC0x) + mstore(add(_pVk, 32), IC0y) + + // Compute the linear combination vk_x + + g1_mulAccC(_pVk, IC1x, IC1y, calldataload(add(pubSignals, 0))) + + g1_mulAccC(_pVk, IC2x, IC2y, calldataload(add(pubSignals, 32))) + + g1_mulAccC(_pVk, IC3x, IC3y, calldataload(add(pubSignals, 64))) + + g1_mulAccC(_pVk, IC4x, IC4y, calldataload(add(pubSignals, 96))) + + g1_mulAccC(_pVk, IC5x, IC5y, calldataload(add(pubSignals, 128))) + + g1_mulAccC(_pVk, IC6x, IC6y, calldataload(add(pubSignals, 160))) + + g1_mulAccC(_pVk, IC7x, IC7y, calldataload(add(pubSignals, 192))) + + g1_mulAccC(_pVk, IC8x, IC8y, calldataload(add(pubSignals, 224))) + + g1_mulAccC(_pVk, IC9x, IC9y, calldataload(add(pubSignals, 256))) + + g1_mulAccC(_pVk, IC10x, IC10y, calldataload(add(pubSignals, 288))) + + g1_mulAccC(_pVk, IC11x, IC11y, calldataload(add(pubSignals, 320))) + + g1_mulAccC(_pVk, IC12x, IC12y, calldataload(add(pubSignals, 352))) + + g1_mulAccC(_pVk, IC13x, IC13y, calldataload(add(pubSignals, 384))) + + g1_mulAccC(_pVk, IC14x, IC14y, calldataload(add(pubSignals, 416))) + + g1_mulAccC(_pVk, IC15x, IC15y, calldataload(add(pubSignals, 448))) + + g1_mulAccC(_pVk, IC16x, IC16y, calldataload(add(pubSignals, 480))) + + g1_mulAccC(_pVk, IC17x, IC17y, calldataload(add(pubSignals, 512))) + + g1_mulAccC(_pVk, IC18x, IC18y, calldataload(add(pubSignals, 544))) + + g1_mulAccC(_pVk, IC19x, IC19y, calldataload(add(pubSignals, 576))) + + g1_mulAccC(_pVk, IC20x, IC20y, calldataload(add(pubSignals, 608))) + + g1_mulAccC(_pVk, IC21x, IC21y, calldataload(add(pubSignals, 640))) + + g1_mulAccC(_pVk, IC22x, IC22y, calldataload(add(pubSignals, 672))) + + // -A + mstore(_pPairing, calldataload(pA)) + mstore(add(_pPairing, 32), mod(sub(q, calldataload(add(pA, 32))), q)) + + // B + mstore(add(_pPairing, 64), calldataload(pB)) + mstore(add(_pPairing, 96), calldataload(add(pB, 32))) + mstore(add(_pPairing, 128), calldataload(add(pB, 64))) + mstore(add(_pPairing, 160), calldataload(add(pB, 96))) + + // alpha1 + mstore(add(_pPairing, 192), alphax) + mstore(add(_pPairing, 224), alphay) + + // beta2 + mstore(add(_pPairing, 256), betax1) + mstore(add(_pPairing, 288), betax2) + mstore(add(_pPairing, 320), betay1) + mstore(add(_pPairing, 352), betay2) + + // vk_x + mstore(add(_pPairing, 384), mload(add(pMem, pVk))) + mstore(add(_pPairing, 416), mload(add(pMem, add(pVk, 32)))) + + // gamma2 + mstore(add(_pPairing, 448), gammax1) + mstore(add(_pPairing, 480), gammax2) + mstore(add(_pPairing, 512), gammay1) + mstore(add(_pPairing, 544), gammay2) + + // C + mstore(add(_pPairing, 576), calldataload(pC)) + mstore(add(_pPairing, 608), calldataload(add(pC, 32))) + + // delta2 + mstore(add(_pPairing, 640), deltax1) + mstore(add(_pPairing, 672), deltax2) + mstore(add(_pPairing, 704), deltay1) + mstore(add(_pPairing, 736), deltay2) + + let success := staticcall(sub(gas(), 2000), 8, _pPairing, 768, _pPairing, 0x20) + + isOk := and(success, mload(_pPairing)) + } + + let pMem := mload(0x40) + mstore(0x40, add(pMem, pLastMem)) + + // Validate that all evaluations ∈ F + + checkField(calldataload(add(_pubSignals, 0))) + + checkField(calldataload(add(_pubSignals, 32))) + + checkField(calldataload(add(_pubSignals, 64))) + + checkField(calldataload(add(_pubSignals, 96))) + + checkField(calldataload(add(_pubSignals, 128))) + + checkField(calldataload(add(_pubSignals, 160))) + + checkField(calldataload(add(_pubSignals, 192))) + + checkField(calldataload(add(_pubSignals, 224))) + + checkField(calldataload(add(_pubSignals, 256))) + + checkField(calldataload(add(_pubSignals, 288))) + + checkField(calldataload(add(_pubSignals, 320))) + + checkField(calldataload(add(_pubSignals, 352))) + + checkField(calldataload(add(_pubSignals, 384))) + + checkField(calldataload(add(_pubSignals, 416))) + + checkField(calldataload(add(_pubSignals, 448))) + + checkField(calldataload(add(_pubSignals, 480))) + + checkField(calldataload(add(_pubSignals, 512))) + + checkField(calldataload(add(_pubSignals, 544))) + + checkField(calldataload(add(_pubSignals, 576))) + + checkField(calldataload(add(_pubSignals, 608))) + + checkField(calldataload(add(_pubSignals, 640))) + + checkField(calldataload(add(_pubSignals, 672))) + + checkField(calldataload(add(_pubSignals, 704))) + + // Validate all evaluations + let isValid := checkPairing(_pA, _pB, _pC, _pubSignals, pMem) + + mstore(0, isValid) + return(0, 0x20) + } + } +} diff --git a/contracts/lib/groth16-verifiers/Groth16VerifierMTP.sol b/contracts/lib/groth16-verifiers/Groth16VerifierMTP.sol index de520abf..5c3568f0 100644 --- a/contracts/lib/groth16-verifiers/Groth16VerifierMTP.sol +++ b/contracts/lib/groth16-verifiers/Groth16VerifierMTP.sol @@ -18,7 +18,7 @@ along with snarkJS. If not, see . */ -pragma solidity >=0.7.0 <0.9.0; +pragma solidity >=0.8.4 <0.9.0; contract Groth16VerifierMTP { // Scalar field size diff --git a/contracts/lib/groth16-verifiers/Groth16VerifierSig.sol b/contracts/lib/groth16-verifiers/Groth16VerifierSig.sol index 36aea424..da97c6eb 100644 --- a/contracts/lib/groth16-verifiers/Groth16VerifierSig.sol +++ b/contracts/lib/groth16-verifiers/Groth16VerifierSig.sol @@ -18,7 +18,7 @@ along with snarkJS. If not, see . */ -pragma solidity >=0.7.0 <0.9.0; +pragma solidity >=0.8.4 <0.9.0; contract Groth16VerifierSig { // Scalar field size diff --git a/contracts/lib/groth16-verifiers/Groth16VerifierStateTransition.sol b/contracts/lib/groth16-verifiers/Groth16VerifierStateTransition.sol index 6bfdd28a..52d567b6 100644 --- a/contracts/lib/groth16-verifiers/Groth16VerifierStateTransition.sol +++ b/contracts/lib/groth16-verifiers/Groth16VerifierStateTransition.sol @@ -18,7 +18,7 @@ along with snarkJS. If not, see . */ -pragma solidity >=0.7.0 <0.9.0; +pragma solidity >=0.8.4 <0.9.0; import "../../interfaces/IStateTransitionVerifier.sol"; diff --git a/contracts/lib/groth16-verifiers/Groth16VerifierV3.sol b/contracts/lib/groth16-verifiers/Groth16VerifierV3.sol index 1b6d8b6f..48a9571f 100644 --- a/contracts/lib/groth16-verifiers/Groth16VerifierV3.sol +++ b/contracts/lib/groth16-verifiers/Groth16VerifierV3.sol @@ -18,7 +18,7 @@ along with snarkJS. If not, see . */ -pragma solidity >=0.7.0 <0.9.0; +pragma solidity >=0.8.4 <0.9.0; contract Groth16VerifierV3 { // Scalar field size diff --git a/contracts/test-helpers/RequestValidatorStub.sol b/contracts/test-helpers/RequestValidatorStub.sol new file mode 100644 index 00000000..99bdb5e7 --- /dev/null +++ b/contracts/test-helpers/RequestValidatorStub.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.27; + +import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; +import {IRequestValidator} from "../interfaces/IRequestValidator.sol"; +import {IState} from "../interfaces/IState.sol"; + +/** + * @dev RequestValidatorV2Stub validator + */ +contract RequestValidatorStub is IRequestValidator, ERC165 { + string public constant VERSION = "1.0.0-stub"; + + IRequestValidator.RequestParams private requestParams; + IRequestValidator.ResponseField[] private responseFields; + + function version() public pure override returns (string memory) { + return VERSION; + } + + function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + return + interfaceId == type(IRequestValidator).interfaceId || + super.supportsInterface(interfaceId); + } + + function verify( + bytes calldata, + bytes calldata, + address, + IState + ) external view returns (IRequestValidator.ResponseField[] memory) { + return responseFields; + } + + function stub_setVerifyResults( + IRequestValidator.ResponseField[] calldata _responseFields + ) external { + delete responseFields; + for (uint256 i = 0; i < _responseFields.length; i++) { + responseFields.push(_responseFields[i]); + } + } + + function getRequestParams( + bytes calldata + ) external view returns (IRequestValidator.RequestParams memory) { + return requestParams; + } + + function stub_setRequestParams( + IRequestValidator.RequestParams calldata _requestParams + ) external { + requestParams = _requestParams; + } +} diff --git a/contracts/test-helpers/VerifierTestWrapper.sol b/contracts/test-helpers/VerifierTestWrapper.sol new file mode 100644 index 00000000..a95717ce --- /dev/null +++ b/contracts/test-helpers/VerifierTestWrapper.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.10; + +import {Verifier} from "../verifiers/Verifier.sol"; +import {IState} from "../interfaces/IState.sol"; + +contract VerifierTestWrapper is Verifier { + function initialize(IState state) public initializer { + __Verifier_init(state); + } +} diff --git a/contracts/validators/LinkedMultiQueryValidator.sol b/contracts/validators/LinkedMultiQueryValidator.sol new file mode 100644 index 00000000..ca9abd03 --- /dev/null +++ b/contracts/validators/LinkedMultiQueryValidator.sol @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.10; + +import {ICircuitValidator} from "../interfaces/ICircuitValidator.sol"; +import {IGroth16Verifier} from "../interfaces/IGroth16Verifier.sol"; +import {IRequestValidator} from "../interfaces/IRequestValidator.sol"; +import {IState} from "../interfaces/IState.sol"; +import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; +import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; + +contract LinkedMultiQueryValidator is IRequestValidator, Ownable2StepUpgradeable { + // This should be limited to the real number of queries in which operator != 0 + struct Query { + uint256[] claimPathKey; + uint256[] operator; // when checking SD take operator from here + uint256[] slotIndex; + uint256[][] value; + uint256[] queryHash; + string[] circuitIds; // TODO should it be here that way? + uint256 groupID; + uint256 verifierID; + } + + /// @dev Main storage structure for the contract + /// @custom:storage-location iden3.storage.LinkedMultiQueryValidatorBaseStorage + struct LinkedMultiQueryValidatorBaseStorage { + //TODO do we need this mapping? + mapping(string circuitName => IGroth16Verifier) _supportedCircuits; + string[] _supportedCircuitIds; + } + + // keccak256(abi.encode(uint256(keccak256("iden3.storage.LinkedMultiQueryValidatorBaseStorage")) - 1)) + // & ~bytes32(uint256(0xff)); + bytes32 private constant LinkedMultiQueryValidatorBaseStorageLocation = + 0x2a12018e5edfc1fb8de8bb271d40c512afd1e683a34fc602c9c8e5cfd4529700; + + /// @dev Get the main storage using assembly to ensure specific storage location + function _getLinkedMultiQueryValidatorBaseStorage() + private + pure + returns (LinkedMultiQueryValidatorBaseStorage storage $) + { + assembly { + $.slot := LinkedMultiQueryValidatorBaseStorageLocation + } + } + + function getRequestParams( + bytes calldata params + ) external view override returns (IRequestValidator.RequestParams memory) { + Query memory query = abi.decode(params, (Query)); + return IRequestValidator.RequestParams(query.groupID, query.verifierID, 0); + } + + struct PubSignals { + uint256 linkID; + uint256 merklized; + uint256[] operatorOutput; + uint256[] circuitQueryHash; + } + + string public constant VERSION = "1.0.0-beta"; + string internal constant CIRCUIT_ID = "linkedMultiQuery10"; + uint256 internal constant QUERIES_NUMBER = 10; + + function version() external view override returns (string memory) { + return VERSION; + } + + function initialize(address _groth16VerifierContractAddr, address owner) public initializer { + LinkedMultiQueryValidatorBaseStorage storage $ = _getLinkedMultiQueryValidatorBaseStorage(); + $._supportedCircuits[CIRCUIT_ID] = IGroth16Verifier(_groth16VerifierContractAddr); + $._supportedCircuitIds.push(CIRCUIT_ID); + } + + function verify( + bytes calldata proof, + bytes calldata data, + address sender, + IState state + ) external returns (IRequestValidator.ResponseField[] memory) { + LinkedMultiQueryValidatorBaseStorage storage $ = _getLinkedMultiQueryValidatorBaseStorage(); + + // 0. Parse query + Query memory query = abi.decode(data, (Query)); + + // 1. Parse public signals + ( + uint256[] memory inputs, + uint256[2] memory a, + uint256[2][2] memory b, + uint256[2] memory c + ) = abi.decode(proof, (uint256[], uint256[2], uint256[2][2], uint256[2])); + + PubSignals memory pubSignals = _parsePubSignals(inputs, QUERIES_NUMBER); + + // 1. Verify circuit query hash for 10 + // TODO check + $._supportedCircuits[CIRCUIT_ID].verify(a, b, c, inputs); + _checkQueryHash(query, pubSignals); + _checkGroupId(query.groupID); + + return _getSpecialSignals(pubSignals, query); + } + + error InvalidQueryHash(uint256 expectedQueryHash, uint256 actualQueryHash); + error InvalidGroupID(uint256 groupID); + + function _checkGroupId(uint256 groupID) internal pure { + if (groupID == 0) { + revert InvalidGroupID(groupID); + } + } + + function _checkQueryHash(Query memory query, PubSignals memory pubSignals) internal pure { + for (uint256 i = 0; i < query.operator.length; i++) { + if (query.queryHash[i] != pubSignals.circuitQueryHash[i]) { + revert InvalidQueryHash(query.queryHash[i], pubSignals.circuitQueryHash[i]); + } + } + } + + function _parsePubSignals( + uint256[] memory inputs, + uint256 queriesNumber + ) internal pure returns (PubSignals memory) { + PubSignals memory pubSignals = PubSignals( + 0, + 0, + new uint256[](queriesNumber), + new uint256[](queriesNumber) + ); + + pubSignals.linkID = inputs[0]; + pubSignals.merklized = inputs[1]; + for (uint256 i = 0; i < queriesNumber; i++) { + pubSignals.operatorOutput[i] = inputs[2 + i]; + pubSignals.circuitQueryHash[i] = inputs[2 + queriesNumber + i]; + } + return pubSignals; + } + + function _getSpecialSignals( + PubSignals memory pubSignals, + Query memory query + ) internal pure returns (ResponseField[] memory) { + uint256 operatorCount = 0; + for (uint256 i = 0; i < query.operator.length; i++) { + if (query.operator[i] == 16) { + operatorCount++; + } + } + + uint256 n = 1; + ResponseField[] memory rfs = new ResponseField[](n + operatorCount); + rfs[0] = ResponseField("linkID", pubSignals.linkID); + + uint256 m = 1; + for (uint256 i = 0; i < query.operator.length; i++) { + // TODO consider if can be more gas efficient + if (query.operator[i] == 16) { + rfs[m++] = ResponseField( + string(abi.encodePacked("operatorOutput", Strings.toString(i))), + pubSignals.operatorOutput[i] + ); + } + } + + return rfs; + } +} diff --git a/contracts/verifiers/Verifier.sol b/contracts/verifiers/Verifier.sol index c4f1f824..00b8b096 100644 --- a/contracts/verifiers/Verifier.sol +++ b/contracts/verifiers/Verifier.sol @@ -326,7 +326,7 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { if (s._nullifierSessionIDs[nullifierSessionID] != 0) { revert NullifierSessionIDAlreadyExists(nullifierSessionID); } - s._nullifierSessionIDs[nullifierSessionID] = request.requestId; + s._nullifierSessionIDs[nullifierSessionID] = nullifierSessionID; } } diff --git a/hardhat.config.ts b/hardhat.config.ts index 4c67dc28..5450598d 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -51,6 +51,15 @@ const config: HardhatUserConfig = { }, }, }, + "contracts/test-helpers/VerifierTestWrapper.sol": { + version: "0.8.27", + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + }, + }, "contracts/test-helpers/EmbeddedVerifierWrapper.sol": { version: "0.8.27", settings: { diff --git a/test/utils/validator-pack-utils.ts b/test/utils/validator-pack-utils.ts index ed0d4b37..900c6386 100644 --- a/test/utils/validator-pack-utils.ts +++ b/test/utils/validator-pack-utils.ts @@ -73,3 +73,32 @@ export function packV3ValidatorParams(query: any, allowedIssuers: any[] = []): s ], ); } + +export function packLinkedMultiQueryValidatorParams(query: any): string { + return abiCoder.encode( + [ + "tuple(" + + "uint256[] claimPathKey," + + "uint256[] operator," + + "uint256[] slotIndex," + + "uint256[][] value," + + "uint256[] queryHash," + + "string[] circuitIds," + + "uint256 groupID," + + "uint256 verifierID," + + ")", + ], + [ + { + claimPathKey: query.claimPathKey, + operator: query.operator, + slotIndex: query.slotIndex, + value: query.value, + queryHash: query.queryHash, + circuitIds: query.circuitIds, + groupID: query.groupID, + verifierID: query.verifierID, + }, + ], + ); +} diff --git a/test/validators/linked-multi-query/linked-multi-query.test.ts b/test/validators/linked-multi-query/linked-multi-query.test.ts new file mode 100644 index 00000000..e4ae28e2 --- /dev/null +++ b/test/validators/linked-multi-query/linked-multi-query.test.ts @@ -0,0 +1,66 @@ +import { ethers } from "hardhat"; +import { loadFixture } from "@nomicfoundation/hardhat-toolbox/network-helpers"; +import { packZKProof } from "../../utils/packData"; +import { packLinkedMultiQueryValidatorParams } from "../../utils/validator-pack-utils"; +import { expect } from "chai"; + +describe("Test linkedMultiQuery10.circom", function () { + let validator; + let signer; + + async function deployContractsFixture() { + [signer] = await ethers.getSigners(); + const groth16Verifier = await ethers.deployContract("Groth16VerifierValidatorStub"); + const validator = await ethers.deployContract("LinkedMultiQueryValidator"); + await validator.initialize(await groth16Verifier.getAddress(), signer.address); + return { validator }; + } + + beforeEach(async () => { + ({ validator } = await loadFixture(deployContractsFixture)); + }); + + it("should work", async function () { + const linkId = "1"; + const merklized = "1"; + const operatorOutput1 = "1"; + const operatorOutput2 = "16"; + const queryHash1 = "100"; + const queryHash2 = "200"; + + const inputs = [linkId, merklized] + .concat([operatorOutput1, operatorOutput2]) + .concat(Array(8).fill("0")) + .concat([queryHash1, queryHash2]) + .concat(Array(8).fill("0")); + + const proof = packZKProof( + inputs, + ["0", "0"], + [ + ["0", "0"], + ["0", "0"], + ], + ["0", "0"], + ); + + const query = { + claimPathKey: [0, 0], + operator: [operatorOutput1, operatorOutput2], + slotIndex: [0, 0], + value: [ + [0, 0], + [0, 0], + ], + queryHash: [queryHash1, queryHash2], + circuitIds: [""], + groupID: 1, + verifierID: 1, + }; + + const data = packLinkedMultiQueryValidatorParams(query); + const stateAddress = ethers.ZeroAddress; + + expect(await validator.verify(proof, data, signer.address, stateAddress)).not.to.throw; + }); +}); diff --git a/test/verifier/verifer.test.ts b/test/verifier/verifer.test.ts new file mode 100644 index 00000000..85e25b20 --- /dev/null +++ b/test/verifier/verifer.test.ts @@ -0,0 +1,189 @@ +import { ethers } from "hardhat"; +import { beforeEach } from "mocha"; +import { DeployHelper } from "../../helpers/DeployHelper"; +import { expect } from "chai"; + +describe("Verifer tests", function () { + let sender: any; + let verifier, validator: any; + let request, paramsFromValidator: any; + + async function deployContractsFixture() { + const deployHelper = await DeployHelper.initialize(null, true); + const veriferLib = await ethers.deployContract("VerifierLib"); + const verifier = await ethers.deployContract("VerifierTestWrapper", [], { + libraries: { VerifierLib: await veriferLib.getAddress() }, + }); + + const { state } = await deployHelper.deployStateWithLibraries([], "Groth16VerifierStub"); + await verifier.initialize(await state.getAddress()); + + const authValidatorStub = await ethers.deployContract("AuthValidatorStub"); + const authType = { + authType: "stubAuth", + validator: await authValidatorStub.getAddress(), + params: "0x", + }; + await verifier.setAuthType(authType); + + const validator = await ethers.deployContract("RequestValidatorStub"); + return { verifier, validator }; + } + + describe("Single request tests", function () { + beforeEach(async function () { + [sender] = await ethers.getSigners(); + ({ verifier, validator } = await deployContractsFixture()); + + request = { + requestId: 1, + metadata: "0x", + validator: await validator.getAddress(), + params: "0x", + }; + + paramsFromValidator = { + groupID: 0, + verifierID: 0, + nullifierSessionID: 0, + }; + }); + + it("setRequests: nullifierSessionID may be not unique if EQUAL to 0", async function () { + await validator.stub_setRequestParams(paramsFromValidator); + + await verifier.setRequests([request]); + request.requestId = 2; + await verifier.setRequests([request]); + }); + + it("setRequests: nullifierSessionID must be unique if NOT EQUAL to 0", async function () { + paramsFromValidator.nullifierSessionID = 1; + await validator.stub_setRequestParams(paramsFromValidator); + + await verifier.setRequests([request]); + request.requestId = 2; + await expect(verifier.setRequests([request])) + .to.be.revertedWithCustomError(verifier, "NullifierSessionIDAlreadyExists") + .withArgs(1); + }); + + it("submitResponse: not repeated responseFields from validator", async function () { + await verifier.setRequests([request]); + await validator.stub_setVerifyResults([ + { + name: "someFieldName1", + value: 1, + }, + { + name: "someFieldName2", + value: 2, + }, + ]); + + const authResponse = { + authType: "stubAuth", + proof: "0x", + }; + const response = { + requestId: request.requestId, + proof: "0x", + metadata: "0x", + }; + const crossChainProofs = "0x"; + await verifier.submitResponse(authResponse, [response], crossChainProofs); + + const responseField1 = await verifier.getResponseFieldValue( + request.requestId, + sender, + "someFieldName1", + ); + expect(responseField1).to.be.equal(1); + const resonseField2 = await verifier.getResponseFieldValue( + request.requestId, + sender, + "someFieldName2", + ); + expect(resonseField2).to.be.equal(2); + }); + + it.skip("submitResponse: should throw if repeated responseFields from validator", async function () { + await verifier.setRequests([request]); + await validator.stub_setVerifyResults([ + { + name: "someFieldName1", + value: 1, + }, + { + name: "someFieldName1", + value: 1, + }, + ]); + + const authResponse = { + authType: "stubAuth", + proof: "0x", + }; + const response = { + requestId: request.requestId, + proof: "0x", + metadata: "0x", + }; + const crossChainProofs = "0x"; + await expect(verifier.submitResponse(authResponse, [response], crossChainProofs)) + .to.revertedWithCustomError(verifier, "ResponseFieldAlreadyExists") + .withArgs("someFieldName1"); + }); + + it.skip("submitResponse: userID in response fields should match auth userID", async function () { + await verifier.setRequests([request]); + + let userID = 1; // we assume that userID is hardcoded to 1 in the auth stub contract + await validator.stub_setVerifyResults([ + { + name: "userID", + value: userID, + }, + ]); + + const authResponse = { + authType: "stubAuth", + proof: "0x", + }; + const response = { + requestId: request.requestId, + proof: "0x", + metadata: "0x", + }; + const crossChainProofs = "0x"; + + await verifier.submitResponse(authResponse, [response], crossChainProofs); + + userID = 2; + await validator.stub_setVerifyResults([ + { + name: "userID", + value: userID, + }, + ]); + + await expect(verifier.submitResponse(authResponse, [response], crossChainProofs)) + .to.revertedWithCustomError(verifier, "UserIDMismatch") + .withArgs(1, 2); + }); + + it("submitResponse: a group should be formed by the groupID encoded in requests params", async function () { + // TODO: implement + }); + }); + + describe("Multi request tests", function () { + it("setMultiRequest", async function () { + // TODO check statuses of two different multiRequests pointing to the same requests + }); + + it("getStatus", async function () { + // TODO linkID should be equal to all requests in a group, otherwise multiRequest pointing to it returns false + }); + }); +}); From d51036838cf9df5962248ddc6eee6e1abdd7d6d2 Mon Sep 17 00:00:00 2001 From: daveroga Date: Wed, 15 Jan 2025 17:13:23 +0100 Subject: [PATCH 64/69] update tests with updated stub validator --- contracts/test-helpers/AuthValidatorStub.sol | 2 +- .../RequestValidatorAuthV2Stub.sol | 41 ----------- .../test-helpers/RequestValidatorStub.sol | 16 +++-- .../test-helpers/RequestValidatorV2Stub.sol | 43 ----------- .../test-helpers/RequestValidatorV3Stub.sol | 70 ------------------ .../test-helpers/RequestValidatorV3_2Stub.sol | 70 ------------------ .../universal-verifier-multi-query.test.ts | 71 ++++++++++++++++--- .../universal-verifier-submit-V2.test.ts | 10 ++- test/verifier/universal-verifier.test.ts | 8 ++- test/verifier/verifer.test.ts | 4 +- 10 files changed, 88 insertions(+), 247 deletions(-) delete mode 100644 contracts/test-helpers/RequestValidatorAuthV2Stub.sol delete mode 100644 contracts/test-helpers/RequestValidatorV2Stub.sol delete mode 100644 contracts/test-helpers/RequestValidatorV3Stub.sol delete mode 100644 contracts/test-helpers/RequestValidatorV3_2Stub.sol diff --git a/contracts/test-helpers/AuthValidatorStub.sol b/contracts/test-helpers/AuthValidatorStub.sol index 8ca0aa2a..a54bf676 100644 --- a/contracts/test-helpers/AuthValidatorStub.sol +++ b/contracts/test-helpers/AuthValidatorStub.sol @@ -9,7 +9,7 @@ import {IState} from "../interfaces/IState.sol"; * @dev AuthValidatorStub validator */ contract AuthValidatorStub is IAuthValidator, ERC165 { - string public constant VERSION = "1.0.0-mock"; + string public constant VERSION = "1.0.0-stub"; function version() public pure override returns (string memory) { return VERSION; diff --git a/contracts/test-helpers/RequestValidatorAuthV2Stub.sol b/contracts/test-helpers/RequestValidatorAuthV2Stub.sol deleted file mode 100644 index 0d03be66..00000000 --- a/contracts/test-helpers/RequestValidatorAuthV2Stub.sol +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.27; - -import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; -import {IRequestValidator} from "../interfaces/IRequestValidator.sol"; -import {IState} from "../interfaces/IState.sol"; - -/** - * @dev RequestValidatorAuthV2Stub validator - */ -contract RequestValidatorAuthV2Stub is IRequestValidator, ERC165 { - string public constant VERSION = "1.0.0-mock"; - - function version() public pure override returns (string memory) { - return VERSION; - } - - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return - interfaceId == type(IRequestValidator).interfaceId || - super.supportsInterface(interfaceId); - } - - function verify( - bytes calldata, - bytes calldata, - address, - IState - ) external pure override returns (IRequestValidator.ResponseField[] memory) { - IRequestValidator.ResponseField[] memory signals = new IRequestValidator.ResponseField[](1); - signals[0].name = "userID"; - signals[0].value = 1; - return signals; - } - - function getRequestParams( - bytes calldata - ) external pure override returns (IRequestValidator.RequestParams memory) { - return IRequestValidator.RequestParams({groupID: 0, verifierID: 0, nullifierSessionID: 0}); - } -} diff --git a/contracts/test-helpers/RequestValidatorStub.sol b/contracts/test-helpers/RequestValidatorStub.sol index 99bdb5e7..8381829f 100644 --- a/contracts/test-helpers/RequestValidatorStub.sol +++ b/contracts/test-helpers/RequestValidatorStub.sol @@ -6,12 +6,13 @@ import {IRequestValidator} from "../interfaces/IRequestValidator.sol"; import {IState} from "../interfaces/IState.sol"; /** - * @dev RequestValidatorV2Stub validator + * @dev RequestValidatorStub validator */ contract RequestValidatorStub is IRequestValidator, ERC165 { string public constant VERSION = "1.0.0-stub"; - IRequestValidator.RequestParams private requestParams; + mapping(bytes32 hashParams => IRequestValidator.RequestParams requestParams) + private requestParams; IRequestValidator.ResponseField[] private responseFields; function version() public pure override returns (string memory) { @@ -43,14 +44,17 @@ contract RequestValidatorStub is IRequestValidator, ERC165 { } function getRequestParams( - bytes calldata + bytes calldata params ) external view returns (IRequestValidator.RequestParams memory) { - return requestParams; + return requestParams[keccak256(params)]; } function stub_setRequestParams( - IRequestValidator.RequestParams calldata _requestParams + bytes[] calldata _params, + IRequestValidator.RequestParams[] calldata _requestParams ) external { - requestParams = _requestParams; + for (uint256 i = 0; i < _params.length; i++) { + requestParams[keccak256(_params[i])] = _requestParams[i]; + } } } diff --git a/contracts/test-helpers/RequestValidatorV2Stub.sol b/contracts/test-helpers/RequestValidatorV2Stub.sol deleted file mode 100644 index f7938dd3..00000000 --- a/contracts/test-helpers/RequestValidatorV2Stub.sol +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.27; - -import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; -import {IRequestValidator} from "../interfaces/IRequestValidator.sol"; -import {IState} from "../interfaces/IState.sol"; - -/** - * @dev RequestValidatorV2Stub validator - */ -contract RequestValidatorV2Stub is IRequestValidator, ERC165 { - string public constant VERSION = "1.0.0-mock"; - - function version() public pure override returns (string memory) { - return VERSION; - } - - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return - interfaceId == type(IRequestValidator).interfaceId || - super.supportsInterface(interfaceId); - } - - function verify( - bytes calldata, - bytes calldata, - address, - IState - ) external pure override returns (IRequestValidator.ResponseField[] memory) { - IRequestValidator.ResponseField[] memory signals = new IRequestValidator.ResponseField[](2); - signals[0].name = "userID"; - signals[0].value = 1; - signals[1].name = "issuerID"; - signals[1].value = 2; - return signals; - } - - function getRequestParams( - bytes calldata - ) external pure override returns (IRequestValidator.RequestParams memory) { - return IRequestValidator.RequestParams({groupID: 0, verifierID: 0, nullifierSessionID: 0}); - } -} diff --git a/contracts/test-helpers/RequestValidatorV3Stub.sol b/contracts/test-helpers/RequestValidatorV3Stub.sol deleted file mode 100644 index 9b50f67b..00000000 --- a/contracts/test-helpers/RequestValidatorV3Stub.sol +++ /dev/null @@ -1,70 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.27; - -import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; -import {IRequestValidator} from "../interfaces/IRequestValidator.sol"; -import {IState} from "../interfaces/IState.sol"; - -/** - * @dev RequestValidatorV3Stub validator - */ -contract RequestValidatorV3Stub is IRequestValidator, ERC165 { - string public constant VERSION = "1.0.0-mock"; - - struct CredentialAtomicQueryV3 { - uint256 schema; - uint256 claimPathKey; - uint256 operator; - uint256 slotIndex; - uint256[] value; - uint256 queryHash; - uint256[] allowedIssuers; - string[] circuitIds; - bool skipClaimRevocationCheck; - uint256 groupID; - uint256 nullifierSessionID; - uint256 proofType; - uint256 verifierID; - } - - function version() public pure override returns (string memory) { - return VERSION; - } - - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return - interfaceId == type(IRequestValidator).interfaceId || - super.supportsInterface(interfaceId); - } - - function verify( - bytes calldata, - bytes calldata, - address, - IState - ) external pure override returns (IRequestValidator.ResponseField[] memory) { - IRequestValidator.ResponseField[] memory signals = new IRequestValidator.ResponseField[](3); - signals[0].name = "userID"; - signals[0].value = 1; - signals[1].name = "issuerID"; - signals[1].value = 2; - signals[2].name = "linkID"; - signals[2].value = 3; - return signals; - } - - function getRequestParams( - bytes calldata params - ) external pure override returns (IRequestValidator.RequestParams memory) { - CredentialAtomicQueryV3 memory credAtomicQuery = abi.decode( - params, - (CredentialAtomicQueryV3) - ); - return - IRequestValidator.RequestParams({ - groupID: credAtomicQuery.groupID, - verifierID: credAtomicQuery.verifierID, - nullifierSessionID: credAtomicQuery.nullifierSessionID - }); - } -} diff --git a/contracts/test-helpers/RequestValidatorV3_2Stub.sol b/contracts/test-helpers/RequestValidatorV3_2Stub.sol deleted file mode 100644 index 94a6d8ce..00000000 --- a/contracts/test-helpers/RequestValidatorV3_2Stub.sol +++ /dev/null @@ -1,70 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.27; - -import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; -import {IRequestValidator} from "../interfaces/IRequestValidator.sol"; -import {IState} from "../interfaces/IState.sol"; - -/** - * @dev RequestValidatorV3Stub validator - */ -contract RequestValidatorV3_2Stub is IRequestValidator, ERC165 { - string public constant VERSION = "1.0.0-mock"; - - struct CredentialAtomicQueryV3 { - uint256 schema; - uint256 claimPathKey; - uint256 operator; - uint256 slotIndex; - uint256[] value; - uint256 queryHash; - uint256[] allowedIssuers; - string[] circuitIds; - bool skipClaimRevocationCheck; - uint256 groupID; - uint256 nullifierSessionID; - uint256 proofType; - uint256 verifierID; - } - - function version() public pure override returns (string memory) { - return VERSION; - } - - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return - interfaceId == type(IRequestValidator).interfaceId || - super.supportsInterface(interfaceId); - } - - function verify( - bytes calldata, - bytes calldata, - address, - IState - ) external pure override returns (IRequestValidator.ResponseField[] memory) { - IRequestValidator.ResponseField[] memory signals = new IRequestValidator.ResponseField[](3); - signals[0].name = "userID"; - signals[0].value = 1; - signals[1].name = "issuerID"; - signals[1].value = 2; - signals[2].name = "linkID"; - signals[2].value = 4; - return signals; - } - - function getRequestParams( - bytes calldata params - ) external pure override returns (IRequestValidator.RequestParams memory) { - CredentialAtomicQueryV3 memory credAtomicQuery = abi.decode( - params, - (CredentialAtomicQueryV3) - ); - return - IRequestValidator.RequestParams({ - groupID: credAtomicQuery.groupID, - verifierID: credAtomicQuery.verifierID, - nullifierSessionID: credAtomicQuery.nullifierSessionID - }); - } -} diff --git a/test/verifier/universal-verifier-multi-query.test.ts b/test/verifier/universal-verifier-multi-query.test.ts index b8550b27..9c7fe12b 100644 --- a/test/verifier/universal-verifier-multi-query.test.ts +++ b/test/verifier/universal-verifier-multi-query.test.ts @@ -64,13 +64,6 @@ describe("Universal Verifier Multi-request", function () { }, ]; - const authStorageFields = [ - { - name: "userID", - value: 1, - }, - ]; - async function deployContractsFixture() { [signer] = await ethers.getSigners(); signerAddress = await signer.getAddress(); @@ -151,8 +144,19 @@ describe("Universal Verifier Multi-request", function () { verifierID: verifierId, }; - v3Validator = await deployHelper.deployValidatorStub("RequestValidatorV3Stub"); - v3_2Validator = await deployHelper.deployValidatorStub("RequestValidatorV3_2Stub"); + v3Validator = await deployHelper.deployValidatorStub("RequestValidatorStub"); + await v3Validator.stub_setVerifyResults([ + { name: "userID", value: 1 }, + { name: "issuerID", value: 2 }, + { name: "linkID", value: 3 }, + ]); + v3_2Validator = await deployHelper.deployValidatorStub("RequestValidatorStub"); + await v3_2Validator.stub_setVerifyResults([ + { name: "userID", value: 1 }, + { name: "issuerID", value: 2 }, + { name: "linkID", value: 4 }, + ]); + authV2Validator = await deployHelper.deployValidatorStub("AuthValidatorStub"); await verifier.addValidatorToWhitelist(await v3Validator.getAddress()); @@ -182,6 +186,17 @@ describe("Universal Verifier Multi-request", function () { const authType = "authV2"; const params = packV3ValidatorParams(requestQuery1); + await v3Validator.stub_setRequestParams( + [params], + [ + { + groupID: requestQuery1.groupID, + verifierID: requestQuery1.verifierID, + nullifierSessionID: requestQuery1.nullifierSessionID, + }, + ], + ); + const txSetRequests = await verifier.setRequests([ { requestId: requestId, @@ -290,6 +305,22 @@ describe("Universal Verifier Multi-request", function () { const paramsRequest2 = packV3ValidatorParams(requestQuery2); const paramsRequest3 = packV3ValidatorParams(requestQuery3); + await v3Validator.stub_setRequestParams( + [paramsRequest2, paramsRequest3], + [ + { + groupID: requestQuery2.groupID, + verifierID: requestQuery2.verifierID, + nullifierSessionID: requestQuery2.nullifierSessionID, + }, + { + groupID: requestQuery3.groupID, + verifierID: requestQuery3.verifierID, + nullifierSessionID: requestQuery3.nullifierSessionID, + }, + ], + ); + const txSetRequests = await verifier.setRequests([ { requestId: requestId2, @@ -398,6 +429,28 @@ describe("Universal Verifier Multi-request", function () { const paramsRequest2 = packV3ValidatorParams(requestQuery2); const paramsRequest3 = packV3ValidatorParams(requestQuery3); + await v3Validator.stub_setRequestParams( + [paramsRequest2], + [ + { + groupID: requestQuery2.groupID, + verifierID: requestQuery2.verifierID, + nullifierSessionID: requestQuery2.nullifierSessionID, + }, + ], + ); + + await v3_2Validator.stub_setRequestParams( + [paramsRequest3], + [ + { + groupID: requestQuery3.groupID, + verifierID: requestQuery3.verifierID, + nullifierSessionID: requestQuery3.nullifierSessionID, + }, + ], + ); + const txSetRequests = await verifier.setRequests([ { requestId: requestId2, diff --git a/test/verifier/universal-verifier-submit-V2.test.ts b/test/verifier/universal-verifier-submit-V2.test.ts index 7fd48535..d1950a27 100644 --- a/test/verifier/universal-verifier-submit-V2.test.ts +++ b/test/verifier/universal-verifier-submit-V2.test.ts @@ -103,7 +103,11 @@ describe("Universal Verifier submitResponse SigV2 validators", function () { await verifierLib.getAddress(), ); - validatorStub = await deployHelper.deployValidatorStub("RequestValidatorV2Stub"); + validatorStub = await deployHelper.deployValidatorStub("RequestValidatorStub"); + await validatorStub.stub_setVerifyResults([ + { name: "userID", value: 1 }, + { name: "issuerID", value: 2 }, + ]); sig = validatorStub; await verifier.addValidatorToWhitelist(await sig.getAddress()); @@ -190,7 +194,7 @@ describe("Universal Verifier submitResponse SigV2 validators", function () { const status = await verifier.getRequestStatus(signerAddress, requestId); expect(status.isVerified).to.be.true; - expect(status.validatorVersion).to.be.equal("1.0.0-mock"); + expect(status.validatorVersion).to.be.equal("1.0.0-stub"); expect(status.timestamp).to.be.equal(txResTimestamp); await expect(verifier.getRequestStatus(signerAddress, nonExistingRequestId)).to.be.rejectedWith( @@ -221,7 +225,7 @@ describe("Universal Verifier submitResponse SigV2 validators", function () { for (const requestId of requestIdsMulti) { const status = await verifier.getRequestStatus(signerAddress, requestId); expect(status.isVerified).to.be.true; - expect(status.validatorVersion).to.be.equal("1.0.0-mock"); + expect(status.validatorVersion).to.be.equal("1.0.0-stub"); expect(status.timestamp).to.be.equal(txResTimestampMuti); await checkStorageFields(verifier, BigInt(requestId), storageFields); } diff --git a/test/verifier/universal-verifier.test.ts b/test/verifier/universal-verifier.test.ts index 685e7b90..3cb895aa 100644 --- a/test/verifier/universal-verifier.test.ts +++ b/test/verifier/universal-verifier.test.ts @@ -49,7 +49,11 @@ describe("Universal Verifier MTP & SIG validators", function () { const { state: stateContract } = await deployHelper.deployStateWithLibraries(["0x0112"]); const verifierLib = await deployHelper.deployVerifierLib(); - const sigV2Validator = await deployHelper.deployValidatorStub("RequestValidatorV2Stub"); + const sigV2Validator = await deployHelper.deployValidatorStub("RequestValidatorStub"); + await sigV2Validator.stub_setVerifyResults([ + { name: "userID", value: 1 }, + { name: "issuerID", value: 2 }, + ]); const universalVerifier: any = await deployHelper.deployUniversalVerifier( ethSigner, @@ -192,7 +196,7 @@ describe("Universal Verifier MTP & SIG validators", function () { const status = await verifier.getRequestStatus(signerAddress, requestId); expect(status.isVerified).to.be.true; - expect(status.validatorVersion).to.be.equal("1.0.0-mock"); + expect(status.validatorVersion).to.be.equal("1.0.0-stub"); expect(status.timestamp).to.be.equal(txResTimestamp); await expect(verifier.getRequestStatus(signerAddress, nonExistingRequestId)).to.be.rejectedWith( diff --git a/test/verifier/verifer.test.ts b/test/verifier/verifer.test.ts index 85e25b20..9be066f2 100644 --- a/test/verifier/verifer.test.ts +++ b/test/verifier/verifer.test.ts @@ -50,7 +50,7 @@ describe("Verifer tests", function () { }); it("setRequests: nullifierSessionID may be not unique if EQUAL to 0", async function () { - await validator.stub_setRequestParams(paramsFromValidator); + await validator.stub_setRequestParams([request.params], [paramsFromValidator]); await verifier.setRequests([request]); request.requestId = 2; @@ -59,7 +59,7 @@ describe("Verifer tests", function () { it("setRequests: nullifierSessionID must be unique if NOT EQUAL to 0", async function () { paramsFromValidator.nullifierSessionID = 1; - await validator.stub_setRequestParams(paramsFromValidator); + await validator.stub_setRequestParams([request.params], [paramsFromValidator]); await verifier.setRequests([request]); request.requestId = 2; From 296bb68a000549689285c6fe5b0bdfb1f56986f3 Mon Sep 17 00:00:00 2001 From: daveroga Date: Thu, 16 Jan 2025 08:40:16 +0100 Subject: [PATCH 65/69] check requestId correctness --- .../validators/AuthV2Validator_forAuth.sol | 2 +- contracts/verifiers/Verifier.sol | 22 +++++++++++++++++++ test/validators/authv2/index.ts | 1 - .../universal-verifier-submit-V2.test.ts | 6 ++--- test/verifier/universal-verifier.v3.test.ts | 4 +++- test/verifier/verifer.test.ts | 17 ++++++++++++++ 6 files changed, 46 insertions(+), 6 deletions(-) diff --git a/contracts/validators/AuthV2Validator_forAuth.sol b/contracts/validators/AuthV2Validator_forAuth.sol index 8105470b..6be989ef 100644 --- a/contracts/validators/AuthV2Validator_forAuth.sol +++ b/contracts/validators/AuthV2Validator_forAuth.sol @@ -134,7 +134,7 @@ contract AuthV2Validator_forAuth is Ownable2StepUpgradeable, IAuthValidator, ERC PubSignals memory pubSignals = parsePubSignals(inputs); _checkGistRoot(pubSignals.userID, pubSignals.gistRoot, state); - _checkChallenge(pubSignals.challenge, expectedNonce); // expectedNonce + _checkChallenge(pubSignals.challenge, expectedNonce); _verifyZKP(inputs, a, b, c); return pubSignals.userID; } diff --git a/contracts/verifiers/Verifier.sol b/contracts/verifiers/Verifier.sol index 00b8b096..c2dc3de7 100644 --- a/contracts/verifiers/Verifier.sol +++ b/contracts/verifiers/Verifier.sol @@ -28,6 +28,8 @@ error MetadataNotSupportedYet(); error GroupMustHaveAtLeastTwoRequests(uint256 groupID); error NullifierSessionIDAlreadyExists(uint256 nullifierSessionID); error VerifierIDIsNotValid(uint256 requestVerifierID, uint256 expectedVerifierID); +error RequestIdNotValid(); +error RequestIdUsesReservedBytes(); abstract contract Verifier is IVerifier, ContextUpgradeable { /// @dev Key to retrieve the linkID from the proof storage @@ -285,6 +287,8 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { // 2. Set requests checking groups and nullifierSessionID uniqueness for (uint256 i = 0; i < requests.length; i++) { + _checkRequestIdCorrectness(requests[i].requestId); + _checkNullifierSessionIdUniqueness(requests[i]); _checkVerifierID(requests[i]); @@ -316,6 +320,24 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { } } + function _getRequestType(uint256 requestId) internal pure returns (uint8) { + // 0x0000000000000000 - prefix for old uint64 requests + // 0x0000000000000001 - prefix for keccak256 cut to fit in the remaining 192 bits + return uint8(requestId >> 248); + } + + function _checkRequestIdCorrectness(uint256 requestId) internal pure { + // 1. Check prefix + uint8 requestType = _getRequestType(requestId); + if (requestType >= 2) { + revert RequestIdNotValid(); + } + // 2. Check reserved bytes + if (((requestId << 8) >> 200) > 0) { + revert RequestIdUsesReservedBytes(); + } + } + function _checkNullifierSessionIdUniqueness(IVerifier.Request calldata request) internal { VerifierStorage storage s = _getVerifierStorage(); uint256 nullifierSessionID = request diff --git a/test/validators/authv2/index.ts b/test/validators/authv2/index.ts index b25644e0..c8f45285 100644 --- a/test/validators/authv2/index.ts +++ b/test/validators/authv2/index.ts @@ -27,7 +27,6 @@ const testCases: any[] = [ { name: "Validation of Gist root not found", sender: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", - challenge: "0x00", stateTransitions: [ require("../common-data/issuer_from_genesis_state_to_first_transition_v3.json"), ], diff --git a/test/verifier/universal-verifier-submit-V2.test.ts b/test/verifier/universal-verifier-submit-V2.test.ts index d1950a27..00dac230 100644 --- a/test/verifier/universal-verifier-submit-V2.test.ts +++ b/test/verifier/universal-verifier-submit-V2.test.ts @@ -197,9 +197,9 @@ describe("Universal Verifier submitResponse SigV2 validators", function () { expect(status.validatorVersion).to.be.equal("1.0.0-stub"); expect(status.timestamp).to.be.equal(txResTimestamp); - await expect(verifier.getRequestStatus(signerAddress, nonExistingRequestId)).to.be.rejectedWith( - `RequestIdNotFound(${nonExistingRequestId})`, - ); + await expect(verifier.getRequestStatus(signerAddress, nonExistingRequestId)) + .to.be.revertedWithCustomError(verifier, "RequestIdNotFound") + .withArgs(nonExistingRequestId); const requestIdsMulti = requestIds.slice(1, 3); const txMulti = await verifier.submitResponse( diff --git a/test/verifier/universal-verifier.v3.test.ts b/test/verifier/universal-verifier.v3.test.ts index 3a17126d..85033ab0 100644 --- a/test/verifier/universal-verifier.v3.test.ts +++ b/test/verifier/universal-verifier.v3.test.ts @@ -559,7 +559,9 @@ describe("Universal Verifier V3 validator", function () { params: params2, }, ]), - ).to.be.rejectedWith(`NullifierSessionIDAlreadyExists(${query2.nullifierSessionID})`); + ) + .to.be.revertedWithCustomError(verifier, "NullifierSessionIDAlreadyExists") + .withArgs(query2.nullifierSessionID); }); it("Test set request fails with VerifierIDIsNotValid", async () => { diff --git a/test/verifier/verifer.test.ts b/test/verifier/verifer.test.ts index 9be066f2..8871f1ce 100644 --- a/test/verifier/verifer.test.ts +++ b/test/verifier/verifer.test.ts @@ -68,6 +68,23 @@ describe("Verifer tests", function () { .withArgs(1); }); + it("setRequests: requestId should be valid and not using reserved bytes", async function () { + await validator.stub_setRequestParams([request.params], [paramsFromValidator]); + + request.requestId = BigInt(2 ** 256) - BigInt(1); + + await expect(verifier.setRequests([request])).to.be.revertedWithCustomError( + verifier, + "RequestIdNotValid", + ); + + request.requestId = BigInt(2 ** 248) + BigInt(2 ** 247); + await expect(verifier.setRequests([request])).to.be.revertedWithCustomError( + verifier, + "RequestIdUsesReservedBytes", + ); + }); + it("submitResponse: not repeated responseFields from validator", async function () { await verifier.setRequests([request]); await validator.stub_setVerifyResults([ From 78610dd1be6cdfe13b81231573b3a139249470ac Mon Sep 17 00:00:00 2001 From: daveroga Date: Thu, 16 Jan 2025 10:08:12 +0100 Subject: [PATCH 66/69] add logic from skip tests in verifier test --- contracts/verifiers/Verifier.sol | 45 +++++++++++++++++++++++++++++--- test/verifier/verifer.test.ts | 8 +++--- 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/contracts/verifiers/Verifier.sol b/contracts/verifiers/Verifier.sol index c2dc3de7..860d2f5e 100644 --- a/contracts/verifiers/Verifier.sol +++ b/contracts/verifiers/Verifier.sol @@ -24,12 +24,14 @@ error LinkIDNotTheSameForGroupedRequests(); error UserIDNotFound(uint256 userID); error UserIDNotLinkedToAddress(uint256 userID, address userAddress); error UserNotAuthenticated(); +error UserIDMismatch(uint256 userIDFromAuth, uint256 userIDFromResponse); error MetadataNotSupportedYet(); error GroupMustHaveAtLeastTwoRequests(uint256 groupID); error NullifierSessionIDAlreadyExists(uint256 nullifierSessionID); error VerifierIDIsNotValid(uint256 requestVerifierID, uint256 expectedVerifierID); error RequestIdNotValid(); error RequestIdUsesReservedBytes(); +error ResponseFieldAlreadyExists(string responseFieldName); abstract contract Verifier is IVerifier, ContextUpgradeable { /// @dev Key to retrieve the linkID from the proof storage @@ -462,13 +464,13 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { // TODO: Get userID from responses that has userID informed (LinkedMultiquery doesn't have userID) - uint256 userIDFromReponse; + uint256 userIDFromAuthResponse; AuthTypeData storage authTypeData = $._authMethods[authResponse.authType]; bytes32 expectedNonce = keccak256(abi.encode(sender, responses)); // Authenticate user - userIDFromReponse = authTypeData.validator.verify( + userIDFromAuthResponse = authTypeData.validator.verify( authResponse.proof, authTypeData.params, sender, @@ -476,11 +478,11 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { expectedNonce ); - if (userIDFromReponse == 0) { + if (userIDFromAuthResponse == 0) { revert UserNotAuthenticated(); } - // 3. Verify all the responses, write proof results (under the userID key from the auth of the user), + // 3. Verify all the responses, check userID from signals and write proof results, // emit events (existing logic) for (uint256 i = 0; i < responses.length; i++) { IVerifier.Response memory response = responses[i]; @@ -493,6 +495,12 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { $._state ); + // Check if userID from authResponse is the same as the one in the signals + _checkUserIDMatch(userIDFromAuthResponse, signals); + + // Check that response fields are not repeated + _checkSinals(signals); + $.writeProofResults(response.requestId, sender, signals); if (response.metadata.length > 0) { @@ -501,6 +509,35 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { } } + function _checkUserIDMatch( + uint256 userIDFromAuthResponse, + IRequestValidator.ResponseField[] memory signals + ) internal pure { + for (uint256 j = 0; j < signals.length; j++) { + if ( + keccak256(abi.encodePacked(signals[j].name)) == + keccak256(abi.encodePacked("userID")) + ) { + if (userIDFromAuthResponse != signals[j].value) { + revert UserIDMismatch(userIDFromAuthResponse, signals[j].value); + } + } + } + } + + function _checkSinals(IRequestValidator.ResponseField[] memory signals) internal pure { + for (uint256 j = 0; j < signals.length; j++) { + for (uint256 k = j + 1; k < signals.length; k++) { + if ( + keccak256(abi.encodePacked(signals[j].name)) == + keccak256(abi.encodePacked(signals[k].name)) + ) { + revert ResponseFieldAlreadyExists(signals[j].name); + } + } + } + } + /** * @dev Updates a request * @param request The request data diff --git a/test/verifier/verifer.test.ts b/test/verifier/verifer.test.ts index 8871f1ce..87804cbb 100644 --- a/test/verifier/verifer.test.ts +++ b/test/verifier/verifer.test.ts @@ -71,14 +71,14 @@ describe("Verifer tests", function () { it("setRequests: requestId should be valid and not using reserved bytes", async function () { await validator.stub_setRequestParams([request.params], [paramsFromValidator]); - request.requestId = BigInt(2 ** 256) - BigInt(1); + request.requestId = BigInt(2 ** 256) - BigInt(1); // requestId without valid prefix 0x0000000000000000 or 0x0000000000000001 await expect(verifier.setRequests([request])).to.be.revertedWithCustomError( verifier, "RequestIdNotValid", ); - request.requestId = BigInt(2 ** 248) + BigInt(2 ** 247); + request.requestId = BigInt(2 ** 247); // requestId uses reserved bytes await expect(verifier.setRequests([request])).to.be.revertedWithCustomError( verifier, "RequestIdUsesReservedBytes", @@ -124,7 +124,7 @@ describe("Verifer tests", function () { expect(resonseField2).to.be.equal(2); }); - it.skip("submitResponse: should throw if repeated responseFields from validator", async function () { + it("submitResponse: should throw if repeated responseFields from validator", async function () { await verifier.setRequests([request]); await validator.stub_setVerifyResults([ { @@ -152,7 +152,7 @@ describe("Verifer tests", function () { .withArgs("someFieldName1"); }); - it.skip("submitResponse: userID in response fields should match auth userID", async function () { + it("submitResponse: userID in response fields should match auth userID", async function () { await verifier.setRequests([request]); let userID = 1; // we assume that userID is hardcoded to 1 in the auth stub contract From 1641d15ad2f40098aac3507aa34802990c57616d Mon Sep 17 00:00:00 2001 From: daveroga Date: Thu, 16 Jan 2025 10:29:03 +0100 Subject: [PATCH 67/69] add auth validator stub set verify results method --- contracts/test-helpers/AuthValidatorStub.sol | 10 ++++-- .../universal-verifier-multi-query.test.ts | 1 + .../universal-verifier-submit-V2.test.ts | 1 + test/verifier/universal-verifier.test.ts | 1 + test/verifier/universal-verifier.v3.test.ts | 31 ++++++++++--------- test/verifier/verifer.test.ts | 2 ++ 6 files changed, 30 insertions(+), 16 deletions(-) diff --git a/contracts/test-helpers/AuthValidatorStub.sol b/contracts/test-helpers/AuthValidatorStub.sol index a54bf676..aabea0de 100644 --- a/contracts/test-helpers/AuthValidatorStub.sol +++ b/contracts/test-helpers/AuthValidatorStub.sol @@ -11,6 +11,8 @@ import {IState} from "../interfaces/IState.sol"; contract AuthValidatorStub is IAuthValidator, ERC165 { string public constant VERSION = "1.0.0-stub"; + uint256 private userID; + function version() public pure override returns (string memory) { return VERSION; } @@ -26,7 +28,11 @@ contract AuthValidatorStub is IAuthValidator, ERC165 { address, IState, bytes32 - ) external pure override returns (uint256 userID) { - return 1; + ) external view override returns (uint256) { + return userID; + } + + function stub_setVerifyResults(uint256 _userID) external { + userID = _userID; } } diff --git a/test/verifier/universal-verifier-multi-query.test.ts b/test/verifier/universal-verifier-multi-query.test.ts index 9c7fe12b..05be61dd 100644 --- a/test/verifier/universal-verifier-multi-query.test.ts +++ b/test/verifier/universal-verifier-multi-query.test.ts @@ -158,6 +158,7 @@ describe("Universal Verifier Multi-request", function () { ]); authV2Validator = await deployHelper.deployValidatorStub("AuthValidatorStub"); + await authV2Validator.stub_setVerifyResults(1); await verifier.addValidatorToWhitelist(await v3Validator.getAddress()); await verifier.addValidatorToWhitelist(await v3_2Validator.getAddress()); diff --git a/test/verifier/universal-verifier-submit-V2.test.ts b/test/verifier/universal-verifier-submit-V2.test.ts index 00dac230..c322f1d5 100644 --- a/test/verifier/universal-verifier-submit-V2.test.ts +++ b/test/verifier/universal-verifier-submit-V2.test.ts @@ -114,6 +114,7 @@ describe("Universal Verifier submitResponse SigV2 validators", function () { await verifier.connect(); const authV2Validator = await deployHelper.deployValidatorStub("AuthValidatorStub"); + await authV2Validator.stub_setVerifyResults(1); await verifier.setAuthType({ authType: authType, diff --git a/test/verifier/universal-verifier.test.ts b/test/verifier/universal-verifier.test.ts index 3cb895aa..0aff3627 100644 --- a/test/verifier/universal-verifier.test.ts +++ b/test/verifier/universal-verifier.test.ts @@ -65,6 +65,7 @@ describe("Universal Verifier MTP & SIG validators", function () { await universalVerifier.connect(); const authV2Validator = await deployHelper.deployValidatorStub("AuthValidatorStub"); + await authV2Validator.stub_setVerifyResults(1); return { ethSigner, diff --git a/test/verifier/universal-verifier.v3.test.ts b/test/verifier/universal-verifier.v3.test.ts index 85033ab0..923f163d 100644 --- a/test/verifier/universal-verifier.v3.test.ts +++ b/test/verifier/universal-verifier.v3.test.ts @@ -14,6 +14,8 @@ import stateTransition13 from "../validators/common-data/issuer_from_first_state import { packZKProof } from "../utils/packData"; import { TEN_YEARS } from "../../helpers/constants"; +const userID = 23013175891893363078841232968022302880776034013620341061794940968520126978n; + const storageFields = [ { name: "issuerID", @@ -21,7 +23,7 @@ const storageFields = [ }, { name: "userID", - value: 23013175891893363078841232968022302880776034013620341061794940968520126978n, + value: userID, }, { name: "timestamp", value: 1642074362n }, { @@ -97,6 +99,7 @@ describe("Universal Verifier V3 validator", function () { await universalVerifier.connect(); const authV2Validator = await deployHelper.deployValidatorStub("AuthValidatorStub"); + await authV2Validator.stub_setVerifyResults(userID); return { stateContract, v3Validator, authV2Validator, universalVerifier }; }; @@ -186,22 +189,22 @@ describe("Universal Verifier V3 validator", function () { const metadatas = "0x"; - await expect( - verifier.submitResponse( + //await expect( + await verifier.submitResponse( + { + authType: authType, + proof, + }, + [ { - authType: authType, + requestId, proof, + metadata: metadatas, }, - [ - { - requestId, - proof, - metadata: metadatas, - }, - ], - crossChainProofs, - ), - ).not.to.be.rejected; + ], + crossChainProofs, + ); + //).not.to.be.rejected; await checkStorageFields(verifier, BigInt(requestId), storageFields); }); diff --git a/test/verifier/verifer.test.ts b/test/verifier/verifer.test.ts index 87804cbb..475bce73 100644 --- a/test/verifier/verifer.test.ts +++ b/test/verifier/verifer.test.ts @@ -19,6 +19,8 @@ describe("Verifer tests", function () { await verifier.initialize(await state.getAddress()); const authValidatorStub = await ethers.deployContract("AuthValidatorStub"); + await authValidatorStub.stub_setVerifyResults(1); + const authType = { authType: "stubAuth", validator: await authValidatorStub.getAddress(), From 9f33dec127b160b645d45e5b8803afa50db0a4ba Mon Sep 17 00:00:00 2001 From: daveroga Date: Thu, 16 Jan 2025 15:05:24 +0100 Subject: [PATCH 68/69] idType for verifier and some updates to verifier test --- contracts/interfaces/IVerifier.sol | 19 +--- contracts/verifiers/Verifier.sol | 10 ++- test/verifier/verifer.test.ts | 139 ++++++++++++++++++++++++++++- 3 files changed, 143 insertions(+), 25 deletions(-) diff --git a/contracts/interfaces/IVerifier.sol b/contracts/interfaces/IVerifier.sol index 0c923cc0..d3009cd7 100644 --- a/contracts/interfaces/IVerifier.sol +++ b/contracts/interfaces/IVerifier.sol @@ -54,15 +54,6 @@ interface IVerifier { address creator; uint256 verifierId; } - /** - * @dev GroupedRequests. Structure for auth proof status. - * @param groupId Group id of the requests. - * @param requests Requests of the group. - */ - struct GroupedRequests { - uint256 groupId; - Request[] requests; - } /** * @dev Response. Structure for response. @@ -75,15 +66,7 @@ interface IVerifier { bytes proof; bytes metadata; } - /** - * @dev GroupedResponses. Structure for grouped responses. - * @param groupId Group id of the responses. - * @param responses Responses of the group. - */ - struct GroupedResponses { - uint256 groupId; - Response[] responses; - } + /** * @dev AuthResponse. Structure for auth response. * @param authType Auth type of the proof response. diff --git a/contracts/verifiers/Verifier.sol b/contracts/verifiers/Verifier.sol index 860d2f5e..cb73bffd 100644 --- a/contracts/verifiers/Verifier.sol +++ b/contracts/verifiers/Verifier.sol @@ -79,6 +79,7 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { bytes32 internal constant VerifierStorageLocation = 0x11369addde4aae8af30dcf56fa25ad3d864848d3201d1e9197f8b4da18a51a00; + bytes2 internal constant VerifierIdType = 0x01A1; using VerifierLib for VerifierStorage; /** @@ -161,10 +162,11 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { function __Verifier_init_unchained(IState state) internal onlyInitializing { _setState(state); - // initial calculation of verifierID from contract address and default id type from State contract - VerifierStorage storage s = _getVerifierStorage(); - bytes2 idType = s._state.getDefaultIdType(); - uint256 calculatedVerifierID = GenesisUtils.calcIdFromEthAddress(idType, address(this)); + // initial calculation of verifierID from contract address and verifier id type defined + uint256 calculatedVerifierID = GenesisUtils.calcIdFromEthAddress( + VerifierIdType, + address(this) + ); _setVerifierID(calculatedVerifierID); } diff --git a/test/verifier/verifer.test.ts b/test/verifier/verifer.test.ts index 475bce73..11627c46 100644 --- a/test/verifier/verifer.test.ts +++ b/test/verifier/verifer.test.ts @@ -7,8 +7,15 @@ describe("Verifer tests", function () { let sender: any; let verifier, validator: any; let request, paramsFromValidator: any; + let multiRequest: any; + let signer: any; + let signerAddress: string; + let verifierId: any; async function deployContractsFixture() { + [signer] = await ethers.getSigners(); + signerAddress = await signer.getAddress(); + const deployHelper = await DeployHelper.initialize(null, true); const veriferLib = await ethers.deployContract("VerifierLib"); const verifier = await ethers.deployContract("VerifierTestWrapper", [], { @@ -37,6 +44,8 @@ describe("Verifer tests", function () { [sender] = await ethers.getSigners(); ({ verifier, validator } = await deployContractsFixture()); + verifierId = await verifier.getVerifierID(); + request = { requestId: 1, metadata: "0x", @@ -49,6 +58,13 @@ describe("Verifer tests", function () { verifierID: 0, nullifierSessionID: 0, }; + + multiRequest = { + multiRequestId: 1, + requestIds: [request.requestId], + groupIds: [], + metadata: "0x", + }; }); it("setRequests: nullifierSessionID may be not unique if EQUAL to 0", async function () { @@ -87,6 +103,33 @@ describe("Verifer tests", function () { ); }); + it("getRequest: requestId should exist", async function () { + let requestObject = verifier.getRequest(request.requestId); + expect(requestObject) + .to.be.revertedWithCustomError(verifier, "RequestIdNotFound") + .withArgs(request.requestId); + + paramsFromValidator.verifierID = verifierId; + await validator.stub_setRequestParams([request.params], [paramsFromValidator]); + await verifier.setRequests([request]); + requestObject = await verifier.getRequest(request.requestId); + + expect(requestObject.requestId).to.be.equal(request.requestId); + expect(requestObject.metadata).to.be.equal(request.metadata); + expect(requestObject.validator).to.be.equal(request.validator); + expect(requestObject.params).to.be.equal(request.params); + expect(requestObject.creator).to.be.equal(await signer.getAddress()); + expect(requestObject.verifierId).to.be.equal(verifierId); + }); + + it("getRequestStatus: requestId should exist", async function () { + const nonExistingRequestId = 2; + + await expect(verifier.getRequestStatus(signerAddress, nonExistingRequestId)) + .to.be.revertedWithCustomError(verifier, "RequestIdNotFound") + .withArgs(nonExistingRequestId); + }); + it("submitResponse: not repeated responseFields from validator", async function () { await verifier.setRequests([request]); await validator.stub_setVerifyResults([ @@ -197,11 +240,101 @@ describe("Verifer tests", function () { }); describe("Multi request tests", function () { - it("setMultiRequest", async function () { - // TODO check statuses of two different multiRequests pointing to the same requests + before(async function () { + [sender] = await ethers.getSigners(); + ({ verifier, validator } = await deployContractsFixture()); + + request = { + requestId: 1, + metadata: "0x", + validator: await validator.getAddress(), + params: "0x", + }; + + paramsFromValidator = { + groupID: 0, + verifierID: 0, + nullifierSessionID: 0, + }; + + multiRequest = { + multiRequestId: 1, + requestIds: [request.requestId], + groupIds: [], + metadata: "0x", + }; + }); + + it("setMultiRequest: multi request should not exist", async function () { + await verifier.setRequests([request]); + + await verifier.setMultiRequest(multiRequest); + await expect(verifier.setMultiRequest(multiRequest)) + .revertedWithCustomError(verifier, "MultiRequestIdAlreadyExists") + .withArgs(multiRequest.multiRequestId); + }); + + it("setMultiRequest: check statuses of two different multiRequests pointing to the same requests", async function () { + const multiRequest2 = { ...multiRequest, multiRequestId: 2 }; + await verifier.setMultiRequest(multiRequest2); + + let isMultiRequestVerified = await verifier.isMultiRequestVerified( + multiRequest.multiRequestId, + signerAddress, + ); + expect(isMultiRequestVerified).to.be.false; + + let isMultiRequest2Verified = await verifier.isMultiRequestVerified( + multiRequest2.multiRequestId, + signerAddress, + ); + expect(isMultiRequest2Verified).to.be.false; + + const userID = 1; // we assume that userID is hardcoded to 1 in the auth stub contract + await validator.stub_setVerifyResults([ + { + name: "userID", + value: userID, + }, + ]); + + const authResponse = { + authType: "stubAuth", + proof: "0x", + }; + const response = { + requestId: request.requestId, + proof: "0x", + metadata: "0x", + }; + const crossChainProofs = "0x"; + + await verifier.submitResponse(authResponse, [response], crossChainProofs); + + //check statuses of two different multiRequests pointing to the same requests after response + isMultiRequestVerified = await verifier.isMultiRequestVerified( + multiRequest.multiRequestId, + signerAddress, + ); + expect(isMultiRequestVerified).to.be.true; + + isMultiRequest2Verified = await verifier.isMultiRequestVerified( + multiRequest2.multiRequestId, + signerAddress, + ); + expect(isMultiRequest2Verified).to.be.true; + }); + + it("getMultiRequestStatus: multi request should exist", async function () { + const nonExistingMultiRequestId = 5; + await expect(verifier.getMultiRequestStatus(nonExistingMultiRequestId, signerAddress)) + .to.be.revertedWithCustomError(verifier, "MultiRequestIdNotFound") + .withArgs(nonExistingMultiRequestId); + await expect(verifier.getMultiRequestStatus(multiRequest.multiRequestId, signerAddress)).not + .to.be.rejected; }); - it("getStatus", async function () { + it.skip("getMultiRequestStatus: linkID should be equal to all requests in a group, otherwise multiRequest pointing to it returns false", async function () { // TODO linkID should be equal to all requests in a group, otherwise multiRequest pointing to it returns false }); }); From faf099723024a69d19d7efa1370c1a3723248f25 Mon Sep 17 00:00:00 2001 From: daveroga Date: Thu, 16 Jan 2025 19:22:08 +0100 Subject: [PATCH 69/69] fix enumerable and check existence in proof response fields --- contracts/interfaces/IVerifier.sol | 12 ++++++++++ contracts/lib/VerifierLib.sol | 13 +++++++++- contracts/verifiers/Verifier.sol | 35 +++++++++++++-------------- test/verifier/verifer.test.ts | 38 ++++++++++++++++++++++-------- 4 files changed, 68 insertions(+), 30 deletions(-) diff --git a/contracts/interfaces/IVerifier.sol b/contracts/interfaces/IVerifier.sol index d3009cd7..473601a6 100644 --- a/contracts/interfaces/IVerifier.sol +++ b/contracts/interfaces/IVerifier.sol @@ -143,6 +143,18 @@ interface IVerifier { */ function getRequestsCount() external view returns (uint256); + /** + * @dev Get the group of requests count. + * @return Group of requests count. + */ + function getGroupsCount() external view returns (uint256); + + /** + * @dev Get the group of requests. + * @return Group of requests. + */ + function getGroupedRequests(uint256 groupID) external view returns (uint256[] memory); + /** * @dev Checks if a request ID exists * @param requestId The ID of the request diff --git a/contracts/lib/VerifierLib.sol b/contracts/lib/VerifierLib.sol index 2e39ad5c..dd637f4f 100644 --- a/contracts/lib/VerifierLib.sol +++ b/contracts/lib/VerifierLib.sol @@ -5,6 +5,8 @@ import {Verifier} from "../verifiers/Verifier.sol"; import {IRequestValidator} from "../interfaces/IRequestValidator.sol"; import {IAuthValidator} from "../interfaces/IAuthValidator.sol"; +error ResponseFieldAlreadyExists(string responseFieldName); + /** * @title VerifierLib * @dev A library for writing proof results. @@ -22,7 +24,10 @@ library VerifierLib { uint256 blockTimestamp; // This empty reserved space is put in place to allow future versions // (see https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#storage-gaps) - uint256[46] __gap; + string[] keys; + // introduce artificial shift + 1 to avoid 0 index + mapping(string key => uint256 keyIndex) keyIndexes; + uint256[44] __gap; } /** @@ -54,6 +59,12 @@ library VerifierLib { // We only keep only 1 proof now without history. Prepared for the future if needed. for (uint256 i = 0; i < responseFields.length; i++) { proof.storageFields[responseFields[i].name] = responseFields[i].value; + if (proof.keyIndexes[responseFields[i].name] == 0) { + proof.keys.push(responseFields[i].name); + proof.keyIndexes[responseFields[i].name] = proof.keys.length; + } else { + revert ResponseFieldAlreadyExists(responseFields[i].name); + } } proof.isVerified = true; diff --git a/contracts/verifiers/Verifier.sol b/contracts/verifiers/Verifier.sol index cb73bffd..a98773be 100644 --- a/contracts/verifiers/Verifier.sol +++ b/contracts/verifiers/Verifier.sol @@ -31,7 +31,6 @@ error NullifierSessionIDAlreadyExists(uint256 nullifierSessionID); error VerifierIDIsNotValid(uint256 requestVerifierID, uint256 expectedVerifierID); error RequestIdNotValid(); error RequestIdUsesReservedBytes(); -error ResponseFieldAlreadyExists(string responseFieldName); abstract contract Verifier is IVerifier, ContextUpgradeable { /// @dev Key to retrieve the linkID from the proof storage @@ -464,8 +463,6 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { // 1. Process crossChainProofs $._state.processCrossChainProofs(crossChainProofs); - // TODO: Get userID from responses that has userID informed (LinkedMultiquery doesn't have userID) - uint256 userIDFromAuthResponse; AuthTypeData storage authTypeData = $._authMethods[authResponse.authType]; @@ -500,9 +497,6 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { // Check if userID from authResponse is the same as the one in the signals _checkUserIDMatch(userIDFromAuthResponse, signals); - // Check that response fields are not repeated - _checkSinals(signals); - $.writeProofResults(response.requestId, sender, signals); if (response.metadata.length > 0) { @@ -527,19 +521,6 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { } } - function _checkSinals(IRequestValidator.ResponseField[] memory signals) internal pure { - for (uint256 j = 0; j < signals.length; j++) { - for (uint256 k = j + 1; k < signals.length; k++) { - if ( - keccak256(abi.encodePacked(signals[j].name)) == - keccak256(abi.encodePacked(signals[k].name)) - ) { - revert ResponseFieldAlreadyExists(signals[j].name); - } - } - } - } - /** * @dev Updates a request * @param request The request data @@ -812,6 +793,22 @@ abstract contract Verifier is IVerifier, ContextUpgradeable { return _getVerifierStorage()._requestIds.length; } + /** + * @dev Get the group of requests count. + * @return Group of requests count. + */ + function getGroupsCount() public view returns (uint256) { + return _getVerifierStorage()._groupIds.length; + } + + /** + * @dev Get the group of requests. + * @return Group of requests. + */ + function getGroupedRequests(uint256 groupID) public view returns (uint256[] memory) { + return _getVerifierStorage()._groupedRequests[groupID]; + } + /** * @dev Gets the address of the state contract linked to the verifier * @return address State contract address diff --git a/test/verifier/verifer.test.ts b/test/verifier/verifer.test.ts index 11627c46..97870537 100644 --- a/test/verifier/verifer.test.ts +++ b/test/verifier/verifer.test.ts @@ -5,7 +5,7 @@ import { expect } from "chai"; describe("Verifer tests", function () { let sender: any; - let verifier, validator: any; + let verifier, verifierLib, validator: any; let request, paramsFromValidator: any; let multiRequest: any; let signer: any; @@ -17,9 +17,9 @@ describe("Verifer tests", function () { signerAddress = await signer.getAddress(); const deployHelper = await DeployHelper.initialize(null, true); - const veriferLib = await ethers.deployContract("VerifierLib"); + const verifierLib = await ethers.deployContract("VerifierLib"); const verifier = await ethers.deployContract("VerifierTestWrapper", [], { - libraries: { VerifierLib: await veriferLib.getAddress() }, + libraries: { VerifierLib: await verifierLib.getAddress() }, }); const { state } = await deployHelper.deployStateWithLibraries([], "Groth16VerifierStub"); @@ -36,13 +36,13 @@ describe("Verifer tests", function () { await verifier.setAuthType(authType); const validator = await ethers.deployContract("RequestValidatorStub"); - return { verifier, validator }; + return { verifier, verifierLib, validator }; } describe("Single request tests", function () { beforeEach(async function () { [sender] = await ethers.getSigners(); - ({ verifier, validator } = await deployContractsFixture()); + ({ verifier, verifierLib, validator } = await deployContractsFixture()); verifierId = await verifier.getVerifierID(); @@ -103,6 +103,28 @@ describe("Verifer tests", function () { ); }); + it("setRequests: a group should be formed by the groupID encoded in requests params", async function () { + const groupID = 1; + const groupRequest1 = { ...request, groupID }; + const groupRequest2 = { ...request, requestId: 2, groupID }; + paramsFromValidator.groupID = 1; + await validator.stub_setRequestParams([groupRequest1.params], [paramsFromValidator]); + await validator.stub_setRequestParams([groupRequest2.params], [paramsFromValidator]); + + let groupsCount = await verifier.getGroupsCount(); + expect(groupsCount).to.be.equal(0); + + await verifier.setRequests([groupRequest1, groupRequest2]); + + groupsCount = await verifier.getGroupsCount(); + expect(groupsCount).to.be.equal(1); + + const groupedRequests = await verifier.getGroupedRequests(groupID); + expect(groupedRequests.length).to.be.equal(2); + expect(groupedRequests[0]).to.be.equal(groupRequest1.requestId); + expect(groupedRequests[1]).to.be.equal(groupRequest2.requestId); + }); + it("getRequest: requestId should exist", async function () { let requestObject = verifier.getRequest(request.requestId); expect(requestObject) @@ -193,7 +215,7 @@ describe("Verifer tests", function () { }; const crossChainProofs = "0x"; await expect(verifier.submitResponse(authResponse, [response], crossChainProofs)) - .to.revertedWithCustomError(verifier, "ResponseFieldAlreadyExists") + .to.revertedWithCustomError(verifierLib, "ResponseFieldAlreadyExists") .withArgs("someFieldName1"); }); @@ -233,10 +255,6 @@ describe("Verifer tests", function () { .to.revertedWithCustomError(verifier, "UserIDMismatch") .withArgs(1, 2); }); - - it("submitResponse: a group should be formed by the groupID encoded in requests params", async function () { - // TODO: implement - }); }); describe("Multi request tests", function () {